Many changes here and there. I lost track... :-(
Current work is on MLSE and SD Decoding etc. MLSE is currently not 100% working, the scalings are maybe off?!
This commit is contained in:
@@ -83,6 +83,14 @@ classdef MLSE < handle
|
||||
% impulse respnse to remove from signal
|
||||
obj.DIR = flip(obj.DIR); %i.e. -0.2676 -0.0478 1.0000
|
||||
|
||||
% % make the combined impulse-response have net gain = 1
|
||||
% h = obj.DIR(:);
|
||||
% h = h / sum(h);
|
||||
% obj.DIR = h.';
|
||||
|
||||
% Normalize the Trellis states to =1 RMS
|
||||
obj.trellis_states = obj.trellis_states ./ rms(obj.trellis_states);
|
||||
|
||||
% seems to be the only way to use combvec for a flexible amount
|
||||
% of vectors. 'combs' contains all trellis states
|
||||
pre_comb_mat = repmat(obj.trellis_states,length(obj.DIR)-1,1);
|
||||
@@ -116,6 +124,7 @@ classdef MLSE < handle
|
||||
|
||||
% first: RMS normalization of input data (rms==1)
|
||||
data_in = data_in ./ rms(data_in);
|
||||
data_in = data_in - mean(data_in);
|
||||
|
||||
% then, match amplitude levels of input signal to those of the calculated ideal symbols
|
||||
% i.e. match the rms values of data_in to noise_free_received (rms=1.xx)
|
||||
@@ -125,6 +134,10 @@ classdef MLSE < handle
|
||||
end
|
||||
end
|
||||
|
||||
y_clean = conv( data_ref, flip(obj.DIR), "same" );
|
||||
sigma2 = var( data_in - y_clean );
|
||||
inv2s2 = 1/(2*sigma2);
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%% FORWARD PASS (VITERBI -Alpha's) %%%%%
|
||||
|
||||
@@ -134,7 +147,7 @@ classdef MLSE < handle
|
||||
|
||||
% first start is evaluated without ISI/ wihout the full Impulse response
|
||||
% so simply use the constellation here
|
||||
bm = -(data_in(1) - last_sym).^2 ;
|
||||
bm = -(data_in(1) - last_sym).^2 * inv2s2;
|
||||
pm = pm + bm;
|
||||
[alpha(:,1),pm_survivor_fw_idx(:,1)] = max(pm,[],2);
|
||||
pm = repmat(alpha(:,1).',nStates,1);
|
||||
@@ -143,10 +156,10 @@ classdef MLSE < handle
|
||||
% Forward Recursion (FSM Computation)
|
||||
for n = 2:length(data_in)
|
||||
|
||||
bm = -(data_in(n) - noise_free_received).^2 ;
|
||||
bm = -(data_in(n) - noise_free_received).^2 * inv2s2;
|
||||
pm = pm + bm;
|
||||
[alpha(:,n),pm_survivor_fw_idx(:,n)] = max(pm,[],2); % choose lowest path metric as new state
|
||||
pm = repmat(alpha(:,n).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state)
|
||||
[alpha(:,n),pm_survivor_fw_idx(:,n)] = max(pm,[],2); % choose lowest path metric as new state
|
||||
pm = repmat(alpha(:,n).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state)
|
||||
|
||||
bm_fw(:,:,n) = bm;
|
||||
|
||||
@@ -177,7 +190,7 @@ classdef MLSE < handle
|
||||
% predecessor
|
||||
for h = length(data_in)-1:-1:1
|
||||
|
||||
bm = -(data_in(h+1) - noise_free_received).^2 ;
|
||||
bm = -(data_in(h+1) - noise_free_received).^2 * inv2s2;
|
||||
pm = pm + bm.';
|
||||
[beta(:,h),pm_survivor_bw_idx(:,h)] = max(pm,[],2); % choose lowest path metric as new state
|
||||
pm = repmat(beta(:,h).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state)
|
||||
@@ -214,10 +227,22 @@ classdef MLSE < handle
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%% FORWARD PASS PAM2,4,8 %%%%%
|
||||
|
||||
nml_LLP = LLP - max(LLP);
|
||||
expLLP = exp(nml_LLP); %subtract highst value for better numerical stability, LLP's are not always close to zero
|
||||
state_prob = expLLP ./ sum(expLLP);
|
||||
nml_LLP = LLP - max(LLP); %subtract highest value for better numerical stability, LLP's are not always close to zero
|
||||
expLLP = exp(nml_LLP);
|
||||
state_prob = expLLP ./ sum(expLLP); % sums to one (or numerically close to one)
|
||||
|
||||
% compute symbol‐posteriors from LLP in the log‐domain:
|
||||
amax = max(LLP,[],1);
|
||||
logZ = amax + log(sum(exp(LLP - amax), 1));
|
||||
logPstate = LLP - logZ; % still in log‐domain
|
||||
state_prob = exp(logPstate); % exact, sums to 1
|
||||
|
||||
% figure
|
||||
% hold on;
|
||||
% for i = 1:obj.M
|
||||
% scatter(1:length(expLLP),expLLP(i,:),1,'.');
|
||||
% end
|
||||
% scatter(1:length(expLLP),max(expLLP(:,:)),3,'.');
|
||||
|
||||
if obj.M == 6
|
||||
|
||||
@@ -305,8 +330,8 @@ classdef MLSE < handle
|
||||
|
||||
% Number of symbols and bits per symbol
|
||||
num_bits = log2(length(obj.trellis_states)); % 2 bits per symbol
|
||||
bit_mapping = PAMmapper(length(obj.trellis_states),0,"eth_style",0).demap(first_sym./rms(first_sym));
|
||||
|
||||
% bit_mapping = PAMmapper(length(obj.trellis_states),0,"eth_style",0).demap(first_sym./rms(first_sym));
|
||||
bit_mapping = PAMmapper(length(obj.trellis_states),0,"eth_style",0).showBitMapping;
|
||||
% Initialize LLR storage
|
||||
LLR_maxlogmap = zeros(length(data_in),num_bits);
|
||||
LLR_exact = zeros(length(data_in),num_bits);
|
||||
@@ -343,8 +368,8 @@ classdef MLSE < handle
|
||||
llr1 = LLR_exact(idx_bit_0,k);
|
||||
|
||||
% Calculate mutual information for bit position k
|
||||
I0 = mean(log2(1 + exp(llr0(100:end-100)))); % exp(--LLR) = exp(positive) > 1
|
||||
I1 = mean(log2(1 + exp(-llr1(100:end-100)))); % exp(-+LLR) = exp(negative) < 1
|
||||
I0 = mean(log2(1 + exp(llr0))); % exp(--LLR) = exp(positive) > 1
|
||||
I1 = mean(log2(1 + exp(-llr1))); % exp(-+LLR) = exp(negative) < 1
|
||||
MI(k) = 1 - 0.5 * (I0 + I1);
|
||||
end
|
||||
|
||||
@@ -354,8 +379,7 @@ classdef MLSE < handle
|
||||
|
||||
VITERBI_ESTIMATION_SYMBOLS = VITERBI_ESTIMATION_SYMBOLS./rms(VITERBI_ESTIMATION_SYMBOLS);
|
||||
|
||||
|
||||
debug = 0;
|
||||
debug = 1;
|
||||
if debug
|
||||
%%% DEBUG PLOT LIKELIHOOD RATIOS %%%
|
||||
figure(115);clf
|
||||
@@ -380,6 +404,7 @@ classdef MLSE < handle
|
||||
if debug
|
||||
tx_bits = reshape(tx_bits',[],1);
|
||||
|
||||
disp('Start DEBUG MLSE:')
|
||||
% DECIDE based on Viterbi traceback
|
||||
VITERBI_ESTIMATION_SYMBOLS = VITERBI_ESTIMATION_SYMBOLS./rms(VITERBI_ESTIMATION_SYMBOLS);
|
||||
rx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(VITERBI_ESTIMATION_SYMBOLS');
|
||||
@@ -417,13 +442,19 @@ classdef MLSE < handle
|
||||
rx_bits = reshape(rx_bits',[],1);
|
||||
[~,~,ber_fw,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
|
||||
fprintf('FW BER: %.2e \n',ber_fw);
|
||||
|
||||
disp('Stop DEBUG MLSE:')
|
||||
disp('')
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
methods (Access=private)
|
||||
function s = logsumexp(a,dim)
|
||||
% returns log(sum(exp(a),dim)) safely
|
||||
amax = max(a,[],dim);
|
||||
s = amax + log(sum(exp(a - amax), dim));
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
152
Classes/04_DSP/Sequence Detection/MLSE_new.m
Normal file
152
Classes/04_DSP/Sequence Detection/MLSE_new.m
Normal file
@@ -0,0 +1,152 @@
|
||||
classdef MLSE_new < handle
|
||||
%MLSE: BCJR‐based soft‐output for PAM‐M sequence estimation & GMI
|
||||
|
||||
properties
|
||||
M % PAM order (e.g. 4)
|
||||
DIR % channel impulse response
|
||||
trellis_states % PAM constellation levels (e.g. [-3 -1 1 3])
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = MLSE_new(opts)
|
||||
arguments
|
||||
opts.M double = 4;
|
||||
opts.DIR double = 1;
|
||||
opts.trellis_states double = [-3 -1 1 3];
|
||||
end
|
||||
obj.M = opts.M;
|
||||
obj.DIR = opts.DIR;
|
||||
obj.trellis_states = opts.trellis_states;
|
||||
end
|
||||
|
||||
function [hd_out, LLR, NGMI] = process(obj, signalclass,ref_symbolclass)
|
||||
% rx, tx: column vectors of equalized and reference symbols
|
||||
data_in = signalclass.signal;
|
||||
shape_in = size(data_in);
|
||||
data_ref = ref_symbolclass.signal;
|
||||
|
||||
[data_out_hd,LLR,GMI] = obj.bcjr_soft(data_in,data_ref);
|
||||
try
|
||||
data_out_hd = reshape(data_out_hd,shape_in(1),shape_in(2));
|
||||
catch
|
||||
warning('output reshaping failed after MLSE');
|
||||
end
|
||||
|
||||
signalclass_hd = signalclass;
|
||||
signalclass_hd.signal = data_out_hd;
|
||||
|
||||
|
||||
|
||||
|
||||
[hd_out, LLR, NGMI] = obj.bcjr_soft(rx, tx);
|
||||
end
|
||||
end
|
||||
|
||||
methods (Access=private)
|
||||
function [hd_sym, LLR, NGMI] = bcjr_soft(obj, y, x)
|
||||
%--- build trellis states & branch outputs ---
|
||||
DIR = flip(obj.DIR(:));
|
||||
S = combvec(obj.trellis_states, obj.trellis_states)';
|
||||
prev = S(:,1); cur = S(:,2);
|
||||
branch_out = prev*DIR(1) + cur*DIR(2); % for 2‐tap
|
||||
|
||||
N = numel(y);
|
||||
M = obj.M;
|
||||
m = log2(M);
|
||||
|
||||
%--- forward/backward metrics (max‐log) ---
|
||||
nStates = numel(obj.trellis_states);
|
||||
alpha = -inf(nStates,N);
|
||||
beta = -inf(nStates,N);
|
||||
bm_fw = zeros(nStates,nStates,N);
|
||||
|
||||
% branch metrics
|
||||
sigma2 = var(y - x);
|
||||
inv2sigma2 = 1/(2*sigma2);
|
||||
for n=1:N
|
||||
bm = -((y(n)-branch_out).^2)* inv2sigma2;
|
||||
bm = reshape(bm, nStates, nStates);
|
||||
bm_fw(:,:,n) = bm;
|
||||
end
|
||||
% forward
|
||||
alpha(:,1) = max(bm_fw(:,:,1),[],2);
|
||||
for n=2:N
|
||||
mat = alpha(:,n-1) + bm_fw(:,:,n);
|
||||
alpha(:,n) = max(mat,[],2);
|
||||
end
|
||||
% backward
|
||||
beta(:,N) = 0;
|
||||
for n=N-1:-1:1
|
||||
mat = beta(:,n+1).' + bm_fw(:,:,n+1);
|
||||
beta(:,n) = max(mat,[],2);
|
||||
end
|
||||
|
||||
% Precompute the “zero‐th” forward metric
|
||||
alpha0 = zeros(nStates,1);
|
||||
|
||||
LLP = -inf(nStates,nStates,N);
|
||||
for n = 1:N
|
||||
% Select the correct previous alpha column
|
||||
if n == 1
|
||||
a_prev = alpha0;
|
||||
else
|
||||
a_prev = alpha(:,n-1);
|
||||
end
|
||||
|
||||
% Combine α, branch metric, and β
|
||||
mat = a_prev + bm_fw(:,:,n) + beta(:,n).';
|
||||
LLP(:,:,n) = mat;
|
||||
end
|
||||
|
||||
%--- compute LLRs symbol‐wise via log‐sum‐exp over branches ---
|
||||
levels = obj.trellis_states;
|
||||
bit_map = PAMmapper(M,0).demap(levels');
|
||||
LLR = zeros(N,m);
|
||||
for n=1:N
|
||||
% sum branch posteriors per symbol
|
||||
llp_n = LLP(:,:,n);
|
||||
logZ = logsumexp(llp_n(:));
|
||||
post = exp(llp_n - logZ);
|
||||
% aggregate per symbol index
|
||||
Psym = zeros(M,1);
|
||||
for i=1:nStates
|
||||
for j=1:nStates
|
||||
% branch (i->j) emits symbol 'cur(j)'
|
||||
idx = find(levels==cur(j));
|
||||
Psym(idx) = Psym(idx) + post(i,j);
|
||||
end
|
||||
end
|
||||
% bit‐LLR from symbol posterior
|
||||
for k=1:m
|
||||
idx1 = bit_map(:,k)==1;
|
||||
idx0 = ~idx1;
|
||||
P1 = sum(Psym(idx1));
|
||||
P0 = sum(Psym(idx0));
|
||||
LLR(n,k) = log(P1/P0);
|
||||
end
|
||||
end
|
||||
|
||||
%--- hard decisions ---
|
||||
[~,symIdx] = max(LLR,[],2);
|
||||
hd_sym = levels(symIdx);
|
||||
|
||||
%--- GMI via Alvarado Eq.(30) ---
|
||||
tx_bits = PAMmapper(M,0).demap(x);
|
||||
MI = zeros(1,m);
|
||||
for k=1:m
|
||||
r0 = LLR(tx_bits(:,k)==0,k);
|
||||
r1 = LLR(tx_bits(:,k)==1,k);
|
||||
I0 = mean(log2(1+exp(-r0)));
|
||||
I1 = mean(log2(1+exp(+r1)));
|
||||
MI(k) = 1 - 0.5*(I0+I1);
|
||||
end
|
||||
GMI = sum(MI);
|
||||
NGMI = GMI/m;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function s = logsumexp(a)
|
||||
m = max(a(:));
|
||||
s = m + log(sum(exp(a-m), 'all'));
|
||||
end
|
||||
Reference in New Issue
Block a user