New MPI mitigation schemes // Duobinary // Start of FTN schemes
This commit is contained in:
735
Classes/04_DSP/Equalizer/EQ.m
Normal file
735
Classes/04_DSP/Equalizer/EQ.m
Normal file
@@ -0,0 +1,735 @@
|
||||
classdef EQ
|
||||
%EQ Summary of this class goes here
|
||||
% Detailed explanation goes here
|
||||
|
||||
properties
|
||||
Ne %Number of feed forward coefficients (1st, 2nd and 3rd order)
|
||||
Nb %Number of decision feedback coefficients (1st, 2nd and 3rd order)
|
||||
K %Number of samples per symbol
|
||||
delay %Delay of incoming signal
|
||||
training_length %Number of training symbols
|
||||
training_loops %Number of loops through sequence for training mode
|
||||
ideal_dfe %Error free DFE decisions
|
||||
|
||||
DB_aim %Aim at duobinary output sequence
|
||||
|
||||
M %Order of the PAM constellation (only relevant in case of DB aim)
|
||||
|
||||
FFEmu %mu parameter for FFE part in training mode (0 means normalized LMS)
|
||||
DFEmu % mu parameter for DFE part in training mode
|
||||
dd_loops % Number of loops through sequence for DD mode
|
||||
DDmu % mu parameters for DD mode (individual value for each order)
|
||||
DCmu % mu parameter for the dc tap
|
||||
|
||||
l1act %Activate/deactive l1 regularization
|
||||
rho %Parameter for speed of coeff shrinking
|
||||
epsilon %Reciprocal value of the magnitude of the coeff to converge to zero (1st,2nd,3rd order)
|
||||
thres %Theshold for neglecting coefficienties (1st,2nd,3rd order)
|
||||
static_act %Activate/deactive static coefficient reduction
|
||||
|
||||
mode2nd %0: no reduction | 1: polynomial | 2: restricted to interval
|
||||
len_2nd %length of the interval (only for 2nd order mode = 2)
|
||||
|
||||
mode3rd %0: no reduction | 1: polynomial | 2: restricted to interval
|
||||
len_3rd %length of the interval (only for 3rd order mode = 3/4)
|
||||
|
||||
plottrain
|
||||
plotfinal
|
||||
load_decisions
|
||||
|
||||
save_taps
|
||||
|
||||
|
||||
%during simulation
|
||||
k0
|
||||
b
|
||||
b2
|
||||
b3
|
||||
e
|
||||
e2
|
||||
e3
|
||||
|
||||
coeff_number
|
||||
constellation_in
|
||||
|
||||
error_log
|
||||
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = EQ(options)
|
||||
%EQ Construct an instance of this class
|
||||
% Detailed explanation goes here
|
||||
arguments(Input)
|
||||
options.Ne = [10 0 0] %Number of feed forward coefficients (1st, 2nd and 3rd order)
|
||||
options.Nb = [10 0 0]%Number of decision feedback coefficients (1st, 2nd and 3rd order)
|
||||
options.K = 1 %Number of samples per symbol
|
||||
options.delay = 0 %Delay of incoming signal
|
||||
options.training_length = 1024 %Number of training symbols
|
||||
options.training_loops = 1 %Number of loops through sequence for training mode
|
||||
options.ideal_dfe = 0 %Error free DFE decisions
|
||||
|
||||
options.DB_aim %Aim at duobinary output sequence
|
||||
|
||||
options.M = 1 %Order of the PAM constellation (only relevant in case of DB aim)
|
||||
|
||||
options.FFEmu = 0 %mu parameter for FFE part in training mode (0 means normalized LMS)
|
||||
options.DFEmu = 0.005 % mu parameter for DFE part in training mode
|
||||
options.dd_loops = 1% Number of loops through sequence for DD mode
|
||||
options.DDmu = [0.0004 0.0004 0.0004 0.0004 ] % mu parameters for DD mode (individual value for each order)
|
||||
options.DCmu = 0.005 % mu parameter for the dc tap
|
||||
|
||||
options.l1act = 0 %Activate/deactive l1 regularization
|
||||
options.rho = 5e-4%Parameter for speed of coeff shrinking
|
||||
options.epsilon = [10 100 1000] %Reciprocal value of the magnitude of the coeff to converge to zero (1st,2nd,3rd order)
|
||||
options.thres = [5e-3 4e-3 5e-4]%Theshold for neglecting coefficienties (1st,2nd,3rd order)
|
||||
options.static_act = 0 %Activate/deactive static coefficient reduction
|
||||
|
||||
options.mode2nd = 1%0: no reduction | 1: polynomial | 2: restricted to interval
|
||||
options.len_2nd = 1 %length of the interval (only for 2nd order mode = 2)
|
||||
|
||||
options.mode3rd = 1%0: no reduction | 1: polynomial | 2: restricted to interval
|
||||
options.len_3rd = 1%length of the interval (only for 3rd order mode = 3/4)
|
||||
|
||||
options.plottrain = 0
|
||||
options.plotfinal = 0
|
||||
options.load_decisions = 0
|
||||
options.save_taps = 0
|
||||
end
|
||||
|
||||
fn = fieldnames(options);
|
||||
for n = 1:numel(fn)
|
||||
obj.(fn{n}) = options.(fn{n});
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function [signalclass_out,error_log] = process(obj,signalclass_in, reference_signalclass_in)
|
||||
|
||||
% actual processing of the signal (steps 1. - 3.)
|
||||
[signalclass_in.signal,error_log] = obj.process_(signalclass_in.signal', reference_signalclass_in.signal');
|
||||
|
||||
signalclass_in.signal = signalclass_in.signal';
|
||||
% append to logbook
|
||||
lbdesc = ['EQ '];
|
||||
signalclass_in = signalclass_in.logbookentry(lbdesc);
|
||||
|
||||
% write to output
|
||||
signalclass_out = signalclass_in;
|
||||
|
||||
end
|
||||
|
||||
function [yout,error_log] = process_(obj,data_in,ref_in)
|
||||
%METHOD1 Summary of this method goes here
|
||||
% Detailed explanation goes here
|
||||
|
||||
if obj.DB_aim
|
||||
ref_DB = zeros(size(ref_in));
|
||||
for k = 1:length(ref_in)
|
||||
if k == 1
|
||||
ref_DB(k) = ref_in(k);
|
||||
else
|
||||
ref_DB(k) = ref_in(k) + ref_in(k-1);
|
||||
end
|
||||
end
|
||||
ref_in = ref_DB;
|
||||
end
|
||||
|
||||
ref = [zeros(1,obj.Nb(1)-1) ref_in zeros(1,obj.Nb(1))];
|
||||
|
||||
if isreal(ref)
|
||||
cplx = 0;
|
||||
else
|
||||
cplx = 1;
|
||||
end
|
||||
|
||||
if obj.static_act
|
||||
obj.mode2nd = obj.mode2nd + 1;
|
||||
obj.mode3rd = obj.mode3rd + 1;
|
||||
else
|
||||
obj.mode2nd = 1;
|
||||
obj.mode3rd = 1;
|
||||
end
|
||||
|
||||
if obj.mode2nd == 1
|
||||
N2 = (obj.Ne(2)*(obj.Ne(2)+1))/2; % Number of coefficients for second order
|
||||
elseif obj.mode2nd == 2
|
||||
N2 = obj.Ne(2);
|
||||
elseif obj.mode2nd == 3
|
||||
N2 = (obj.len_2nd+1)*(2*obj.Ne(2)-obj.len_2nd)/2;
|
||||
elseif obj.mode2nd == 4
|
||||
N2 = (ceil(obj.Ne(2)/2)+1)*(2*obj.Ne(2)-ceil(obj.Ne(2)/2))/2;
|
||||
end
|
||||
|
||||
if obj.mode3rd == 1
|
||||
if cplx
|
||||
N3 = obj.Ne(3)^2*(obj.Ne(3)+1)/2;
|
||||
else
|
||||
N3 = obj.Ne(3)*(obj.Ne(3)+1)*(obj.Ne(3)+2)/6; % Number of coefficients for third order
|
||||
end
|
||||
elseif obj.mode3rd == 2
|
||||
N3 = obj.Ne(3);
|
||||
elseif obj.mode3rd == 3
|
||||
N3 = obj.Ne(3)^2;
|
||||
elseif obj.mode3rd == 4
|
||||
N3 = round(1/6*(obj.len_3rd+1)*(obj.len_3rd+2)*(3*obj.Ne(3)-2*obj.len_3rd));
|
||||
elseif obj.mode3rd == 5
|
||||
N3 = 2*obj.Ne(3)*obj.len_3rd-obj.len_3rd*(obj.len_3rd+1)+obj.Ne(3);
|
||||
end
|
||||
|
||||
Nb2 = (obj.Nb(2)*(obj.Nb(2)+1))/2;
|
||||
Nb3 = obj.Nb(3)*(obj.Nb(3)+1)*(obj.Nb(3)+2)/6;
|
||||
|
||||
data_in = data_in/sqrt(mean(abs(data_in).^2)); % power normalization of input sequence
|
||||
|
||||
if obj.FFEmu == 0
|
||||
norm_fac2 = sqrt(mean(abs(data_in.^2).^2)); % power normalization for second and third order terms
|
||||
norm_fac3 = sqrt(mean(abs(data_in.^3).^2)); % (not necessary, but seems to be more stable if applied --> same as different mu values for linear and nl terms)
|
||||
else
|
||||
norm_fac2 = 1;
|
||||
norm_fac3 = 1;
|
||||
end
|
||||
|
||||
norm_fac_DFE2 = sqrt(mean(abs(ref_in.^2).^2)); % same for DFE input (reference)
|
||||
norm_fac_DFE3 = sqrt(mean(abs(ref_in.^3).^2));
|
||||
|
||||
data = [zeros(1,floor(obj.Ne(1)/2)) data_in zeros(1,obj.Ne(1))];
|
||||
|
||||
delta_2 = round((obj.Ne(1)-obj.Ne(2))/2);
|
||||
delta_3 = round((obj.Ne(1)-obj.Ne(3))/2);
|
||||
|
||||
delta_DFE2 = 1;%round((obj.Nb-obj.Nb(2))/2);
|
||||
delta_DFE3 = 1;%round((obj.Nb-obj.Nb(3))/2);
|
||||
|
||||
% calculate the indices for the combination of second and third order symbols
|
||||
% - done in advance because it's the same for each iteration, so time
|
||||
% can be saved
|
||||
|
||||
[ind_mat_2nd,ind_mat_3rd] = obj.calc_ind(obj.Ne(2),N2,obj.Ne(3),N3,obj.mode2nd,obj.mode3rd,obj.len_2nd,obj.len_3rd,cplx);
|
||||
|
||||
[ind_mat_DFE_2nd,ind_mat_DFE_3rd] = obj.calc_DFE_ind(obj.Nb(2),Nb2,obj.Nb(3),Nb3);
|
||||
|
||||
if obj.l1act
|
||||
epsilon_ = diag([ones(1,obj.Ne(1))*obj.epsilon(1) ones(1,N2)*obj.epsilon(2) ones(1,N3)*obj.epsilon(3)]);
|
||||
end
|
||||
|
||||
obj.k0 = obj.delay; % input delay compared to training sequence
|
||||
error_log = [];
|
||||
|
||||
if 1 % obj.active
|
||||
%% Calculation of the filter coefficients in training based LMS mode
|
||||
|
||||
e_ = zeros(obj.Ne(1)+N2+N3,1); % initialization of filter coefficients
|
||||
|
||||
b_ = zeros(obj.Nb(1)+Nb2+Nb3,1);
|
||||
|
||||
e_dc = mean(data_in); % initilaization of the dc tap with the mean value of the whole data
|
||||
|
||||
for trainloops = 1:obj.training_loops
|
||||
cnt = 1;
|
||||
m = obj.k0+1; % starting symbol index at the delay compared to the training sequence
|
||||
|
||||
|
||||
|
||||
% n => index in rx data sequence
|
||||
%Step From: Oversampling(=2) * Startdelay + 1
|
||||
%Step Width: Oversampling(=2)
|
||||
%Step To: Oversampling(=2) * Training Length
|
||||
for n = obj.K*obj.k0+1:obj.K:obj.K*obj.training_length
|
||||
|
||||
% m => index in reference sequence
|
||||
m = m+1;
|
||||
|
||||
%
|
||||
%dc_ = mean(data(obj.Ne(1)+n+(obj.K-1):-1:n+obj.K).');
|
||||
% cut symbols from rx data sequence
|
||||
X_1 = data(obj.Ne(1)+n+(obj.K-1):-1:n+obj.K).';
|
||||
|
||||
[X_2,X_3] = obj.calc_nl_vecs(X_1,ind_mat_2nd,ind_mat_3rd,norm_fac2,norm_fac3,delta_2,delta_3,cplx);
|
||||
|
||||
input_vec = [X_1;X_2;X_3];
|
||||
|
||||
% cut symbols from desired data sequence (correct symbols in training mode)
|
||||
D_1 = ref(obj.Nb(1)-obj.k0+m-2:-1:m-obj.k0-1).';
|
||||
|
||||
[D_2,D_3] = obj.calc_nl_vecs(D_1,ind_mat_DFE_2nd,ind_mat_DFE_3rd,norm_fac_DFE2,norm_fac_DFE3,delta_DFE2,delta_DFE3,cplx);
|
||||
|
||||
reference_vec = [D_1;D_2;D_3];
|
||||
|
||||
e_ffe = e_.'*input_vec;
|
||||
e_dfe = b_.'*reference_vec;
|
||||
|
||||
error = e_dc + e_ffe - e_dfe - ref_in(m-obj.k0);
|
||||
|
||||
%error = e_dc + e_.'*input_vec - b_.'*reference_vec - ref_in(m-obj.k0);
|
||||
|
||||
if real(obj.FFEmu)
|
||||
if obj.l1act
|
||||
sgn_e = e_;
|
||||
sgn_e(e_~=0) = e_(e_~=0)./abs(e_(e_~=0));
|
||||
e_ = e_ - obj.rho*sgn_e./(1+epsilon_*abs(e_)) - error*input_vec*obj.FFEmu;
|
||||
else
|
||||
e_ = e_ - error*conj(input_vec)*obj.FFEmu; %classic LMS gradient decay
|
||||
end
|
||||
else
|
||||
if obj.l1act
|
||||
sgn_e = e_;
|
||||
sgn_e(e_~=0) = e_(e_~=0)./abs(e_(e_~=0));
|
||||
e_ = e_ - obj.rho*sgn_e./(1+epsilon_*abs(e_)) - error*input_vec/(input_vec.'*input_vec);
|
||||
else
|
||||
e_ = e_ - error*input_vec/(input_vec.'*input_vec);
|
||||
end
|
||||
end
|
||||
|
||||
e_dc = e_dc - obj.DCmu*error;
|
||||
|
||||
obj.error_log.e_ffe(cnt,trainloops) = e_ffe;
|
||||
obj.error_log.e_dfe(cnt,trainloops) = e_dfe;
|
||||
obj.error_log.e_(cnt,trainloops) = error;
|
||||
|
||||
cnt = cnt+1;
|
||||
if obj.Nb(1) > 0
|
||||
b_ = b_ + obj.DFEmu*error*reference_vec; % Seems like normalized DFE has worse performance
|
||||
end
|
||||
|
||||
% figure(111);stem((e_),'Markersize',2);ylim([-1 1]);title('FFE Filter Taps');
|
||||
%
|
||||
% figure(222);
|
||||
% stem(input_vec);ylim([-3 3]);
|
||||
% hold on;
|
||||
% stem(reference_vec);ylim([-3 3]);
|
||||
% yline(error,'LineWidth',2); title('Input Vector');
|
||||
% hold off
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
%%
|
||||
% Plot the intermediate coefficients after training mode
|
||||
obj.b = b_(1:obj.Nb(1));
|
||||
obj.b2 = b_(obj.Nb(1)+1:obj.Nb(1)+Nb2);
|
||||
obj.b3 = b_(obj.Nb(1)+Nb2+1:end);
|
||||
obj.e = e_(1:obj.Ne(1));
|
||||
obj.e2 = e_(obj.Ne(1)+1:obj.Ne(1)+N2);
|
||||
obj.e3 = e_(obj.Ne(1)+N2+1:end);
|
||||
|
||||
if obj.plottrain
|
||||
figure(8052)
|
||||
sgtitle('Training Coeff')
|
||||
subplot(2,3,1); stem((obj.e),'Markersize',2);
|
||||
title('FFE coeff linear')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,2); stem(obj.e2,'Markersize',2);
|
||||
title('FFE coeff nl 2nd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,3); stem(obj.e3,'Markersize',2);
|
||||
title('FFE coeff nl 3rd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,4);stem(obj.b,'Markersize',2);
|
||||
title('DFE coeff linear')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,5);stem(obj.b2,'Markersize',2);
|
||||
title('DFE coeff nl 2nd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,6);stem(obj.b3,'Markersize',2);
|
||||
title('DFE coeff nl 3rd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
%set(gcf,'Position',[200 500 700 400])
|
||||
end
|
||||
|
||||
|
||||
if obj.l1act
|
||||
neg_lin = find(abs(obj.e) < obj.thres(1));
|
||||
neg_2nd = find(abs(obj.e2) < obj.thres(2));
|
||||
neg_3rd = find(abs(obj.e3) < obj.thres(3));
|
||||
|
||||
neg = [neg_lin;neg_2nd+obj.Ne(1);neg_3rd+obj.Ne(1)+N2]; % indices of the neglected coefficients
|
||||
|
||||
rel_lin = find(abs(obj.e) >= obj.thres(1));
|
||||
rel_2nd = find(abs(obj.e2) >= obj.thres(2));
|
||||
rel_3rd = find(abs(obj.e3) >= obj.thres(3));
|
||||
|
||||
obj.coeff_number = length(rel_lin)+2*length(rel_2nd)+3*length(rel_3rd);
|
||||
|
||||
rel = [rel_lin;rel_2nd+obj.Ne(1);rel_3rd+obj.Ne(1)+N2]; % indices of the relevant coefficients
|
||||
e_(neg) = 0;
|
||||
|
||||
ind_mat_2nd(neg_2nd,:) = [];
|
||||
|
||||
ind_mat_3rd(neg_3rd,:) = [];
|
||||
end
|
||||
|
||||
%% decision directed mode
|
||||
if ~obj.DB_aim
|
||||
constellation_in_ = unique(ref_in); % getting the symbol constellation from reference data
|
||||
else
|
||||
if obj.M == 2
|
||||
constellation_in_ = [-3 -2 -1 0 1 2 3]/sqrt(5)*2;
|
||||
elseif obj.M == 2.5
|
||||
constellation_in_ = [-5 -4 -3 -2 -1 0 1 2 3 4 5]/sqrt(10)*2;
|
||||
elseif obj.M == 3
|
||||
constellation_in_ = [-7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7]/sqrt(21)*2;
|
||||
else
|
||||
constellation_in_ = unique(ref_in);
|
||||
end
|
||||
end
|
||||
|
||||
obj.constellation_in = constellation_in_;
|
||||
|
||||
if obj.l1act
|
||||
coeff = [e_(rel);b_]; % combine FFE and DFE coefficient vectors for DD mode
|
||||
else
|
||||
coeff = [e_;b_];
|
||||
end
|
||||
|
||||
for dd_loop = 1:obj.dd_loops
|
||||
cnt = 1;
|
||||
m = 0;
|
||||
output_vec = zeros(1,floor(length(data_in)/obj.K)); % initilaization of the output vector
|
||||
dd_DFE = zeros(obj.Nb(1),1);
|
||||
D_2 = zeros(Nb2,1);
|
||||
D_3 = zeros(Nb3,1);
|
||||
|
||||
if all(obj.DDmu == obj.DDmu(1))
|
||||
mu_mat = obj.DDmu(1);
|
||||
else
|
||||
if obj.l1act
|
||||
mu_mat = diag([ones(1,length(rel_lin))*obj.DDmu(1) ones(1,length(rel_2nd))*obj.DDmu(2) ones(1,length(rel_3rd))*obj.DDmu(3) ones(1,obj.Nb)*obj.DDmu(4)]);
|
||||
else
|
||||
mu_mat = diag([ones(1,obj.Ne(1))*obj.DDmu(1) ones(1,N2)*obj.DDmu(2) ones(1,N3)*obj.DDmu(3) ones(1,obj.Nb(1)+Nb2+Nb3)*obj.DDmu(4)]);
|
||||
end
|
||||
end
|
||||
|
||||
if obj.load_decisions
|
||||
pathn = evalin('base','modeldir');
|
||||
temp = load([pathn, 'MLSE_out', '.mat']) ;
|
||||
%eval(['dd_out_vals = temp.', 'a', ';']) ;
|
||||
dd_out_vals=temp.a;
|
||||
dd_out = zeros(size(data_in));
|
||||
dd_out(1:2:length(data_in)) = dd_out_vals;
|
||||
else
|
||||
dd_out = zeros(size(data_in));
|
||||
end
|
||||
|
||||
for k = 1:obj.K:length(data_in)
|
||||
m=m+1; % Symbol index
|
||||
|
||||
X_1 = data(obj.Ne(1)+k-1:-1:k).';
|
||||
|
||||
[X_2,X_3] = obj.calc_nl_vecs(X_1,ind_mat_2nd,ind_mat_3rd,norm_fac2,norm_fac3,delta_2,delta_3,cplx);
|
||||
|
||||
if obj.l1act
|
||||
input_vec = [X_1(rel_lin);X_2;X_3;-dd_DFE;-D_2;-D_3];
|
||||
else
|
||||
input_vec = [X_1;X_2;X_3;-dd_DFE;-D_2;-D_3];
|
||||
end
|
||||
|
||||
output_vec(m) = e_dc + input_vec.'*coeff;
|
||||
|
||||
if ~obj.load_decisions
|
||||
[~,dd_idx] = min(abs(output_vec(m) - constellation_in_)); % decision for closest constellation point
|
||||
dd_out(k) = constellation_in_(dd_idx);
|
||||
end
|
||||
|
||||
|
||||
if obj.Nb(1) > 0
|
||||
dd_DFE(2:end) = dd_DFE(1:end-1);
|
||||
dd_DFE(1) = dd_out(k);
|
||||
|
||||
if obj.ideal_dfe && m > obj.k0
|
||||
dd_DFE(1) = ref_in(m-obj.k0);
|
||||
end
|
||||
[D_2,D_3] = obj.calc_nl_vecs(dd_DFE,ind_mat_DFE_2nd,ind_mat_DFE_3rd,norm_fac_DFE2,norm_fac_DFE3,delta_DFE2,delta_DFE3,cplx);
|
||||
end
|
||||
% if dd_loop ~= 21
|
||||
error = output_vec(m) - dd_out(k);
|
||||
% else
|
||||
% error = 0;
|
||||
% end
|
||||
|
||||
coeff = coeff - mu_mat*error*conj(input_vec);
|
||||
% e_save(:,save_ind) = coeff;
|
||||
% save_ind = save_ind+1;
|
||||
|
||||
if 1%mu_mat ~= 0
|
||||
e_dc = e_dc - obj.DCmu*error;
|
||||
error_log(cnt,dd_loop) = e_dc;
|
||||
cnt = cnt+1;
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
%figure(2023);plot(error_log(:,1))
|
||||
% shifting the output sequence by k0 symbols
|
||||
yout = (circshift(output_vec.',-(obj.k0))).'; %(circshift(dd_out.',-(obj.k0))).';
|
||||
|
||||
e_ = coeff(1:end-obj.Nb(1)-Nb2-Nb3);
|
||||
b_ = coeff(end-obj.Nb(1)-Nb2-Nb3+1:end);
|
||||
|
||||
if obj.l1act
|
||||
obj.e = e_(1:length(rel_lin));
|
||||
obj.e2 = e_(length(rel_lin)+1:length(rel_lin)+length(rel_2nd));
|
||||
obj.e3 = e_(length(rel_lin)+length(rel_2nd)+1:end);
|
||||
else
|
||||
obj.e = e_(1:obj.Ne(1));
|
||||
obj.e2 = e_(obj.Ne(1)+1:obj.Ne(1)+N2);
|
||||
obj.e3 = e_(obj.Ne(1)+N2+1:end);
|
||||
end
|
||||
obj.b = b_(1:obj.Nb(1));
|
||||
obj.b2 = b_(obj.Nb(1)+1:obj.Nb(1)+Nb2);
|
||||
obj.b3 = b_(obj.Nb(1)+Nb2+1:end);
|
||||
|
||||
% plot the final coefficients after DD mode
|
||||
if obj.plotfinal
|
||||
figure(8054)
|
||||
if obj.l1act
|
||||
sgtitle('Final Coeff')
|
||||
subplot(2,3,1); stem(rel_lin,obj.e,'Markersize',2);
|
||||
title('FFE coeff linear')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,2); stem(rel_2nd,obj.e2,'Markersize',2);
|
||||
title('FFE coeff nl 2nd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,3); stem(rel_3rd,obj.e3,'Markersize',2);
|
||||
title('FFE coeff nl 3rd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,4);stem(obj.b,'Markersize',2);
|
||||
title('DFE coeff linear')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,5);stem(obj.b2,'Markersize',2);
|
||||
title('DFE coeff nl 2nd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,6);stem(obj.b3,'Markersize',2);
|
||||
title('DFE coeff nl 3rd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
else
|
||||
sgtitle('Final Coeff')
|
||||
subplot(2,3,1); stem(obj.e/max(e_),'Markersize',2);
|
||||
title('FFE coeff linear')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,2); stem(obj.e2,'Markersize',2);
|
||||
title('FFE coeff nl 2nd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,3); stem(obj.e3,'Markersize',2);
|
||||
title('FFE coeff nl 3rd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,4);stem(obj.b,'Markersize',2);
|
||||
title('DFE coeff linear')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,5);stem(obj.b2,'Markersize',2);
|
||||
title('DFE coeff nl 2nd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,6);stem(obj.b3,'Markersize',2);
|
||||
title('DFE coeff nl 3rd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
end
|
||||
set(gcf,'Position',[1000 500 700 400])
|
||||
end
|
||||
|
||||
% save frequency response to the work space
|
||||
if obj.save_taps
|
||||
% save the FFE coefficients to the work space
|
||||
% pathn = evalin('base','modeldir');
|
||||
% eval([obj.field_ffe, ' = obj.e ;']) ;
|
||||
% eval([obj.field_dfe, ' = b ;']) ;
|
||||
% eval(['save(''', pathn, '\',obj.filen,''', ''', obj.field_ffe,''', ''',obj.field_dfe,''') ;']) ;
|
||||
|
||||
save("coefficients",obj.e, obj.b);
|
||||
end
|
||||
|
||||
else
|
||||
yout = data_in;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function [X_2,X_3] = calc_nl_vecs(obj,X_1,ind_mat_2,ind_mat_3,norm_fac2,norm_fac3,delta_2,delta_3,cplx)
|
||||
% calculation of the vectors containing all combinations of input symbols
|
||||
% of second and third order based on the linear symbols
|
||||
|
||||
if ind_mat_2(1) > 0
|
||||
input_vec_se = X_1(delta_2:end)/norm_fac2;%(K*(k0-1):end)
|
||||
X_2 = input_vec_se(ind_mat_2(:,1)).*input_vec_se(ind_mat_2(:,2));
|
||||
else
|
||||
X_2 = [];
|
||||
end
|
||||
|
||||
if ind_mat_3(1) > 0
|
||||
if cplx
|
||||
input_vec_th = X_1(delta_3:end)/norm_fac3;
|
||||
X_3 = input_vec_th(ind_mat_3(:,1)).*input_vec_th(ind_mat_3(:,2)).*conj(input_vec_th(ind_mat_3(:,3)));
|
||||
else
|
||||
input_vec_th = X_1(delta_3:end)/norm_fac3;
|
||||
X_3 = input_vec_th(ind_mat_3(:,1)).*input_vec_th(ind_mat_3(:,2)).*input_vec_th(ind_mat_3(:,3));
|
||||
end
|
||||
else
|
||||
X_3 = [];
|
||||
end
|
||||
end
|
||||
|
||||
function [ind_mat_2nd,ind_mat_3rd] = calc_ind(obj,Ne2,N2,Ne3,N3,mode2nd,mode3rd,len_2nd,len_3rd,cplx)
|
||||
|
||||
if Ne2 > 0
|
||||
ind_mat_2nd = NaN(N2,2);
|
||||
count=1;
|
||||
if mode2nd == 1
|
||||
for t = 1:Ne2
|
||||
for u = t:Ne2
|
||||
ind_mat_2nd(count,:) = [t u];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
elseif mode2nd == 2
|
||||
for t = 1:Ne2
|
||||
ind_mat_2nd(t,:) = [t t];
|
||||
end
|
||||
elseif mode2nd == 3
|
||||
for t = 1:Ne2
|
||||
for u = t:Ne2
|
||||
if u-t<=len_2nd
|
||||
ind_mat_2nd(count,:) = [t u];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif mode2nd == 4
|
||||
for t = 1:Ne2
|
||||
for u = t:Ne2
|
||||
if u-t<=ceil(Ne2/2)
|
||||
ind_mat_2nd(count,:) = [t u];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
ind_mat_2nd = 0;
|
||||
end
|
||||
|
||||
if Ne3 > 0
|
||||
ind_mat_3rd = NaN(N3,3);
|
||||
count=1;
|
||||
if mode3rd == 1
|
||||
if cplx
|
||||
for t = 1:Ne3
|
||||
for u = t:Ne3
|
||||
for v = 1:Ne3
|
||||
ind_mat_3rd(count,:) = [t u v];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for t = 1:Ne3
|
||||
for u = t:Ne3
|
||||
for v = u:Ne3
|
||||
ind_mat_3rd(count,:) = [t u v];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif mode3rd == 2
|
||||
for t = 1:Ne3
|
||||
ind_mat_3rd(t,:) = [t t t];
|
||||
end
|
||||
elseif mode3rd == 3
|
||||
for t = 1:Ne3
|
||||
for u = t:Ne3
|
||||
ind_mat_3rd(count,:) = [t t u];
|
||||
if t ~= u
|
||||
count = count + 1;
|
||||
ind_mat_3rd(count,:) = [t u u];
|
||||
end
|
||||
count = count + 1;
|
||||
end
|
||||
end
|
||||
elseif mode3rd == 4
|
||||
for t = 1:Ne3
|
||||
for u = t:Ne3
|
||||
for v = u:Ne3
|
||||
if u-t<=len_3rd && v-t<=len_3rd
|
||||
ind_mat_3rd(count,:) = [t u v];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif mode3rd == 5
|
||||
for t = 1:Ne3
|
||||
for u = t:Ne3
|
||||
if u-t<=len_3rd
|
||||
ind_mat_3rd(count,:) = [t t u];
|
||||
if t ~= u
|
||||
count = count + 1;
|
||||
ind_mat_3rd(count,:) = [t u u];
|
||||
end
|
||||
count = count + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ind_mat_3rd2 = NaN(N3,3);
|
||||
count = 1;
|
||||
% for t = 1:Ne3
|
||||
% ind_mat_3rd2(count,:) = [t t t];
|
||||
% count = count + 1;
|
||||
% end
|
||||
for t = 1:Ne3
|
||||
% ind_mat_3rd2(count,:) = [t t t];
|
||||
% count = count + 1;
|
||||
for u = t:min(Ne3,t+len_3rd)
|
||||
for v = unique([t u])
|
||||
ind_mat_3rd2(count,:) = [t v u];
|
||||
count = count + 1;
|
||||
% ind_mat_3rd2(count,:) = [t u u];
|
||||
% count = count + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
ind_mat_3rd = 0;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function [ind_mat_2nd,ind_mat_3rd] = calc_DFE_ind(obj,Ne2,N2,Ne3,N3)
|
||||
|
||||
if Ne2 > 0
|
||||
ind_mat_2nd = NaN(N2,2);
|
||||
count=1;
|
||||
for t = 1:Ne2
|
||||
for u = t:Ne2
|
||||
ind_mat_2nd(count,:) = [t u];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
else
|
||||
ind_mat_2nd = 0;
|
||||
end
|
||||
|
||||
if Ne3 > 0
|
||||
ind_mat_3rd = NaN(N3,3);
|
||||
count=1;
|
||||
for t = 1:Ne3
|
||||
for u = t:Ne3
|
||||
for v = u:Ne3
|
||||
ind_mat_3rd(count,:) = [t u v];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
ind_mat_3rd = 0;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
727
Classes/04_DSP/Equalizer/EQ_copy.m
Normal file
727
Classes/04_DSP/Equalizer/EQ_copy.m
Normal file
@@ -0,0 +1,727 @@
|
||||
classdef EQ_copy
|
||||
%EQ Summary of this class goes here
|
||||
% Detailed explanation goes here
|
||||
|
||||
properties
|
||||
Ne %Number of feed forward coefficients (1st, 2nd and 3rd order)
|
||||
Nb %Number of decision feedback coefficients (1st, 2nd and 3rd order)
|
||||
K %Number of samples per symbol
|
||||
delay %Delay of incoming signal
|
||||
training_length %Number of training symbols
|
||||
training_loops %Number of loops through sequence for training mode
|
||||
ideal_dfe %Error free DFE decisions
|
||||
|
||||
DB_aim %Aim at duobinary output sequence
|
||||
|
||||
M %Order of the PAM constellation (only relevant in case of DB aim)
|
||||
|
||||
FFEmu %mu parameter for FFE part in training mode (0 means normalized LMS)
|
||||
DFEmu % mu parameter for DFE part in training mode
|
||||
dd_loops % Number of loops through sequence for DD mode
|
||||
DDmu % mu parameters for DD mode (individual value for each order)
|
||||
DCmu % mu parameter for the dc tap
|
||||
|
||||
l1act %Activate/deactive l1 regularization
|
||||
rho %Parameter for speed of coeff shrinking
|
||||
epsilon %Reciprocal value of the magnitude of the coeff to converge to zero (1st,2nd,3rd order)
|
||||
thres %Theshold for neglecting coefficienties (1st,2nd,3rd order)
|
||||
static_act %Activate/deactive static coefficient reduction
|
||||
|
||||
mode2nd %0: no reduction | 1: polynomial | 2: restricted to interval
|
||||
len_2nd %length of the interval (only for 2nd order mode = 2)
|
||||
|
||||
mode3rd %0: no reduction | 1: polynomial | 2: restricted to interval
|
||||
len_3rd %length of the interval (only for 3rd order mode = 3/4)
|
||||
|
||||
plottrain
|
||||
plotfinal
|
||||
load_decisions
|
||||
|
||||
save_taps
|
||||
|
||||
|
||||
%during simulation
|
||||
k0
|
||||
b
|
||||
b2
|
||||
b3
|
||||
e
|
||||
e2
|
||||
e3
|
||||
|
||||
coeff_number
|
||||
constellation_in
|
||||
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = EQ_copy(options)
|
||||
%EQ Construct an instance of this class
|
||||
% Detailed explanation goes here
|
||||
arguments(Input)
|
||||
options.Ne = [10 0 0] %Number of feed forward coefficients (1st, 2nd and 3rd order)
|
||||
options.Nb = [10 0 0]%Number of decision feedback coefficients (1st, 2nd and 3rd order)
|
||||
options.K = 1 %Number of samples per symbol
|
||||
options.delay = 0 %Delay of incoming signal
|
||||
options.training_length = 1024 %Number of training symbols
|
||||
options.training_loops = 1 %Number of loops through sequence for training mode
|
||||
options.ideal_dfe = 0 %Error free DFE decisions
|
||||
|
||||
options.DB_aim %Aim at duobinary output sequence
|
||||
|
||||
options.M = 1 %Order of the PAM constellation (only relevant in case of DB aim)
|
||||
|
||||
options.FFEmu = 0 %mu parameter for FFE part in training mode (0 means normalized LMS)
|
||||
options.DFEmu = 0.005 % mu parameter for DFE part in training mode
|
||||
options.dd_loops = 1% Number of loops through sequence for DD mode
|
||||
options.DDmu = [0.0004 0.0004 0.0004 0.0004 ] % mu parameters for DD mode (individual value for each order)
|
||||
options.DCmu = 0.005 % mu parameter for the dc tap
|
||||
|
||||
options.l1act = 0 %Activate/deactive l1 regularization
|
||||
options.rho = 5e-4%Parameter for speed of coeff shrinking
|
||||
options.epsilon = [10 100 1000] %Reciprocal value of the magnitude of the coeff to converge to zero (1st,2nd,3rd order)
|
||||
options.thres = [5e-3 4e-3 5e-4]%Theshold for neglecting coefficienties (1st,2nd,3rd order)
|
||||
options.static_act = 0 %Activate/deactive static coefficient reduction
|
||||
|
||||
options.mode2nd = 1%0: no reduction | 1: polynomial | 2: restricted to interval
|
||||
options.len_2nd = 1 %length of the interval (only for 2nd order mode = 2)
|
||||
|
||||
options.mode3rd = 1%0: no reduction | 1: polynomial | 2: restricted to interval
|
||||
options.len_3rd = 1%length of the interval (only for 3rd order mode = 3/4)
|
||||
|
||||
options.plottrain = 0
|
||||
options.plotfinal = 0
|
||||
options.load_decisions = 0
|
||||
options.save_taps = 0
|
||||
end
|
||||
|
||||
fn = fieldnames(options);
|
||||
for n = 1:numel(fn)
|
||||
obj.(fn{n}) = options.(fn{n});
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function [signalclass_out,error_log] = process(obj,signalclass_in, reference_signalclass_in)
|
||||
|
||||
% actual processing of the signal (steps 1. - 3.)
|
||||
[signalclass_in.signal,error_log] = obj.process_(signalclass_in.signal', reference_signalclass_in.signal');
|
||||
|
||||
signalclass_in.signal = signalclass_in.signal';
|
||||
% append to logbook
|
||||
lbdesc = ['EQ '];
|
||||
signalclass_in = signalclass_in.logbookentry(lbdesc);
|
||||
|
||||
% write to output
|
||||
signalclass_out = signalclass_in;
|
||||
|
||||
end
|
||||
|
||||
function [yout,error_log] = process_(obj,data_in,ref_in)
|
||||
%METHOD1 Summary of this method goes here
|
||||
% Detailed explanation goes here
|
||||
|
||||
if obj.DB_aim
|
||||
ref_DB = zeros(size(ref_in));
|
||||
for k = 1:length(ref_in)
|
||||
if k == 1
|
||||
ref_DB(k) = ref_in(k);
|
||||
else
|
||||
ref_DB(k) = ref_in(k) + ref_in(k-1);
|
||||
end
|
||||
end
|
||||
ref_in = ref_DB;
|
||||
end
|
||||
|
||||
ref = [zeros(1,obj.Nb(1)-1) ref_in zeros(1,obj.Nb(1))];
|
||||
|
||||
if isreal(ref)
|
||||
cplx = 0;
|
||||
else
|
||||
cplx = 1;
|
||||
end
|
||||
|
||||
if obj.static_act
|
||||
obj.mode2nd = obj.mode2nd + 1;
|
||||
obj.mode3rd = obj.mode3rd + 1;
|
||||
else
|
||||
obj.mode2nd = 1;
|
||||
obj.mode3rd = 1;
|
||||
end
|
||||
|
||||
if obj.mode2nd == 1
|
||||
N2 = (obj.Ne(2)*(obj.Ne(2)+1))/2; % Number of coefficients for second order
|
||||
elseif obj.mode2nd == 2
|
||||
N2 = obj.Ne(2);
|
||||
elseif obj.mode2nd == 3
|
||||
N2 = (obj.len_2nd+1)*(2*obj.Ne(2)-obj.len_2nd)/2;
|
||||
elseif obj.mode2nd == 4
|
||||
N2 = (ceil(obj.Ne(2)/2)+1)*(2*obj.Ne(2)-ceil(obj.Ne(2)/2))/2;
|
||||
end
|
||||
|
||||
if obj.mode3rd == 1
|
||||
if cplx
|
||||
N3 = obj.Ne(3)^2*(obj.Ne(3)+1)/2;
|
||||
else
|
||||
N3 = obj.Ne(3)*(obj.Ne(3)+1)*(obj.Ne(3)+2)/6; % Number of coefficients for third order
|
||||
end
|
||||
elseif obj.mode3rd == 2
|
||||
N3 = obj.Ne(3);
|
||||
elseif obj.mode3rd == 3
|
||||
N3 = obj.Ne(3)^2;
|
||||
elseif obj.mode3rd == 4
|
||||
N3 = round(1/6*(obj.len_3rd+1)*(obj.len_3rd+2)*(3*obj.Ne(3)-2*obj.len_3rd));
|
||||
elseif obj.mode3rd == 5
|
||||
N3 = 2*obj.Ne(3)*obj.len_3rd-obj.len_3rd*(obj.len_3rd+1)+obj.Ne(3);
|
||||
end
|
||||
|
||||
Nb2 = (obj.Nb(2)*(obj.Nb(2)+1))/2;
|
||||
Nb3 = obj.Nb(3)*(obj.Nb(3)+1)*(obj.Nb(3)+2)/6;
|
||||
|
||||
data_in = data_in/sqrt(mean(abs(data_in).^2)); % power normalization of input sequence
|
||||
|
||||
if obj.FFEmu == 0
|
||||
norm_fac2 = sqrt(mean(abs(data_in.^2).^2)); % power normalization for second and third order terms
|
||||
norm_fac3 = sqrt(mean(abs(data_in.^3).^2)); % (not necessary, but seems to be more stable if applied --> same as different mu values for linear and nl terms)
|
||||
else
|
||||
norm_fac2 = 1;
|
||||
norm_fac3 = 1;
|
||||
end
|
||||
|
||||
norm_fac_DFE2 = sqrt(mean(abs(ref_in.^2).^2)); % same for DFE input (reference)
|
||||
norm_fac_DFE3 = sqrt(mean(abs(ref_in.^3).^2));
|
||||
|
||||
data = [zeros(1,floor(obj.Ne(1)/2)) data_in zeros(1,obj.Ne(1))];
|
||||
|
||||
delta_2 = round((obj.Ne(1)-obj.Ne(2))/2);
|
||||
delta_3 = round((obj.Ne(1)-obj.Ne(3))/2);
|
||||
|
||||
delta_DFE2 = 1;%round((obj.Nb-obj.Nb(2))/2);
|
||||
delta_DFE3 = 1;%round((obj.Nb-obj.Nb(3))/2);
|
||||
|
||||
% calculate the indices for the combination of second and third order symbols
|
||||
% - done in advance because it's the same for each iteration, so time
|
||||
% can be saved
|
||||
|
||||
[ind_mat_2nd,ind_mat_3rd] = obj.calc_ind(obj.Ne(2),N2,obj.Ne(3),N3,obj.mode2nd,obj.mode3rd,obj.len_2nd,obj.len_3rd,cplx);
|
||||
|
||||
[ind_mat_DFE_2nd,ind_mat_DFE_3rd] = obj.calc_DFE_ind(obj.Nb(2),Nb2,obj.Nb(3),Nb3);
|
||||
|
||||
if obj.l1act
|
||||
epsilon_ = diag([ones(1,obj.Ne(1))*obj.epsilon(1) ones(1,N2)*obj.epsilon(2) ones(1,N3)*obj.epsilon(3)]);
|
||||
end
|
||||
|
||||
obj.k0 = obj.delay; % input delay compared to training sequence
|
||||
error_log = [];
|
||||
|
||||
if 1 % obj.active
|
||||
%% Calculation of the filter coefficients in training based LMS mode
|
||||
|
||||
e_ = zeros(obj.Ne(1)+N2+N3,1); % initialization of filter coefficients
|
||||
|
||||
b_ = zeros(obj.Nb(1)+Nb2+Nb3,1);
|
||||
|
||||
e_dc = mean(data_in); % initilaization of the dc tap with the mean value of the whole data
|
||||
|
||||
for trainloops = 1:obj.training_loops
|
||||
cnt = 1;
|
||||
m = obj.k0+1; % starting symbol index at the delay compared to the training sequence
|
||||
|
||||
|
||||
|
||||
% n => index in rx data sequence
|
||||
%Step From: Oversampling(=2) * Startdelay + 1
|
||||
%Step Width: Oversampling(=2)
|
||||
%Step To: Oversampling(=2) * Training Length
|
||||
for n = obj.K*obj.k0+1:obj.K:obj.K*obj.training_length
|
||||
|
||||
% m => index in reference sequence
|
||||
m = m+1;
|
||||
|
||||
%
|
||||
%dc_ = mean(data(obj.Ne(1)+n+(obj.K-1):-1:n+obj.K).');
|
||||
% cut symbols from rx data sequence
|
||||
X_1 = data(obj.Ne(1)+n+(obj.K-1):-1:n+obj.K).';
|
||||
|
||||
[X_2,X_3] = obj.calc_nl_vecs(X_1,ind_mat_2nd,ind_mat_3rd,norm_fac2,norm_fac3,delta_2,delta_3,cplx);
|
||||
|
||||
input_vec = [X_1;X_2;X_3];
|
||||
|
||||
% cut symbols from desired data sequence (correct symbols in training mode)
|
||||
D_1 = ref(obj.Nb(1)-obj.k0+m-2:-1:m-obj.k0-1).';
|
||||
|
||||
[D_2,D_3] = obj.calc_nl_vecs(D_1,ind_mat_DFE_2nd,ind_mat_DFE_3rd,norm_fac_DFE2,norm_fac_DFE3,delta_DFE2,delta_DFE3,cplx);
|
||||
|
||||
reference_vec = [D_1;D_2;D_3];
|
||||
|
||||
e_ffe = e_.'*input_vec;
|
||||
e_dfe = b_.'*reference_vec;
|
||||
|
||||
error = e_dc + e_ffe - e_dfe - ref_in(m-obj.k0);
|
||||
%error = e_dc + e_.'*input_vec - b_.'*reference_vec - ref_in(m-obj.k0);
|
||||
|
||||
if real(obj.FFEmu)
|
||||
if obj.l1act
|
||||
sgn_e = e_;
|
||||
sgn_e(e_~=0) = e_(e_~=0)./abs(e_(e_~=0));
|
||||
e_ = e_ - obj.rho*sgn_e./(1+epsilon_*abs(e_)) - error*input_vec*obj.FFEmu;
|
||||
else
|
||||
e_ = e_ - error*conj(input_vec)*obj.FFEmu; %classic LMS gradient decay
|
||||
end
|
||||
else
|
||||
if obj.l1act
|
||||
sgn_e = e_;
|
||||
sgn_e(e_~=0) = e_(e_~=0)./abs(e_(e_~=0));
|
||||
e_ = e_ - obj.rho*sgn_e./(1+epsilon_*abs(e_)) - error*input_vec/(input_vec.'*input_vec);
|
||||
else
|
||||
e_ = e_ - error*input_vec/(input_vec.'*input_vec);
|
||||
end
|
||||
end
|
||||
|
||||
e_dc = e_dc - obj.DCmu*error;
|
||||
error_log(cnt,trainloops) = error;
|
||||
cnt = cnt+1;
|
||||
if obj.Nb(1) > 0
|
||||
b_ = b_ + obj.DFEmu*error*reference_vec; % Seems like normalized DFE has worse performance
|
||||
end
|
||||
|
||||
% figure(111);stem((e_),'Markersize',2);ylim([-1 1]);title('FFE Filter Taps');
|
||||
%
|
||||
% figure(222);
|
||||
% stem(input_vec);ylim([-3 3]);
|
||||
% hold on;
|
||||
% stem(reference_vec);ylim([-3 3]);
|
||||
% yline(error,'LineWidth',2); title('Input Vector');
|
||||
% hold off
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
%%
|
||||
% Plot the intermediate coefficients after training mode
|
||||
obj.b = b_(1:obj.Nb(1));
|
||||
obj.b2 = b_(obj.Nb(1)+1:obj.Nb(1)+Nb2);
|
||||
obj.b3 = b_(obj.Nb(1)+Nb2+1:end);
|
||||
obj.e = e_(1:obj.Ne(1));
|
||||
obj.e2 = e_(obj.Ne(1)+1:obj.Ne(1)+N2);
|
||||
obj.e3 = e_(obj.Ne(1)+N2+1:end);
|
||||
|
||||
if obj.plottrain
|
||||
figure(8052)
|
||||
sgtitle('Training Coeff')
|
||||
subplot(2,3,1); stem((obj.e),'Markersize',2);
|
||||
title('FFE coeff linear')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,2); stem(obj.e2,'Markersize',2);
|
||||
title('FFE coeff nl 2nd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,3); stem(obj.e3,'Markersize',2);
|
||||
title('FFE coeff nl 3rd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,4);stem(obj.b,'Markersize',2);
|
||||
title('DFE coeff linear')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,5);stem(obj.b2,'Markersize',2);
|
||||
title('DFE coeff nl 2nd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,6);stem(obj.b3,'Markersize',2);
|
||||
title('DFE coeff nl 3rd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
%set(gcf,'Position',[200 500 700 400])
|
||||
end
|
||||
|
||||
|
||||
if obj.l1act
|
||||
neg_lin = find(abs(obj.e) < obj.thres(1));
|
||||
neg_2nd = find(abs(obj.e2) < obj.thres(2));
|
||||
neg_3rd = find(abs(obj.e3) < obj.thres(3));
|
||||
|
||||
neg = [neg_lin;neg_2nd+obj.Ne(1);neg_3rd+obj.Ne(1)+N2]; % indices of the neglected coefficients
|
||||
|
||||
rel_lin = find(abs(obj.e) >= obj.thres(1));
|
||||
rel_2nd = find(abs(obj.e2) >= obj.thres(2));
|
||||
rel_3rd = find(abs(obj.e3) >= obj.thres(3));
|
||||
|
||||
obj.coeff_number = length(rel_lin)+2*length(rel_2nd)+3*length(rel_3rd);
|
||||
|
||||
rel = [rel_lin;rel_2nd+obj.Ne(1);rel_3rd+obj.Ne(1)+N2]; % indices of the relevant coefficients
|
||||
e_(neg) = 0;
|
||||
|
||||
ind_mat_2nd(neg_2nd,:) = [];
|
||||
|
||||
ind_mat_3rd(neg_3rd,:) = [];
|
||||
end
|
||||
|
||||
%% decision directed mode
|
||||
if ~obj.DB_aim
|
||||
constellation_in_ = unique(ref_in); % getting the symbol constellation from reference data
|
||||
else
|
||||
if obj.M == 2
|
||||
constellation_in_ = [-3 -2 -1 0 1 2 3]/sqrt(5)*2;
|
||||
elseif obj.M == 2.5
|
||||
constellation_in_ = [-5 -4 -3 -2 -1 0 1 2 3 4 5]/sqrt(10)*2;
|
||||
elseif obj.M == 3
|
||||
constellation_in_ = [-7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7]/sqrt(21)*2;
|
||||
else
|
||||
constellation_in_ = unique(ref_in);
|
||||
end
|
||||
end
|
||||
|
||||
obj.constellation_in = constellation_in_;
|
||||
|
||||
if obj.l1act
|
||||
coeff = [e_(rel);b_]; % combine FFE and DFE coefficient vectors for DD mode
|
||||
else
|
||||
coeff = [e_;b_];
|
||||
end
|
||||
|
||||
for dd_loop = 1:obj.dd_loops
|
||||
cnt = obj.training_length+1;
|
||||
m = 0;
|
||||
output_vec = zeros(1,floor(length(data_in)/obj.K)); % initilaization of the output vector
|
||||
dd_DFE = zeros(obj.Nb(1),1);
|
||||
D_2 = zeros(Nb2,1);
|
||||
D_3 = zeros(Nb3,1);
|
||||
|
||||
if all(obj.DDmu == obj.DDmu(1))
|
||||
mu_mat = obj.DDmu(1);
|
||||
else
|
||||
if obj.l1act
|
||||
mu_mat = diag([ones(1,length(rel_lin))*obj.DDmu(1) ones(1,length(rel_2nd))*obj.DDmu(2) ones(1,length(rel_3rd))*obj.DDmu(3) ones(1,obj.Nb)*obj.DDmu(4)]);
|
||||
else
|
||||
mu_mat = diag([ones(1,obj.Ne(1))*obj.DDmu(1) ones(1,N2)*obj.DDmu(2) ones(1,N3)*obj.DDmu(3) ones(1,obj.Nb(1)+Nb2+Nb3)*obj.DDmu(4)]);
|
||||
end
|
||||
end
|
||||
|
||||
if obj.load_decisions
|
||||
pathn = evalin('base','modeldir');
|
||||
temp = load([pathn, 'MLSE_out', '.mat']) ;
|
||||
%eval(['dd_out_vals = temp.', 'a', ';']) ;
|
||||
dd_out_vals=temp.a;
|
||||
dd_out = zeros(size(data_in));
|
||||
dd_out(1:2:length(data_in)) = dd_out_vals;
|
||||
else
|
||||
dd_out = zeros(size(data_in));
|
||||
end
|
||||
|
||||
for k = 1:obj.K:length(data_in)
|
||||
m=m+1; % Symbol index
|
||||
|
||||
X_1 = data(obj.Ne(1)+k-1:-1:k).';
|
||||
|
||||
[X_2,X_3] = obj.calc_nl_vecs(X_1,ind_mat_2nd,ind_mat_3rd,norm_fac2,norm_fac3,delta_2,delta_3,cplx);
|
||||
|
||||
if obj.l1act
|
||||
input_vec = [X_1(rel_lin);X_2;X_3;-dd_DFE;-D_2;-D_3];
|
||||
else
|
||||
input_vec = [X_1;X_2;X_3;-dd_DFE;-D_2;-D_3];
|
||||
end
|
||||
|
||||
output_vec(m) = e_dc + input_vec.'*coeff;
|
||||
|
||||
if ~obj.load_decisions
|
||||
[~,dd_idx] = min(abs(output_vec(m) - constellation_in_)); % decision for closest constellation point
|
||||
dd_out(k) = constellation_in_(dd_idx);
|
||||
end
|
||||
|
||||
if obj.Nb(1) > 0
|
||||
dd_DFE(2:end) = dd_DFE(1:end-1);
|
||||
dd_DFE(1) = dd_out(k);
|
||||
|
||||
if obj.ideal_dfe && m > obj.k0
|
||||
dd_DFE(1) = ref_in(m-obj.k0);
|
||||
end
|
||||
[D_2,D_3] = obj.calc_nl_vecs(dd_DFE,ind_mat_DFE_2nd,ind_mat_DFE_3rd,norm_fac_DFE2,norm_fac_DFE3,delta_DFE2,delta_DFE3,cplx);
|
||||
end
|
||||
% if dd_loop ~= 21
|
||||
error = output_vec(m) - dd_out(k);
|
||||
% else
|
||||
% error = 0;
|
||||
% end
|
||||
|
||||
coeff = coeff - mu_mat*error*conj(input_vec);
|
||||
% e_save(:,save_ind) = coeff;
|
||||
% save_ind = save_ind+1;
|
||||
|
||||
if mu_mat ~= 0
|
||||
e_dc = e_dc - obj.DCmu*error;
|
||||
error_log(cnt,dd_loop) = error;
|
||||
cnt = cnt+1;
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
%figure(2023);plot(error_log(:,1))
|
||||
% shifting the output sequence by k0 symbols
|
||||
yout = (circshift(output_vec.',-(obj.k0))).'; %(circshift(dd_out.',-(obj.k0))).';
|
||||
|
||||
e_ = coeff(1:end-obj.Nb(1)-Nb2-Nb3);
|
||||
b_ = coeff(end-obj.Nb(1)-Nb2-Nb3+1:end);
|
||||
|
||||
if obj.l1act
|
||||
obj.e = e_(1:length(rel_lin));
|
||||
obj.e2 = e_(length(rel_lin)+1:length(rel_lin)+length(rel_2nd));
|
||||
obj.e3 = e_(length(rel_lin)+length(rel_2nd)+1:end);
|
||||
else
|
||||
obj.e = e_(1:obj.Ne(1));
|
||||
obj.e2 = e_(obj.Ne(1)+1:obj.Ne(1)+N2);
|
||||
obj.e3 = e_(obj.Ne(1)+N2+1:end);
|
||||
end
|
||||
obj.b = b_(1:obj.Nb(1));
|
||||
obj.b2 = b_(obj.Nb(1)+1:obj.Nb(1)+Nb2);
|
||||
obj.b3 = b_(obj.Nb(1)+Nb2+1:end);
|
||||
|
||||
% plot the final coefficients after DD mode
|
||||
if obj.plotfinal
|
||||
figure(8054)
|
||||
if obj.l1act
|
||||
sgtitle('Final Coeff')
|
||||
subplot(2,3,1); stem(rel_lin,obj.e,'Markersize',2);
|
||||
title('FFE coeff linear')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,2); stem(rel_2nd,obj.e2,'Markersize',2);
|
||||
title('FFE coeff nl 2nd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,3); stem(rel_3rd,obj.e3,'Markersize',2);
|
||||
title('FFE coeff nl 3rd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,4);stem(obj.b,'Markersize',2);
|
||||
title('DFE coeff linear')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,5);stem(obj.b2,'Markersize',2);
|
||||
title('DFE coeff nl 2nd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,6);stem(obj.b3,'Markersize',2);
|
||||
title('DFE coeff nl 3rd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
else
|
||||
sgtitle('Final Coeff')
|
||||
subplot(2,3,1); stem(obj.e/max(e_),'Markersize',2);
|
||||
title('FFE coeff linear')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,2); stem(obj.e2,'Markersize',2);
|
||||
title('FFE coeff nl 2nd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,3); stem(obj.e3,'Markersize',2);
|
||||
title('FFE coeff nl 3rd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,4);stem(obj.b,'Markersize',2);
|
||||
title('DFE coeff linear')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,5);stem(obj.b2,'Markersize',2);
|
||||
title('DFE coeff nl 2nd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
subplot(2,3,6);stem(obj.b3,'Markersize',2);
|
||||
title('DFE coeff nl 3rd')
|
||||
xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
|
||||
end
|
||||
set(gcf,'Position',[1000 500 700 400])
|
||||
end
|
||||
|
||||
% save frequency response to the work space
|
||||
if obj.save_taps
|
||||
% save the FFE coefficients to the work space
|
||||
% pathn = evalin('base','modeldir');
|
||||
% eval([obj.field_ffe, ' = obj.e ;']) ;
|
||||
% eval([obj.field_dfe, ' = b ;']) ;
|
||||
% eval(['save(''', pathn, '\',obj.filen,''', ''', obj.field_ffe,''', ''',obj.field_dfe,''') ;']) ;
|
||||
|
||||
save("coefficients",obj.e, obj.b);
|
||||
end
|
||||
|
||||
else
|
||||
yout = data_in;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function [X_2,X_3] = calc_nl_vecs(obj,X_1,ind_mat_2,ind_mat_3,norm_fac2,norm_fac3,delta_2,delta_3,cplx)
|
||||
% calculation of the vectors containing all combinations of input symbols
|
||||
% of second and third order based on the linear symbols
|
||||
|
||||
if ind_mat_2(1) > 0
|
||||
input_vec_se = X_1(delta_2:end)/norm_fac2;%(K*(k0-1):end)
|
||||
X_2 = input_vec_se(ind_mat_2(:,1)).*input_vec_se(ind_mat_2(:,2));
|
||||
else
|
||||
X_2 = [];
|
||||
end
|
||||
|
||||
if ind_mat_3(1) > 0
|
||||
if cplx
|
||||
input_vec_th = X_1(delta_3:end)/norm_fac3;
|
||||
X_3 = input_vec_th(ind_mat_3(:,1)).*input_vec_th(ind_mat_3(:,2)).*conj(input_vec_th(ind_mat_3(:,3)));
|
||||
else
|
||||
input_vec_th = X_1(delta_3:end)/norm_fac3;
|
||||
X_3 = input_vec_th(ind_mat_3(:,1)).*input_vec_th(ind_mat_3(:,2)).*input_vec_th(ind_mat_3(:,3));
|
||||
end
|
||||
else
|
||||
X_3 = [];
|
||||
end
|
||||
end
|
||||
|
||||
function [ind_mat_2nd,ind_mat_3rd] = calc_ind(obj,Ne2,N2,Ne3,N3,mode2nd,mode3rd,len_2nd,len_3rd,cplx)
|
||||
|
||||
if Ne2 > 0
|
||||
ind_mat_2nd = NaN(N2,2);
|
||||
count=1;
|
||||
if mode2nd == 1
|
||||
for t = 1:Ne2
|
||||
for u = t:Ne2
|
||||
ind_mat_2nd(count,:) = [t u];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
elseif mode2nd == 2
|
||||
for t = 1:Ne2
|
||||
ind_mat_2nd(t,:) = [t t];
|
||||
end
|
||||
elseif mode2nd == 3
|
||||
for t = 1:Ne2
|
||||
for u = t:Ne2
|
||||
if u-t<=len_2nd
|
||||
ind_mat_2nd(count,:) = [t u];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif mode2nd == 4
|
||||
for t = 1:Ne2
|
||||
for u = t:Ne2
|
||||
if u-t<=ceil(Ne2/2)
|
||||
ind_mat_2nd(count,:) = [t u];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
ind_mat_2nd = 0;
|
||||
end
|
||||
|
||||
if Ne3 > 0
|
||||
ind_mat_3rd = NaN(N3,3);
|
||||
count=1;
|
||||
if mode3rd == 1
|
||||
if cplx
|
||||
for t = 1:Ne3
|
||||
for u = t:Ne3
|
||||
for v = 1:Ne3
|
||||
ind_mat_3rd(count,:) = [t u v];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for t = 1:Ne3
|
||||
for u = t:Ne3
|
||||
for v = u:Ne3
|
||||
ind_mat_3rd(count,:) = [t u v];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif mode3rd == 2
|
||||
for t = 1:Ne3
|
||||
ind_mat_3rd(t,:) = [t t t];
|
||||
end
|
||||
elseif mode3rd == 3
|
||||
for t = 1:Ne3
|
||||
for u = t:Ne3
|
||||
ind_mat_3rd(count,:) = [t t u];
|
||||
if t ~= u
|
||||
count = count + 1;
|
||||
ind_mat_3rd(count,:) = [t u u];
|
||||
end
|
||||
count = count + 1;
|
||||
end
|
||||
end
|
||||
elseif mode3rd == 4
|
||||
for t = 1:Ne3
|
||||
for u = t:Ne3
|
||||
for v = u:Ne3
|
||||
if u-t<=len_3rd && v-t<=len_3rd
|
||||
ind_mat_3rd(count,:) = [t u v];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif mode3rd == 5
|
||||
for t = 1:Ne3
|
||||
for u = t:Ne3
|
||||
if u-t<=len_3rd
|
||||
ind_mat_3rd(count,:) = [t t u];
|
||||
if t ~= u
|
||||
count = count + 1;
|
||||
ind_mat_3rd(count,:) = [t u u];
|
||||
end
|
||||
count = count + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ind_mat_3rd2 = NaN(N3,3);
|
||||
count = 1;
|
||||
% for t = 1:Ne3
|
||||
% ind_mat_3rd2(count,:) = [t t t];
|
||||
% count = count + 1;
|
||||
% end
|
||||
for t = 1:Ne3
|
||||
% ind_mat_3rd2(count,:) = [t t t];
|
||||
% count = count + 1;
|
||||
for u = t:min(Ne3,t+len_3rd)
|
||||
for v = unique([t u])
|
||||
ind_mat_3rd2(count,:) = [t v u];
|
||||
count = count + 1;
|
||||
% ind_mat_3rd2(count,:) = [t u u];
|
||||
% count = count + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
ind_mat_3rd = 0;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function [ind_mat_2nd,ind_mat_3rd] = calc_DFE_ind(obj,Ne2,N2,Ne3,N3)
|
||||
|
||||
if Ne2 > 0
|
||||
ind_mat_2nd = NaN(N2,2);
|
||||
count=1;
|
||||
for t = 1:Ne2
|
||||
for u = t:Ne2
|
||||
ind_mat_2nd(count,:) = [t u];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
else
|
||||
ind_mat_2nd = 0;
|
||||
end
|
||||
|
||||
if Ne3 > 0
|
||||
ind_mat_3rd = NaN(N3,3);
|
||||
count=1;
|
||||
for t = 1:Ne3
|
||||
for u = t:Ne3
|
||||
for v = u:Ne3
|
||||
ind_mat_3rd(count,:) = [t u v];
|
||||
count = count + 1 ;
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
ind_mat_3rd = 0;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
486
Classes/04_DSP/Equalizer/EQ_silas.m
Normal file
486
Classes/04_DSP/Equalizer/EQ_silas.m
Normal file
@@ -0,0 +1,486 @@
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
456
Classes/04_DSP/Equalizer/EQ_silas_ofc.m
Normal file
456
Classes/04_DSP/Equalizer/EQ_silas_ofc.m
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
395
Classes/04_DSP/Equalizer/EQ_silas_plain.m
Normal file
395
Classes/04_DSP/Equalizer/EQ_silas_plain.m
Normal file
@@ -0,0 +1,395 @@
|
||||
classdef EQ_silas_plain < 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
|
||||
error_log
|
||||
|
||||
mu_dc_train
|
||||
mu_ffe_train
|
||||
mu_dfe_train
|
||||
|
||||
mu_dc_dd
|
||||
mu_combined_dd
|
||||
|
||||
delay
|
||||
trainlength
|
||||
sps
|
||||
|
||||
trainloops
|
||||
ddloops
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = EQ_silas_plain(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_combined_dd = [0.0004 0.0005 0.0006 0.0007 ];
|
||||
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,error_log] = process(obj,signalclass_in, reference_signalclass_in)
|
||||
|
||||
assert(signalclass_in.fs / reference_signalclass_in.fs == obj.sps)
|
||||
|
||||
% 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);
|
||||
|
||||
% 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)
|
||||
|
||||
for tloop = 1:obj.trainloops
|
||||
m = 1+obj.delay;
|
||||
cnt = 1;
|
||||
for n = obj.sps*obj.delay+1:obj.sps:obj.sps*obj.trainlength
|
||||
m = m+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);
|
||||
|
||||
% Calculate the Error
|
||||
obj.e_ffe = obj.e.' * x_in_vnle_format;
|
||||
obj.e_dfe = obj.b.' * d_vnle_format;
|
||||
|
||||
obj.error = obj.e_dc + obj.e_ffe - obj.e_dfe - obj.d(obj.Nb(1)-1+m-obj.delay);
|
||||
obj.error_log(tloop,cnt) = obj.error;
|
||||
|
||||
%update FFE coefficients with LMS
|
||||
obj.e = obj.e - obj.error*conj(x_in_vnle_format)*obj.mu_ffe_train;
|
||||
|
||||
%update DFE coefficients with LMS
|
||||
obj.b = obj.b + obj.mu_dfe_train*obj.error*d_vnle_format;
|
||||
|
||||
%update DC error
|
||||
obj.e_dc = obj.e_dc - obj.error .* obj.mu_dc_train;
|
||||
cnt = cnt+1;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function decisionDirectedMode(obj)
|
||||
|
||||
%start the dd mode with coefficients from training
|
||||
coeff = [obj.e;obj.b];
|
||||
|
||||
for ddloop = 1:obj.ddloops
|
||||
|
||||
m = 0;
|
||||
|
||||
if all(obj.mu_combined_dd == obj.mu_combined_dd(1))
|
||||
mu_mat = obj.mu_combined_dd(1);
|
||||
else
|
||||
mu_mat = diag([ones(1,obj.Ce(1))*obj.mu_combined_dd(1)... %1st order ffe
|
||||
ones(1,obj.Ce(2))*obj.mu_combined_dd(2)... %2nd order ffe
|
||||
ones(1,obj.Ce(3))*obj.mu_combined_dd(3)... %3rd order ffe
|
||||
ones(1,sum(obj.Cb))*obj.mu_combined_dd(4)]); %all order dfe
|
||||
end
|
||||
|
||||
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);
|
||||
|
||||
for k = 1:obj.sps:obj.x_length
|
||||
|
||||
m=m+1;
|
||||
|
||||
%get Sigal input vectors with correct length for VNLE
|
||||
x = obj.x_in(obj.Ne(1)+k-1:-1:k).';
|
||||
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) = obj.e_dc + x_d.'* coeff;
|
||||
|
||||
%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 = y(m) - d_hat(k);
|
||||
|
||||
%Update coefficients (both FFE and DFE)
|
||||
coeff = coeff - (mu_mat * (obj.error * conj(x_d)));
|
||||
|
||||
if 1 %mu_mat ~= 0
|
||||
obj.e_dc = obj.e_dc - obj.mu_dc_dd * obj.error;
|
||||
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)
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
425
Classes/04_DSP/Equalizer/EQ_silas_sliding_window_dc_removal.m
Normal file
425
Classes/04_DSP/Equalizer/EQ_silas_sliding_window_dc_removal.m
Normal file
@@ -0,0 +1,425 @@
|
||||
classdef EQ_silas_sliding_window_dc_removal < 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_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)
|
||||
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = EQ_silas_sliding_window_dc_removal(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_blocklength = 1;
|
||||
options.eq_updatelatency = 1;
|
||||
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_avg_block = zeros(obj.eq_blocklength,1);
|
||||
|
||||
for tloop = 1:obj.trainloops
|
||||
m = 1+obj.delay;
|
||||
|
||||
for n = obj.sps*obj.delay+1:obj.sps:obj.sps*obj.trainlength
|
||||
m = m+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;
|
||||
|
||||
dc_avg_block = circshift(dc_avg_block,1);
|
||||
|
||||
dc_avg_block(1) = obj.e_ffe;
|
||||
|
||||
if n > obj.eq_blocklength * obj.sps
|
||||
obj.e_dc = obj.e_ffe - mean(dc_avg_block).* obj.mu_dc_train;
|
||||
else
|
||||
obj.e_dc = 0;
|
||||
end
|
||||
|
||||
obj.e_dfe = obj.b.' * d_vnle_format;
|
||||
|
||||
% Calculate the Error
|
||||
obj.error = 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;
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function decisionDirectedMode(obj)
|
||||
|
||||
%start the dd mode with coefficients from training
|
||||
coeff = [obj.e;obj.b];
|
||||
|
||||
|
||||
for ddloop = 1:obj.ddloops
|
||||
|
||||
dc_avg_block = zeros(obj.eq_blocklength,1);
|
||||
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));
|
||||
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);
|
||||
|
||||
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).';
|
||||
|
||||
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 ;
|
||||
y(m) = y_(m) - obj.e_dc(end) ;
|
||||
|
||||
dc_avg_block = circshift(dc_avg_block,1);
|
||||
|
||||
dc_avg_block(1) = y(m);
|
||||
|
||||
obj.e_dc(m) = (y(m) - mean(dc_avg_block)) .* obj.mu_dc_dd;
|
||||
|
||||
%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 = y(m) - d_hat(k);
|
||||
|
||||
%Update coefficients (both FFE and DFE)
|
||||
% obj.e = obj.e - obj.error * mu_ffe * conj(x_vnle);
|
||||
% obj.b = obj.b + obj.error * mu_dfe * conj(d_vnle);
|
||||
% coeff = [obj.e;obj.b];
|
||||
|
||||
coeff = coeff - mu_mat*obj.error*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(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)
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
172
Classes/04_DSP/Equalizer/FFE.m
Normal file
172
Classes/04_DSP/Equalizer/FFE.m
Normal file
@@ -0,0 +1,172 @@
|
||||
classdef FFE < handle
|
||||
% Implementation of plain and simple FFE.
|
||||
% 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);
|
||||
|
||||
properties
|
||||
sps % usually 2
|
||||
order
|
||||
e
|
||||
error
|
||||
|
||||
len_tr
|
||||
mu_tr
|
||||
epochs_tr
|
||||
|
||||
mu_dd
|
||||
epochs_dd
|
||||
|
||||
constellation
|
||||
|
||||
decide
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = FFE(options)
|
||||
arguments(Input)
|
||||
|
||||
options.sps = 2;
|
||||
options.order = 15;
|
||||
|
||||
options.len_tr = 4096;
|
||||
options.mu_tr = 0;
|
||||
options.epochs_tr = 5;
|
||||
|
||||
options.mu_dd = 1e-5;
|
||||
options.epochs_dd = 5;
|
||||
|
||||
options.decide = false;
|
||||
|
||||
end
|
||||
|
||||
fn = fieldnames(options);
|
||||
for n = 1:numel(fn)
|
||||
obj.(fn{n}) = options.(fn{n});
|
||||
end
|
||||
|
||||
obj.e = zeros(obj.order,1);
|
||||
obj.error = 0;
|
||||
|
||||
end
|
||||
|
||||
function [X] = process(obj, X, D)
|
||||
|
||||
% actual processing of the signal (steps 1. - 3.)
|
||||
% 1 normalize RMS
|
||||
X = X.normalize("mode","rms");
|
||||
|
||||
obj.constellation = unique(D.signal);
|
||||
|
||||
% Training Mode
|
||||
training = 1;
|
||||
showviz = 0;
|
||||
obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training,showviz);
|
||||
|
||||
% Decision Directed Mode
|
||||
N = X.length;
|
||||
training = 0;
|
||||
showviz = 0;
|
||||
[signal,decision]=obj.equalize(X.signal, D.signal,obj.mu_dd,obj.epochs_dd,N,training,showviz);
|
||||
|
||||
% Output Signal
|
||||
if obj.decide
|
||||
X.signal = decision;
|
||||
else
|
||||
X.signal = signal;
|
||||
end
|
||||
X.fs = D.fs; %change sampling frequency of outgoing signal from fdac e.g. 2 sps to symbol spaced = fsym
|
||||
lbdesc = [num2str(obj.order),' tap FFE'];
|
||||
X = X.logbookentry(lbdesc); % append to logbook
|
||||
|
||||
|
||||
end
|
||||
|
||||
function [y,d_hat] = equalize(obj,x,d,mio,epochs,N,training,showviz)
|
||||
|
||||
arguments
|
||||
obj
|
||||
x
|
||||
d
|
||||
mio
|
||||
epochs
|
||||
N
|
||||
training
|
||||
showviz
|
||||
end
|
||||
|
||||
x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)];
|
||||
|
||||
if showviz
|
||||
f = figure(111);
|
||||
subplot(2,2,1:2);
|
||||
hold on
|
||||
a = scatter(1:numel(x),x,1,'.');
|
||||
a2 = scatter(1,1,1,'.');
|
||||
a3 = scatter(1,1,2,'.');
|
||||
a4 = xline(1);
|
||||
ylim([-3 3])
|
||||
xlim([0 length(x)]);
|
||||
% subplot(2,2,3)
|
||||
% dplot = x(1:1+500);
|
||||
% b = scatter(1:numel(dplot),dplot,5,'x');
|
||||
% xline(1)
|
||||
% xline(obj.order)
|
||||
% ylim([-3 3])
|
||||
% xlim([0 500]);
|
||||
subplot(2,2,3:4)
|
||||
c = stem(obj.e);
|
||||
ylim([-1 1])
|
||||
drawnow
|
||||
end
|
||||
|
||||
for epoch = 1 : epochs
|
||||
symbol = 0;
|
||||
for sample = 1 : obj.sps : N
|
||||
|
||||
symbol = symbol+1;
|
||||
|
||||
U = x(obj.order+sample-1:-1:sample);
|
||||
|
||||
y(symbol,1) = obj.e.' * U; % Calculating output of LMS __ * |
|
||||
|
||||
if training
|
||||
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);
|
||||
end
|
||||
|
||||
err(symbol) = y(symbol) - d_hat(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
|
||||
end
|
||||
|
||||
if mod(sample,100) == 1 && showviz
|
||||
a2.XData = 1:2*numel(y);
|
||||
a2.YData = repelem(y, 2);
|
||||
a3.XData = 1:2*numel(d_hat);
|
||||
a3.YData = repelem(d_hat, 2);
|
||||
a4.Value = sample;
|
||||
% b.YData = x(symbol:symbol+500);
|
||||
c.YData = obj.e;
|
||||
drawnow;
|
||||
end
|
||||
|
||||
obj.error(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
158
Classes/04_DSP/Equalizer/FFE_DCremoval.m
Normal file
158
Classes/04_DSP/Equalizer/FFE_DCremoval.m
Normal file
@@ -0,0 +1,158 @@
|
||||
classdef FFE_DCremoval < handle
|
||||
% Implementation of plain and simple FFE.
|
||||
% 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);
|
||||
|
||||
properties
|
||||
sps % usually 2
|
||||
order
|
||||
e
|
||||
error
|
||||
|
||||
len_tr
|
||||
mu_tr
|
||||
epochs_tr
|
||||
|
||||
mu_dd
|
||||
epochs_dd
|
||||
|
||||
mu_dc
|
||||
dc_buffer_len
|
||||
|
||||
constellation
|
||||
|
||||
decide
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = FFE_DCremoval(options)
|
||||
arguments(Input)
|
||||
|
||||
options.sps = 2;
|
||||
options.order = 15;
|
||||
|
||||
options.len_tr = 4096;
|
||||
options.mu_tr = 0;
|
||||
options.epochs_tr = 5;
|
||||
|
||||
options.mu_dd = 1e-5;
|
||||
options.epochs_dd = 5;
|
||||
|
||||
options.mu_dc = 0.05;
|
||||
options.dc_buffer_len = 1;
|
||||
|
||||
options.decide = false;
|
||||
|
||||
end
|
||||
|
||||
assert(options.dc_buffer_len>0);
|
||||
|
||||
fn = fieldnames(options);
|
||||
for n = 1:numel(fn)
|
||||
obj.(fn{n}) = options.(fn{n});
|
||||
end
|
||||
|
||||
obj.e = zeros(obj.order,1);
|
||||
obj.error = 0;
|
||||
|
||||
end
|
||||
|
||||
function [X] = process(obj, X, D)
|
||||
|
||||
% actual processing of the signal (steps 1. - 3.)
|
||||
% 1 normalize RMS
|
||||
X = X.normalize("mode","rms");
|
||||
|
||||
obj.constellation = unique(D.signal);
|
||||
|
||||
% Training Mode
|
||||
training = 1;
|
||||
obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training);
|
||||
|
||||
% Decision Directed Mode
|
||||
N = X.length;
|
||||
training = 0;
|
||||
[signal,decision]=obj.equalize(X.signal, D.signal,obj.mu_dd,obj.epochs_dd,N,training);
|
||||
|
||||
% Output Signal
|
||||
if obj.decide
|
||||
X.signal = decision;
|
||||
else
|
||||
X.signal = signal;
|
||||
end
|
||||
X.fs = D.fs; %change sampling frequency of outgoing signal from fdac e.g. 2 sps to symbol spaced = fsym
|
||||
lbdesc = [num2str(obj.order),' tap FFE'];
|
||||
X = X.logbookentry(lbdesc); % append to logbook
|
||||
|
||||
|
||||
end
|
||||
|
||||
function [y,d_hat] = equalize(obj,x,d,mio,epochs,N,training)
|
||||
|
||||
arguments
|
||||
obj
|
||||
x
|
||||
d
|
||||
mio
|
||||
epochs
|
||||
N
|
||||
training
|
||||
end
|
||||
|
||||
x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)];
|
||||
|
||||
err = 0;
|
||||
e_dc_buffer = NaN(obj.dc_buffer_len,1);
|
||||
e_dc_est = 0;
|
||||
|
||||
for epoch = 1 : epochs
|
||||
symbol = 0;
|
||||
for sample = 1 : obj.sps : N
|
||||
|
||||
symbol = symbol+1;
|
||||
|
||||
U = x(obj.order+sample-1:-1:sample);
|
||||
|
||||
y(symbol,1) = e_dc_est + obj.e.' * U; % Calculating output of LMS __ * |
|
||||
|
||||
if training
|
||||
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);
|
||||
end
|
||||
|
||||
err(symbol) = y(symbol) - d_hat(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
|
||||
end
|
||||
|
||||
%Update the dc estimation every n-th symbol. This is a
|
||||
%trivial implementation of parallel EQ´s where the
|
||||
%errors are not apparent in every step. See Silas OFC
|
||||
%2023 "MPI mitigation adaptive DC removal
|
||||
if mod(symbol,length(e_dc_buffer)) == 0
|
||||
e_dc_buffer(1) = e_dc_est - obj.mu_dc * err(symbol);
|
||||
e_dc_buffer = circshift(e_dc_buffer,1);
|
||||
e_dc_est = mean(e_dc_buffer,"omitnan");
|
||||
else
|
||||
e_dc_buffer(1) = e_dc_est - obj.mu_dc * err(symbol);
|
||||
e_dc_buffer = circshift(e_dc_buffer,1);
|
||||
end
|
||||
|
||||
obj.error(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
194
Classes/04_DSP/Equalizer/FFE_DFE.m
Normal file
194
Classes/04_DSP/Equalizer/FFE_DFE.m
Normal file
@@ -0,0 +1,194 @@
|
||||
classdef FFE_DFE < handle
|
||||
% Implementation of plain and simple FFE.
|
||||
% 1) Training mode (stable performance when you use NLMS)
|
||||
% 2) Decision directed mode
|
||||
|
||||
% Eq = FFE_DFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"ffe_mu_dd",1e-4,"dfe_mu_dd",5e-4,"ffe_mu_tr",0,"dfe_mu_tr",0,"ffe_order",21,"dfe_order",0,"sps",2,"decide",1);
|
||||
|
||||
properties
|
||||
sps % usually 2
|
||||
ffe_order
|
||||
dfe_order
|
||||
e
|
||||
b
|
||||
error
|
||||
|
||||
len_tr
|
||||
ffe_mu_tr
|
||||
dfe_mu_tr
|
||||
epochs_tr
|
||||
|
||||
ffe_mu_dd
|
||||
dfe_mu_dd
|
||||
epochs_dd
|
||||
|
||||
constellation
|
||||
|
||||
decide
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = FFE_DFE(options)
|
||||
arguments(Input)
|
||||
|
||||
options.sps = 2;
|
||||
|
||||
options.ffe_order = 15;
|
||||
options.dfe_order = 2;
|
||||
|
||||
options.len_tr = 4096;
|
||||
options.ffe_mu_tr = 0;
|
||||
options.dfe_mu_tr = 0;
|
||||
options.epochs_tr = 5;
|
||||
|
||||
options.ffe_mu_dd = 1e-5;
|
||||
options.dfe_mu_dd = 1e-5;
|
||||
options.epochs_dd = 5;
|
||||
|
||||
options.decide = false;
|
||||
|
||||
end
|
||||
|
||||
fn = fieldnames(options);
|
||||
for n = 1:numel(fn)
|
||||
obj.(fn{n}) = options.(fn{n});
|
||||
end
|
||||
|
||||
obj.e = zeros(obj.ffe_order,1);
|
||||
obj.b = zeros(obj.dfe_order,1);
|
||||
obj.error = 0;
|
||||
|
||||
end
|
||||
|
||||
function [X] = process(obj, X, D)
|
||||
|
||||
% actual processing of the signal (steps 1. - 3.)
|
||||
% 1 normalize RMS
|
||||
X = X.normalize("mode","rms");
|
||||
|
||||
obj.constellation = unique(D.signal);
|
||||
|
||||
% Training Mode
|
||||
training = 1;
|
||||
showviz = 0;
|
||||
obj.equalize(X.signal, D.signal,obj.ffe_mu_tr,obj.dfe_mu_tr,obj.epochs_tr,obj.len_tr,training,showviz);
|
||||
|
||||
% Decision Directed Mode
|
||||
N = X.length;
|
||||
training = 0;
|
||||
showviz = 0;
|
||||
[signal,decision]=obj.equalize(X.signal, D.signal,obj.ffe_mu_dd,obj.dfe_mu_dd,obj.epochs_dd,N,training,showviz);
|
||||
|
||||
% Output Signal
|
||||
if obj.decide
|
||||
X.signal = decision;
|
||||
else
|
||||
X.signal = signal;
|
||||
end
|
||||
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
|
||||
|
||||
|
||||
end
|
||||
|
||||
function [y,d_hat] = equalize(obj,x,d,ffe_mu,dfe_mu,epochs,N,training,showviz)
|
||||
|
||||
arguments
|
||||
obj
|
||||
x
|
||||
d
|
||||
ffe_mu
|
||||
dfe_mu
|
||||
epochs
|
||||
N
|
||||
training
|
||||
showviz
|
||||
end
|
||||
|
||||
|
||||
mu = diag([ones(1,obj.ffe_order(1))*ffe_mu(1) ...
|
||||
ones(1,obj.dfe_order(1))*dfe_mu(1) ]);
|
||||
|
||||
|
||||
x = [zeros(floor(obj.ffe_order/2),1); x; zeros(obj.ffe_order,1)];
|
||||
%d = [zeros(obj.dfe_order-1,1); d; zeros(obj.dfe_order,1)];
|
||||
d_ = zeros(obj.dfe_order(1),1);
|
||||
coeff = [obj.e;obj.b];
|
||||
|
||||
if showviz
|
||||
f = figure(111);
|
||||
subplot(2,2,1:2);
|
||||
hold on
|
||||
a = scatter(1:numel(x),x,1,'.');
|
||||
a2 = scatter(1,1,1,'.');
|
||||
a3 = scatter(1,1,2,'.');
|
||||
a4 = xline(1);
|
||||
ylim([-3 3])
|
||||
xlim([0 length(x)]);
|
||||
subplot(2,2,3:4)
|
||||
c = stem(obj.e);
|
||||
ylim([-1 1])
|
||||
drawnow
|
||||
end
|
||||
|
||||
for epoch = 1 : epochs
|
||||
symbol = 0;
|
||||
for sample = 1 : obj.sps : N
|
||||
|
||||
symbol = symbol+1;
|
||||
|
||||
x_ = x(obj.ffe_order+sample-1:-1:sample);
|
||||
v = [x_;d_];
|
||||
|
||||
y(symbol,1) = coeff.' * v; % Calculating output of LMS __ * |
|
||||
|
||||
if training
|
||||
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);
|
||||
end
|
||||
|
||||
err(symbol) = y(symbol) - d_hat(symbol); % Instantaneous error
|
||||
|
||||
if ~all(mu == 0,'all') %not all mu values are zero
|
||||
coeff = coeff - (mu * err(symbol) * v) ; % Weight update rule of LMS
|
||||
else
|
||||
normalizationfactor = (v.' * v);
|
||||
coeff = coeff - err(symbol) * v / normalizationfactor; % Weight update rule of NLMS
|
||||
end
|
||||
|
||||
% Append new decision to decision feedback
|
||||
if obj.dfe_order(1) > 0
|
||||
%shift up one index
|
||||
d_(2:end) = d_(1:end-1);
|
||||
%replace 1st index with current estimation
|
||||
d_(1) = d_hat(symbol);
|
||||
end
|
||||
|
||||
if mod(sample,100) == 1 && showviz
|
||||
a2.XData = 1:2*numel(y);
|
||||
a2.YData = repelem(y, 2);
|
||||
a3.XData = 1:2*numel(d_hat);
|
||||
a3.YData = repelem(d_hat, 2);
|
||||
a4.Value = sample;
|
||||
c.YData = obj.e;
|
||||
drawnow;
|
||||
end
|
||||
|
||||
obj.error(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
obj.e = coeff(1:obj.ffe_order);
|
||||
obj.b = coeff(obj.ffe_order+1:end);
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
211
Classes/04_DSP/Equalizer/FFE_FFDCAVG.m
Normal file
211
Classes/04_DSP/Equalizer/FFE_FFDCAVG.m
Normal file
@@ -0,0 +1,211 @@
|
||||
classdef FFE_FFDCAVG < handle
|
||||
% Implementation of plain and simple FFE.
|
||||
% 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);
|
||||
|
||||
properties
|
||||
sps % usually 2
|
||||
order
|
||||
e
|
||||
error
|
||||
|
||||
len_tr
|
||||
mu_tr
|
||||
epochs_tr
|
||||
|
||||
mu_dd
|
||||
epochs_dd
|
||||
|
||||
mu_buff
|
||||
|
||||
constellation
|
||||
|
||||
decide
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = FFE_FFDCAVG(options)
|
||||
arguments(Input)
|
||||
|
||||
options.sps = 2;
|
||||
options.order = 15;
|
||||
|
||||
options.len_tr = 4096;
|
||||
options.mu_tr = 0;
|
||||
options.epochs_tr = 5;
|
||||
|
||||
options.mu_dd = 1e-5;
|
||||
options.epochs_dd = 5;
|
||||
|
||||
options.mu_buff = 0;
|
||||
|
||||
options.decide = false;
|
||||
|
||||
end
|
||||
|
||||
fn = fieldnames(options);
|
||||
for n = 1:numel(fn)
|
||||
obj.(fn{n}) = options.(fn{n});
|
||||
end
|
||||
|
||||
obj.e = zeros(obj.order,1);
|
||||
obj.error = 0;
|
||||
|
||||
end
|
||||
|
||||
function [X] = process(obj, X, D)
|
||||
|
||||
% actual processing of the signal (steps 1. - 3.)
|
||||
% 1 normalize RMS
|
||||
X = X.normalize("mode","rms");
|
||||
|
||||
obj.constellation = unique(D.signal);
|
||||
|
||||
% Training Mode
|
||||
training = 1;
|
||||
showviz = 0;
|
||||
obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training,showviz);
|
||||
|
||||
% Decision Directed Mode
|
||||
N = X.length;
|
||||
training = 0;
|
||||
showviz = 0;
|
||||
[signal,decision]=obj.equalize(X.signal, D.signal,obj.mu_dd,obj.epochs_dd,N,training,showviz);
|
||||
|
||||
% Output Signal
|
||||
if obj.decide
|
||||
X.signal = decision;
|
||||
else
|
||||
X.signal = signal;
|
||||
end
|
||||
X.fs = D.fs; %change sampling frequency of outgoing signal from fdac e.g. 2 sps to symbol spaced = fsym
|
||||
lbdesc = [num2str(obj.order),' tap FFE'];
|
||||
X = X.logbookentry(lbdesc); % append to logbook
|
||||
|
||||
|
||||
end
|
||||
|
||||
function [y,d_hat] = equalize(obj,x,d,mio,epochs,N,training,showviz)
|
||||
|
||||
arguments
|
||||
obj
|
||||
x
|
||||
d
|
||||
mio
|
||||
epochs
|
||||
N
|
||||
training
|
||||
showviz
|
||||
end
|
||||
|
||||
x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)];
|
||||
|
||||
if showviz
|
||||
f = figure(111);
|
||||
subplot(2,2,1:2);
|
||||
hold on
|
||||
a = scatter(1:numel(x),x,1,'.');
|
||||
a2 = scatter(1,1,1,'.');
|
||||
a3 = scatter(1,1,2,'.');
|
||||
a4 = xline(1);
|
||||
ylim([-3 3])
|
||||
xlim([0 length(x)]);
|
||||
% subplot(2,2,3)
|
||||
% dplot = x(1:1+500);
|
||||
% b = scatter(1:numel(dplot),dplot,5,'x');
|
||||
% xline(1)
|
||||
% xline(obj.order)
|
||||
% ylim([-3 3])
|
||||
% xlim([0 500]);
|
||||
subplot(2,2,3:4)
|
||||
c = stem(obj.e);
|
||||
ylim([-1 1])
|
||||
drawnow
|
||||
end
|
||||
|
||||
|
||||
for epoch = 1 : epochs
|
||||
symbol = 0;
|
||||
|
||||
err_buffer = zeros(numel(obj.constellation),112);
|
||||
dc_err = zeros(numel(obj.constellation),1);
|
||||
dc_sto = NaN(numel(obj.constellation),N);
|
||||
|
||||
for sample = 1 : obj.sps : N
|
||||
|
||||
symbol = symbol+1;
|
||||
|
||||
U = x(obj.order+sample-1:-1:sample);
|
||||
|
||||
y(symbol,1) = obj.e.' * U; % Calculating output of LMS __ * |
|
||||
|
||||
if training
|
||||
[~,symbol_idx] = min(abs(d(symbol) - obj.constellation)); % decision for closest constellation point
|
||||
d_hat(symbol,1) = d(symbol);
|
||||
else
|
||||
always_correct_decision = 0;
|
||||
if always_correct_decision
|
||||
[~,symbol_idx] = min(abs(d(symbol) - obj.constellation)); % decision for closest constellation point
|
||||
else
|
||||
[~,symbol_idx] = min(abs(y(symbol) - obj.constellation)); % decision for closest constellation point
|
||||
end
|
||||
d_hat(symbol,1) = obj.constellation(symbol_idx);
|
||||
end
|
||||
|
||||
err(symbol) = y(symbol) - d_hat(symbol); % Instantaneous error
|
||||
|
||||
if 1
|
||||
%use buffer for dc-error
|
||||
err_buffer(symbol_idx,1) = err(symbol);
|
||||
err_buffer(symbol_idx,:) = circshift(err_buffer(symbol_idx,:),1);
|
||||
dc_sto(symbol_idx,symbol) = obj.mu_buff*mean(err_buffer(symbol_idx,:));
|
||||
y(symbol) = y(symbol) - obj.mu_buff * mean(err_buffer(symbol_idx,:));
|
||||
else
|
||||
%or use 1+alpha*D as adaptive error
|
||||
dc_err(symbol_idx) = dc_err(symbol_idx) + obj.mu_buff * err(symbol);
|
||||
dc_sto(symbol_idx,symbol) = dc_err(symbol_idx);
|
||||
y(symbol) = y(symbol) - dc_err(symbol_idx);
|
||||
end
|
||||
|
||||
if training
|
||||
[~,symbol_idx] = min(abs(d(symbol) - obj.constellation)); % decision for closest constellation point
|
||||
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);
|
||||
end
|
||||
|
||||
err(symbol) = y(symbol) - d_hat(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
|
||||
end
|
||||
|
||||
if mod(sample,100) == 1 && showviz
|
||||
a2.XData = 1:2*numel(y);
|
||||
a2.YData = repelem(y, 2);
|
||||
|
||||
a3.XData = 1:2*numel(d_hat);
|
||||
a3.YData = repelem(d_hat, 2);
|
||||
|
||||
a4.Value = sample;
|
||||
% b.YData = x(symbol:symbol+500);
|
||||
c.YData = obj.e;
|
||||
drawnow;
|
||||
end
|
||||
|
||||
obj.error(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
149
Classes/04_DSP/Equalizer/FFE_adaptive_decision.m
Normal file
149
Classes/04_DSP/Equalizer/FFE_adaptive_decision.m
Normal file
@@ -0,0 +1,149 @@
|
||||
classdef FFE_adaptive_decision < handle
|
||||
% Implementation of plain and simple FFE.
|
||||
% 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);
|
||||
|
||||
properties
|
||||
sps % usually 2
|
||||
order
|
||||
e
|
||||
error
|
||||
|
||||
len_tr
|
||||
mu_tr
|
||||
epochs_tr
|
||||
|
||||
mu_dd
|
||||
epochs_dd
|
||||
|
||||
buffer_length
|
||||
|
||||
constellation
|
||||
|
||||
decide
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = FFE_adaptive_decision(options)
|
||||
arguments(Input)
|
||||
|
||||
options.sps = 2;
|
||||
options.order = 15;
|
||||
|
||||
options.len_tr = 4096;
|
||||
options.mu_tr = 0;
|
||||
options.epochs_tr = 5;
|
||||
|
||||
options.mu_dd = 1e-5;
|
||||
options.epochs_dd = 5;
|
||||
|
||||
options.buffer_length = 100;
|
||||
|
||||
options.decide = false;
|
||||
|
||||
end
|
||||
|
||||
fn = fieldnames(options);
|
||||
for n = 1:numel(fn)
|
||||
obj.(fn{n}) = options.(fn{n});
|
||||
end
|
||||
|
||||
obj.e = zeros(obj.order,1);
|
||||
obj.error = 0;
|
||||
|
||||
end
|
||||
|
||||
function [X] = process(obj, X, D)
|
||||
|
||||
% actual processing of the signal (steps 1. - 3.)
|
||||
% 1 normalize RMS
|
||||
X = X.normalize("mode","rms");
|
||||
|
||||
obj.constellation = unique(D.signal);
|
||||
|
||||
% Training Mode
|
||||
training = 1;
|
||||
showviz = 0;
|
||||
obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training,showviz);
|
||||
|
||||
% Decision Directed Mode
|
||||
N = X.length;
|
||||
training = 0;
|
||||
showviz = 0;
|
||||
[signal,decision]=obj.equalize(X.signal, D.signal,obj.mu_dd,obj.epochs_dd,N,training,showviz);
|
||||
|
||||
% Output Signal
|
||||
if obj.decide
|
||||
X.signal = decision;
|
||||
else
|
||||
X.signal = signal;
|
||||
end
|
||||
X.fs = D.fs; %change sampling frequency of outgoing signal from fdac e.g. 2 sps to symbol spaced = fsym
|
||||
lbdesc = [num2str(obj.order),' tap FFE'];
|
||||
X = X.logbookentry(lbdesc); % append to logbook
|
||||
|
||||
|
||||
end
|
||||
|
||||
function [y,d_hat] = equalize(obj,x,d,mio,epochs,N,training,showviz)
|
||||
|
||||
arguments
|
||||
obj
|
||||
x
|
||||
d
|
||||
mio
|
||||
epochs
|
||||
N
|
||||
training
|
||||
showviz
|
||||
end
|
||||
|
||||
x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)];
|
||||
|
||||
for epoch = 1 : epochs
|
||||
symbol = 0;
|
||||
% y_buffer = zeros(numel(obj.constellation),500);
|
||||
y_buffer = repmat(obj.constellation,1,obj.buffer_length);
|
||||
for sample = 1 : obj.sps : N
|
||||
|
||||
symbol = symbol+1;
|
||||
|
||||
U = x(obj.order+sample-1:-1:sample);
|
||||
|
||||
y(symbol,1) = obj.e.' * U; % Calculating output of LMS __ * |
|
||||
|
||||
if training
|
||||
[~,symbol_idx] = min(abs(d(symbol) - obj.constellation)); % decision for closest constellation point
|
||||
d_hat(symbol,1) = d(symbol);
|
||||
else
|
||||
y_buffer(y_buffer==0) = NaN;
|
||||
adap_constellation = mean(y_buffer,2,"omitnan");
|
||||
[~,symbol_idx] = min(abs(y(symbol) - adap_constellation)); % decision for closest constellation point
|
||||
d_hat(symbol,1) = obj.constellation(symbol_idx);
|
||||
end
|
||||
|
||||
y_buffer(symbol_idx,1) = y(symbol);
|
||||
y_buffer(symbol_idx,:) = circshift(y_buffer(symbol_idx,:),1);
|
||||
|
||||
err(symbol) = y(symbol) - d_hat(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
|
||||
end
|
||||
|
||||
|
||||
obj.error(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
296
Classes/04_DSP/Equalizer/VNLE.m
Normal file
296
Classes/04_DSP/Equalizer/VNLE.m
Normal file
@@ -0,0 +1,296 @@
|
||||
classdef VNLE < handle
|
||||
% Implementation of plain and simple FFE.
|
||||
% 1) Training mode (stable performance when you use NLMS)
|
||||
% 2) Decision directed mode
|
||||
|
||||
% Eq = VNLE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",[0.0004 0.0005 0.0006],"mu_tr",0,"order",[25,2,2],"sps",2,"decide",1);
|
||||
% Somehow it is not possible to use only 1 nonlinear order
|
||||
|
||||
properties
|
||||
sps % usually 2
|
||||
order
|
||||
e
|
||||
error
|
||||
|
||||
len_tr
|
||||
mu_tr
|
||||
epochs_tr
|
||||
|
||||
mu_dd
|
||||
epochs_dd
|
||||
|
||||
constellation
|
||||
|
||||
decide
|
||||
|
||||
x_norm
|
||||
ce
|
||||
ie2
|
||||
ie3
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = VNLE(options)
|
||||
arguments(Input)
|
||||
|
||||
options.sps = 2;
|
||||
options.order = [15,2,2];
|
||||
|
||||
options.len_tr = 4096;
|
||||
options.mu_tr = 0;
|
||||
options.epochs_tr = 5;
|
||||
|
||||
options.mu_dd = 1e-5;
|
||||
options.epochs_dd = 5;
|
||||
|
||||
options.decide = false;
|
||||
|
||||
end
|
||||
|
||||
fn = fieldnames(options);
|
||||
for n = 1:numel(fn)
|
||||
obj.(fn{n}) = options.(fn{n});
|
||||
end
|
||||
|
||||
|
||||
obj.error = 0;
|
||||
|
||||
end
|
||||
|
||||
function [X] = process(obj, X, D)
|
||||
|
||||
% actual processing of the signal (steps 1. - 3.)
|
||||
% 1 normalize RMS
|
||||
X = X.normalize("mode","rms");
|
||||
|
||||
obj.constellation = unique(D.signal);
|
||||
obj.x_norm = obj.calcPowerNormalization(X.signal);
|
||||
obj.ce = obj.calcVNLEMemoryLength(obj.order);
|
||||
[obj.ie2,obj.ie3] = obj.calcIndiceVectors(obj.order);
|
||||
|
||||
obj.e = zeros( sum(obj.ce) ,1);
|
||||
|
||||
% Training Mode
|
||||
training = 1;
|
||||
showviz = 0;
|
||||
obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training,showviz);
|
||||
|
||||
% Decision Directed Mode
|
||||
N = X.length;
|
||||
training = 0;
|
||||
showviz = 0;
|
||||
[signal,decision]=obj.equalize(X.signal, D.signal,obj.mu_dd,obj.epochs_dd,N,training,showviz);
|
||||
|
||||
% Output Signal
|
||||
if obj.decide
|
||||
X.signal = decision;
|
||||
else
|
||||
X.signal = signal;
|
||||
end
|
||||
X.fs = D.fs; %change sampling frequency of outgoing signal from fdac e.g. 2 sps to symbol spaced = fsym
|
||||
lbdesc = [num2str(obj.order),' tap FFE'];
|
||||
X = X.logbookentry(lbdesc); % append to logbook
|
||||
|
||||
|
||||
end
|
||||
|
||||
function [y,d_hat] = equalize(obj,x,d,mu,epochs,N,training,showviz)
|
||||
|
||||
arguments
|
||||
obj
|
||||
x
|
||||
d
|
||||
mu
|
||||
epochs
|
||||
N
|
||||
training
|
||||
showviz
|
||||
end
|
||||
|
||||
if all(mu == mu(1))
|
||||
% mu = mu(1);
|
||||
mu = diag(ones(1,sum(obj.ce))*mu(1));
|
||||
else
|
||||
mu = diag([ones(1,obj.ce(1))*mu(1) ...
|
||||
ones(1,obj.ce(2))*mu(2) ...
|
||||
ones(1,obj.ce(3))*mu(3) ]);
|
||||
end
|
||||
|
||||
x = [zeros(floor(obj.order(1)/2),1); x; zeros(obj.order(1),1)];
|
||||
|
||||
if showviz
|
||||
f = figure(111);
|
||||
subplot(2,2,1:2);
|
||||
hold on
|
||||
a = scatter(1:numel(x),x,1,'.');
|
||||
a2 = scatter(1,1,1,'.');
|
||||
a3 = scatter(1,1,2,'.');
|
||||
a4 = xline(1);
|
||||
ylim([-3 3])
|
||||
xlim([0 length(x)]);
|
||||
subplot(2,2,3:4)
|
||||
c = stem(obj.e);
|
||||
ylim([-1 1])
|
||||
drawnow
|
||||
end
|
||||
|
||||
for epoch = 1 : epochs
|
||||
symbol = 0;
|
||||
for sample = 1 : obj.sps : N
|
||||
|
||||
symbol = symbol+1;
|
||||
|
||||
|
||||
% x_in = x(obj.order(1)+sample+(obj.sps-1):-1:sample+obj.sps);
|
||||
x_in = x(obj.order(1)+sample-1:-1:sample);
|
||||
x_in = obj.calcVNLENonlinVecs(x_in,obj.ie2,obj.ie3,obj.order,obj.x_norm);
|
||||
|
||||
y(symbol,1) = obj.e.' * x_in; % Calculating output of LMS __ * |
|
||||
|
||||
if training
|
||||
err(symbol) = y(symbol) - d(symbol); % Instantaneous error
|
||||
else
|
||||
[~,symbol_idx] = min(abs(y(symbol) - obj.constellation)); % decision for closest constellation point
|
||||
d_hat(symbol,1) = obj.constellation(symbol_idx);
|
||||
err(symbol) = y(symbol) - d_hat(symbol); % Instantaneous error
|
||||
end
|
||||
|
||||
if ~all(mu==0,'all') %mu has not only zeros
|
||||
obj.e = obj.e - (mu * err(symbol) * x_in) ; % Weight update rule of LMS
|
||||
else
|
||||
normalizationfactor = (x_in.' * x_in);
|
||||
obj.e = obj.e - err(symbol) * x_in / normalizationfactor; % Weight update rule of NLMS
|
||||
end
|
||||
|
||||
if mod(sample,100) == 1 && showviz
|
||||
a2.XData = 1:2*numel(y);
|
||||
a2.YData = repelem(y, 2);
|
||||
a3.XData = 1:2*numel(d_hat);
|
||||
a3.YData = repelem(d_hat, 2);
|
||||
a4.Value = sample;
|
||||
% b.YData = x(symbol:symbol+500);
|
||||
c.YData = obj.e;
|
||||
drawnow;
|
||||
end
|
||||
|
||||
obj.error(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
indvec2nd=[];
|
||||
indvec3rd=[];
|
||||
for o = 2:numel(N)
|
||||
n = N(o);
|
||||
v = 1:n; % Ursprünglicher Vektor
|
||||
row = 1;
|
||||
|
||||
% Schleifen zur Generierung des Indize Vektors
|
||||
switch o
|
||||
|
||||
case 2
|
||||
|
||||
indvec2nd = zeros(n*(n+1)/2, o);
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user