159 lines
4.7 KiB
Matlab
159 lines
4.7 KiB
Matlab
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
|
||
|