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:
@@ -269,6 +269,7 @@ classdef Signal
|
||||
SignalPower = [obj.power];
|
||||
Nase = [0];
|
||||
SignalCopy = obj.signal;
|
||||
SignalCopy = [];
|
||||
ModifierName = class(CallingModifier);
|
||||
|
||||
|
||||
@@ -365,7 +366,7 @@ classdef Signal
|
||||
if options.normalizeTo0dB
|
||||
p_lin = p_lin./ max(p_lin);
|
||||
p_dbm = 10*log10(p_lin); % normalized to 0 dB
|
||||
ylab = "normalized to 0 dB";
|
||||
ylab = "Normalized PSD";
|
||||
else
|
||||
p_dbm = 10*log10(p_lin);
|
||||
ylab = "Power (dB/Hz)";
|
||||
@@ -395,7 +396,7 @@ classdef Signal
|
||||
if options.normalizeToNyquist == 0
|
||||
xlabel("Frequency in GHz");
|
||||
edgetick = 2^(nextpow2(obj.fs*1e-9));
|
||||
xticks(-edgetick:16:edgetick);
|
||||
xticks(-edgetick:32:edgetick);
|
||||
xlim([100*round(min(w)/100,1)-10, 100*round(max(w)/100,1)+10])
|
||||
xlim([-128 128]);%256GSa/s
|
||||
else
|
||||
@@ -406,13 +407,20 @@ classdef Signal
|
||||
ylabel(ylab);
|
||||
|
||||
try
|
||||
ylim([max(min(floor(min(p_dbm))-3, ax.YLim(1)),-40), min(max(ceil(max(p_dbm))+3, ax.YLim(2)),10)]);
|
||||
ylim([max(min(floor(min(p_dbm))-3, ax.YLim(1)),-40), min(max(ceil(max(p_dbm))+3, ax.YLim(2)),10)]);
|
||||
catch
|
||||
ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]);
|
||||
ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]);
|
||||
end
|
||||
ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]);
|
||||
|
||||
if options.normalizeTo0dB
|
||||
% ylim([-40, 3]);
|
||||
ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]);
|
||||
else
|
||||
ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]);
|
||||
end
|
||||
|
||||
yticks(-200:10:10);
|
||||
grid on; grid minor;
|
||||
grid on;% grid minor;
|
||||
legend
|
||||
% legend('Interpreter','none');
|
||||
|
||||
@@ -912,8 +920,8 @@ classdef Signal
|
||||
elseif mode == 1
|
||||
% generate eye diagram using histogram
|
||||
|
||||
maxA = max(sig(100:end-100))*2;
|
||||
minA = min(sig(100:end-100))*2;
|
||||
maxA = max(sig(100:end-100))*1.3;
|
||||
minA = min(sig(100:end-100))*1.3;
|
||||
|
||||
% maxA = 0.0015;
|
||||
% minA = 0;
|
||||
@@ -926,8 +934,8 @@ classdef Signal
|
||||
nn=histcounts(data_ind_y(n,:),1:histpoints+1);
|
||||
hist_data(:,n)=flip(nn.'); %without flip, the eye is upside down :-(
|
||||
end
|
||||
|
||||
ax = gca;
|
||||
|
||||
|
||||
|
||||
plot_data = 20*log10(hist_data);
|
||||
plot_data(plot_data==-Inf) = 0;
|
||||
@@ -944,27 +952,29 @@ classdef Signal
|
||||
if isa(obj,'Opticalsignal')
|
||||
title(['Optical Eye ',options.displayname])
|
||||
ylabel("Power in mW");
|
||||
y_tickstring = string(linspace(maxA.*1e3,minA.*1e3,16));
|
||||
y_tickstring = string(linspace(maxA.*1e3,minA.*1e3,6));
|
||||
min_ = min(abs(obj.signal(100:end-100)).^2);
|
||||
max_ = abs(max(obj.signal(100:end-100)).^2);
|
||||
elseif isa(obj,'Electricalsignal')
|
||||
title(['Electrical Eye ',options.displayname])
|
||||
ylabel("Voltage in V");
|
||||
y_tickstring = string(linspace(maxA,minA,16));
|
||||
y_tickstring = string(linspace(maxA,minA,6));
|
||||
min_ = min(obj.signal(100:end-100));
|
||||
max_ = abs(max(obj.signal(100:end-100)));
|
||||
else
|
||||
title(['Digital Eye ',options.displayname])
|
||||
ylabel("Digital Signal Amplitude");
|
||||
y_tickstring = string(linspace(maxA,minA,16));
|
||||
y_tickstring = string(linspace(maxA,minA,6));
|
||||
min_ = min(obj.signal(100:end-100));
|
||||
max_ = abs(max(obj.signal(100:end-100)));
|
||||
end
|
||||
xlabel('Time in ps')
|
||||
|
||||
|
||||
|
||||
% add information
|
||||
|
||||
if 0
|
||||
if 1
|
||||
|
||||
pwr_dbm = round(obj.power,3);
|
||||
pwr_lin = obj.power("unit",power_notation.W);
|
||||
@@ -1073,13 +1083,15 @@ classdef Signal
|
||||
|
||||
end
|
||||
|
||||
yticks(linspace(0,histpoints,16));
|
||||
yticks(linspace(0,histpoints,6));
|
||||
y_tickstring = sprintfc('%.2f', y_tickstring);
|
||||
yticklabels(y_tickstring);
|
||||
|
||||
xticks(linspace(0,histpoints_horizontal,8))
|
||||
xticks(linspace(0,histpoints_horizontal,6))
|
||||
x_tickstring = sprintfc('%.2f', linspace(0, 2/fsym, 8) .* 1e12);
|
||||
xticklabels(x_tickstring);
|
||||
|
||||
grid off
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ classdef M8199B < AWG
|
||||
|
||||
fdac = 256e9;
|
||||
|
||||
Lp_awg = Filter('filtdegree',4,"f_cutoff",75e9,"fs",fdac*options.kover,"filterType",filtertypes.butterworth,"active",true);
|
||||
Lp_awg = Filter('filtdegree',3,"f_cutoff",75e9,"fs",fdac*options.kover,"filterType",filtertypes.gaussian,"active",true);
|
||||
|
||||
obj = obj@AWG("fdac",fdac,"dac_min",dac_min,"dac_max",dac_max,"lpf_active",1,"H_lpf",Lp_awg,"kover",options.kover,...
|
||||
"bit_resolution",5.5,"normalize2dac",1,"upsampling_method","samplehold");
|
||||
|
||||
@@ -136,6 +136,13 @@ classdef PAMsource
|
||||
|
||||
%%%%%% Duobinary %%%%%%%%%%%
|
||||
|
||||
if obj.db_precode
|
||||
obj.duobinary_mode = db_mode.db_precoded;
|
||||
end
|
||||
if obj.db_encode
|
||||
obj.duobinary_mode = db_mode.db_encoded;
|
||||
end
|
||||
|
||||
switch obj.duobinary_mode
|
||||
|
||||
case db_mode.no_db
|
||||
|
||||
@@ -1,189 +1,171 @@
|
||||
classdef Fiber
|
||||
%FIBER Summary of this class goes here
|
||||
% Detailed explanation goes here
|
||||
% Fiber: Simulate optical fiber signal propagation using
|
||||
% the split-step Fourier method (SSFM).
|
||||
|
||||
properties
|
||||
% Simulation sampling frequency [Hz]
|
||||
fsimu
|
||||
% Fiber length [m]
|
||||
fiber_length
|
||||
% Attenuation coefficient [dB/km]
|
||||
alpha
|
||||
% Dispersion parameter D [s/m^2]
|
||||
D
|
||||
% Dispersion slope [s/m^3]
|
||||
Dslope
|
||||
% Reference wavelength [m]
|
||||
lambda0
|
||||
% Nonlinear coefficient [1/(W·m)]
|
||||
gamma
|
||||
% Maximum allowed nonlinear phase change per step [rad]
|
||||
dphimax
|
||||
|
||||
% Derived parameters:
|
||||
% Second-order dispersion coefficient β2 [s^2/m]
|
||||
b2
|
||||
% Third-order dispersion coefficient β3 [s^3/m]
|
||||
b3
|
||||
% Linear attenuation constant [1/m]
|
||||
alpha_lin
|
||||
% Frequency-domain linear operator per unit length
|
||||
linstep
|
||||
end
|
||||
|
||||
methods
|
||||
function obj = Fiber(options)
|
||||
%FIBER Construct an instance of this class
|
||||
% Detailed explanation goes here
|
||||
% Constructor: initialize fiber physical and simulation parameters.
|
||||
arguments
|
||||
options.fsimu
|
||||
options.fiber_length = 0
|
||||
options.alpha = 0.2
|
||||
options.D = 17
|
||||
options.Dslope = 0.06
|
||||
options.lambda0 = 1550
|
||||
options.gamma = 0
|
||||
options.dphimax = 5e-3
|
||||
options.fsimu % Sampling frequency [Hz]
|
||||
options.fiber_length = 0 % Fiber length [km]
|
||||
options.alpha = 0.2 % Attenuation [dB/km]
|
||||
options.D = 17 % Dispersion D parameter [ps/(nm·km)]
|
||||
options.Dslope = 0.06 % Dispersion slope [ps/(nm^2·km)]
|
||||
options.lambda0 = 1550 % Reference wavelength [nm]
|
||||
options.gamma = 0 % Nonlinear coefficient [1/(W·km)]
|
||||
options.dphimax = 5e-3 % Max phase step [rad]
|
||||
end
|
||||
|
||||
obj.fsimu = options.fsimu;
|
||||
obj.fiber_length = options.fiber_length*1000; %km
|
||||
obj.alpha = options.alpha;
|
||||
obj.D = options.D*1e-6;
|
||||
obj.Dslope = options.Dslope*1e3;
|
||||
obj.lambda0 = options.lambda0*1e-9;
|
||||
obj.gamma = options.gamma;
|
||||
obj.dphimax = options.dphimax;
|
||||
|
||||
|
||||
|
||||
% Assign inputs to object properties, converting units to SI.
|
||||
obj.fsimu = options.fsimu;
|
||||
obj.fiber_length = options.fiber_length * 1e3; % km → m
|
||||
obj.alpha = options.alpha;
|
||||
obj.D = options.D * 1e-6; % ps/(nm·km) → s/(m·m)
|
||||
obj.Dslope = options.Dslope * 1e3; % ps/(nm^2·km) → s/m^2
|
||||
obj.lambda0 = options.lambda0 * 1e-9; % nm → m
|
||||
obj.gamma = options.gamma; % 1/(W·km) (assumed → 1/(W·m) internally)
|
||||
obj.dphimax = options.dphimax;
|
||||
end
|
||||
|
||||
function signalclass_out = process(obj,signalclass_in)
|
||||
function signalclass_out = process(obj, signalclass_in)
|
||||
% process: Apply fiber propagation to input signal class.
|
||||
% Calls the internal SSFM routine and logs the operation.
|
||||
|
||||
% actual processing of the signal (steps 1. - 3.)
|
||||
signalclass_in = obj.process_(signalclass_in);
|
||||
% Run internal split-step propagation
|
||||
signalclass = obj.process_(signalclass_in);
|
||||
|
||||
% append to logbook
|
||||
lbdesc = 'Fiber ';
|
||||
signalclass_in = signalclass_in.logbookentry(lbdesc);
|
||||
|
||||
% write to output
|
||||
signalclass_out = signalclass_in;
|
||||
% Add entry to logbook for tracking
|
||||
signalclass = signalclass.logbookentry('Fiber ');
|
||||
|
||||
% Return processed signal class
|
||||
signalclass_out = signalclass;
|
||||
end
|
||||
|
||||
function opt_sig = process_(obj,opt_sig)
|
||||
%METHOD1 Summary of this method goes here
|
||||
% Detailed explanation goes here
|
||||
function opt_sig = process_(obj, opt_sig)
|
||||
% process_: Internal routine for one-step fiber propagation.
|
||||
% Computes linear and (optionally) nonlinear effects.
|
||||
|
||||
signal = opt_sig.signal;
|
||||
% Extract time-domain field and wavelength
|
||||
signal = opt_sig.signal;
|
||||
lambda_signal = opt_sig.lambda;
|
||||
|
||||
obj.D = obj.D + (lambda_signal-obj.lambda0)*obj.Dslope;
|
||||
|
||||
obj.b2 = -obj.D*lambda_signal^2/(2*pi*Constant.LightSpeed);
|
||||
obj.b3 = ((lambda_signal.^2/(2*pi*Constant.LightSpeed)).^2*obj.Dslope);
|
||||
obj.alpha_lin = obj.alpha/10*log(10)/1000;
|
||||
|
||||
% Update dispersion parameter D for current wavelength
|
||||
obj.D = obj.D + (lambda_signal - obj.lambda0) * obj.Dslope;
|
||||
|
||||
% Compute dispersion coefficients (β2, β3)
|
||||
obj.b2 = -obj.D * lambda_signal^2 / (2 * pi * Constant.LightSpeed);
|
||||
obj.b3 = (lambda_signal^2 / (2 * pi * Constant.LightSpeed))^2 * obj.Dslope;
|
||||
|
||||
% Convert attenuation from dB/km to linear 1/m
|
||||
obj.alpha_lin = obj.alpha / 10 * log(10) / 1e3;
|
||||
|
||||
% Build frequency axis for FFT operations
|
||||
N = length(signal);
|
||||
faxis = linspace(-obj.fsimu/2,obj.fsimu/2,N+1);
|
||||
faxis = ifftshift(faxis(:,1:end-1));
|
||||
faxis = faxis';
|
||||
|
||||
obj.linstep = -obj.alpha_lin/2 - 2*1j*pi^2*obj.b2*faxis.^2 - 4/3*1j*pi^3*obj.b3*faxis.^3;
|
||||
faxis = linspace(-obj.fsimu/2, obj.fsimu/2, N+1);
|
||||
faxis = faxis(1:end-1); % drop redundant endpoint
|
||||
faxis = ifftshift(faxis)'; % center zero frequency
|
||||
|
||||
if 0
|
||||
H = exp((obj.linstep)*obj.fiber_length);
|
||||
|
||||
figure(222)
|
||||
hold on
|
||||
plot(faxis.*1e-9,abs(real(Y)),'LineStyle','-','DisplayName','Abs(real) part of complex TF');
|
||||
xlabel("Frequency in GHz")
|
||||
ylabel("$ R|(H(\omega, L))|$")
|
||||
end
|
||||
% Define linear operator per meter in frequency domain
|
||||
obj.linstep = -obj.alpha_lin/2 ... % half-step loss
|
||||
- 1j*2*pi^2*obj.b2 .* faxis.^2 ... % second-order dispersion
|
||||
- 1j*(4/3)*pi^3*obj.b3 .* faxis.^3; % third-order dispersion
|
||||
|
||||
% Choose linear-only or nonlinear SSFM based on gamma
|
||||
if obj.gamma ~= 0
|
||||
% Full adaptive SSFM with nonlinear Schrödinger solver
|
||||
opt_out = obj.NLSE(signal);
|
||||
else
|
||||
opt_out = ifft( fft(signal) .* exp(obj.linstep*obj.fiber_length) ); % only one linear step
|
||||
% Single-step linear propagation in frequency domain
|
||||
opt_out = ifft( fft(signal) .* exp(obj.linstep * obj.fiber_length) );
|
||||
end
|
||||
|
||||
%TODO: attenuate nase ...
|
||||
|
||||
% Update output field in signal class
|
||||
opt_sig.signal = opt_out;
|
||||
|
||||
end
|
||||
|
||||
function [yout] = NLSE(obj, xin)
|
||||
|
||||
maxPow = obj.gamma.*max(abs(xin).^2);
|
||||
|
||||
Leff = obj.dphimax / maxPow ;
|
||||
|
||||
dz = Leff;
|
||||
function yout = NLSE(obj, xin)
|
||||
% NLSE: Solve nonlinear Schrödinger equation by adaptive split-step
|
||||
% xin: input time-domain field
|
||||
% Returns yout: output time-domain field after propagation
|
||||
|
||||
% Initial estimate of effective length per step
|
||||
maxPow = obj.gamma * max(abs(xin).^2);
|
||||
Leff = obj.dphimax / maxPow;
|
||||
dz = Leff;
|
||||
z_prop = 0;
|
||||
|
||||
yout = fft(xin);
|
||||
|
||||
yout = ((yout).*exp(obj.linstep*dz/2));
|
||||
% Apply initial half-step linear operator
|
||||
yout = fft(xin) .* exp(obj.linstep * dz/2);
|
||||
|
||||
% Loop until full fiber length is reached
|
||||
while true
|
||||
|
||||
% Inverse FFT to time domain for nonlinear phase shift
|
||||
yout = ifft(yout);
|
||||
|
||||
Leff = dz;
|
||||
|
||||
% Nonlinear phase rotation per segment
|
||||
power = abs(yout).^2;
|
||||
Hnl = exp( -1j*obj.gamma*power*Leff);
|
||||
yout = yout .* Hnl;
|
||||
Hnl = exp(-1j * obj.gamma * power * dz);
|
||||
yout = yout .* Hnl;
|
||||
|
||||
% Accumulate propagation distance
|
||||
z_prop = z_prop + dz;
|
||||
|
||||
maxPow = obj.gamma*max(abs(yout).^2);
|
||||
Leff = obj.dphimax/maxPow;
|
||||
|
||||
dz_new = Leff;
|
||||
% Compute adaptive step for next interval
|
||||
maxPow = obj.gamma * max(abs(yout).^2);
|
||||
dz_new = obj.dphimax / maxPow;
|
||||
|
||||
% If remaining length shorter than new step, finish loop
|
||||
if z_prop + dz_new > obj.fiber_length
|
||||
dz_new = obj.fiber_length - z_prop;
|
||||
break
|
||||
break;
|
||||
end
|
||||
|
||||
yout = fft(yout);
|
||||
|
||||
yout = ((yout).*exp(obj.linstep*(dz/2+dz_new/2)));
|
||||
|
||||
dz = dz_new;
|
||||
|
||||
% Half-step linear operator bridging segments
|
||||
yout = fft(yout) .* exp(obj.linstep * ((dz/2) + (dz_new/2)));
|
||||
dz = dz_new;
|
||||
end
|
||||
|
||||
yout = fft(yout);
|
||||
|
||||
yout = ((yout).*exp(obj.linstep*(dz/2+dz_new/2)));
|
||||
|
||||
% Final propagation segment: combine half-steps and nonlinear
|
||||
yout = fft(yout) .* exp(obj.linstep * ((dz/2) + (dz_new/2)));
|
||||
yout = ifft(yout);
|
||||
|
||||
Leff = dz;
|
||||
|
||||
% Last nonlinear phase shift
|
||||
power = abs(yout).^2;
|
||||
Hnl = exp(-1j * obj.gamma * power * dz_new);
|
||||
yout = yout .* Hnl;
|
||||
|
||||
Hnl = exp( -1j*obj.gamma*power*Leff);
|
||||
|
||||
yout = yout .* Hnl;
|
||||
|
||||
yout = fft(yout);
|
||||
|
||||
yout = ((yout).*exp(obj.linstep*(dz/2)));
|
||||
|
||||
% Final half-step linear operator to complete SSFM
|
||||
yout = fft(yout) .* exp(obj.linstep * (dz_new/2));
|
||||
yout = ifft(yout);
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -44,8 +44,6 @@ classdef DBHandler < handle
|
||||
|
||||
% datasource = "jdbc:mysql://134.245.243.254:3306/labor";
|
||||
|
||||
|
||||
|
||||
obj.conn = database( ...
|
||||
string(obj.dataBase), ... % Database name
|
||||
"silas", ... % Username
|
||||
@@ -645,7 +643,7 @@ classdef DBHandler < handle
|
||||
cleanedTable.(varNames{i}) = numCol;
|
||||
else
|
||||
% Clean double-quoted SQL literals (e.g., ""no_db"")
|
||||
cleanedTable.(varNames{i}) = strrep(string(col), '""', '"');
|
||||
cleanedTable.(varNames{i}) = strrep(string(col), '"', '');
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user