# Conflicts:
#	Classes/00_signals/Signal.m
#	Functions/EQ_structures/duobinary_signaling.m
#	Functions/EQ_structures/vnle_postfilter_mlse.m
#	Functions/EQ_visuals/showLevelScatter.m
This commit is contained in:
Silas Labor Zizou
2025-12-15 15:44:26 +01:00
72 changed files with 3085 additions and 4654 deletions

View File

@@ -5,6 +5,7 @@ classdef Opticalsignal < Signal
properties
nase
lambda
polrot
end
@@ -18,6 +19,7 @@ classdef Opticalsignal < Signal
options.logbook
options.lambda
options.nase
options.polrot
end
obj = obj@Signal(signal);

View File

@@ -108,6 +108,7 @@ classdef Signal
options.logbook
options.nase
options.lambda
options.polrot
end
fn = fieldnames(options);
@@ -122,7 +123,7 @@ classdef Signal
%convert to optical
o_sig = Opticalsignal(obj.signal,"fs",obj.fs,"lambda",options.lambda,"logbook",obj.logbook,"nase",options.nase);
o_sig = Opticalsignal(obj.signal,"fs",obj.fs,"lambda",options.lambda,"logbook",obj.logbook,"nase",options.nase,"polrot",options.polrot);
elseif isa(obj,'Informationsignal')
@@ -141,7 +142,7 @@ classdef Signal
arguments
obj
options.fignum
options.fignum = randi(1000)
options.displayname = [];
options.timeframe = 0;
options.clear = 0;
@@ -187,7 +188,7 @@ classdef Signal
ylabel('Amplitude');
end
% Add legend if not already present
if isempty(get(gca, 'Legend'))
legend;
@@ -269,6 +270,7 @@ classdef Signal
SignalPower = [obj.power];
Nase = [0];
SignalCopy = obj.signal;
SignalCopy = [];
ModifierName = class(CallingModifier);
@@ -342,83 +344,151 @@ classdef Signal
arguments
obj
options.fignum
options.fignum = 2025
options.displayname = "";
options.color = [];
options.linestyle = '-';
options.normalizeToNyquist = 0;
options.addDCoffset = 0;
options.normalizeToDC = 0;
options.normalizeTo0dB = 0;
options.max_num_lines = []; % Leave empty or omit to disable line rotation
options.fft_length = [];
% --- NEW options ---
options.useWavelengthAxis (1,1) logical = false % plot x-axis in wavelength
options.lambda0_nm (1,1) double = 1310 % center wavelength [nm]
end
if isempty(options.fft_length)
options.fft_length = 2^(nextpow2(length(obj.signal))-9);
end
if options.normalizeToNyquist == 0
[p_lin,w] = pwelch(obj.signal,hanning(options.fft_length),options.fft_length/2,options.fft_length,obj.fs,"centered","power","mean");
w = w.*1e-9;
[p_lin,f_Hz] = pwelch(obj.signal, hanning(options.fft_length), ...
options.fft_length/2, options.fft_length, ...
obj.fs, "centered", "power", "mean");
f_GHz = f_Hz*1e-9; % keep frequency vector for frequency axis
else
[p_lin,w] = pwelch(obj.signal,hanning(options.fft_length),options.fft_length/2,options.fft_length,"centered","power","mean");
[p_lin,f_rad] = pwelch(obj.signal, hanning(options.fft_length), ...
options.fft_length/2, options.fft_length, ...
"centered", "power", "mean");
% In normalized mode, pwelch returns rad/sample centered on 0.
% We'll keep f_rad for the x-axis in that mode.
end
% p_lin = movmean(p_lin,4);
if options.normalizeTo0dB
p_lin = p_lin./ max(p_lin);
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)";
end
% --- If requested, build wavelength axis from frequency offset ---
if options.useWavelengthAxis && options.normalizeToNyquist == 0
c = physconst('LightSpeed'); % [m/s]
lambda0_m = options.lambda0_nm*1e-9; % center wavelength [m]
f_c = c / lambda0_m; % carrier frequency [Hz]
% exact mapping
f_abs = f_c + f_Hz; % absolute frequency [Hz]
lambda_m = c ./ f_abs; % wavelength [m]
lambda_nm = lambda_m * 1e9; % wavelength [nm]
% assign axis
x_vec = lambda_nm(:);
x_label = "Wavelength [nm]";
% Sort to ensure axis is ascending
[x_vec, sortIdx] = sort(x_vec, 'ascend');
p_dbm = p_dbm(sortIdx, :);
else
% Frequency or normalized axes
if options.normalizeToNyquist == 0
x_vec = f_GHz;
x_label = "Frequency in GHz";
else
x_vec = f_rad; % normalized frequency in rad/sample
x_label = "Normalized Frequency";
end
end
figure(options.fignum);
ax = gca;
hold on
if isempty(options.color)
hLine = plot(w,p_dbm,'DisplayName',options.displayname,'LineWidth',1);
else
hLine = plot(w,p_dbm,'DisplayName',options.displayname,'LineWidth',1,'Color',options.color);
p_dbm = p_dbm+options.addDCoffset;
if options.normalizeToDC
[~,min_idx]=min(abs(f_GHz));
pow_at_dc = p_dbm(min_idx);
p_dbm = p_dbm-pow_at_dc;
end
% p_dbm = movmean(p_dbm,10);
for s = 1:min(size(p_dbm))
if isempty(options.color)
plot(x_vec, p_dbm(:,s), 'DisplayName', options.displayname, 'LineWidth', 1);
else
plot(x_vec, p_dbm(:,s), 'DisplayName', options.displayname, 'LineWidth', 1, 'Color', options.color,'LineStyle',options.linestyle);
end
end
% If user wants to limit the number of lines, check and remove old lines
% Limit number of lines if requested
if ~isempty(options.max_num_lines) && options.max_num_lines > 0
allLines = findall(ax, 'Type', 'Line');
if length(allLines) > options.max_num_lines
% Sort lines by creation order. Usually, the oldest lines appear first in allLines.
% If needed, you can sort by UserData or other criteria.
numToRemove = length(allLines) - options.max_num_lines;
delete(allLines(1:numToRemove));
end
end
if options.normalizeToNyquist == 0
xlabel("Frequency in GHz");
edgetick = 2^(nextpow2(obj.fs*1e-9));
xticks(-edgetick:16:edgetick);
xlim([100*round(min(w)/100,1)-10, 100*round(max(w)/100,1)+10])
xlim([-128 128]);%256GSa/s
% Axis labels and limits
xlabel(x_label);
if options.useWavelengthAxis && options.normalizeToNyquist == 0
xlim([min(x_vec) max(x_vec)]);
else
xlabel("Normalized Frequency");
xlim([-pi, pi]);
if options.normalizeToNyquist == 0
% Keep your existing freq handling (you can fine-tune as needed)
% xlim([-128 128]); % example for 256 GSa/s if desired
xlim([min(x_vec) max(x_vec)]);
else
xlim([-pi, pi]);
end
end
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)]);
catch
ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]);
% --- Y-Axis scaling (auto with margin) ---
y_min = min(p_dbm(:));
y_max = max(p_dbm(:));
% Add 5% dynamic range margin on both sides
y_range = y_max - y_min;
if y_range == 0
y_range = 10; % fallback if flat
end
ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]);
yticks(-200:10:10);
grid on; grid minor;
legend
% legend('Interpreter','none');
y_margin = 0.05 * y_range;
ylim([y_min - y_margin, y_max + y_margin]);
% Set ticks automatically, avoid overpopulation
try
yticks(round(linspace(y_min, y_max, min(10, max(4, ceil(y_range/10))))));
end
grid on;
end
function move_it_spectrum(obj,options)
arguments
obj
@@ -546,7 +616,7 @@ classdef Signal
options.unit power_notation = power_notation.dBm
end
pow = mean(abs(obj.signal).^2);
pow = sum(mean(abs(obj.signal).^2));
switch options.unit
case power_notation.dBm
@@ -661,9 +731,9 @@ classdef Signal
options.fs_ref = 0;
options.debug_plots = 0;
end
S = {};
inverted = -1;
sequenceFound = 0;
@@ -693,10 +763,10 @@ classdef Signal
return
end
if mean(w) > 10
warning('No sync possible, check code of findpeaks! Look at correlation')
pkpos = sort(pkpos);
if mean(w) > 10 || mean(p) > 10
return
else
sequenceFound = 1;
end
@@ -707,33 +777,33 @@ classdef Signal
findpeaks(abs(co./max(co)),'MinPeakDistance',length(b)/2,'MinPeakHeight',0.2,'NPeaks',maxpeaknum,'SortStr','descend')
end
shifts = lags(pkpos);
sequenceStarts = shifts;
% shifts = shifts(shifts>=0);
shifts = shifts(shifts>=0);
if numel(shifts) > 0
%Cut occurences of ref signal from signal (only positive shifts)
if all(sign(co(pkpos))==-1)
inverted = 1;
end
for c = shifts
sig = obj.delay(-c,'mode','samples');
sig.signal = sig.signal(1:length(b)) .* -inverted;
sig.signal = sig.signal(1:length(b));% .* -inverted;
S{end+1,1} = sig;
end
%return/keep the sinal with the highest correlation (only within positive shifts)
[~,idx]=max(pks);
obj.signal = S{idx}.signal;
%put signal with highest corr. to first index in S array
swap = S{1};
S{1} = S{idx};
S{idx} = swap;
% %return/keep the sinal with the highest correlation (only within positive shifts)
% [~,idx]=max(pks);
% obj.signal = S{idx}.signal;
% %put signal with highest corr. to first index in S array
% swap = S{1};
% S{1} = S{idx};
% S{idx} = swap;
for c = 1:numel(shifts)
S{c}.logbook = [];
end
@@ -762,10 +832,6 @@ classdef Signal
end
%%
function obj = filter(obj,a,b)
@@ -912,11 +978,11 @@ 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;
maxA = 0.12;
minA = -0.08;
difference= maxA-minA;
@@ -927,7 +993,7 @@ classdef Signal
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 +1010,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 1
if 0
pwr_dbm = round(obj.power,3);
pwr_lin = obj.power("unit",power_notation.W);
@@ -1073,18 +1141,20 @@ classdef Signal
end
yticks(linspace(0,histpoints,16));
y_tickstring = sprintfc('%.2f', y_tickstring);
yticklabels(y_tickstring);
grid off
xticks(linspace(0,histpoints_horizontal,8))
x_tickstring = sprintfc('%.2f', linspace(0, 2/fsym, 8) .* 1e12);
xticklabels(x_tickstring);
end
yticks(linspace(0,histpoints,6));
y_tickstring = sprintfc('%.2f', y_tickstring);
yticklabels(y_tickstring);
xticks(linspace(0,histpoints_horizontal,6))
x_tickstring = sprintfc('%.2f', linspace(0, 2/fsym, 8) .* 1e12);
xticklabels(x_tickstring);
%
end
% disp('h');

View File

@@ -139,6 +139,8 @@ classdef ChannelFreqResp < handle
fnew = linspace(0,fstarget/2,length(Target)/2+1);
fnew = fnew(2:end-1);
% Old frequency axis (should be much coarser)
idx_old = find((obj.faxis > 0) .* (obj.faxis < fstarget/2)); %positions of all Frequencies smaller than fs/2
int_fold = obj.faxis(idx_old); %old frequencies from 0 to fs/2
@@ -149,6 +151,10 @@ classdef ChannelFreqResp < handle
% interpolate the frequency response that had a coarse frequency resolution (e.g. 256 bins) to the current frequency resolution (e.g. 21843 bins)
iH = interp1(int_fold, real(H_inv(idx_old)) ,fnew, 'linear') + 1i*interp1(int_fold, imag(H_inv(idx_old)) ,fnew, 'linear');
if 0
figure(7);hold on;plot(fnew,20*log10(abs(iH)))
end
% set all NaN values to the fist/ last non-NaN value
nH = find(~isnan(iH),1,'first');
iH(1:nH)=iH(nH);
@@ -171,6 +177,8 @@ classdef ChannelFreqResp < handle
% five frequencies -> should be the vaue at f=0=DC component?
iH = iH./mean(abs(iH)); %why 1:5??
dc = 20*log10(abs(mean(abs(iH(1:5)))));
% set maximum amplification
% set als values higher than hmax to hmax and keep the
% phase information by multiplication with respective
@@ -206,7 +214,7 @@ classdef ChannelFreqResp < handle
function plot(obj)
figure(55);
figure();
clf;
Havg = obj.H;
@@ -225,7 +233,7 @@ classdef ChannelFreqResp < handle
xlim([0.2 .5*max(obj.faxis)*1e-9]);
grid on;
figure(56);
figure();
clf;
@@ -246,7 +254,7 @@ classdef ChannelFreqResp < handle
xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on;
%%% plot for publication
figure(1234);
figure(101);
hold all;
box on;
title('Magnitude Freq. Response');
@@ -269,6 +277,25 @@ classdef ChannelFreqResp < handle
legend('Interpreter','latex')
grid on;
%
figure(100); hold on;
Havg = obj.H;
Havg = Havg./max(Havg);
Hall = obj.H_all;
for i = 1:size(Hall,1)
Hall(i,:) = Hall(i,:)./max(Hall(i,:));
end
%1)
col = cbrewer2('Paired',8);
hold all;box on;title('Magnitude Freq. Response');
plot(obj.faxis/1e9, 20*log10(abs(Hall)),'linewidth',0.1,'LineStyle','-','Color',col(1,:),'HandleVisibility','off') ;
xlim([0.2 .5*max(obj.faxis)*1e-9]);
plot(obj.faxis/1e9, 20*log10(abs(Havg)),'LineWidth',2,'Color',col(2,:));
grid on;
end

View File

@@ -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");

View File

@@ -29,7 +29,7 @@ classdef PAMmapper
obj.levels = obj.get_levels();
obj.scaling = rms(obj.get_levels());
obj.scaling = obj.get_scaling;%rms(obj.get_levels());
obj.eth_style = options.eth_style;
@@ -45,7 +45,7 @@ classdef PAMmapper
signal_in = signal_in.logbookentry(lbdesc,obj);
out = signal_in;
else
out = signal_in;
out = obj.map_(signal_in);
end
end
@@ -283,6 +283,21 @@ classdef PAMmapper
end
end
function scaling = get_scaling(obj)
switch obj.M
case 2
scaling = 1;
case 4
scaling = sqrt(5);
case 6
scaling = sqrt(10);
case 8
scaling = sqrt(21);
case 16
scaling = 1;
end
end
function [data_out] = demap_(obj,data_in)
data_in= data_in';
@@ -328,8 +343,9 @@ classdef PAMmapper
if ~obj.eth_style
data_in = data_in/(sqrt(mean(abs(data_in).^2)));
% data_in = data_in/(sqrt(mean(abs(data_in).^2)));
data_in = data_in*sqrt(10);
%data_in = data_in*sqrt((5^2 + 3^2 + 1^2 + 5^2 + 3^2 + 1^2)/6);
if size(data_in,2) > 1
data_in = data_in.';
@@ -449,7 +465,7 @@ classdef PAMmapper
function [Signal_out] = quantize(obj,Signal_in)
constellation = obj.get_levels();
constellation = constellation ./ rms(constellation);
constellation = constellation ./ obj.scaling;
issignalclass = 0;
if isa(Signal_in,'Signal')
@@ -480,7 +496,9 @@ classdef PAMmapper
end
function bitmap = showBitMapping(obj)
bitmap = obj.demap([obj.levels ./ obj.scaling]');
bitmap = obj.demap((obj.levels ./ obj.scaling)');
end
end

View File

@@ -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
@@ -164,7 +171,7 @@ classdef PAMsource
%%%%% Pulse-forming %%%%%%
if obj.applypulseform
%%% MY CODE
if 0
if 1
digi_sig = obj.pulseformer.process(symbols);
else

View File

@@ -108,7 +108,7 @@ classdef Amplifier
if obj.gain_mode == gain_mode.output_power
%get linear gain for output power mode
pow_in = mean(abs(xin.^2)) ; % lin input power
pow_in = sum(mean(abs(xin.^2)),2) ; % lin input power
pow_out = 10^(obj.amplification_db/10 - 3) ; % dBm to lin
a_lin = sqrt(pow_out/pow_in) ;

View File

@@ -70,7 +70,7 @@ classdef EML
[signalclass_in.signal,obj] = obj.process_(signalclass_in.signal);
% cast the inform. signal to electrical signal
signalclass_in = Opticalsignal(signalclass_in,"fs",obj.fsimu,"logbook",signalclass_in.logbook,"lambda",obj.lambda*1e-9,"nase",0);
signalclass_in = Opticalsignal(signalclass_in,"fs",obj.fsimu,"logbook",signalclass_in.logbook,"lambda",obj.lambda*1e-9,"nase",0,"polrot",0);
% append to logbook
lbdesc = [num2str(obj.lambda),' nm Laser with ',num2str(obj.power),' dBm P_out. Linew.=',num2str(obj.linewidth*1e-6),' MHz. Modulation mode: ',char(obj.mode) ];

View File

@@ -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

View File

@@ -53,6 +53,7 @@ classdef Duobinary
% bk(k+1) =mod(data(k)-bk(k),M);
% end
% THIS WAS USED!
for k = 2:numel(data)
bk(k) = mod(data(k)-bk(k-1),M);
end

View File

@@ -3,24 +3,34 @@ classdef FFE < handle
% 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);
%LMS: mu in order of 0.001 for acceptable convergence speed
%NLMS: mu in order of 0.01 for acceptable convergence speed
%RLS: mu is lambda -> 0.99 -> 1 (has a strong dependency on this! use a loop to find out best values)
% FFE("epochs_tr",5,"epochs_dd",2,"len_tr",2^13,"mu_dd",mu_dd,"mu_tr",mu_tr,"order",25,"sps",2,"decide",0, "adaption",adaption_method(adaption),"dd_mode",use_dd_mode);
properties
sps % usually 2
order
e
e_tr
error
debug_struct
len_tr
mu_tr
epochs_tr
mu_dd
adaption_technique % nlms, lms, rls
dd_mode % 1 or 0 to set DD-mode on or off
mu_dd %weight update in dd mode
epochs_dd
constellation
P % covariance matrix of rls
decide
constellation % symbol constellation
decide %wether to return the (hard) decisions or the result after FFE (soft)
end
methods
@@ -34,6 +44,8 @@ classdef FFE < handle
options.mu_tr = 0;
options.epochs_tr = 5;
options.adaption_technique adaption_method = adaption_method.lms;
options.dd_mode = 1;
options.mu_dd = 1e-5;
options.epochs_dd = 5;
@@ -59,10 +71,15 @@ classdef FFE < handle
obj.constellation = unique(D.signal);
delta = 0.05;
obj.P = (1/delta) * eye(obj.order);
% Training Mode
training = 1;
showviz = 0;
obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training,showviz);
obj.e_tr = obj.e;
% Decision Directed Mode
n = X.length;
@@ -71,7 +88,7 @@ classdef FFE < handle
[signal,decision]=obj.equalize(X.signal, D.signal,obj.mu_dd,obj.epochs_dd,n,training,showviz);
% Output Signal
if obj.decide
if obj.decide
X.signal = decision;
else
X.signal = signal;
@@ -83,24 +100,25 @@ classdef FFE < handle
Noi = X;
Noi = X - D;
end
function [y,d_hat] = equalize(obj,x,d,mio,epochs,N,training,showviz)
function [y,d_hat] = equalize(obj,x,d,mu,epochs,N,training,showviz)
arguments
obj
x
d
mio
epochs
N
training
obj
x
d
mu
epochs
N
training
showviz
end
x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)];
lambda = mu;
if training
mask = ones(obj.order,1);
@@ -111,8 +129,17 @@ classdef FFE < handle
end
mask = ones(obj.order,1);
maincursor_pos=ceil(length(obj.e)/2);
always_ideal_decision = 0;
save_debug = 0;
grad =0;
weight = 0;
update = 0;
if mu == 0 || (~obj.dd_mode && ~training)
epochs = 1;
end
for epoch = 1 : epochs
symbol = 0;
for sample = 1 : obj.sps : N
@@ -124,30 +151,75 @@ classdef FFE < handle
y(symbol,1) = (obj.e.*mask).' * U; % Calculating output of LMS __ * |
if training
d_hat(symbol,1) = d(symbol);
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);
if ~always_ideal_decision
[~,symbol_idx] = min(abs(y(symbol) - obj.constellation)); % decision for closest constellation point
d_hat(symbol,1) = obj.constellation(symbol_idx);
else
d_hat(symbol,1) = d(symbol);
end
end
err(symbol) = y(symbol) - d_hat(symbol); % Instantaneous error
% err(symbol) = y(symbol) - d_hat(symbol); % Instantaneous error
err(symbol) = d_hat(symbol) - y(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
if training || obj.dd_mode
switch obj.adaption_technique
case adaption_method.lms
% mu used as update weight (suggestion: 0.001)
weight = mu;
grad = err(symbol) * U;
update = grad * weight;
obj.e = obj.e + update;
case adaption_method.nlms
% mu used as update weight (suggestion: 0.01-0.05; bit higher during tr)
normU = ((U.'*U)) + eps;
weight = mu / normU;
grad = err(symbol) * U;
update = grad * weight;
obj.e = obj.e + update;
case adaption_method.rls
% RLSGain:
denom = lambda + U.' * obj.P * U;
k = (obj.P * U) / denom;
% Gewichtsupdate:
update = k * err(symbol);
obj.e = obj.e + update;
% P-MatrixUpdate:
obj.P = (1/lambda) * (obj.P - k * (U.' * obj.P));
end
end
obj.error(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error
if save_debug
obj.debug_struct.error(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error
if training
obj.debug_struct.error_tr(epoch,symbol) = err(symbol) * err(symbol)'; % Instantaneous square error
obj.debug_struct.update_tr(epoch,symbol) = update.'*update ./ rms(obj.e);
end
obj.debug_struct.main_cursor(epoch,symbol) = abs(obj.e(maincursor_pos));
obj.debug_struct.mu_nlms(epoch,symbol) = weight;
obj.debug_struct.update_gradient(epoch,symbol) = grad.'*grad;
obj.debug_struct.update(epoch,symbol) = update.'*update ./ rms(obj.e);
end
end
end
end
end
end
end

View File

@@ -57,6 +57,8 @@ classdef FFE_DCremoval < handle
obj.e = zeros(obj.order,1);
obj.error = 0;
obj.dc_buffer_len = floor(obj.dc_buffer_len);
end
function [X,Noi] = process(obj, X, D)
@@ -155,16 +157,15 @@ classdef FFE_DCremoval < handle
end
end
if ~training
figure(1122)
hold on
scatter(1:numel(e_dc_save),e_dc_save,1,'.');
scatter(1:numel(y),y,1,'.');
end
% if ~training
% figure(1122)
% hold on
% scatter(1:numel(e_dc_save),e_dc_save,1,'.');
% % scatter(1:numel(y),y,1,'.');
%
% end
end
end
end
end

View File

@@ -60,7 +60,7 @@ classdef FFE_DFE < handle
end
function [X] = process(obj, X, D)
function [X,Noi] = process(obj, X, D)
% actual processing of the signal (steps 1. - 3.)
% 1 normalize RMS
@@ -88,6 +88,9 @@ classdef FFE_DFE < handle
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
Noi = X;
Noi = X - D;
end
@@ -185,8 +188,6 @@ classdef FFE_DFE < handle
obj.e = coeff(1:obj.ffe_order);
obj.b = coeff(obj.ffe_order+1:end);
end
end

View File

@@ -31,7 +31,7 @@ classdef Postfilter < handle
end
function signalclass_out = process(obj,signalclass_in,noiseclass_in,options)
function [signalclass_out,noiseclass_out] = process(obj,signalclass_in,noiseclass_in,options)
arguments
obj
@@ -55,7 +55,8 @@ classdef Postfilter < handle
else
end
noiseclass_in = noiseclass_in.filter(obj.coefficients,1);
signalclass_in = signalclass_in.filter(obj.coefficients,1);
% append to logbook
@@ -64,7 +65,7 @@ classdef Postfilter < handle
% write to output
signalclass_out = signalclass_in;
noiseclass_out = noiseclass_in;
end
function showFilter(obj,options)

View File

@@ -6,6 +6,10 @@ classdef MLSE < handle
DIR
trellis_states
duobinary_output
trellis_state_mode
trellis_exclusion
debug
scale_mode
end
methods (Access=public)
@@ -19,7 +23,10 @@ classdef MLSE < handle
options.DIR double = [1];
options.trellis_states double = [-3 -1 1 3];
options.duobinary_output logical = false;
options.trellis_state_mode = 2;
options.trellis_exclusion = 0;
options.scale_mode = 2;
options.debug = 0;
end
%
@@ -29,28 +36,64 @@ classdef MLSE < handle
obj.(fn{n}) = options.(fn{n});
end
end
% do more stuff
end
function [signalclass_hd,signalclass_sd] = process(obj,signalclass,ref_symbolclass)
function [signalclass_hd,LLR,GMI] = process(obj,signalclass,ref_symbolclass)
arguments
obj
signalclass
ref_symbolclass
end
data_in = signalclass.signal;
shape_in = size(data_in);
data_ref = ref_symbolclass.signal;
[data_out_hd,data_out_sd] = obj.process_(data_in,data_ref);
[data_out_hd,LLR,GMI] = obj.process_(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;
signalclass_sd = signalclass;
signalclass_sd.signal = data_out_sd;
% signalclass_sd = signalclass;
% signalclass_sd.signal = data_out_sd;
end
function [VITERBI_ESTIMATION_SYMBOLS,soft_decisions] = process_(obj,data_in,data_ref)
function [VITERBI_ESTIMATION_SYMBOLS,LLR_maxlogmap,GMI] = process_(obj,data_in,data_ref)
arguments
obj
data_in
data_ref
end
debug = obj.debug;
trellis_state_mode = obj.trellis_state_mode; % General: States should match the target states of the prev. EQ (EQ's job was to reduce the error between signal and the target)
% 0 = use provided states (MUST provide the correct states);
% 1 = normalize to = 1 rms;
% 2 = use target symbols;
% 3 = use statistical levels
% 3 analyzes avg of rx signal levels - can help with nonlinear impairments
trellis_exclusion = obj.trellis_exclusion; % PAM-6 only (only if data is NOT precoded!)
scale_mode = obj.scale_mode; % scale_mode:
% 0 = no scaling,
% 1 = RMSscale MODEL,
% 2 = MMSE/time-corrscale MODEL,
% 3 = RMSscale DATA,
% 4 = MMSE/time-corrscale DATA
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%% PREPARATIONS %%%%%%%%
% remove unnecessary zeros at start of impulse response to keep
% number of trellis states minimal
@@ -63,399 +106,557 @@ classdef MLSE < handle
obj.DIR = [0 obj.DIR];
end
% impulse respnse to remove from signal
obj.DIR = flip(obj.DIR); %i.e. -0.2676 -0.0478 1.0000
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%% PREPARATIONS %%%%%%%%
tx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(data_ref);
%%%% Separate the equalized signal into the respective levels based on the actually transmitted level
constellation = unique(data_ref);
decisionLevels = (constellation(1:end-1) + constellation(2:end)) / 2;
tx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(data_ref);
% Trellis States
if trellis_state_mode == 1 % Normalize the Trellis states to =1 RMS
obj.trellis_states = obj.trellis_states ./ rms(obj.trellis_states);
% impulse respnse i.e. [0.5, 1.0000]
obj.DIR = flip(obj.DIR);
elseif trellis_state_mode == 2 %simply use the states from the ref signal (should be a robust option)
obj.trellis_states = reshape(unique(data_ref),1,length(unique(data_ref)));
elseif trellis_state_mode == 3 %use_statistical_levels
%%%% Separate the equalized signal into the respective levels based on the actually transmitted level
constellation = unique(data_ref);
% find actual levels from rx signal
symbols_for_lvl = NaN(numel(constellation),length(data_ref));
for l = 1:numel(constellation)
level_amplitude = constellation(l);
symbols_for_lvl(l,data_ref==level_amplitude) = data_in(data_ref==level_amplitude);
end
%replace the trellis states
avg_levels = mean(symbols_for_lvl,2,'omitnan');
obj.trellis_states = sort(avg_levels)';
%also replace the whole ref signal (PAM-M) levels
[~, idx] = ismember(data_ref, unique(data_ref));
data_ref = avg_levels(idx);
end
% RMS normalization of input data
data_in = data_in ./ rms(data_in);
% 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);
pre_comb_cell = mat2cell(pre_comb_mat,ones(1,size(pre_comb_mat,1)),size(pre_comb_mat,2));
combs = fliplr(combvec(pre_comb_cell{:}).');
first_sym = combs(:,1); % das ist das älteste/ trailing Symbol aus der sequenz
last_sym = combs(:,end); %hiermit wird entschieden/ das ist das cursor symbol am ende der sequenz
% Save first and last symbol of each state
first_sym = combs(:,1);
last_sym = combs(:,end);
states = sum(combs,2);
nStates = length(last_sym);
% Calculate all possible input symbols for the desired impulse
% response. Row number is the index of the previous state,
% column number is the index of the next state
% noise free received == branch metrics
noise_free_received = zeros(length(states),length(states));
count_row = 1;
count_col = 1;
for l1 = 1:length(states)
for l2 = 1:length(states)
if sum(combs(l2,2:end) == combs(l1,1:end-1)) == size(combs,2)-1
noise_free_received(count_row,count_col) = sum(combs(l2,:).*obj.DIR(end:-1:2)) + last_sym(l1)*obj.DIR(1);
else
noise_free_received(count_row,count_col) = inf;
% % Calculate all possible input symbols for the desired impulse
% % response. Row number is the index of the previous state,
% % column number is the index of the next state
% % noise free received == branch metrics
% assumes: last_sym = combs(:,end); % already defined earlier
levels = sort(unique(obj.trellis_states(:)).');
edges = [levels(1) levels(end)]; % edge levels (0 and 5 in PAM6)
noise_free_received = inf(nStates,nStates); % rows: to, cols: from
edge_edge_mask = false(nStates,nStates); % rows: to, cols: from
for from = 1:nStates
for to = 1:nStates
% valid transition if shift-register overlap holds
if all(combs(to,2:end) == combs(from,1:end-1))
% noiseless sample for the 'to' state reached from 'from'
noise_free_received(to,from) = ...
dot(combs(to,:), obj.DIR(end:-1:2)) + last_sym(from)*obj.DIR(1);
% mark edgeedge candidate (to be excluded only on evenodd steps)
edge_edge_mask(to,from) = ...
(last_sym(from)==edges(1) || last_sym(from)==edges(2)) && ...
(last_sym(to) ==edges(1) || last_sym(to) ==edges(2));
end
count_row = count_row + 1;
end
count_col = count_col + 1;
count_row = 1;
end
% 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
if isreal(data_in)
if obj.M == round(obj.M)
data_in = data_in * rms(noise_free_received(noise_free_received ~= inf),'all','omitnan');
end
h = flip(obj.DIR(:)).';
data_in = data_in(:);
y_ideal = conv(data_ref(:), h, "same");
switch scale_mode
case 0
g = 1; b = 0;
case 1 % RMS: scale model to data
g = rms(data_in)/rms(y_ideal); b = mean(data_in) - g*mean(y_ideal);
case 2 % MMSE/time-corr: scale states to data
[c,lags] = xcorr(data_in(:), y_ideal, 64);
[~,ix] = max(abs(c));
lag = lags(ix);
y_ideal = circshift(y_ideal, lag);
mu_y = mean(data_in(:));
mu_i = mean(y_ideal);
y_c = data_in(:)-mu_y;
yi_c = y_ideal-mu_i;
g = (yi_c'*y_c)/(yi_c'*yi_c);
b = mu_y - g*mu_i;
case 3 % RMS flipped: scale data to model
gd = rms(y_ideal)/rms(data_in); bd = mean(y_ideal) - gd*mean(data_in);
data_in = gd*data_in + bd;
g = 1; b = 0;
case 4 % MMSE/time-corr flipped: scale data to states
[c,lags] = xcorr(data_in(:), y_ideal(:), 64);
[~,ix] = max(abs(c));
lag = lags(ix);
y_ideal = circshift(y_ideal(:), lag);
mu_y = mean(data_in(:));
mu_i = mean(y_ideal);
y_c = data_in(:) - mu_y; % data_in centered
yi_c = y_ideal - mu_i; % ideal centered
g = (y_c' * yi_c) / (y_c' * y_c);
b = mu_i - g * mu_y;
data_in = g * data_in(:) + b;
g = 1; b = 0;
end
% apply (g,b) to model and compute common sigma
noise_free_received = g*noise_free_received + b;
last_sym = g*last_sym + b;
sigma2 = mean(abs(data_in - (g*y_ideal + b)).^2);
inv2s2 = 1/(2*sigma2);
if debug
figure(100); clf; hold on
showLevelScatter(data_in, data_ref, "fignum", 100);
yline(noise_free_received(:), 'DisplayName','Transition States','Color','red','HandleVisibility','off');
yline(obj.trellis_states(:), 'DisplayName','Transition States','Color','green','LineWidth',2,'HandleVisibility','off')
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% FORWARD PASS %%%%%
%%%%% FORWARD PASS (VITERBI -Alpha's) %%%%%
% Initialize the output vector
pm = zeros(length(states),length(states));
bm_fw = zeros(length(states),length(states),length(data_in));
pm = zeros(nStates,nStates);
bm_fw = zeros(nStates,nStates,length(data_in));
% first start is evaluated without ISI/ wihout the full Impulse response
% so simply use the constellation here
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);
bm_fw(:,:,1) = pm;
% Forward Recursion (FSM Computation)
for n = 1:length(data_in)
for n = 2:length(data_in)
bm = -(data_in(n) - noise_free_received).^2 * inv2s2;
% exclude edgeedge transitions only for even->odd steps && PAM-6
if mod(n,2) == 0 && obj.M == 6 && trellis_exclusion
bm(edge_edge_mask) = -Inf;
end
bm = abs(data_in(n) - noise_free_received).^2;
pm = pm + bm;
[pm_survivor_fw(:,n),pm_survivor_fw_idx(:,n)] = min(pm,[],2); % choose lowest path metric as new state
pm = repmat(pm_survivor_fw(:,n).',length(states),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 (get min distance for all state transitions towards a new state)
pm = repmat(alpha(:,n).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state)
bm_fw(:,:,n) = bm;
end
% we can now get the best path as min
best_fw_path = NaN(1,length(data_in)+1);
viterbi_path = NaN(1,length(data_in));
% find ideal trellis path by going through the trellis backwards
[~,best_fw_path(length(data_in)+1)] = min(pm_survivor_fw(:,length(data_in)));
[~,viterbi_path(length(data_in))] = max(alpha(:,length(data_in)));
for n = length(data_in):-1:2
viterbi_path(n-1) = pm_survivor_fw_idx(viterbi_path(n),n);
end
if debug
% alpha_ = alpha - min(alpha) + eps;
% figure();hold on;
% n = 10;
% scatter(1:n,obj.trellis_states(repmat([1:numel(obj.trellis_states)]',1,n)),abs(alpha_(:,end-n+1:end)),'Marker','o','LineWidth',1);
% scatter(1:n,obj.trellis_states(viterbi_path(end-n+1:end)),500,'Marker','x','LineWidth',1,'MarkerEdgeColor','green');
% % scatter(1:n,data_ref(end-n+1:end),500,'Marker','x','LineWidth',1,'MarkerEdgeColor','red');
% yticks(obj.trellis_states);
% ylim([min(obj.trellis_states)-1 max(obj.trellis_states)+1]);
%% ----- Build true path from known sequence (data_ref) -----
% Memory length used by VA (L = length(obj.DIR)-1 symbols stored in state)
L = size(combs,2); % each row of combs is the L-tap state vector
N = length(data_in);
% Map each trellis level to an index (1..M)
[~, level_to_idx] = ismember(levels, levels); %#ok<ASGLU> % identity map
[ok_ref, ref_idx] = ismember(data_ref(:).', levels);
if ~all(ok_ref)
warning('Some data_ref symbols are not in "levels". True-path build may fail.');
end
% Precompute next_state(from_state, u_idx) LUT such that:
% combs(next_state,:) == [ combs(from_state,2:end) , levels(u_idx) ]
next_state = zeros(size(combs,1), numel(levels), 'uint32');
for from = 1:size(combs,1)
prefix = combs(from,2:end); % what must match in 'to' for a valid transition
for ui = 1:numel(levels)
target = [prefix, levels(ui)];
% find the unique 'to' whose state vector equals target
to = find(all(bsxfun(@eq, combs, target), 2), 1, 'first');
if isempty(to), to = 0; end
next_state(from, ui) = to;
end
end
% Initialize true path at n=1: pick any state with last_sym == data_ref(1)
% Prefer one whose suffix matches the first available history if L>1.
cand = find(last_sym == data_ref(1));
if isempty(cand)
% fallback: choose closest in amplitude (should not happen if levels match)
[~,ix] = min(abs(last_sym - data_ref(1)));
cand = ix;
end
true_state_path = zeros(1,N,'uint32');
true_state_path(1) = cand(1);
% Propagate forward using the known inputs data_ref(n)
for n = 2:N
ui = ref_idx(n); % index of the actual transmitted level at time n
from = true_state_path(n-1);
if from==0 || ui==0
true_state_path(n) = 0;
else
true_state_path(n) = next_state(from, ui);
if true_state_path(n)==0
% Safety fallback: if no valid transition found (should not happen)
% choose any to-state whose vector matches shift+current symbol
target = [combs(from,2:end), levels(ui)];
to = find(all(bsxfun(@eq, combs, target), 2), 1, 'first');
if isempty(to), to = from; end
true_state_path(n) = to;
end
end
end
%% ----- Collect branch metrics along decoded vs. true path -----
bm_decoded = nan(1,N);
bm_true = nan(1,N);
% n=1 in your code stores pm into bm_fw(:,:,1); real BMs start at n>=2
for n = 2:N
% Decoded path: to = viterbi_path(n), from = survivor that fed it
to_d = viterbi_path(n);
from_d = pm_survivor_fw_idx(to_d, n);
bm_decoded(n) = bm_fw(to_d, from_d, n);
% True path: transition true_state_path(n-1) -> true_state_path(n)
to_t = true_state_path(n);
from_t = true_state_path(n-1);
if to_t>0 && from_t>0
bm_true(n) = bm_fw(to_t, from_t, n);
end
end
% Convert to "cost" for intuitive plotting (your BM is a log-likelihood)
cost_dec = -bm_decoded;
cost_true= -bm_true;
%% ----- Plot a short window for clarity -----
win = max(2, N-20000):N; % last 200 samples (adjust as needed)
figure('Color','w'); hold on; grid on; box on;
plot(win, cost_true(win), 'LineWidth',1.2, 'DisplayName','True path cost (BM)');
plot(win, cost_dec(win), 'LineWidth',1.2, 'DisplayName','Decoded path cost (BM)');
xlabel('Time index n'); ylabel('Branch cost'); title('Branch metrics along true vs decoded path');
legend('Location','best');
%% ----- Optional: overlay symbol levels for the same window -----
yyaxis right
plot(win, data_in(win), ':', 'LineWidth',0.8, 'DisplayName','y(n)');
ylabel('Amplitude');
end
VITERBI_ESTIMATION_SYMBOLS(1:length(data_in)) = first_sym(viterbi_path);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% BACKWARD PASS %%%%%
%%%%% BACKWARD PASS (Beta's) %%%%%
% Initialize the output vector
pm = zeros(length(states),length(states));
pm_survivor_bw = zeros(length(states),length(data_in)+1);
pm_survivor_bw_idx = zeros(length(states),length(data_in)+1);
bm_bw = zeros(length(states),length(states),length(data_in)+1);
pm = zeros(nStates,nStates);
beta = zeros(nStates,length(data_in));
pm_survivor_bw_idx = zeros(nStates,length(data_in));
bm_bw = zeros(nStates,nStates,length(data_in));
% starting with the state that has the lowest sum path
% metric, follow the stored information about the
% predecessor
for h = length(data_in):-1:1
for h = length(data_in)-1:-1:1
best_fw_path(h) = pm_survivor_fw_idx(best_fw_path(h+1),h);
bm = -(data_in(h+1) - noise_free_received).^2 * inv2s2;
% exclude edgeedge transitions only for even->odd steps && PAM-6
if mod(h+1, 2) == 0 && obj.M == 6 && trellis_exclusion
bm(edge_edge_mask) = -Inf;
end
bm = abs(data_in(h) - noise_free_received).^2;
pm = pm + bm.';
[pm_survivor_bw(:,h),pm_survivor_bw_idx(:,h)] = min(pm,[],2); % choose lowest path metric as new state
pm = repmat(pm_survivor_bw(:,h).',length(states),1); % update pm (chosen state to 2nd dimension -> FROM state)
[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)
bm_bw(:,:,h) = bm;
end
VITERBI_ESTIMATION_IDX(1:length(data_in)) = first_sym(best_fw_path(2:end));
VITERBI_ESTIMATION_SYMBOLS(1:length(data_in)) = constellation(best_fw_path(2:end));
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% FORWARD PASS %%%%%
%%%%% FORWARD PASS PAM 2,4,8 (Combine Alpha and Beta to yield LLP's) %%%%%
%calc the log probabilities (llp's)
for k = 2:length(data_in)
fsm = repmat(pm_survivor_fw(:,k-1)',[length(states),1]); %pm_survivor_fw size: length(states)xlength(sequence)
bm = bm_fw(:,:,k); %bm_fw size: length(states)xlength(states)xlength(sequence)
bsm = repmat(pm_survivor_bw(:,k)',[length(states),1]); %pm_survivor_bw size: length(states)xlength(sequence)+1
for k = 1:length(data_in)
if k == 1
alpha_ = repmat(alpha(:,k)',[nStates,1])';
beta_ = beta(:,k);
LLP(:,k) = max(alpha_ + beta_,[],2);
else
alpha_ = repmat(alpha(:,k-1)',[nStates,1])';
gamma_ = bm_fw(:,:,k)';
beta_ = beta(:,k);
LLP(:,k) = max(alpha_ + gamma_,[],1) + beta_';
end
llp(:,k) = min(fsm+bm+bsm,[],2);
end
% Compute soft output PAM4 stream from the metric_sym
soft_output = zeros(length(data_in),1); % Expected symbol value per stage
symbol_prob = zeros(length(data_in), length(states)); % Store full probability distribution (optional)
llp = llp';
for n = 1:length(data_in)
metrics = llp(n, :); % A posteriori metric for each PAM4 candidate
% For numerical stability, subtract the maximum metric before exponentiating
maxMetric = min(metrics);
expMetrics = exp(metrics - maxMetric);
probs = expMetrics / sum(expMetrics); % Normalize to get probabilities
symbol_prob(n, :) = probs; % (Optional) store distribution for analysis
% Compute the soft output as the expected value of the PAM4 symbols
soft_output(n) = sum(probs .* constellation');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% FORWARD PASS PAM2,4,8 %%%%%
% These are interchangeable... second is chatgpt:
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 symbolposteriors from LLP in the logdomain:
amax = max(LLP,[],1);
logZ = amax + log(sum(exp(LLP - amax), 1));
logPstate = LLP - logZ; % still in logdomain
state_prob = exp(logPstate); % exact, sums to 1
if obj.M == 6
num_bits = 5;
% all possible transitions (for now 36, including the "edges"
% of the QAM 32 constellation)
states = PAMmapper(6,0,"eth_style",0).levels;
pam6transitions = combvec(states,states)'; % pam6transitions =
% [-5 -5;
% -3 -5;
% -1 -5; ...
pam6bits = PAMmapper(6,0,"eth_style",0).demap(reshape(pam6transitions',[],1)./sqrt(10));
pam6bits = reshape(pam6bits',5,[])';
[ok1, idx_sym_1] = ismember(pam6transitions(:,1), states);
[ok2, idx_sym_2] = ismember(pam6transitions(:,2), states);
assert(all(ok1)&all(ok2), 'Some transition amplitude not found in trellis_states')
pam6ind = [idx_sym_1, idx_sym_2];
tx_bits_pam6_reshaped = reshape(tx_bits,5,[])'; % N x 5
numPairs = floor(size(LLP,2)/2);
LLR_exact = zeros(numPairs,5);
LLR_maxlogmap = zeros(numPairs,5);
for k = 1:numPairs
symbol1 = 2*k-1;
symbol2 = 2*k;
LLP1 = LLP (:,symbol1);
LLP2 = LLP (:,symbol2);
prob1 = state_prob(:,symbol1);
prob2 = state_prob(:,symbol2);
% 36 jointmetrics M = log P(i)*P(j) = L1(i)+L2(j)
Mij = LLP1(pam6ind(:,1)) + LLP2(pam6ind(:,2));
pij = prob1(pam6ind(:,1)) .* prob2(pam6ind(:,2));
% now for each of the 5 bits do exact-LLR or max-log
for b = 1:num_bits
idx_sym_1 = pam6bits(:,b)==1;
idx_bit_1 = pam6bits(:,b)==0;
%--- exact LLR from probabilities
P1 = sum(pij(idx_sym_1));
P0 = sum(pij(idx_bit_1));
LLR_exact(k,b) = log(P1./P0);
%--- max-log:
LLR_maxlogmap(k,b) = max( Mij(idx_sym_1) ) - max( Mij(idx_bit_1) ); % N x num_bits
end
end
MI = zeros(1, num_bits);
for k = 1:num_bits
idx_bit_1 = (tx_bits_pam6_reshaped(:,k) == 0); %wo sind die 1en
idx_sym_1 = (tx_bits_pam6_reshaped(:,k) == 1); %wo sind die 0en
%LLR's for all actually transmitted ones or zeros
llr0 = LLR_exact(idx_bit_1,k);
llr1 = LLR_exact(idx_sym_1,k);
% Calculate mutual information for bit position k
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
GMI = sum(MI); % Total mutual information per symbol
GMI = GMI/2;
else
% 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).showBitMapping;
% Initialize LLR storage
LLR_maxlogmap = zeros(length(data_in),num_bits);
LLR_exact = zeros(length(data_in),num_bits);
% Compute bit-wise LLRs
for bit_idx = 1:num_bits
% Find indices where bit is 0 and where it is 1
idx_bit_0 = bit_mapping(:,bit_idx) == 0;
idx_bit_1 = bit_mapping(:,bit_idx) == 1;
% Sum over log-probabilities
% Max-Log approximation: using max instead of sum)
LLR_maxlogmap(:,bit_idx) = max(LLP(idx_bit_1,:), [], 1) - max(LLP(idx_bit_0,:), [], 1);
% Sum probabilities over states for which the bit is 1 and 0, respectively.
P0 = sum(state_prob(idx_bit_0, :),1);
P1 = sum(state_prob(idx_bit_1, :),1);
LLR_exact(:,bit_idx) = log(P1./P0); % N x num_bits
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% CALC NGMI %%%%%
MI = zeros(1, num_bits);
LLR_exact = LLR_exact;
for k = 1:num_bits
idx_bit_0 = (tx_bits(:,k) == 0); %wo sind die 1en
idx_bit_1 = (tx_bits(:,k) == 1); %wo sind die 0en
%LLR's for all actually transmitted ones or zeros
llr0 = LLR_exact(idx_bit_0,k);
llr1 = LLR_exact(idx_bit_1,k);
% Calculate mutual information for bit position k
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
GMI = sum(MI); % Total mutual information for 2 symbols
end
% Number of symbols and bits per symbol
num_symbols = constellation;
num_bits = 2; % 2 bits per symbol
VITERBI_ESTIMATION_SYMBOLS = VITERBI_ESTIMATION_SYMBOLS./rms(VITERBI_ESTIMATION_SYMBOLS);
bit_mapping = PAMmapper(4,0,"eth_style",1).showBitMapping; % Each row corresponds to the symbol above
if debug
%%% DEBUG PLOT LIKELIHOOD RATIOS %%%
figure(115);clf
subplot(2,1,1)
for bit = 1:num_bits
hold on;
histogram(LLR_exact(:,bit),1000,"DisplayName",sprintf('Actual LLR of Bit Pos %d',bit),'LineStyle','none','FaceAlpha',0.4);
end
legend
% Initialize LLR storage
llr = zeros(num_bits, length(data_in));
% Compute bit-wise LLRs
for bit_idx = 1:num_bits
% Find indices where bit is 0 and where it is 1
idx_bit_0 = find(bit_mapping(:,bit_idx) == 0);
idx_bit_1 = find(bit_mapping(:,bit_idx) == 1);
% Sum over log-probabilities (Max-Log approximation: using min instead of sum)
llr(:,bit_idx) = min(llp(:,idx_bit_1), [], 1) - min(llp(:,idx_bit_0), [], 1);
subplot(2,1,2)
for bit = 1:num_bits
hold on;
histogram(LLR_maxlogmap(:,bit),1000,"DisplayName",sprintf('Max Log LLR of Bit Pos %d',bit),'LineStyle','none','FaceAlpha',0.4);
end
legend
end
% Convert LLR values to a hard-decision bit stream
bit_stream = llr < 0;
[~,~,ber_llr,~] = calc_ber(bit_stream',tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
fprintf('LLR BER : %.2e \n',ber_llr);
% directly decide based on lowest LLP index
[~,llp_based_state_seq]=min(llp);
LLP_EST(1:length(data_in)) = constellation(llp_based_state_seq);
rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(LLP_EST');
[~,~,ber_llp,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
fprintf('LLP BER : %.2e \n',ber_llp);
% [~,~,ber_llp,~] = calc_ber(circshift(rx_bits,1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
% fprintf('LLP BER +1: %.2e \n',ber_llp);
% [~,~,ber_llp,~] = calc_ber(circshift(rx_bits,-1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
% fprintf('LLP BER -1: %.2e \n',ber_llp);
%%%% DECIDE based on Viterbi traceback
rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(VITERBI_ESTIMATION_SYMBOLS');
[~,~,ber_viterbi,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
fprintf('Viterbi BER: %.2e \n',ber_viterbi);
% [~,~,ber_viterbi,~] = calc_ber(circshift(rx_bits,1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
% fprintf('Viterbi BER: %.2e \n',ber_viterbi);
% directly decide based on the FW path metrics
[~,fw_direct_state_seq]=min(pm_survivor_fw);
FW_EST(1:length(data_in)) = constellation(fw_direct_state_seq);
rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(FW_EST');
[~,~,ber_fw,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
fprintf('FW BER: %.2e \n',ber_fw);
% directly decide based on the BW path metrics
[~,bw_direct_state_seq]=min(pm_survivor_bw);
BW_EST(1:length(data_in)) = constellation(bw_direct_state_seq(2:end));
rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(BW_EST');
[~,~,ber_bw,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
fprintf('BW BER: %.2e \n',ber_bw);
% [~,~,ber_viterbi,~] = calc_ber(circshift(rx_bits,1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
% fprintf('BW BER: %.2e \n',ber_viterbi);
% [~,~,ber_viterbi,~] = calc_ber(circshift(rx_bits,-1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
% fprintf('BW BER: %.2e \n',ber_viterbi);
PAMmapper(4,0,"eth_style",1).showBitMapping
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
tx_symbolpos = zeros(numel(constellation),length(data_ref));
est_symbolpos = zeros(numel(constellation),length(data_ref));
for lvl = 1:numel(constellation)
tx_symbolpos(lvl,data_ref==constellation(lvl)) = 1;
est_symbolpos(lvl,VITERBI_ESTIMATION_IDX==first_sym(lvl)) = 1;
est_symbolpos_llm(lvl,llp_based_state_seq==lvl) = 1;
%%%%% CHECK BER's %%%%%
if debug
tx_bits = reshape(tx_bits',[],1);
fprintf('\n')
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');
rx_bits = reshape(rx_bits',[],1);
[~,numErr,ber_viterbi,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
fprintf('Viterbi BER: %.2e \n',ber_viterbi);
fprintf('Viterbi Errors = %d\n', numErr);
pairs = reshape(VITERBI_ESTIMATION_SYMBOLS,2,[]).';
levels = sort(unique(VITERBI_ESTIMATION_SYMBOLS));
isedge = ismember(pairs, [levels(1) levels(end)]);
isforbidden = sum(isedge,2)==2;
fprintf('Found %d forbidden transitions (evenodd edges).\n', nnz(isforbidden));
% Convert LLR values to a hard-decision bit stream
bit_stream = LLR_maxlogmap > 0; %ratio separates lower or higher than =0 -> simply decode for the negative values
bit_stream = reshape(bit_stream',[],1);
[~,~,ber_llr,~] = calc_ber(bit_stream,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
fprintf('LLR MaxLogMAP BER : %.2e \n',ber_llr);
% Convert LLR values to a hard-decision bit stream
bit_stream = LLR_exact > 0; %ratio separates lower or higher than =0 -> simply decode for the negative value
bit_stream = reshape(bit_stream',[],1);
[~,~,ber_llr,~] = calc_ber(bit_stream,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
fprintf('LLR BER : %.2e \n',ber_llr);
% DECIDE based on lowest LLP index and check BER
[~,llp_based_state_seq]=max(LLP);
LLP_EST(1:length(data_in)) = first_sym(llp_based_state_seq);
LLP_EST = LLP_EST./rms(LLP_EST);
rx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(LLP_EST');
rx_bits = reshape(rx_bits',[],1);
[~,~,ber_llp,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
fprintf('LLP BER : %.2e \n',ber_llp);
% directly decide based on the FW path metrics
[~,fw_direct_state_seq]=max(alpha);
FW_EST(1:length(data_in)) = first_sym(fw_direct_state_seq);
FW_EST = FW_EST./rms(FW_EST);
rx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(FW_EST');
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:')
fprintf('\n')
end
est_symbolpos= logical(est_symbolpos);
tx_symbolpos = logical(tx_symbolpos);
est_symbolpos_llm = logical(est_symbolpos_llm);
figure(299);clf;hold on;
llp_filt = NaN(length(states),length(data_in));
llp_ = llp - min(llp,[],1);
for c = 2%:numel(constellation)
for c2 = 1:3%numel(constellation)
idx = est_symbolpos_llm(c,:);
llp_filt(c2,idx) = (llp(c2,idx));
plotidx = 2:200000;
scatter(plotidx,llp_filt(c2,plotidx),1,'o','LineWidth',1,'DisplayName',sprintf('LLP of Symbol %d | %d was transmitted',c2,c));
end
end
% Assume llp is a 4 x N matrix (4 PAM-4 symbols, N time steps)
[numSymbols, numTimeSteps] = size(llp);
P_symbols = zeros(numSymbols, numTimeSteps); % To hold the soft output probabilities
for k = 1:numTimeSteps
% Compute the exponentials. (Use -llp_shifted if llp's are costs.)
exponents = -llp_(:, k);
% Normalize to form a probability vector.
P_symbols(:, k) = exponents / sum(exponents);
end
% Optionally, if you prefer a single soft output value per symbol (an expectation),
% define your PAM-4 constellation levels, e.g.:
soft_output = sum(P_symbols .* constellation, 1); % 1 x N vector of soft outputs
%%%% BITWISE LLR's ??? %%%%%
figure(100);
clf
hold on
title('LLR between inner and outer bits')
bitmapping = PAMmapper(numel(constellation),0).demap(constellation);
for bp = 1:size(bitmapping,2)
pos_bitone = find(bitmapping(:,bp)==1);
pos_bitzero = find(bitmapping(:,bp)~=1);
llr_bits(bp,:) = min(llp(pos_bitone,:),[],1) - min(llp(pos_bitzero,:),[],1);
subplot(size(bitmapping,2),1,bp)
scatter(1:length(data_ref),llr_bits(bp,:),1,'.');
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% From: Log-Likelihood Probabilities LLP --> To: Log-Likelihood Ratios LLR
for s = 1:length(states)-1
llr(s,:) = llp_(s,:) - llp_(s+1,:); % subtract llp's of successive const. points to get llr's
end
% Build Sets of LLR's that contain only those values at
% timepoint k, where the symbols:
% a) were actually transmitted: set "S"
% b) were decoded by viterbi: set "SC"
S = NaN(length(states)-1,length(data_in));
SC = NaN(length(states)-1,length(data_in));
llp_inf = llp_;
llp_inf(llp_==0) = Inf;
[~,scnd_idx] = min(llp_inf,[],1) ;
for s = 1:length(states)-1
%%% SET "S"
% find all time indices k, where const point s was actually transmitted
indice = tx_symbolpos(s,:)==1;
S(s,indice) = llr(s,indice);
%calc mean of all llr's where symbol s was transmitted
K(s,1) = mean(S(s,indice),'omitnan');
% find all time indices k, where const. point s+1 was actually transmitted
indice_plusone = tx_symbolpos(s+1,:)==1;
S(s,indice_plusone) = llr(s,indice_plusone);
K(s,2) = mean(S(s,indice_plusone),'omitnan');
%%% SET "SC"
% find all time indices k, where symbol s was decoded
% idx: find positions in time where a symbol s was decoded
idx = est_symbolpos_llm(s,:);
% idx 2: find positions where the strongest competitor is
% s+1, i.e. the second best llp is at s+1
idx2 = scnd_idx == s+1;
SC(s,idx&idx2) = llr(s,idx&idx2);
KC(s,1) = mean(SC(s,idx&idx2),'omitnan');
% find all time indices k, where symbol s+1 was decoded
% idx: find positions in time where a symbol s+1 was decoded
idx = est_symbolpos_llm(s+1,:);
idx2 = scnd_idx == s;
% idx 2: find positions where the strongest competitor is
% s, i.e. the second best llp is at s
SC(s,idx&idx2) = llr(s,idx&idx2);
KC(s,2) = mean(SC(s,idx&idx2),'omitnan');
% scale the sets, using the average (?) of the
llrcn(s,:) = SC(s,:) * ( constellation(s+1)-constellation(s) ) ./ ( K(s,2)-K(s,1)) + decisionLevels(s);
end
figure(111)
title("Bla")
clf
for s = 1:length(states)-1
figure(1111)
hold on
title(sprintf('llrcn = llp %d - llp %d',s, s+1,s, s+1));
scatter(1:length(llrcn),llrcn(s,:),1,'.');
% STUFENARTIGE LLR'S UND LLP'S %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
figure(111)
subplot(1,length(states),s)
hold on
title(sprintf('llr = llp %d - llp %d \n SC=llr(tx sym = %d or %d)',s, s+1,s, s+1));
scatter(1:length(llr),llr(s,:),1,'.');
scatter(1:length(SC),SC(s,:),1,'.');
yline([K(s,1),K(s,2)]);
low = min(min(llr));
hi = max(max(llr));
ylim([low,hi]);
subplot(1,length(states),length(states))
hold on
scatter(1:length(llr),llr(s,:),1,'.');
ylim([low,hi]);
% HISTOGRAMME %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
figure(112)
subplot(length(states),1,s)
hold on
title(sprintf('histogram of llr; between const point %d - %d',s, s+1));
histogram(llrcn(s,:),10000,'EdgeAlpha',0)
% histogram(SC(s,:),10000,'EdgeAlpha',0)
xlim([-20, 20]);
subplot(length(states),1,length(states))
hold on
histogram(llrcn(s,:),10000,'EdgeAlpha',0)
% histogram(SC(s,:),10000,'EdgeAlpha',0)
% xlim([-20, 20]);
end
softdecisions = mean(llrcn,1,'omitnan');
distance = abs(VITERBI_ESTIMATION_SYMBOLS-llrcn);
[win_cost,win_idx] = min(distance,[],1);
for i = 1:length(data_in)
soft_decisions(i) = llrcn(win_idx(i),i);
end
soft_decisions = max(llrcn,[],1);
showLevelHistogram(soft_decisions,data_ref)
figure()
% scatter(1:length(data_in),VITERBI_ESTIMATION_SYMBOLS,1,'.');
scatter(1:length(data_in),soft_decisions,1,'.');
MLM_ESTIMATION(1:length(data_in)) = PAMmapper(numel(constellation),0).quantize(soft_decisions)';
rx_bits = PAMmapper(numel(constellation),0).demap(MLM_ESTIMATION');
[~,~,ber_mlm,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
fprintf('MLM BER: %.2e \n',ber_mlm);
end
end
methods (Access=private)
end
end

View File

@@ -42,6 +42,7 @@ classdef TransmissionPerformance
0.8733, 0.8790, 0.8848, 0.8905, 0.8962, ...
0.9019, 0.9077, 0.9134, 0.9191, 0.9248, ...
0.9306, 0.9363, 0.9420, 0.9477, 0.9535];
NGMITHRESHOLDS_SDHD = [0.8116, 0.8167, 0.8241, 0.8317, 0.8401, ...
0.8459, 0.8512, 0.8574, 0.8685, 0.8746, ...
0.8829, 0.8892, 0.8958, 0.9022, 0.9090,...
@@ -60,7 +61,7 @@ classdef TransmissionPerformance
%% LUT for KP4-FEC and Inner Code https://grouper.ieee.org/groups/802/3/dj/public/23_03/patra_3dj_01b_2303.pdf
CODE_RATE_KP4_AND_INNER = [0.885799];
CODE_RATE_KP4_AND_INNER = [0.885799]; %https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=9979198 -> 199.8 / 224 = 0.891 als code rate
BERTHRESHOLDS_KP4_AND_INNER = 4.85e-3;
% https://www.ieee802.org/3/bs/public/14_11/parthasarathy_3bs_01a_1114.pdf
@@ -153,11 +154,21 @@ classdef TransmissionPerformance
netrates.SDHD.Threshold = NaN(1, numMeasurements);
end
if ~isempty(ber)
netrates.STAIR.GrossRate = NaN(1, numMeasurements);
netrates.STAIR.NetRate = NaN(1, numMeasurements);
netrates.STAIR.CodeRate = NaN(1, numMeasurements);
netrates.STAIR.Threshold = NaN(1, numMeasurements);
netrates.HD.GrossRate = NaN(1, numMeasurements);
netrates.HD.NetRate = NaN(1, numMeasurements);
netrates.HD.CodeRate = NaN(1, numMeasurements);
netrates.HD.Threshold = NaN(1, numMeasurements);
netrates.KP4.GrossRate = NaN(1, numMeasurements);
netrates.KP4.NetRate = NaN(1, numMeasurements);
netrates.KP4.CodeRate = NaN(1, numMeasurements);
netrates.KP4.Threshold = NaN(1, numMeasurements);
netrates.KP4_hamming.GrossRate = NaN(1, numMeasurements);
netrates.KP4_hamming.NetRate = NaN(1, numMeasurements);
netrates.KP4_hamming.CodeRate = NaN(1, numMeasurements);
@@ -200,11 +211,47 @@ classdef TransmissionPerformance
end
if ~isempty(idxBER)
codeRate = obj.CODE_RATES_HD(idxBER);
netrates.STAIR.NetRate(i) = grossRate(i) * codeRate;
netrates.STAIR.GrossRate(i) = grossRate(i) ;
netrates.STAIR.CodeRate(i) = codeRate;
netrates.STAIR.Threshold(i) = obj.BERTHRESHOLDS_HD(idxBER);
end
% HD FEC 3,8e-3
idxBER = [];
for j = length(obj.BERTHRESHOLDS_HDFEC):-1:1
if ber(i) <= obj.BERTHRESHOLDS_HDFEC(j)
idxBER = j;
break;
end
end
if ~isempty(idxBER)
codeRate = obj.CODE_RATE_HDFEC(idxBER);
netrates.HD.NetRate(i) = grossRate(i) * codeRate;
netrates.HD.GrossRate(i) = grossRate(i) ;
netrates.HD.CodeRate(i) = codeRate;
netrates.HD.Threshold(i) = obj.BERTHRESHOLDS_HD(idxBER);
netrates.HD.Threshold(i) = obj.CODE_RATE_HDFEC(idxBER);
end
% KP4
idxBER = [];
for j = length(obj.BERTHRESHOLDS_KP4):-1:1
if ber(i) <= obj.BERTHRESHOLDS_KP4(j)
idxBER = j;
break;
end
end
if ~isempty(idxBER)
codeRate = obj.CODE_RATE_KP4(idxBER);
netrates.KP4.NetRate(i) = grossRate(i) * codeRate;
netrates.KP4.GrossRate(i) = grossRate(i) ;
netrates.KP4.CodeRate(i) = codeRate;
netrates.KP4.Threshold(i) = obj.CODE_RATE_KP4(idxBER);
end
% KP4 + inner Hamming
idxBER = [];
for j = length(obj.BERTHRESHOLDS_KP4_AND_INNER):-1:1
if ber(i) <= obj.BERTHRESHOLDS_KP4_AND_INNER(j)
@@ -220,6 +267,7 @@ classdef TransmissionPerformance
netrates.KP4_hamming.Threshold(i) = obj.BERTHRESHOLDS_KP4_AND_INNER(idxBER);
end
% O FEC
idxBER = [];
for j = length(obj.BERTHRESHOLDS_O_FEC):-1:1
if ber(i) <= obj.BERTHRESHOLDS_O_FEC(j)

View File

@@ -5,7 +5,7 @@ classdef DBHandler < handle
properties
conn % Database connection object
pathToDB % Path to the SQLite database
dataBase % Path to the SQLite database
tableNames % Cell array containing names of all tables in the database
tables = struct(); % Structure containing MATLAB tables for each database table
distinctValues
@@ -21,7 +21,7 @@ classdef DBHandler < handle
% obj = DBHandler('pathToDB', 'path/to/database.db');
arguments
options.pathToDB = ""; % Default value for pathToDB if not provided
options.dataBase = ""; % Default value for pathToDB if not provided
options.type = "mysql";
end
@@ -37,12 +37,15 @@ classdef DBHandler < handle
try
if options.type == "sqlite"
obj.conn = sqlite(obj.pathToDB);
obj.conn = sqlite(obj.dataBase);
elseif options.type == "mysql"
datasource = "jdbc:mysql://134.245.243.254:3306/labor";
% datasource = "jdbc:mysql://134.245.243.254:3306/labor";
obj.conn = database( ...
"labor", ... % Database name
string(obj.dataBase), ... % Database name
"silas", ... % Username
"silas", ... % Password (or getSecret)
"Vendor", "MySQL", ...
@@ -60,7 +63,9 @@ classdef DBHandler < handle
obj.refresh();
else
error('DB seems to be corrupt')
end
end
@@ -68,9 +73,11 @@ classdef DBHandler < handle
function obj = refresh(obj)
% Get table names and the first rows of each table to understand the structure
warning off
obj.getTableNames();
obj.getTables();
obj.getDistinctValues();
warning on
% obj.getDistinctValues();
end
function obj = getTableNames(obj)
@@ -83,7 +90,7 @@ classdef DBHandler < handle
result = fetch(obj.conn, 'SELECT name FROM sqlite_master WHERE type="table"');
obj.tableNames = result.name;
end
catch e
error('Failed to retrieve table names: %s', e.message);
end
@@ -169,14 +176,6 @@ classdef DBHandler < handle
function healthyDB = dbIsHealthy(obj)
healthyDB = false;
num_runs = obj.fetch('SELECT COUNT(*) AS total_runs FROM Runs');
num_configs = obj.fetch('SELECT COUNT(*) AS total_configurations FROM Configurations');
num_meas = obj.fetch('SELECT COUNT(*) AS total_measurements FROM Measurements');
assert((num_runs{1,1}==num_configs{1,1})&&(num_configs{1,1}==num_meas{1,1}),'Different num of entries per table');
%Check for any duplicate paths
duplictae_raw = obj.fetch("SELECT COALESCE(Runs.rx_raw_path,'NaN') AS rx_raw_path, COUNT(*) AS occurrences FROM Runs GROUP BY rx_raw_path HAVING COUNT(*) > 1");
duplictae_sync = obj.fetch("SELECT COALESCE(Runs.rx_sync_path,'NaN') AS rx_sync_path, COUNT(*) AS occurrences FROM Runs GROUP BY rx_sync_path HAVING COUNT(*) > 1");
@@ -186,11 +185,11 @@ classdef DBHandler < handle
fprintf('Raw Rx Paths: Found %d duplictaes of %s \n',duplictae_raw.occurrences(i),duplictae_raw.rx_raw_path(i));
end
end
healthyDB = true;
end
function lastID = appendToTable(obj, tableName, newRow)
% appendToTable Appends a new row to the specified table
%
@@ -206,40 +205,50 @@ classdef DBHandler < handle
error('Table %s does not exist in the database or has not been fetched.', tableName);
end
% Convert newRow to a table if it is a struct
% Handle struct preprocessing before table conversion
if isstruct(newRow)
fields = fieldnames(newRow);
emptyFields = structfun(@isempty,newRow);
if sum(emptyFields) > 0
emptyFieldNames = fields(emptyFields); % use () to get a cell array
for idx = 1:numel(emptyFieldNames)
newRow.(emptyFieldNames{idx}) = NaN;
% % Handle empty fields
% emptyFields = structfun(@isempty, newRow);
% if sum(emptyFields) > 0
% emptyFieldNames = fields(emptyFields);
% for idx = 1:numel(emptyFieldNames)
% newRow.(emptyFieldNames{idx}) = NaN;
% end
% end
% Convert any non-scalar numeric (including [] and vectors) into JSON
for i = 1:numel(fields)
name = fields{i};
value = newRow.(name);
if isnumeric(value) && ~isscalar(value)
% jsonencode([]) -> "[]"
% jsonencode([a,b,c]) -> "[a,b,c]"
newRow.(name) = jsonencode(value);
end
end
% Convert to table
newRow = struct2table(newRow);
end
% Ensure the new row matches the structure of the existing table
existingTableStructure = obj.tables.(tableName);
% Perform data type checks and conversions
% Perform remaining data type checks and conversions
for colName = newRow.Properties.VariableNames
% Extract the value and its intended column type
value = newRow.(colName{1});
existingValue = existingTableStructure.(colName{1});
% If the value is a class object, convert it to JSON format
if isobject(value) && ~isdatetime(value) && ~isa(value,"string")
if iscell(value) && ~isempty(value)
if ischar(value{1}) || isstring(value{1})
newRow.(colName{1}) = value{1};
end
elseif isobject(value) && ~isdatetime(value) && ~isa(value, "string")
newRow.(colName{1}) = string(jsonencode(value));
% If the value is a character array, convert it to a string
elseif ischar(value)
newRow.(colName{1}) = string(value);
elseif isdatetime(value)
% newRow.(colName{1}) = string(value);
end
end
@@ -247,7 +256,6 @@ classdef DBHandler < handle
% Parameters for retry logic
maxRetries = 50;
basePause = 0.05; % seconds
attempt = 0;
success = false;
@@ -256,37 +264,41 @@ classdef DBHandler < handle
if obj.type == "mysql"
newRow_ = obj.convertTableToCellStrings(newRow);
end
sqlwrite(obj.conn, tableName, newRow);
success = true; % Write successful
sqlwrite(obj.conn, tableName, newRow,"Catalog",obj.dataBase);
success = true;
catch e
if contains(e.message, 'database is locked') || contains(e.message, 'cannot rollback transaction')
attempt = attempt + 1;
pauseTime = basePause * (1 + rand()); % Add random jitter to reduce collision chance
fprintf('Database locked. Retry %d/%d after %.3f seconds...\n', attempt, maxRetries, pauseTime);
pauseTime = basePause * (1 + rand());
fprintf('Database locked. Retry %d/%d after %.3f seconds...\n', ...
attempt, maxRetries, pauseTime);
pause(pauseTime);
else
fprintf('Error details:\n');
fprintf('Column values:\n');
disp(newRow);
error('Failed to append to the table %s: %s', tableName, e.message);
end
end
end
if ~success
error('Failed to append to table %s after %d retries due to database lock.', tableName, maxRetries);
error('Failed to append to table %s after %d retries due to database lock.', ...
tableName, maxRetries);
end
% Retrieve the measurement_id of the newly inserted row for linking other tables
% Retrieve the ID of the newly inserted row
if obj.type == "mysql"
queue = "SELECT LAST_INSERT_ID()";
query = "SELECT LAST_INSERT_ID()";
elseif obj.type == "sqlite"
queue = "SELECT last_insert_rowid()";
query = "SELECT last_insert_rowid()";
end
result = fetch(obj.conn, queue);
lastID = result{1, 1}; % Access the value directly from the table
result = fetch(obj.conn, query);
lastID = result{1, 1};
end
function exists = checkIfRunExists(obj, table2check, column2check, value2check)
function [exists, count] = checkIfRunExists(obj, table2check, column2check, value2check)
% checkIfRunExists Checks if a specific value exists in a specified column of a table
%
% Usage:
@@ -311,7 +323,11 @@ classdef DBHandler < handle
end
% Construct the query to check for the value in the specified column
query = sprintf('SELECT COUNT(*) FROM %s WHERE %s = "%s"', table2check, column2check, value2check);
if isnumeric(value2check)
query = sprintf('SELECT COUNT(*) FROM %s WHERE %s = %d', table2check, column2check, value2check);
else
query = sprintf('SELECT COUNT(*) FROM %s WHERE %s = "%s"', table2check, column2check, value2check);
end
% Execute the query and pass the value2check to avoid SQL injection issues
try
@@ -325,12 +341,21 @@ classdef DBHandler < handle
exists = count > 0;
if exists
disp(['The value "', value2check, '" already exists in the column "', column2check, '" of the table "', table2check, '".']);
% disp(['The value "', num2str(value2check), '" already exists in the column "', column2check, '" of the table "', table2check, '".']);
else
% disp(['The value "', value2check, '" does not exist in the column "', column2check, '" of the table "', table2check, '".']);
end
end
function hashStr = calcHash(~,object)
jsonStr = jsonencode(object);
md = java.security.MessageDigest.getInstance('MD5');
md.update(uint8(jsonStr));
hashBytes = typecast(md.digest, 'uint8');
hashStr = lower(dec2hex(hashBytes)');
hashStr = lower(strtrim(hashStr(:)')); % Convert to a lowercase string
end
function resultID = addProcessingResult(obj, run_id, resultData, eqParamsData)
% addProcessingResult Adds a processing result and links it to an EqualizerParameters entry.
@@ -348,18 +373,19 @@ classdef DBHandler < handle
% resultID: The result_id of the newly inserted ProcessingResults entry.
% 1. Compute hash for equalizer parameters
jsonStr = jsonencode(eqParamsData);
md = java.security.MessageDigest.getInstance('MD5');
md.update(uint8(jsonStr));
hashBytes = typecast(md.digest, 'uint8');
hashStr = lower(dec2hex(hashBytes)');
hashStr = lower(strtrim(hashStr(:)')); % Convert to a lowercase string
% jsonStr = jsonencode(eqParamsData);
% md = java.security.MessageDigest.getInstance('MD5');
% md.update(uint8(jsonStr));
% hashBytes = typecast(md.digest, 'uint8');
% hashStr = lower(dec2hex(hashBytes)');
% hashStr = lower(strtrim(hashStr(:)')); % Convert to a lowercase string
hashStr = obj.calcHash(eqParamsData);
% Add hash to equalizer parameters
eqParamsData.config_hash = hashStr;
eqParamsData.hash = hashStr;
% 2. Check if an equalizer configuration with the same hash exists
queryStr = sprintf('SELECT eq_id FROM EqualizerParameters WHERE config_hash = ''%s''', eqParamsData.config_hash);
queryStr = sprintf('SELECT eq_id FROM Equalizer WHERE hash = ''%s''', eqParamsData.hash);
existingEntry = obj.fetch(queryStr);
if ~isempty(existingEntry)
@@ -367,27 +393,30 @@ classdef DBHandler < handle
eq_id = existingEntry{1,1};
else
% Insert the new equalizer configuration and get its eq_id
eq_id = obj.appendToTable('EqualizerParameters', eqParamsData);
eqParamsData = eqParamsData.toStruct;
eq_id = obj.appendToTable('Equalizer', eqParamsData);
end
% 3. Add the equalizer configuration reference and run_id to resultData
resultData.eqParam_id = eq_id;
resultData = resultData.toStruct;
resultData.eq_id = eq_id;
resultData.run_id = run_id;
% 4. Compute hash for the processing result
tempResultData = rmfield(resultData, 'date_of_processing');
resultJsonStr = jsonencode(tempResultData);
md2 = java.security.MessageDigest.getInstance('MD5'); % Create a new MD5 instance
md2.update(uint8(resultJsonStr));
resultHashBytes = typecast(md2.digest, 'uint8');
resultHashStr = lower(dec2hex(resultHashBytes)');
resultHashStr = lower(strtrim(resultHashStr(:)')); % Convert to a lowercase string
% resultJsonStr = jsonencode(tempResultData);
% md2 = java.security.MessageDigest.getInstance('MD5'); % Create a new MD5 instance
% md2.update(uint8(resultJsonStr));
% resultHashBytes = typecast(md2.digest, 'uint8');
% resultHashStr = lower(dec2hex(resultHashBytes)');
% resultHashStr = lower(strtrim(resultHashStr(:)')); % Convert to a lowercase string
resultHashStr = obj.calcHash(tempResultData);
% Add the result hash to resultData
resultData.result_hash = resultHashStr;
resultData.hash = resultHashStr;
% 5. Check if an identical processing result already exists
queryStr2 = sprintf('SELECT result_id FROM Results WHERE result_hash = ''%s''', resultData.result_hash);
queryStr2 = sprintf('SELECT result_id FROM Results WHERE hash = ''%s''', resultData.hash);
existingResult = obj.fetch(queryStr2);
if ~isempty(existingResult)
@@ -397,7 +426,66 @@ classdef DBHandler < handle
return;
end
% 6. Insert the processing result
% 6. check if obj.tables.Results matches Metricstruct
% Fields to exclude from comparison
excludeFields = {'result_id', 'run_id', 'eq_id'};
% 7. Chack for new fields in Metric struct and append to
% database if necessary
ms=Metricstruct;
metricFields = setdiff(fieldnames(ms), excludeFields);
tableFields = setdiff(fieldnames(obj.tables.Results), excludeFields);
% Check matches and find missing fields
matches = all(ismember(metricFields, tableFields));
if ~matches
missingFields = setdiff(metricFields, tableFields);
% If there are missing fields, add them to the SQL table
if ~isempty(missingFields)
for i = 1:length(missingFields)
fieldName = missingFields{i};
% Determine SQL data type based on MATLAB class
fieldValue = ms.(fieldName);
if isnumeric(fieldValue)
if isinteger(fieldValue)
sqlType = 'INTEGER';
else
sqlType = 'REAL';
end
elseif ischar(fieldValue) || isstring(fieldValue)
sqlType = 'TEXT';
elseif isdatetime(fieldValue)
sqlType = 'DATETIME';
elseif iscell(fieldValue) || isstruct(fieldValue) || islogical(fieldValue)
sqlType = 'TEXT'; % Store as JSON
else
sqlType = 'TEXT'; % Default to TEXT for unknown types
end
% Create ALTER TABLE query
queryStr = sprintf('ALTER TABLE Results ADD COLUMN %s %s', fieldName, sqlType);
try
% Execute the query
obj.fetch(queryStr);
fprintf('Added field "%s" of type %s to Results table\n', fieldName, sqlType);
catch ME
fprintf('Error adding field "%s": %s\n', fieldName, ME.message);
end
end
else
fprintf('No missing fields to add.\n');
end
end
% 8. Insert the processing result
resultID = obj.appendToTable('Results', resultData);
end
@@ -486,17 +574,32 @@ classdef DBHandler < handle
end
function executeSQL(obj, query)
% This method executes an SQL statement using MATLAB's execute function.
execute(obj.conn, query);
end
function answer = fetch(obj,query)
answer = fetch(obj.conn,query);
function answer = fetch(obj, query)
maxFast = 20; maxSlow = 30;
for attempt = 1:maxSlow
try
answer = fetch(obj.conn, query);
return
catch ME
if attempt < maxFast
pause(0.1)
else
pause(1)
end
lastErr = ME;
end
end
error('Database fetch failed after %d attempts:\n%s', maxSlow, lastErr.getReport())
end
function [result,query] = queryDB(obj, filterParams, selectedFields)
% getPathsWithFlexibleFilter Retrieves values from Runs table with flexible filtering
% and lets the user select which fields to include in the SELECT statement.
@@ -518,22 +621,6 @@ classdef DBHandler < handle
selectedFields = [];
end
% Step 1: Prompt the user to input filter parameters if not provided
if isempty(filterParams)
filterParams = obj.promptFilterParameters();
end
% Step 2: Prompt the user to select fields to include in the SELECT statement
if isempty(selectedFields)
selectedFields = obj.promptSelectFields();
else
if iscell(selectedFields)
elseif isstruct(selectedFields)
end
end
% Step 3: Construct the SQL query based on the inputs
query = obj.constructSQLQuery(filterParams, selectedFields);
@@ -558,7 +645,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
@@ -619,132 +706,238 @@ classdef DBHandler < handle
end
end
function query = constructSQLQuery(obj, filterParams, selectedFields)
% constructSQLQuery Constructs the SQL query based on filter parameters and selected fields.
%
% If selectedFields is provided as a struct, it is converted to a cell array.
% The conversion takes the field names and creates entries like:
% {'selectedFields.fieldName'} for each field.
% Input check for selectedFields: if it's a struct, convert it to a cell array.
if isstruct(selectedFields)
arguments
obj
filterParams
selectedFields
end
% -------- Step 1: Normalize selectedFields to {'Table.field', ...} --------
if isempty(selectedFields) || (ischar(selectedFields) && strcmpi(selectedFields, 'all'))
selectedFields = obj.getTableFieldNames('Runs'); % default
elseif isstruct(selectedFields)
newFields = {};
tableNames = fieldnames(selectedFields);
for t = 1:numel(tableNames)
tableStruct = selectedFields.(tableNames{t});
fieldNames = fieldnames(tableStruct);
for f = 1:numel(fieldNames)
if isequal(tableStruct.(fieldNames{f}), 1)
newFields{end+1} = sprintf('%s.%s', tableNames{t}, fieldNames{f});
fns = fieldnames(tableStruct);
for f = 1:numel(fns)
if isequal(tableStruct.(fns{f}), 1)
newFields{end+1} = sprintf('%s.%s', tableNames{t}, fns{f}); %#ok<AGROW>
end
end
end
selectedFields = newFields;
end
% Construct the SELECT clause dynamically based on user selection.
% (Assuming that when provided as a cell array, each entry is of the form
% 'TableName.fieldName' or, in our conversion case, 'selectedFields.fieldName'.)
selectClause = 'SELECT DISTINCT ';
for i = 1:numel(selectedFields)
fieldParts = strsplit(selectedFields{i}, '.');
% If the field comes from the struct conversion, its first part is 'selectedFields'
% and the actual field name is in the second part.
if strcmp(fieldParts{1}, 'selectedFields')
tableName = fieldParts{1}; % not used for type checking below
fieldName = fieldParts{2};
else
tableName = fieldParts{1};
fieldName = fieldParts{2};
end
% Parse the table names actually referenced by the SELECT
reqTables = unique(cellfun(@(s) extractBefore(s, '.'), selectedFields, ...
'UniformOutput', false));
% Decide on COALESCE depending on the field type.
% If the table is known in obj.tables and the field is numeric, use 'NaN'.
if isfield(obj.tables, tableName) && isfield(obj.tables.(tableName), fieldName) && isnumeric(obj.tables.(tableName).(fieldName))
selectClause = [selectClause, 'COALESCE(', selectedFields{i}, ', ''NaN'') AS ', fieldName];
else
selectClause = [selectClause, 'COALESCE(', selectedFields{i}, ', '''') AS ', fieldName];
end
% -------- Step 2: Build SELECT with COALESCE wrapper as you already do ----
selectClause = obj.generateCoalesceString(selectedFields);
if i < numel(selectedFields)
selectClause = [selectClause, ', '];
else
selectClause = [selectClause, ' '];
% -------- Step 3: FROM and minimal JOIN plan ------------------------------
% Decide main table: prefer the first explicitly referenced table, else 'Runs'
if ~isempty(reqTables)
mainTable = reqTables{1};
else
mainTable = 'Runs';
end
% If WHERE references a table not in reqTables (e.g., Runs.*), make sure its present.
whereClause = '';
if ~isempty(filterParams)
whereClause = obj.generateWhereClause(filterParams);
% Heuristic: add 'Runs' if WHERE clause mentions 'Runs.'
if contains(whereClause, 'Runs.')
reqTables = unique([reqTables; {'Runs'}]); %#ok<AGROW>
end
end
% Ensure main table is included
if ~ismember(mainTable, reqTables)
reqTables = unique([mainTable; reqTables]); %#ok<AGROW>
end
% --- Adaptive FROM Clause ---
mainTable = 'Runs';
fromClause = ['FROM ', mainTable, ' '];
% Well build joins only for the required tables (minus the main)
otherTables = setdiff(reqTables, {mainTable});
% Prepare join buffers
normalJoins = '';
equalizerJoin = '';
% Keep track of whats already in the FROM graph (start with main)
present = string(mainTable);
joins = strings(0,1);
tableNamesAll = fieldnames(obj.tables);
for t = 1:numel(tableNamesAll)
tableName = tableNamesAll{t};
% Helper lambdas
hasField = @(tbl, fld) isfield(obj.tables.(char(tbl)), char(fld));
canJoinBy = @(left, right, key) hasField(left, key) && hasField(right, key);
if strcmpi(tableName, mainTable) || strcmpi(tableName, 'sqlite_sequence')
continue;
% A small helper that adds a LEFT JOIN if the right table isn't present yet
function addJoinByKey(rightTbl, key)
if any(present == string(rightTbl))
return; % already joined
end
% Prefer to join against an already-present table that has the key
anchor = '';
for k = 1:numel(present)
if canJoinBy(char(present(k)), rightTbl, key)
anchor = char(present(k));
break;
end
end
if isempty(anchor)
% No anchor in current graph; if the right table is 'Equalizer' and key is eq_id,
% try to ensure a bridge table with eq_id exists (Results or a dashboard view).
if strcmpi(rightTbl,'Equalizer') && strcmpi(key,'eq_id')
% Bring in one eq_id-capable table if it is requested
bridgeOrder = {'Results','dashboard_old','dashboard_new','dashboard_ungrouped'};
for b = 1:numel(bridgeOrder)
br = bridgeOrder{b};
if ismember(br, reqTables) && ~any(present == string(br)) && hasField(obj.tables.(br),'eq_id')
% Attach bridge by run_id if possible, otherwise leave for eq_id
if any(present == "Runs") && hasField(obj.tables.(br),'run_id') && hasField(obj.tables.('Runs'),'run_id')
joins(end+1,1) = "LEFT JOIN " + br + " ON Runs.run_id = " + br + ".run_id";
present(end+1,1) = string(br);
anchor = br; % we can now anchor Equalizer on eq_id to this
break;
else
% Fallback: anchor to main if it shares eq_id
for k = 1:numel(present)
pk = char(present(k));
if canJoinBy(pk, br, 'eq_id')
joins(end+1,1) = "LEFT JOIN " + br + " ON " + pk + ".eq_id = " + br + ".eq_id";
present(end+1,1) = string(br);
anchor = br;
break;
end
end
if ~isempty(anchor), break; end
end
end
end
end
end
% Re-scan for an anchor (maybe the bridge helped)
if isempty(anchor)
for k = 1:numel(present)
if canJoinBy(char(present(k)), rightTbl, key)
anchor = char(present(k));
break;
end
end
end
if isempty(anchor)
% As a final fallback, if the main table is Runs and right has run_id, join by run_id
if strcmpi(mainTable,'Runs') && hasField(obj.tables.(rightTbl),'run_id') && hasField(obj.tables.('Runs'),'run_id')
anchor = 'Runs';
key = 'run_id';
end
end
if isempty(anchor)
% Could not find a path; skip join silently (or throw if you prefer strict)
return;
end
joins(end+1,1) = "LEFT JOIN " + rightTbl + " ON " + anchor + "." + key + " = " + rightTbl + "." + key;
present(end+1,1) = string(rightTbl);
end
if isfield(obj.tables.(tableName), 'run_id')
% Direct join to Runs
normalJoins = [normalJoins, 'LEFT JOIN ', tableName, ' ON ', mainTable, '.run_id = ', tableName, '.run_id '];
elseif isfield(obj.tables.(tableName), 'eq_id')
% Equalizer depends on Results, collect this join separately
equalizerJoin = ['LEFT JOIN ', tableName, ' ON Results.eqParam_id = ', tableName, '.eq_id '];
% First pass: if WHERE uses Runs.* and mainTable isnt Runs, ensure Runs is in the graph
if contains(string(whereClause), "Runs.") && ~any(present == "Runs")
% Try to join Runs to whatever has run_id (mainTable ideally)
if hasField(mainTable, 'run_id') && hasField('Runs', 'run_id')
joins(end+1,1) = "LEFT JOIN Runs ON " + string(mainTable) + ".run_id = Runs.run_id";
present(end+1,1) = "Runs";
end
end
% Combine joins: normal joins first, Equalizer last
fromClause = [fromClause, normalJoins, equalizerJoin];
% Join the required tables with minimal edges
for i = 1:numel(otherTables)
tbl = otherTables{i};
% Prefer run_id join if possible, else eq_id, else skip
if any(present == "Runs") && hasField(tbl,'run_id')
addJoinByKey(tbl, 'run_id');
elseif hasField(tbl,'run_id') && hasField(mainTable,'run_id')
addJoinByKey(tbl, 'run_id');
elseif hasField(tbl,'eq_id')
addJoinByKey(tbl, 'eq_id');
else
% no obvious key; skip
end
end
% Build the FROM clause
fromClause = "FROM " + string(mainTable) + " " + strjoin(joins, " ");
% -------- Step 4: WHERE (unchanged logic) ------------------------------
if ~isempty(whereClause)
query = char(strjoin([selectClause, fromClause, "WHERE " + string(whereClause)], " "));
else
query = char(strjoin([selectClause, fromClause], " "));
end
end
% --- WHERE Clause Construction ---
baseQuery = [selectClause, ' ', fromClause, 'WHERE '];
function whereClause = generateWhereClause(obj, filterParams)
filterClauses = [];
tableNames_ = fieldnames(filterParams);
for t = 1:numel(tableNames_)
tableName = tableNames_{t};
% If filterParams is a DbFilterParameter object, get its internal structure
if isa(filterParams, 'QueryFilter')
filterParams = filterParams.toStruct();
end
% Now proceed with the structure
tableNames = fieldnames(filterParams);
for t = 1:numel(tableNames)
tableName = tableNames{t};
tableParams = filterParams.(tableName);
fieldNames = fieldnames(tableParams);
for i = 1:numel(fieldNames)
fieldName = fieldNames{i};
value = tableParams.(fieldName);
fullName = sprintf('%s.%s', tableName, fieldName);
% Handle various types of values for SQL query construction
if isempty(value)
% Skip empty values
if isempty(value) || (isa(value, 'QueryFilter') && isempty(value.value))
continue;
elseif isnumeric(value) && isnan(value)
filterClause = sprintf('%s IS NULL', fullName);
elseif isnumeric(value) && ~isEnumeration(value)
filterClause = sprintf('%s = %f', fullName, value);
elseif islogical(value) || (isnumeric(value) && ismember(value, [0, 1])) && ~isEnumeration(value)
filterClause = sprintf('%s = %d', fullName, value);
elseif ischar(value) || isstring(value)
filterClause = sprintf('%s = ''%s''', fullName, char(value));
elseif isEnumeration(value)
filterClause = sprintf('%s = ''%s''', fullName, value);
else
error('Unsupported data type for field "%s".', fullName);
end
filterClauses = [filterClauses, filterClause, ' AND '];
% Handle Filter class
if isa(value, 'SqlFilter')
if isnumeric(value.value)
filterClause = sprintf('%s %s %f', ...
fullName, value.operator, value.value);
elseif ischar(value.value) || isstring(value.value)
filterClause = sprintf('%s %s ''%s''', ...
fullName, value.operator, char(value.value));
else
continue; % Skip unsupported types
end
filterClauses = [filterClauses, filterClause, ' AND '];
else
% Handle direct values (legacy support)
if isnumeric(value) && isnan(value)
filterClause = sprintf('%s IS NULL', fullName);
elseif isnumeric(value)
filterClause = sprintf('%s = %f', fullName, value);
elseif ischar(value) || isstring(value)
filterClause = sprintf('%s = ''%s''', fullName, char(value));
else
continue; % Skip unsupported types
end
filterClauses = [filterClauses, filterClause, ' AND '];
end
end
end
% Remove trailing ' AND ' if any filters were added.
% Remove trailing ' AND ' if any filters were added
if ~isempty(filterClauses)
filterClauses = filterClauses(1:end-5);
query = [selectClause, ' ', fromClause, 'WHERE ', filterClauses];
whereClause = filterClauses(1:end-5);
else
query = [selectClause, ' ', fromClause];
whereClause = '';
end
end
@@ -980,5 +1173,59 @@ classdef DBHandler < handle
end
function fieldNames = getTableFieldNames(obj, tableName)
% Returns all field names for a given table as a cell array in the format {'TableName.fieldName'}
if isfield(obj.tables, tableName)
% Get raw field names
rawFields = fieldnames(obj.tables.(tableName));
% Create cell array with table name prefix
fieldNames = cellfun(@(x) [tableName, '.', x], ...
rawFields, ...
'UniformOutput', false);
else
error('Table "%s" not found in obj.tables.', tableName);
end
end
function coalesceStr = generateCoalesceString(obj, selectedFields)
% Generates a COALESCE string for selected fields
% Input:
% selectedFields: cell array of strings in format {'Table.field'}
% e.g., {'Runs.run_id', 'Runs.bitrate'}
% Output:
% coalesceStr: string with COALESCE statements
arguments
obj
selectedFields cell
end
% Initialize cell array to store each COALESCE statement
coalesceStatements = cell(length(selectedFields), 1);
% Generate COALESCE statement for each field
for i = 1:length(selectedFields)
% Split table and field name
parts = strsplit(selectedFields{i}, '.');
if length(parts) ~= 2
error('Field name must be in format "Table.field": %s', selectedFields{i});
end
tableName = parts{1};
fieldName = parts{2};
% Generate COALESCE statement
coalesceStatements{i} = sprintf('COALESCE(%s.%s, ''NaN'') AS %s ', ...
tableName, fieldName, fieldName);
end
% Join with comma, newline and MATLAB string continuation
coalesceStr = strjoin(coalesceStatements, [', ' sprintf('\n ')]);
% Add initial newline and spacing for formatting
coalesceStr = [sprintf('SELECT DISTINCT \n ') coalesceStr];
end
end
end

View File

@@ -1,76 +0,0 @@
function copyStylingFrom(figNumSource, figNumTgt)
% Get handles to the source and target figures
sourceFig = figure(figNumSource);
targetFig = figure(figNumTgt);
% Get axes of source and target figures
sourceAxes = findall(sourceFig, 'type', 'axes');
targetAxes = findall(targetFig, 'type', 'axes');
% Ensure the number of axes match
if length(sourceAxes) ~= length(targetAxes)
error('Number of axes in source and target figures must be the same.');
end
% Loop through each pair of axes and copy styling properties
for i = 1:length(sourceAxes)
copyAxesProperties(sourceAxes(i), targetAxes(i));
end
% Apply general figure properties if desired
targetFig.Color = sourceFig.Color; % Background color
end
function copyAxesProperties(sourceAx, targetAx)
% List of properties to copy from source to target axes
propsToCopy = {'XColor', 'YColor', 'ZColor', 'FontSize', 'FontName', ...
'GridColor', 'GridLineStyle', 'MinorGridColor', 'Box', ...
'XGrid', 'YGrid', 'ZGrid', 'XMinorGrid', 'YMinorGrid', 'ZMinorGrid', ...
'LineWidth', 'TitleFontSizeMultiplier', 'LabelFontSizeMultiplier'};
% Copy properties from source to target
for i = 1:length(propsToCopy)
try
targetAx.(propsToCopy{i}) = sourceAx.(propsToCopy{i});
catch
% Skip property if it doesn't exist or can't be copied
end
end
% Copy axis labels and titles
targetAx.Title.String = sourceAx.Title.String;
targetAx.XLabel.String = sourceAx.XLabel.String;
targetAx.YLabel.String = sourceAx.YLabel.String;
targetAx.ZLabel.String = sourceAx.ZLabel.String;
% Copy children elements like lines, patches, etc.
sourceChildren = allchild(sourceAx);
targetChildren = allchild(targetAx);
% Ensure the number of children elements match
if length(sourceChildren) ~= length(targetChildren)
warning('Number of elements in source and target axes differ. Styling may not be applied completely.');
end
% Copy properties of children (like lines, patches, etc.), except colors and legends
for i = 1:min(length(sourceChildren), length(targetChildren))
copyObjectProperties(sourceChildren(i), targetChildren(i));
end
end
function copyObjectProperties(sourceObj, targetObj)
% List of common properties to copy for plot elements (lines, patches, etc.)
propsToCopy = {'LineStyle', 'LineWidth', 'Marker', 'MarkerSize', ...
'MarkerEdgeColor', 'MarkerFaceColor', 'DisplayName'};
% Copy properties from source to target, excluding colors
for i = 1:length(propsToCopy)
try
if ~contains(propsToCopy{i}, 'Color') % Skip color properties
targetObj.(propsToCopy{i}) = sourceObj.(propsToCopy{i});
end
catch
% Skip property if it doesn't exist or can't be copied
end
end
end

View File

@@ -51,8 +51,9 @@ classdef DataStorage < handle
function save(obj,path)
try
save(path,"obj");
catch
catch e
disp(e.message)
disp('Provide save path')
end
end
@@ -136,7 +137,21 @@ classdef DataStorage < handle
if ~isempty(tmp)
if isa(tmp,'double')
value(i) = tmp ;
try
value(i,:) = tmp ;
catch
a = size(value,2);
b = size(tmp,2);
if a > b
value(i,:) =[tmp,NaN(1,a-b)] ;
elseif a < b
value(i,:) = tmp(1:size(value,2)) ;
else
error('unknwon case...')
end
end
elseif isa(tmp,'Signal') || isa(tmp,'struct') || isa(tmp,'Exfo_laser') || isa(tmp,'DC_supply')
if i == 1
value = {};
@@ -150,7 +165,7 @@ classdef DataStorage < handle
end
value{i} = tmp{1} ;
else
value{i} = tmp{1} ;
value{i} = tmp ;
end
else

View File

@@ -1,251 +0,0 @@
% Script, that shows the data management routine :-)
loadExistingWareHouse = 0;
if loadExistingWareHouse
[file, path] = uigetfile();
wh = load([path filesep file]);
wh = wh.wh;
wh.showInfo;
else
% 1) Define all your parameters, best practice directly constructs a
% structure
params = struct;
params.l = [2,10];
params.dispersion = [0];
params.sgm = [0];
% params.pol = ["YXYXYXYX","YXXYYXXY","YYYYYYYY"];
params.pol = ["alternated","paired","copolarized"];
params.p_in = [3];
params.p_out = [-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2];
params.pmd = [0.1];
params.gamma = [0.0023];
params.realization = [1:20];
params.numchannels = [16];
params.center_wavelength = floor([getSweepWavelengths(35, 50e9, 1310)] .* 1000) ./ 1000 ;
params.center_wavelength = [1285 1287 1290 1292 1295];
params.center_wavelength = 1310;
params.channelspacing = [400e9];
params.random_zdw = [0];
%wh = warehouse :-)
wh = DataStorage(params);
wh.showInfo;
wh.addStorage("ber");
wh.addStorage("totalBer");
end
%2) Simulate a bunch of data - TO BE IMPLEMENTED HERE - for now use scripts
%from Sebastian
%3) Once the simulation folder is around, specifiy path and analyze dirs
path = uigetdir('C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations');
allMat = getAllFilesInFolder(path,'.mat');
allErr = getAllFilesInFolder(path,'.err');
%allMat = dir([path filesep '*.mat']);
%allErr = dir([path filesep '*.err']);
if numel(allMat) == 0
warning('You defined an empty folder. Could not locate any .mat file.')
else
fprintf('%-20s', 'Err Files:'); fprintf('%-12s', num2str(numel(allErr))); fprintf('\n');
fprintf('%-20s', 'Mat Files:'); fprintf('%-12s', num2str(numel(allMat))); fprintf('\n');
fprintf('%-20s', 'Missing Mat Files:'); fprintf('%-12s', num2str(numel(allErr)-numel(allMat))); fprintf('\n');
end
%4) Now load that data
f = waitbar(0,'Please wait...');
cnt = 0;
for num = 1:numel(allMat)
fileName = allMat(num).name;
fileFolder = allMat(num).path;
fileExt = allMat(num).ext;
%
matFile = load([fileFolder filesep fileName fileExt]);
matFile = matFile.loop_data;
% ____________________________________
% FIND THE DATAPOINT CURRENTLY LOADED
zdw = 1310;
channelplan = "symmetric";
channelspacing = str2double(strrep(regexp(fileName,'(_chsp)+([\d]*)','match'),'_chsp','')).*1e9;
numchannels = str2double(strrep(regexp(fileName,'(ch)+(_)+([\d]*)','match'),'ch_',''));
center_wavelength = str2double(insertAfter(strrep(regexp(fileName,'(lambda)+([\d]*)','match'),'lambda',''),4,'.'));
center_wavelength = floor(center_wavelength * 1000) / 1000;
if center_wavelength == 2192
continue
end
center_wavelength = 1310;
random_zdw = str2double(strrep(regexp(fileName,'(rzwd)+([\d])','match'),'rzwd',''));
l = str2double(strrep(regexp(fileName,'([L])+(_)+([\d]*)','match'),'L_',''));
d = str2double(strrep(regexp(fileName,'([D])+(_)+([\d]*)','match'),'D_',''));
if d == 0
sgm = false;
else
sgm = true;
end
if numel(regexp(fileName,'(YYYY)','match')) > 1
pol = "copolarized";
elseif numel(regexp(fileName,'(YXXY)','match')) > 1
pol = "paired";
elseif numel(regexp(fileName,'(YXYX)','match')) > 1
pol = "alternated";
else
pol = "copolarized";
end
p_in = str2double(strrep(regexp(fileName,'(pow_)+([-,\d]{1})','match'),'pow_',''));
pmd = 0.1;
gamma = 0.0023;
realiz = str2double(strrep(regexp(fileName,'(r)+([-,\d]{1,3})','match'),'r',''));
% ____________________________________
% Get the information you want from current file
rop=[];
ber = [];
for pow = 2:12
module_number = '';
for p = 1:11 %11 because there are 11 ROP branches in model
% get ROP
if p == 1
p_out = matFile.dp_optatten_para.atten;
else
p_out = matFile.("dp_optatten__"+(p)+"_para").atten;
end
p_out = round(p_out-10*log10(numel(matFile.config.parameters.common.wavelengthPlan)));
for c = 1:numel(matFile.config.parameters.common.wavelengthPlan)
ber(c) = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,c}.ber;
end
totalBer = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,end}.totalBer;
if totalBer > 0.2 && channelspacing == 400e9 && pol == "alternated"
disp("stopping here");
pause;
end
% ____________________________________
% Add value to warehouse at the correct position
wh.addValueToStorage(ber,'ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw);
wh.getStoValue('ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw);
wh.addValueToStorage(totalBer,'totalBer',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels,center_wavelength,channelspacing,random_zdw);
end
end
waitbar(num/numel(allMat),f,'Loading your data');
end
close(f)
% 4) Hey! the warehouse is here and (hopefully) filled with data :-)
% Create a save dialog
defaultDir = 'C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations\';
defaultExt = '*.mat';
[filename, pathname] = uiputfile(fullfile(defaultDir, defaultExt),'', 'wh.mat');
% Check if the user pressed Cancel
if isequal(filename, 0) || isequal(pathname, 0)
disp('Save operation canceled.');
else
% Save the variable to the selected file
save(fullfile(pathname, filename), 'wh');
disp(['Variable "wh" saved to: ', fullfile(pathname, filename)]);
end
function matFileStructArray = getAllFilesInFolder(folderPath,extension)
% Get a list of all files in the current folder
currentFolderFiles = dir(fullfile(folderPath, '*'));
% Exclude '.' and '..' directories
currentFolderFiles = currentFolderFiles(~ismember({currentFolderFiles.name}, {'.', '..'}));
% Initialize the structure array for .mat files
matFileStructArray = struct('path', {}, 'name', {}, 'ext', {});
% Loop over each file in the current folder
for i = 1:length(currentFolderFiles)
currentFile = currentFolderFiles(i);
% Check if the current item is a file and has a .mat extension
if ~currentFile.isdir && endsWith(currentFile.name, extension, 'IgnoreCase', true)
% If it's a .mat file, add it to the structure array
[matFileStructArray(end + 1).path,matFileStructArray(end+1).name, matFileStructArray(end+1).ext] = fileparts(fullfile(folderPath, currentFile.name));
elseif currentFile.isdir
% If it's a directory, recursively call the function
subfolderPath = fullfile(folderPath, currentFile.name);
subfolderMatFiles = getAllFilesInFolder(subfolderPath,extension);
% Add .mat files from the subfolder to the structure array
matFileStructArray = [matFileStructArray, subfolderMatFiles];
end
end
end

View File

@@ -1,250 +0,0 @@
% Script, that shows the data management routine :-)
loadExistingWareHouse = 0;
if loadExistingWareHouse
[file, path] = uigetfile();
wh = load([path filesep file]);
wh = wh.wh;
wh.showInfo;
else
% 1) Define all your parameters, best practice directly constructs a
% structure
params = struct;
params.l = [2, 10];
params.dispersion = [0, 3];
params.sgm = [0, 1];
% params.pol = ["YXYXYXYX","YXXYYXXY","YYYYYYYY"];
params.pol = ["alternated","paired","copolarized"];
params.p_in = [3];
params.p_out = [-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2];
params.pmd = [0.1];
params.gamma = [0.0023];
params.realization = [1:20];
params.numchannels = [1,2,4,8,16];
params.center_wavelength = floor([getSweepWavelengths(35, 50e9, 1310)] .* 1000) ./ 1000 ;
params.center_wavelength = [1285 1287 1290 1292 1295];
params.center_wavelength = 1310;
params.channelspacing = [400e9];
params.random_zdw = [0,1];
%wh = warehouse :-)
wh = DataStorage(params);
wh.showInfo;
wh.addStorage("ber");
wh.addStorage("totalBer");
end
%2) Simulate a bunch of data - TO BE IMPLEMENTED HERE - for now use scripts
%from Sebastian
%3) Once the simulation folder is around, specifiy path and analyze dirs
path = uigetdir('C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations');
allMat = getAllFilesInFolder(path,'.mat');
allErr = getAllFilesInFolder(path,'.err');
%allMat = dir([path filesep '*.mat']);
%allErr = dir([path filesep '*.err']);
if numel(allMat) == 0
warning('You defined an empty folder. Could not locate any .mat file.')
else
fprintf('%-20s', 'Err Files:'); fprintf('%-12s', num2str(numel(allErr))); fprintf('\n');
fprintf('%-20s', 'Mat Files:'); fprintf('%-12s', num2str(numel(allMat))); fprintf('\n');
fprintf('%-20s', 'Missing Mat Files:'); fprintf('%-12s', num2str(numel(allErr)-numel(allMat))); fprintf('\n');
end
%4) Now load that data
f = waitbar(0,'Please wait...');
cnt = 0;
for num = 1:numel(allMat)
fileName = allMat(num).name;
fileFolder = allMat(num).path;
fileExt = allMat(num).ext;
%
matFile = load([fileFolder filesep fileName fileExt]);
matFile = matFile.loop_data;
% ____________________________________
% FIND THE DATAPOINT CURRENTLY LOADED
zdw = 1310;
channelplan = "symmetric";
channelspacing = str2double(strrep(regexp(fileName,'(_chsp)+([\d]*)','match'),'_chsp','')).*1e9;
numchannels = str2double(strrep(regexp(fileName,'(ch)+(_)+([\d]*)','match'),'ch_',''));
center_wavelength = str2double(insertAfter(strrep(regexp(fileName,'(lambda)+([\d]*)','match'),'lambda',''),4,'.'));
center_wavelength = floor(center_wavelength * 1000) / 1000;
if center_wavelength == 2192
continue
end
center_wavelength = 1310;
random_zdw = str2double(strrep(regexp(fileName,'(rzwd)+([\d])','match'),'rzwd',''));
l = str2double(strrep(regexp(fileName,'([L])+(_)+([\d]*)','match'),'L_',''));
d = str2double(strrep(regexp(fileName,'([D])+(_)+([\d]*)','match'),'D_',''));
if d == 0
sgm = false;
else
sgm = true;
end
if numel(regexp(fileName,'(YYYY)','match')) > 1
pol = "copolarized";
elseif numel(regexp(fileName,'(YXXY)','match')) > 1
pol = "paired";
elseif numel(regexp(fileName,'(YXYX)','match')) > 1
pol = "alternated";
else
pol = "copolarized";
end
p_in = str2double(strrep(regexp(fileName,'(pow_)+([-,\d]{1})','match'),'pow_',''));
pmd = 0.1;
gamma = 0.0023;
realiz = str2double(strrep(regexp(fileName,'(r)+([-,\d]{1,3})','match'),'r',''));
% ____________________________________
% Get the information you want from current file
rop=[];
ber = [];
for pow = 2:12
module_number = '';
for p = 1:11 %11 because there are 11 ROP branches in model
% get ROP
if p == 1
p_out = matFile.dp_optatten_para.atten;
else
p_out = matFile.("dp_optatten__"+(p)+"_para").atten;
end
p_out = round(p_out-10*log10(numel(matFile.config.parameters.common.wavelengthPlan)));
for c = 1:numel(matFile.config.parameters.common.wavelengthPlan)
ber(c) = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,c}.ber;
end
totalBer = matFile.("prms_compare_wdm__"+(p+1)+"_out"){1,end}.totalBer;
if totalBer > 0.2 && channelspacing == 400e9 && pol == "alternated"
disp("stopping here");
pause;
end
% ____________________________________
% Add value to warehouse at the correct position
wh.addValueToStorage(ber,'ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw);
wh.getStoValue('ber',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels, center_wavelength,channelspacing,random_zdw);
wh.addValueToStorage(totalBer,'totalBer',l,d,sgm,pol,p_in,p_out,pmd,gamma,realiz,numchannels,center_wavelength,channelspacing,random_zdw);
end
end
waitbar(num/numel(allMat),f,'Loading your data');
end
close(f)
% 4) Hey! the warehouse is here and (hopefully) filled with data :-)
% Create a save dialog
defaultDir = 'C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations\';
defaultExt = '*.mat';
[filename, pathname] = uiputfile(fullfile(defaultDir, defaultExt),'', 'wh.mat');
% Check if the user pressed Cancel
if isequal(filename, 0) || isequal(pathname, 0)
disp('Save operation canceled.');
else
% Save the variable to the selected file
save(fullfile(pathname, filename), 'wh');
disp(['Variable "wh" saved to: ', fullfile(pathname, filename)]);
end
function matFileStructArray = getAllFilesInFolder(folderPath,extension)
% Get a list of all files in the current folder
currentFolderFiles = dir(fullfile(folderPath, '*'));
% Exclude '.' and '..' directories
currentFolderFiles = currentFolderFiles(~ismember({currentFolderFiles.name}, {'.', '..'}));
% Initialize the structure array for .mat files
matFileStructArray = struct('path', {}, 'name', {}, 'ext', {});
% Loop over each file in the current folder
for i = 1:length(currentFolderFiles)
currentFile = currentFolderFiles(i);
% Check if the current item is a file and has a .mat extension
if ~currentFile.isdir && endsWith(currentFile.name, extension, 'IgnoreCase', true)
% If it's a .mat file, add it to the structure array
[matFileStructArray(end + 1).path,matFileStructArray(end+1).name, matFileStructArray(end+1).ext] = fileparts(fullfile(folderPath, currentFile.name));
elseif currentFile.isdir
% If it's a directory, recursively call the function
subfolderPath = fullfile(folderPath, currentFile.name);
subfolderMatFiles = getAllFilesInFolder(subfolderPath,extension);
% Add .mat files from the subfolder to the structure array
matFileStructArray = [matFileStructArray, subfolderMatFiles];
end
end
end

View File

@@ -1,394 +0,0 @@
%automate plots
[file, path] = uigetfile("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\session_februar_24\wh_mi_nacht.mat");
wh = load([path filesep file]);
wh = wh.wh;
plotJob = struct();
width = 350;
height = 200;
plotJob.Position = [100 100 width 100+height];
cols = cbrewer2("paired",12);
plotJob.color = cols(1,:);
plotJob.l = 2;
plotJob.ch = 16;
plotJob.d = 0;
plotJob.sgm = 0;
plotJob.pol = "copolarized";
plotJob.p_in = 3;
plotJob.gamma = 0;
plotJob.pmd = 0;
plotJob.channelspacing = 400e9;
plotJob.randzdw = 0;
plotJob.plot_ber_curve = 1;
plotJob.plot_3dber_curve = 0;
plotJob.plot_violin = 0;
plotJob.plot_wavelength_sweep = 0;
plotJob.plot_wavelength_sweep_failure_rate = 0;
plotJob.dataStatArg = 'Lineplot with quartiles';
plotJob.plotTypeArg = 'Lines';
plotJob.displayname = 'a';
plotJob.title = 'title';
plotJob.figName = '16 Chann__';
plotJob.xAxisLabel = 'ROP per Channel in dBm';
plotJob.yAxisLabel = 'BER';
% createbercurves(wh,plotJob)
createviolinplots(wh,plotJob);
% createsweepplots(wh,plotJob);
%% 1
function createbercurves(wh,plotJob)
width = 1650;
height = 400;
s = 100;
e = 100;
cols = cbrewer2("paired",12);
numRows = 2;
numCols = 4;
plotJob.figName = '16 Chann_200G';
plotJob.channelspacing = 400e9;
plotJob.ch = 16;
Len = [2,2,2,2,10,10,10,10];
Pol = ["copolarized","alternated","paired","copolarized","copolarized","alternated","paired","copolarized"];
Title = ["Co Polarized","Alternating Pol. Interl.","Paired Pol. Interl.","Link Segmentation",];
D = [0,0,0,3,0,0,0,3];
Sgm = [0,0,0,1,0,0,0,1];
colidx = [4,8,6];
P_launch = [0,3,6];
fig = figure('Name',plotJob.figName);
fig.Position = plotJob.Position;
fig.Units = "centimeters";
fig.Position = [0 0 18 7];
t = tiledlayout(numRows,numCols,'TileSpacing','compact','Padding','compact');
for idx = 1:(numRows * numCols)
% Create subplot
% sp = subplot(numRows, numCols, idx);
nexttile;
plotJob.l = Len(idx);
plotJob.pol = Pol(idx);
plotJob.d = D(idx);
plotJob.sgm = Sgm(idx);
plotJob.randzdw = 1;
for i = 1:3
plotJob.p_in = P_launch(i);
plotJob.color = cols(colidx(i),:);
hold on
plotCurve(wh, plotJob);
end
%
if idx ~= 1 && idx ~= 5 % For example, hide y-axis for subplot 1
set(gca, 'YTickLabel',[]); % Hide y-axis ticks and labels
set(gca,'YGrid','on');
set(gca, 'YLabel', []);
end
if idx ~= 5 && idx ~= 6 && idx ~= 7 && idx ~= 8
set(gca, 'XLabel', []);
set(gca, 'XTickLabel', []);
end
grid on
g = gca;
pos = g.Position;
if idx <= 4
title(Title(idx),'FontSize',8);
% a = annotation('textbox', pos-[0.0020 -0.1434 0.0947 0.3121], 'String', "FEC: 3.8e-3","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','middle','FitBoxToText','on');
% a = annotation('textbox', pos, 'String', "2 km","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','bottom','FitBoxToText','on');
else
% a = annotation('textbox', pos, 'String', "FEC: 3.8e-3","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','middle','FitBoxToText','on');
% a = annotation('textbox', pos, 'String', "10 km","LineStyle","none","FontSize",8,'LineWidth',0.1,'Interpreter','latex',"FontUnits","points",'Vert','bottom','FitBoxToText','on');
end
end
% Create textbox
annotation(fig,'textbox',...
[0.0696078431372547 0.246851385390432 0.0656862745098043 0.0453400503778337],...
'String','10 km',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.288235294117647 0.239294710327459 0.0656862745098043 0.0453400503778338],...
'String','10 km',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.516666666666666 0.241813602015117 0.0656862745098042 0.0453400503778338],...
'String','10 km',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.742156862745097 0.236775818639802 0.0656862745098042 0.0453400503778339],...
'String','10 km',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.071996471804854 0.578899159967702 0.0656862745098039 0.0453400503778341],...
'String',{'2 km'},...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.29596893566113 0.576253721089996 0.0656862745098041 0.0453400503778341],...
'String',{'2 km'},...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.524883914268912 0.580785315705112 0.0656862745098041 0.0453400503778341],...
'String',{'2 km'},...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.753758338909501 0.581291504465311 0.0656862745098037 0.0453400503778341],...
'String',{'2 km'},...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
a=sgtitle(['N=',num2str(plotJob.ch),'; $\Delta f_{\mathrm{ch}}$= ',num2str(plotJob.channelspacing*1e-9),' GHz'],'FontSIze',10);
a.Interpreter = "latex";
lgd = legend('$P_{\mathrm{in}}=0$ dBm','$P_{\mathrm{in}}=3$ dBm','$P_{\mathrm{in}}=6$ dBm','Interpreter','latex');
lgd.NumColumns = 3;
lgd.Layout.Tile = 'south';
copygraphics(t,'BackgroundColor','none');
end
%% 2
function createviolinplots(wh,plotJob)
width = 350;
height = 200;
s = 100;
e = 100;
cols = cbrewer2("paired",12);
numRows = 1;
numCols = 4;
plotJob.ch = 16;
plotJob.p_in = 3;
plotJob.randzdw = 0;
Pol = ["copolarized","copolarized","alternated","paired",];
Title = ["Co Pol.","Link Segmentation","Paired Pol. Interl.","Alternating Pol. Interl."];
D = [0,3,0,0];
Sgm = [0,1,0,0];
colidx = [2];
Len = [2];
plotJob.figName = ['_Violin',num2str(plotJob.ch),' Channels; ',num2str(plotJob.channelspacing*1e-9),' GHz; ',num2str(plotJob.p_in),' dBm; randomized: ', num2str(plotJob.randzdw)];
fig = figure('Name',plotJob.figName);
fig.Position = plotJob.Position;
fig.Units = "centimeters";
fig.Position = [0 0 18 7];
t = tiledlayout(numRows,numCols,'TileSpacing','compact','Padding','compact');
for idx = 1:(numRows * numCols)
% Create subplot
%subplot(numRows, numCols, idx);
nexttile;
plotJob.pol = Pol(idx);
plotJob.d = D(idx);
plotJob.sgm = Sgm(idx);
for i = 1
plotJob.color = cols(colidx(i),:);
plotJob.l = Len(i);
hold on
plotViolin(wh, plotJob);
end
if idx ~= 1 % For example, hide y-axis for subplot 1
%set(gca, 'YTickLabel',[]); % Hide y-axis ticks and labels
set(gca, 'YGrid','on');
set(gca, 'YLabel', []);
end
if idx <= 4
title(Title(idx));
end
end
% Create textbox
annotation(fig,'textbox',...
[0.300019607843137 0.816120906801009 0.108803921568628 0.0906801007556676],...
'String','$P_{\mathrm{in}}=3$ dBm',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.530411764705882 0.81612090680101 0.108803921568628 0.0906801007556676],...
'String','$P_{\mathrm{in}}=3$ dBm',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.755901960784313 0.816120906801011 0.108803921568628 0.0906801007556676],...
'String','$P_{\mathrm{in}}=3$ dBm',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
% Create textbox
annotation(fig,'textbox',...
[0.0696274509803918 0.584382871536529 0.108803921568628 0.0906801007556676],...
'String','$P_{\mathrm{in}}=3$ dBm',...
'LineStyle','none',...
'Interpreter','latex',...
'FontSize',8,...
'FitBoxToText','off');
copygraphics(t,'BackgroundColor','none');
% lgd = legend('$P_{\mathrm{in}}=0$ dBm','$P_{\mathrm{in}}=3$ dBm','$P_{\mathrm{in}}=6$ dBm','Interpreter','latex');
% lgd.NumColumns = 3;
% lgd.Layout.Tile = 'south';
end
%% 3
function createsweepplots(wh,plotJob)
width = 650;
height = 200;
s = 100;
e = 100;
plotJob.Position = [0 0 width e+height];
cols = cbrewer2("paired",12);
numRows = 1;
numCols = 4;
plotJob.channelspacing = 200e9;
plotJob.ch = 16;
plotJob.randzdw = 1;
plotJob.l = 10;
plotJob.p_in = 3;
Pol = ["copolarized","alternated","paired","copolarized"];
Title = ["Co Polarized","Alternating Pol. Interl.","Paired Pol. Interl.","Link Segmentation",];
D = [0,0,0,3];
Sgm = [0,0,0,1];
Channelspacing = [200e9, 200e9];
PlotTypeArg = ["--","-"];
colidx = [6,8,2,4];
Len = [2,10];
plotJob.figName = [num2str(plotJob.ch),num2str(plotJob.channelspacing*1e-9),num2str(plotJob.p_in),'...'];
plotJob.figName = "10km 400ghz";
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on
end
fig.Position = plotJob.Position;
fig.Units = "centimeters";
fig.Position = [0 0 18 7];
for j = 1
plotJob.channelspacing = Channelspacing(j);
plotJob.plotTypeArg = PlotTypeArg(j);
for idx = 1:4
plotJob.color = cols(colidx(idx),:);
plotJob.pol = Pol(idx);
plotJob.d = D(idx);
plotJob.sgm = Sgm(idx);
plotJob.displayname = [char(plotJob.pol)];
hold on
plotBerVsZdwFailureRate(wh, plotJob);
end
end
legend('Location', 'southoutside', 'Orientation', 'horizontal');
%plot channel positions
hold on
chpos = calcWavelengthPlan(plotJob.ch, plotJob.channelspacing, 1310);
xline(chpos,'LineWidth',2,'Alpha',0.4,'HandleVisibility','off');
chpos = calcWavelengthPlan(plotJob.ch, plotJob.channelspacing, chpos(4));
xline(chpos,'LineWidth',2,'LineStyle','--','Alpha',0.1,'HandleVisibility','off');
title(['N=',num2str(plotJob.ch),' $\Delta f_{\mathrm{ch}}$= ',num2str(plotJob.channelspacing*1e-9),' GHz'],'FontSize',10,'Interpreter','latex');
end

View File

@@ -1,83 +0,0 @@
%automate plots
[file, path] = uigetfile("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\");
wh = load([path filesep file]);
wh = wh.wh;
plotJob = struct();
width = 350;
height = 200;
plotJob.Position = [100 100 width 100+height];
cols = cbrewer2("paired",12);
plotJob.color = cols(1,:);
plotJob.l = 1;
plotJob.ch = 1;
plotJob.sgm = 1;
plotJob.pol = "copolarized";
plotJob.p_in = 3;
plotJob.gamma = 0.0023;
plotJob.pmd = 0.1;
plotJob.channelspacing = 400e9;
plotJob.randzdw = 0;
plotJob.plot_ber_curve = 1;
plotJob.plot_3dber_curve = 0;
plotJob.plot_violin = 0;
plotJob.plot_wavelength_sweep = 0;
plotJob.plot_wavelength_sweep_failure_rate = 0;
plotJob.dataStatArg = 'Lineplot with quartiles';
plotJob.plotTypeArg = 'Lines';
plotJob.displayname = 'a';
plotJob.title = 'title';
plotJob.figName = '1 Chann__';
plotJob.xAxisLabel = 'ROP per Channel in dBm';
plotJob.yAxisLabel = 'BER';
plotJob.d = 0;
xAxis = wh.parameter.p_out.values;
D = wh.parameter.dispersion.values;
figure()
ber_ = [];
for d_ = 0:39
if d_ == 0
plotJob.sgm = 0;
ber_(d_+1,:) = wh.getStoValue('ber',plotJob.l,d_,plotJob.sgm,string(plotJob.pol),plotJob.p_in,xAxis,plotJob.pmd,plotJob.gamma,1,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw)';
else
plotJob.sgm = 1;
ber_(d_+1,:) = wh.getStoValue('ber',plotJob.l,d_,plotJob.sgm,string(plotJob.pol),plotJob.p_in,xAxis,plotJob.pmd,plotJob.gamma,1,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw)';
end
hold on
plot(xAxis,ber_(d_+1,:))
set(gca,'yscale','log');
end
yline(3.8e-3);
hdfec = 3.8e-3.*ones(size(xAxis));
for i = 1:size(ber_,1)
ber_series = ber_(i,:);
a = InterX([hdfec(:)';xAxis],[ber_series;xAxis]);
cross(i) = a(2);
end
col = cbrewer2('Paired',8);
figure()
plot(D,cross,'Marker','o','MarkerSize',5,'MarkerEdgeColor',[1,1,1],'MarkerFaceColor',col(2,:),'Color',col(1,:),'LineWidth',1);
grid minor
xlabel('Accumulated Dispersion')
ylabel('Required ROP to reach FEC limit in dB')
line([D(16),D(16)],[-10,cross(16)],'linestyle','--')
line([0,D(16)],[cross(16),cross(16)],'linestyle','--')
line([D(29),D(29)],[-10,cross(29)],'linestyle','--')
line([0,D(29)],[cross(29),cross(29)],'linestyle','--')

View File

@@ -1,52 +0,0 @@
wh = load("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\session_dispersion_validation\wh_with_variation.mat");
wh = wh.wh;
lambda = 1295;
figure(3)
plot(xAxis,getber(wh,lambda),'DisplayName',['w:', num2str(lambda), 'variation']);
yline(3.8e-3,'HandleVisibility','off');
legend
set(gca,'yscale','log');
grid(gca,'on');
grid(gca,'minor');
grid minor
fontsize(gca,8,"points")
fig.Units = "centimeters";
fig.Position = [2 2 8.5 7];
set(gca,'TickLabelInterpreter','latex')
ylim([1e-5,0.5]);
xlim([min(xAxis),-3]);
wh = load("C:\Users\Silas\Documents\MATLAB\Datensätze\Raw_Cluster_Simulations\session_dispersion_validation\wh_no_variation.mat");
wh = wh.wh;
figure(3)
hold on
plot(xAxis,getber(wh,lambda),'DisplayName',['w:', num2str(lambda), 'no variation']);
yline(3.8e-3,'HandleVisibility','off');
legend
set(gca,'yscale','log');
grid(gca,'on');
grid(gca,'minor');
grid minor
fontsize(gca,8,"points")
fig.Units = "centimeters";
fig.Position = [2 2 8.5 7];
set(gca,'TickLabelInterpreter','latex')
ylim([1e-5,0.5]);
xlim([min(xAxis),-3]);
function ber = getber(wh,lambda)
realization = wh.parameter.realization.values(1:end);
xAxis = wh.parameter.p_out.values;
ber = [];
for xl = 1:numel(xAxis)
p_out = xAxis(xl);
temp = wh.getStoValue('ber',10,0,0,"copolarized",3,p_out,0.1,0.0023,realization,1,lambda,400e9,1);
ber(xl) = mean(temp,'all');
end
end

View File

@@ -1,61 +0,0 @@
function generatePlots(wh,plotJob)
% 0) Test for valid query:
p_out = wh.parameter.p_out.values(1);
realization = 9;
if 1 %~isempty(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310))
% test violin
baseName = plotJob.figName;
width = 350;
height = 200;
s = 100;
e = 100;
if plotJob.plot_ber_curve
plotJob.Position = [100 100 width e+height];
plotJob.figName = [baseName, ' zdwvsber'];
plotCurve(wh, plotJob);
end
if plotJob.plot_3dber_curve
plotJob.Position = [100 100 width e+height];
plotJob.figName = [baseName, ' zdwvsber'];
plot3dCurve(wh, plotJob);
end
if plotJob.plot_wavelength_sweep
plotJob.Position = [100 100 width e+height];
plotJob.figName = [baseName, ' zdwvsber'];
plotBerVsZDW(wh, plotJob);
end
if plotJob.plot_wavelength_sweep_failure_rate
plotJob.Position = [100 100 width e+height];
plotJob.figName = [baseName, ' zdwvsber'];
plotBerVsZdwFailureRate(wh, plotJob);
end
if plotJob.plot_violin
plotJob.Position = [s+width 100 width e+height];
plotJob.figName = [baseName, ' violin'];
plotViolin(wh, plotJob);
end
if 0
%2) plotHistogram
plotJob.Position = [s+2*width 100 width e+height];
plotJob.figName = [baseName, ' FEC crossing'];
plotHistogram(wh,plotJob)
end
else
warndlg('The requested Datapoint is not available... This can occur for some edgecase constellations... ')
end
end

View File

@@ -1,248 +0,0 @@
function plotCurve(wh,plotJob)
%PLOTCURVE Summary of this function goes here
% Detailed explanation goes here
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on
end
col = plotJob.color;
% we want to fetch all realizations
if plotJob.pmd == 0
realization = 499;
% realization = 0:7;
else
realization = wh.parameter.realization.values(1:end);
end
realization = wh.parameter.realization.values(1:end);
% get all xAxis values
xAxis = wh.parameter.p_out.values;
% Fetch Data from Warehouse
for xl = 1:numel(xAxis)
p_out = xAxis(xl);
if string(plotJob.dataStatArg) == "Worst"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = max(temp,[],'all');
linew = 1.0;
markersz = 3;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "AVG"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = mean(temp,'all');
linew = 1.0;
markersz = 3;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "Best"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = min(temp,[],'all');
linew = 1.0;
markersz = 3;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(:,xl) = mean(temp,1,"omitnan").';
linew = 1;
markersz = 3;
linestyle = '--';
elseif string(plotJob.dataStatArg) == "Lineplot with quartiles"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = mean(temp,"all","omitnan").';
upperq(xl) = quantile(temp,0.9,"all");
lowerq(xl) = quantile(temp,0.1,"all");
if lowerq(xl) == 0
lowerq(xl) = lowerq(xl-1);
end
% upperq(xl) = 0.5*std(tmp,1,'all','omitnan');
% lowerq(xl) = 0.5*std(tmp,1,'all','omitnan');
% upperq(xl) = max(dataNoNans);
% lowerq(xl) = min(dataNoNans);
% upperq(xl) = mean(tmp,"all","omitnan") + 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp)));
% lowerq(xl) = mean(tmp,"all","omitnan") - 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp)));
linew = 1;
markersz = 1;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations"
tmp = reshape(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw).',[],1);
ber(1:size(tmp,1),xl) = tmp;
linew = 0.3;
markersz = 2;
linestyle = ':';
end
end
xAxis = xAxis;
% Plot Data
if string(plotJob.plotTypeArg) == "Scatter"
for rlz = 1:size(ber,1)
if rlz < size(ber,1)
scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'HandleVisibility','off');
else
scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]);
end
end
elseif string(plotJob.plotTypeArg) == "Lines"
% if ~anynan(ber)
% [xAxis,ber] = interpCurve(xAxis, ber);
% end
for rlz = 1:size(ber,1)
%
if (string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations")||(string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)")
ch = mod(rlz,plotJob.ch);
if ch == 0; ch = plotJob.ch; end
else
ch = plotJob.dataStatArg;
end
if rlz <= size(ber,1)
s = plot3(xAxis,repmat(ch,1,numel(xAxis)),ber(rlz,:),linestyle,'Marker',"o",'MarkerSize',markersz,'MarkerFaceColor',plotJob.color,'LineWidth',linew,'Color',col,'Parent', AxesMain,'HandleVisibility','off');
s.DataTipTemplate.Interpreter = "latex";
s.DataTipTemplate.DataTipRows(1).Label = "Ch: ";
s.DataTipTemplate.DataTipRows(1).Value = repmat(ch,size(ber));
s.DataTipTemplate.DataTipRows(1);
s.DataTipTemplate.DataTipRows(2) = [];
else
if string(plotJob.dataStatArg) == "Lineplot with quartiles"
[hl,hp] = boundedline(xAxis,ber(rlz,:),([(ber(rlz,:)-lowerq(rlz,:));upperq(rlz,:)-ber(rlz,:)]'),'-*', 'alpha','Color',col,'transparency', 0.2);
hp.LineWidth = 1.2;
ho = outlinebounds(hl,hp);
set(ho, 'linestyle', ':', 'color', col);
else
s = plot(xAxis,ber(rlz,:),linestyle,'Marker',"o",'MarkerFaceColor',col,'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'DisplayName',[plotJob.displayname]);
s.DataTipTemplate.Interpreter = "latex";
s.DataTipTemplate.DataTipRows(1).Label = "Ch: ";
s.DataTipTemplate.DataTipRows(1).Value = repmat(string(ch),size(ber));
s.DataTipTemplate.DataTipRows(1)
s.DataTipTemplate.DataTipRows(2) = [];
end
end
end
end
% Draw FEC Threshold Line
%get x data of first children:
%get all linear Values
if 0
linear = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,wh.parameter.p_out.values,0,0,499,"symmetric");
linear = mean(linear,2);
lincurve = findall(AxesMain, 'Type', 'line','DisplayName','Linear Baseline');
%
if isempty(lincurve)
xdata = AxesMain.Children(1).XData;
hdfec = 3.8e-3.*ones(size(xdata));
plot(xdata,linear,'-','Marker',"o",'MarkerSize',3,'LineWidth',4,'Color',[0.6400 0.6400 0.6400],'MarkerFaceColor',[0.6400 0.6400 0.6400],'Parent', AxesMain,'DisplayName','Linear Baseline');
%h = get(gca,'Children');
%set(gca,'Children',[h(2) h(1)])
end
end
feccurve = findall(AxesMain, 'Type', 'line','DisplayName','FEC $3.8*10^{-3}$');
%
if isempty(feccurve)
xdata = xAxis;
hdfec = 3.8e-3.*ones(size(xdata));
for ch = 1:plotJob.ch
plot3(xdata,repmat(ch,1,numel(xAxis)),hdfec,':','MarkerSize',4,'Color','black','MarkerFaceColor','black','LineWidth',0.5,'Parent', AxesMain,'DisplayName','FEC $3.8*10^{-3}$','HandleVisibility','off');
end
%h = get(gca,'Children');
%set(gca,'Children',[h(2) h(1)])
end
% Figure Settings
%title(AxesMain,plotJob.title,"Interpreter","none");
xlabel(AxesMain,plotJob.xAxisLabel,"Interpreter","none");
ylabel(AxesMain,plotJob.yAxisLabel,"Interpreter","none");
set(AxesMain,'zscale','log');
grid(AxesMain,'on');
grid(AxesMain,'minor');
grid minor
view(AxesMain,[42.0619302949062 23.4176470588235]);
%legend(AxesMain);
fontsize(AxesMain,8,"points")
fontname(AxesMain,"Arial")
fig.Position = plotJob.Position;
fig.Units = "centimeters";
fig.Position = [2 2 8.5 7];
set(AxesMain,'TickLabelInterpreter','none')
set(AxesMain.Legend,'Interpreter','none')
% set(gcf,'Units','centimeters')
% set(gcf,'Position',[2 2 9 4.5])
zlim([1e-4,0.3]);
xlim([min(xAxis),-3]);
annotation('textbox', [0.125, 0.32, 0.1, 0.1], 'String', "FEC 3.8e-3","LineStyle","none","FontSize",8,"FontUnits","points")
hold off
end
function vec = removeZeros(vec)
% Find rows that contain only zeros
rows_to_remove = all(vec == 0, 2);
% Remove rows with only zeros
vec(rows_to_remove, :) = [];
end

View File

@@ -1,300 +0,0 @@
function plotBerVsZDW(wh,plotJob)
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on
end
col = plotJob.color;
% we want to fetch all realizations
realization = wh.parameter.realization.values(1:end);
% get all xAxis values
xAxis = wh.parameter.p_out.values;
% get all center wavelengths
wavelengths = wh.parameter.center_wavelength.values;
% totber = NaN(numel(realization),1,numel(xAxis),numel(wavelengths));
% ber = zeros(numel(realization),plotJob.ch,numel(xAxis),numel(wavelengths));
%get BER values for query
for w = 2:numel(wavelengths)
for xl = 1:numel(xAxis)
c_wavelen = wavelengths(w);
p_out = xAxis(xl);
% dim1 : realiz; dim2: channels, dim3: rop, dim4, c_wavelength
temp = removeZeros(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,c_wavelen,plotJob.channelspacing,plotJob.randzdw));
ber(1:size(temp,1),:,xl,w) = temp;
end
end
hdfec = 3.8e-3.*ones(size(xAxis));
zdw_ = [];
zdw_chann = [];
zdw_tot = [];
cf_tot = [];
cf_ = [];
S = [];
Stot = [];
S_chann = [];
cf_chann = [];
cnt = 0;
%get fec thresholds
%linear = squeeze(linear);
for c_wavelen = 1:size(ber,4)
for realiz = 1:size(ber,1)
for chann = 1:size(ber,2)
%finde Schnittpunkt zwischen FEC und BER Kurve
temp_ber = squeeze(ber(realiz,chann,:,c_wavelen)).';
if ~all(temp_ber == 0)
%nur wenn nicht alles nullen sind
crossing_ch = InterX([hdfec;xAxis],[temp_ber;xAxis]);
else
continue
end
%Req. FEC Ergebnis einsortieren
if ~isempty(crossing_ch)
if crossing_ch(2) == 0
print("d")
end
S(realiz,chann,c_wavelen) = crossing_ch(2);
else
S(realiz,chann,c_wavelen) = -1;
cnt = cnt +1;
end
end
end
end
temp_max = -inf;
for i = 1:plotJob.ch
hold on
%S:: 1.dim: realiz; 2.dim: channel; 3.dim: center wavelength
%squeeze a channel:
temp_data = squeeze(S(:,i,:));
%remove realizations that have no entry (only zero)
temp_data = removeZeros(temp_data);
%replace zeros with NAN (e.g. for the wavelengths that have missing realizations)
temp_data(temp_data==0) = NaN;
%plot required ROP for channel and all realizations that cross the
%FEC limit
scatter(wavelengths,temp_data ,5,plotJob.color,'Marker','.');
% %plot mean per channel
% temp_mean = mean(temp_data,'omitnan');
% hold on
% plot(wavelengths,temp_mean,'Marker','*');
%get max overall value
temp_max = max(temp_max,max(temp_data));
end
%plot mean overall
mean_overall = squeeze(mean(S,2));
mean_overall(mean_overall==0) = NaN;
%mean_overall(mean_overall==-1) = NaN;
mean_overall=mean(mean_overall,1,'omitnan');
plot(wavelengths,mean_overall,'Color',plotJob.color);
%plot max overall
scatter(wavelengths,temp_max ,35,plotJob.color,'Marker','v');
%plot channel positions
hold on
chpos = calcWavelengthPlan(plotJob.ch, 400e9, 1310);
xline(chpos,'LineWidth',2,'Alpha',0.2);
chpos = calcWavelengthPlan(plotJob.ch, 400e9, chpos(4));
xline(chpos,'LineWidth',2,'Alpha',0.2);
fig.Position = plotJob.Position;
ylabel('Penalty in dB');
xlabel('Wavelength in nm');
xlim([min(wavelengths),max(wavelengths) ]);
grid minor;
set(gca, 'color', 'none');
legend = [];
fontsize(AxesMain,8,"points")
fig.Position = plotJob.Position;
fig.Units = "centimeters";
fig.Position = [2 2 8.5 7];
set(AxesMain,'TickLabelInterpreter','latex')
set(AxesMain.Legend,'Interpreter','latex')
%
%
%
%
%
% distinct_cf = unique(cf_chann);
%
% for i = 1:length(distinct_cf)
% indices = find(cf_chann==distinct_cf(i));
% cf(i) = distinct_cf(i);
% worst_fec_cross(i) = max(S_chann(indices));
% avg_fec_cross(i) = mean(S_chann(indices));
% end
%
% avg_fec_cross = smooth(avg_fec_cross,5);
%
% figure(224)
% hold on
% scatter(cf_,S,10.*abs(S-mean(S)).*ones(size(S)),'DisplayName',['AVG'],'MarkerEdgeColor',col,'MarkerFaceColor',col,'Marker','.');
% hold on
% scatter(cf(2:end),worst_fec_cross(2:end),15,'DisplayName',['Worst'],'MarkerEdgeColor',col,'MarkerFaceColor',col,'Marker','.','HandleVisibility','off');
% plot(cf(2:end),avg_fec_cross(2:end),'DisplayName',['AVG'],'LineWidth',1,'LineStyle','-','Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2);
% plot(cf(2:end),worst_fec_cross(2:end),'DisplayName',['AVG'],'LineWidth',0.5,'LineStyle','-','Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2);
% ghzgrid = hz2nm(nm2hz(1310)+[0:1:20].*400e9);
% set(gca,'xtick',sort(ghzgrid))
% xlim([min(cf(cf~=0)), 1310.1]);
%
%
% % With matlab internal errorbar function...
% figure(221)
% %plot(cf,avg_fec_cross,'DisplayName',['AVG'],'LineWidth',1,'Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2);
% hold on
% %plot(cf_tot,min(S_chann(1:length(cf_tot),:),[],2),'DisplayName',['AVG'],'LineWidth',1,'LineStyle',':','Color',col,'Marker','^','MarkerFaceColor',col,'MarkerSize',2);
% plot(cf,worst_fec_cross,'DisplayName',['AVG'],'LineWidth',2,'LineStyle','-','Color',col,'Marker','none','MarkerFaceColor',col,'MarkerSize',2);
% ghzgrid = hz2nm(nm2hz(1310)+[0:1:20].*400e9);
% set(gca,'xtick',sort(ghzgrid))
% xlim([1302, 1310.1]);
% xline(ghzgrid,'LineStyle',':','Color',[.7 .7 .7]);
%with
% Stot = movmean(Stot,5);
% figure(222)
% [hl,hp] = boundedline(cf_tot,Stot,[(Stot'-min(S_chann(1:length(cf_tot),:),[],2)),(max(S_chann(1:length(cf_tot),:),[],2)-Stot')], 'alpha','Color',col,'transparency', 0.05);
% ho = outlinebounds(hl,hp);
% set(ho, 'linestyle', ':', 'color', col, 'marker', '.','linewidth',0.5);
% hold on
%
% ghzgrid = hz2nm(nm2hz(1310)+[0:1:12].*400e9);
% xline(ghzgrid);
%plot the total ber
% %figure(22);
% hold on;
% b= movmean(Stot,3);
% plot(AxesMain,cf_tot,b,'LineWidth',2,'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname],'Color',col,'Marker','o');
% hold on
%scatter(AxesMain,zdw_,S,'Marker','+','MarkerEdgeColor',col,'MarkerFaceAlpha',0.4,'MarkerEdgeAlpha',0.4,'LineWidth',0.5,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]);
%ylim(AxesMain,[-9.3 -7]);
% for i = 1:numel(chp)
% hold on
% xline(AxesMain,chp(i),'Color',colr(i,:),'DisplayName',['CH: ', num2str(i)],'LineWidth',1.5);
% hold off
% end
%
% a = movmean(sortrows([zdw_; S]'),10,'Endpoints','discard');
%
% sorted = sortrows([zdw_; S]');
% figure(2)
% scatter(sorted(:,1),sorted(:,2))
%
% ber_sorted = sort(S);
% mean(ber_sorted);
% std(ber_sorted);
% z1 = [];
% penalty = mean(ber_sorted):0.01:mean(ber_sorted)+2;
% for i = 1:length(penalty)
% l = penalty(i);
% if i == 1
% z1 = [z1 sum(ber_sorted(1,:)<l ) / length(ber_sorted) ];
% else
% z1 = [z1 sum(ber_sorted(1,:)<l & ber_sorted(1,:)>l-0.01) / length(ber_sorted) ];
% end
%
% end
%
% penalty_higherthan = 0.5;
% probability = sum(z1(find(penalty>mean(ber_sorted)+penalty_higherthan)));
% disp(['A penalty of more than 1dB has a probability of: ', num2str(probability)]);
% %
% stem(AxesMain,penalty,z1,"filled",'Marker','o','MarkerSize',2,'Color',col);
%
%
% [f1,x1]=ecdf(S(end,:));
% %figure(23);plot(AxesMain,x1,f1,'r','LineWidth',3, 'Color',col);
%
% %plot(AxesMain,a(:,1),a(:,2),'Color',col+1,'Parent', AxesMain(1));
%
% % histogram(AxesMain,S,1000,'EdgeColor','none','FaceAlpha',0.4);
%
%
%
% %
% %scatter(AxesMain,zdw_,S,'Marker','+','MarkerEdgeColor',col,'MarkerFaceAlpha',0.4,'MarkerEdgeAlpha',0.4,'LineWidth',0.5,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]);
% hold on
% %scatter(AxesMain,zdw_chann(:,1),mean(S_chann,2),'Marker','diamond','MarkerEdgeColor',col,'MarkerFaceAlpha',0.6,'LineWidth',7,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]);
% hold off
% %
% % for rlz = 1:size(S,2)
% % zdwval = zdw_(rlz);
% % feccrossing = S(rlz);
% % scatter(AxesMain,zdwval,feccrossing,10,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]);
% % end
%
% xline([1302.03471732576,1304.30061112288,1306.57440520904,1308.85614097425,1311.14586009814,1313.44360455251,1315.74941660391,1318.06333881618]);
end
function vec = removeZeros(vec)
% Find rows that contain only zeros
rows_to_remove = all(vec == 0, 2);
% Remove rows with only zeros
vec(rows_to_remove, :) = [];
end

View File

@@ -1,157 +0,0 @@
function plotBerVsZdwFailureRate(wh,plotJob)
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on
end
col = plotJob.color;
% we want to fetch all realizations
realization = wh.parameter.realization.values(1:end);
% get all xAxis values
xAxis = wh.parameter.p_out.values;
% get all center wavelengths
wavelengths = wh.parameter.center_wavelength.values;
%wavelengths = wavelengths(2:end);
% totber = NaN(numel(realization),1,numel(xAxis),numel(wavelengths));
% ber = zeros(numel(realization),plotJob.ch,numel(xAxis),numel(wavelengths));
%get BER values for query
for w = 1:numel(wavelengths)
for xl = 1:numel(xAxis)
c_wavelen = wavelengths(w);
p_out = xAxis(xl);
% dim1 : realiz; dim2: channels, dim3: rop, dim4, c_wavelength
temp = removeZeros(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,c_wavelen,plotJob.channelspacing,plotJob.randzdw));
ber(1:size(temp,1),:,xl,w) = temp;
end
end
hdfec = 3.8e-3.*ones(size(xAxis));
zdw_ = [];
zdw_chann = [];
zdw_tot = [];
cf_tot = [];
cf_ = [];
S = [];
Stot = [];
S_chann = [];
cf_chann = [];
cnt = 0;
%get fec thresholds
%linear = squeeze(linear);
for c_wavelen = 1:size(ber,4)
for realiz = 1:size(ber,1)
for chann = 1:size(ber,2)
%finde Schnittpunkt zwischen FEC und BER Kurve
temp_ber = squeeze(ber(realiz,chann,:,c_wavelen)).';
if ~all(temp_ber == 0)
%nur wenn nicht alles nullen sind
crossing_ch = InterX([hdfec;xAxis],[temp_ber;xAxis]);
else
continue
end
%Req. FEC Ergebnis einsortieren
if ~isempty(crossing_ch)
if crossing_ch(2) == 0
print("d")
end
S(realiz,chann,c_wavelen) = crossing_ch(2);
else
S(realiz,chann,c_wavelen) = -1;
cnt = cnt +1;
end
end
end
end
temp_max = -inf;
sum_FEC_not_crossed=[];
sum_FEC_crossed=[];
threshold = plotJob.p_in - 10;
for i = 1:plotJob.ch
hold on
%S:: 1.dim: realiz; 2.dim: channel; 3.dim: center wavelength
%squeeze a channel:
temp_data = squeeze(S(:,i,:));
%remove realizations that have no entry (only zero)
temp_data = removeZeros(temp_data);
%replace zeros with NAN (e.g. for the wavelengths that have missing realizations)
temp_data(temp_data==0) = NaN;
%for current channel
FEC_crossed = temp_data < threshold & ~isnan(temp_data);
FEC_not_crossed = temp_data >= threshold & ~isnan(temp_data);
%sum over channels for overall picture
sum_FEC_not_crossed(i,:) = sum(FEC_not_crossed);
sum_FEC_crossed(i,:) = sum(FEC_crossed);
failure_rate_channelwise(i,:) = sum_FEC_not_crossed(i,:)./ ( sum_FEC_crossed(i,:) + sum_FEC_not_crossed(i,:));
end
failure_rate_total = sum(sum_FEC_not_crossed,1) ./ ( sum(sum_FEC_crossed,1) + sum(sum_FEC_not_crossed,1) );
% plot failure rate (nbetween 0 and 1)
plot(wavelengths,failure_rate_total,'Color',plotJob.color,'LineWidth',1,'LineStyle',plotJob.plotTypeArg,'Marker','x','MarkerSize',5,'MarkerFaceColor',plotJob.color,'DisplayName',plotJob.displayname);
%plot max overall
%scatter(wavelengths,failure_rate_channelwise ,35,plotJob.color,'Marker','.');
ylabel('Failure Rate of Link');
xlabel('Wavelength in nm');
xlim([min(wavelengths),max(wavelengths) ]);
ylim([0,1]);
grid minor;
set(gca, 'color', 'none');
legend = [];
% fontsize(AxesMain,8,"points")
fig.Position = plotJob.Position;
fig.Units = "centimeters";
fig.Position = [0 0 12 5 7];
try
set(AxesMain,'TickLabelInterpreter','latex')
set(AxesMain.Legend,'Interpreter','latex')
end
end
function vec = removeZeros(vec)
% Find rows that contain only zeros
rows_to_remove = all(vec == 0, 2);
% Remove rows with only zeros
vec(rows_to_remove, :) = [];
end

View File

@@ -1,51 +0,0 @@
function plotChannelSpacingAna(wh,plotJob)
xAxis = wh.parameter.p_out.values;
realization = wh.parameter.realization.values(1:end);
channelsp = wh.parameter.channelspacing.values(1:end);
channelsp = [200 400].*1e9;
for ch = 1:2
channspacing = channelsp(ch);
for xl = 1:numel(xAxis)
p_out = xAxis(xl);
curber = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.numchannels,channspacing);
curzdw = wh.getStoValue('zdw',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.numchannels,channspacing);
ber(1:size(curber,1),1:size(curber,2),xl) = curber;
zdw(1:size(curber,1),1,xl) = curzdw;
end
ber = squeeze(mean(ber,1));
zdw = squeeze(mean(zdw,1));
hdfec = 3.8e-3.*ones(size(xAxis));
S = [];
wavelength={};
zdw_ = [];
zdw_chann = [];
zdw_tot = [];
S = [];
S_chann = [];
wl = round([1302.03471732576,1304.30061112288,1306.57440520904,1308.85614097425,1311.14586009814,1313.44360455251,1315.74941660391,1318.06333881618],2);
wl = 1:16;
wl = [1.2930 1.2953 1.2975 1.2998 1.3020 1.3043 1.3066 1.3089 1.3111 1.3134 1.3157 1.3181 1.3204 1.3227 1.3251 1.3274];
a = InterX([hdfec(:)';xAxis],[mean(ber,1);xAxis]);
if ~isempty(a)
s(ch) = a(2);
else
s(ch) = NaN;
end
end
figure(2224)
hold on
plot(channelsp,s,'LineWidth',1,'Color',plotJob.color,'Marker','o');

View File

@@ -1,278 +0,0 @@
function plotCurve(wh,plotJob)
%PLOTCURVE Summary of this function goes here
% Detailed explanation goes here
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on
end
col = plotJob.color;
% we want to fetch all realizations
if plotJob.pmd == 0
realization = 499;
% realization = 0:7;
else
realization = wh.parameter.realization.values(1:end);
end
realization = wh.parameter.realization.values(1:end);
% get all xAxis values
xAxis = wh.parameter.p_out.values;
% Fetch Data from Warehouse
for xl = 1:numel(xAxis)
p_out = xAxis(xl);
if string(plotJob.dataStatArg) == "Worst"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = max(temp,[],'all');
linew = 1.0;
markersz = 3;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "AVG"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = mean(temp,'all');
linew = 1.0;
markersz = 3;
linestyle = '--';
elseif string(plotJob.dataStatArg) == "Best"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = min(temp,[],'all');
linew = 1.0;
markersz = 3;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(:,xl) = mean(temp,1,"omitnan").';
linew = 1;
markersz = 1;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "Lineplot with quartiles"
temp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
temp = removeZeros(temp);
ber(xl) = mean(temp,"all","omitnan").';
upperq(xl) = quantile(temp,0.99,"all");
lowerq(xl) = quantile(temp,0.04,"all");
if lowerq(xl) == 0
lowerq(xl) = lowerq(xl-1);
end
% upperq(xl) = 0.5*std(tmp,1,'all','omitnan');
% lowerq(xl) = 0.5*std(tmp,1,'all','omitnan');
% upperq(xl) = max(dataNoNans);
% lowerq(xl) = min(dataNoNans);
% upperq(xl) = mean(tmp,"all","omitnan") + 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp)));
% lowerq(xl) = mean(tmp,"all","omitnan") - 1.96 * (std(tmp,1,'all','omitnan')/sqrt(numel(tmp)));
linew = 1;
markersz = 1;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations"
raw_fetch = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw).';
tmp = reshape(raw_fetch,[],1);
ber(1:size(tmp,1),xl) = tmp;
ber_per_chann(:,:,xl) = raw_fetch;
linew = 0.3;
markersz = 2;
linestyle = ':';
end
end
%routine to remove total outliers (here those wehere the rop curve has a mean BER greater than 0.1)
% ber_per_chann_clean = NaN(size(ber_per_chann));
% for ch = 1:size(ber_per_chann,1)
% bla = squeeze(ber_per_chann(ch,:,:));
% ber_per_chann(ch,find(mean(bla,2)>0.25),:) = NaN;
% cleaned = rmoutliers(bla,"mean",'ThresholdFactor',2);
%
% ber_per_chann_clean(ch,1:size(cleaned,1),1:size(cleaned,2)) = cleaned;
%
% end
%
% ber = [];
% for rop = 1:size(ber_per_chann,3)
% temp = squeeze(ber_per_chann_clean(:,:,rop));
% ber(rop) = mean(temp,"all","omitnan").';
% upperq(rop) = quantile(temp,0.9,"all");
% lowerq(rop) = quantile(temp,0.1,"all");
% end
xAxis = xAxis;
% Plot Data
if string(plotJob.plotTypeArg) == "Scatter"
for rlz = 1:size(ber,1)
if rlz < size(ber,1)
scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'HandleVisibility','off');
else
scatter(xAxis,ber(rlz,:),markersz,'MarkerEdgeColor',col,'MarkerFaceColor',col,'Parent', AxesMain(1),'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]);
end
end
elseif string(plotJob.plotTypeArg) == "Lines"
% if ~anynan(ber)
% [xAxis,ber] = interpCurve(xAxis, ber);
% end
for rlz = 1:size(ber,1)
%
if (string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations")||(string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)")
ch = mod(rlz,plotJob.ch);
if ch == 0; ch = plotJob.ch; end
else
ch = plotJob.dataStatArg;
end
if rlz < size(ber,1)
s = plot(xAxis,ber(rlz,:),linestyle,'Marker',"none",'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'HandleVisibility','off');
s.DataTipTemplate.Interpreter = "latex";
s.DataTipTemplate.DataTipRows(1).Label = "Ch: ";
s.DataTipTemplate.DataTipRows(1).Value = repmat(ch,size(ber));
s.DataTipTemplate.DataTipRows(1);
s.DataTipTemplate.DataTipRows(2) = [];
else
if string(plotJob.dataStatArg) == "Lineplot with quartiles"
[hl,hp] = boundedline(xAxis,ber(rlz,:),([(ber(rlz,:)-lowerq(rlz,:));upperq(rlz,:)-ber(rlz,:)]'),'-o','alpha','Color',col,'transparency', 0.1,'linewidth',0.7);
hl.MarkerFaceColor = col;
hl.MarkerSize = 3;
set(hp,'HandleVisibility','off');
%hp.LineWidth = 1.2;
ho = outlinebounds(hl,hp);
set(ho, 'linestyle', ':', 'color', col,'Linewidth',0.5);
set(ho,'HandleVisibility','off');
else
s = plot(xAxis,ber(rlz,:),linestyle,'Marker',"o",'MarkerFaceColor',col,'MarkerSize',markersz,'LineWidth',linew,'Color',col,'Parent', AxesMain,'DisplayName',[plotJob.displayname]);
s.DataTipTemplate.Interpreter = "latex";
s.DataTipTemplate.DataTipRows(1).Label = "Ch: ";
s.DataTipTemplate.DataTipRows(1).Value = repmat(string(ch),size(ber));
s.DataTipTemplate.DataTipRows(1)
s.DataTipTemplate.DataTipRows(2) = [];
end
end
end
end
% Draw FEC Threshold Line
%get x data of first children:
%get all linear Values
if 0
linear = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,wh.parameter.p_out.values,0,0,499,"symmetric");
linear = mean(linear,2);
lincurve = findall(AxesMain, 'Type', 'line','DisplayName','Linear Baseline');
%
if isempty(lincurve)
xdata = AxesMain.Children(1).XData;
hdfec = 3.8e-3.*ones(size(xdata));
plot(xdata,linear,'-','Marker',"o",'MarkerSize',3,'LineWidth',4,'Color',[0.6400 0.6400 0.6400],'MarkerFaceColor',[0.6400 0.6400 0.6400],'Parent', AxesMain,'DisplayName','Linear Baseline');
%h = get(gca,'Children');
%set(gca,'Children',[h(2) h(1)])
end
end
feccurve = findall(AxesMain, 'Type', 'line','DisplayName','FEC $3.8*10^{-3}$');
%
if isempty(feccurve)
xdata = AxesMain.Children(1).XData;
hdfec = 3.8e-3.*ones(size(xdata));
plot(xdata,hdfec,'--','MarkerSize',4,'Color','black','MarkerFaceColor','black','LineWidth',1,'Parent', AxesMain,'DisplayName','FEC $3.8*10^{-3}$','HandleVisibility','off');
%h = get(gca,'Children');
%set(gca,'Children',[h(2) h(1)])
end
% Figure Settings
%title(AxesMain,plotJob.title,"Interpreter","none");
xlabel(AxesMain,plotJob.xAxisLabel,"Interpreter","latex");
ylabel(AxesMain,plotJob.yAxisLabel,"Interpreter","latex");
set(AxesMain,'yscale','log');
grid(AxesMain,'on');
grid(AxesMain,'minor');
grid minor
%legend(AxesMain);
fontsize(AxesMain,8,"points")
% fontname(AxesMain,"Arial")
fig.Position = plotJob.Position;
fig.Units = "centimeters";
fig.Position = [2 2 8.5 7];
set(AxesMain,'TickLabelInterpreter','latex')
set(AxesMain.Legend,'Interpreter','latex')
% set(gcf,'Units','centimeters')
% set(gcf,'Position',[2 2 9 4.5])
ylim([1e-5,0.3]);
xlim([min(xAxis),-3]);
% annotation('textbox', [0.125, 0.32, 0.1, 0.1], 'String', "FEC 3.8e-3","LineStyle","none","FontSize",8,"FontUnits","points")
hold off
end
function vec = removeZeros(vec)
% Find rows that contain only zeros
rows_to_remove = all(vec == 0, 2);
% Remove rows with only zeros
vec(rows_to_remove, :) = [];
end

View File

@@ -1,151 +0,0 @@
function plotHistogram(wh,plotJob)
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on
end
col = plotJob.color;
% we want to fetch all realizations
realization = wh.parameter.realization.values(1:end);
% get all xAxis values
xAxis = wh.parameter.p_out.values;
% Fetch Data from Warehouse
for xl = 1:numel(xAxis)
p_out = xAxis(xl);
if string(plotJob.dataStatArg) == "Worst"
ber(xl) = max(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)),[],'all');
linew = 2.0;
markersz = 3;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "AVG"
ber(xl) = mean(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)),'all');
linew = 2.0;
markersz = 3;
linestyle = ':';
elseif string(plotJob.dataStatArg) == "Best"
ber(xl) = min(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)),[],'all');
linew = 2.0;
markersz = 3;
linestyle = ':';
elseif string(plotJob.dataStatArg) == "All Channels; mean(PMD Realizations)"
tmp = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp));
if numel(tmp(tmp==0)) ~= 0
disp('Removed all zero values!');
tmp(tmp==0) = NaN;
end
ber(:,xl) = mean(tmp,1,"omitnan").';
linew = 0.7;
markersz = 2;
linestyle = '-';
elseif string(plotJob.dataStatArg) == "All Channels ;All PMD Realizations"
tmp = reshape(wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,string(plotJob.channelplan),string(plotJob.dsp)).',[],1);
ber(1:size(tmp,1),xl) = tmp;
linew = 0.3;
markersz = 2;
linestyle = ':';
end
end
disp('Removed all zero values!');
ber(ber==0) = NaN;
if ~anynan(ber)
[xAxis,ber] = interpCurve(xAxis, ber);
end
% plot FEC Crossing as histogram
hdfec = 3.8e-3.*ones(size(xAxis));
S = [];
for i = 1:size(ber,1)
a = InterX([hdfec;xAxis],[ber(i,:);xAxis]);
if ~isempty(a)
S(:,i) = a;
end
end
%% SUB 1
AxesMain = subplot(2,1,1);
hold on
if ~isempty(S)
histogram(S(end,:),300,'Normalization','probability','FaceColor',col,'EdgeColor',col,'Parent',AxesMain,'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname],'FaceAlpha',0.4,'EdgeAlpha',0.4);
end
xlim([-3 ,9 ]);
ylim([0 .10]);
% Figure Settings
title(AxesMain,['$P_{in}:$ ',num2str(plotJob.p_in-9), 'dBm; L : ', num2str(plotJob.l),'Km'],'Interpreter','latex');
xlabel(AxesMain,plotJob.xAxisLabel,'Interpreter','latex');
ylabel('PDF')
grid(AxesMain,'on');
grid(AxesMain,'minor');
%legend(AxesMain,'Interpreter','latex');
fontsize(AxesMain,24,"pixels")
hold off
%% SUB 2
AxesMain = subplot(2,1,2);
if ~isempty(S)
hold on
[f1,x1]=ecdf(S(end,:));
plot(x1,f1,'r','LineWidth',3, 'Color',col,'DisplayName',[plotJob.dataStatArg,' Ch.: ', plotJob.displayname]);
end
xlim([-3 ,9 ]);
ylim([0 1]);
% Figure Settings
title(AxesMain,['$P_{in}:$ ',num2str(plotJob.p_in-9), 'dBm; L : ', num2str(plotJob.l),'Km'],'Interpreter','latex');
xlabel(AxesMain,plotJob.xAxisLabel,'Interpreter','latex');
ylabel('CDF')
grid(AxesMain,'on');
grid(AxesMain,'minor');
fontsize(AxesMain,24,"pixels")
%legend(AxesMain,'Interpreter','latex');
fig.Position = plotJob.Position;
hold off
end

View File

@@ -1,206 +0,0 @@
function plotViolin(wh,plotJob)
fig = findall(groot, 'Type', 'figure', 'Name', plotJob.figName);
if isvalid(fig)
figure(fig)
fig = get(fig);
AxesMain = fig.CurrentAxes;
hold on
else
fig = figure('name',char(plotJob.figName));
AxesMain = gca;
hold on
end
%% Violin
col = plotJob.color;
% we want to fetch all realizations
if plotJob.pmd == 0
realization = 1;
else
realization = wh.parameter.realization.values(1:end);
end
%realization = 0:8;
% get all xAxis values
xAxis = wh.parameter.p_out.values;
% ber = NaN(500,16,10);
% zdw = NaN(500,1,10);
%get BER values for query
for xl = 1:numel(xAxis)
p_out = xAxis(xl);
curber = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw);
curber= removeZeros(curber);
ber(1:size(curber,1),1:size(curber,2),xl) = curber;
end
% to remove outliers set the percentile range
% a = squeeze(mean(ber,2));
% out = isoutlier(mean(a,2),"percentiles",[0 100]);
% ber = ber(~out,:,:);
% disp(sum(out));
hdfec = 3.8e-3.*ones(size(xAxis));
S = [];
wavelength={};
S = [];
S_chann = [];
wl = calcWavelengthPlan(plotJob.ch,plotJob.channelspacing,1310);
%get fec thresholds
% [C,ia,ib] =intersect(linx,xAxis);
% linear = squeeze(linear);
%ber(ber==0) = NaN;
S_chann_no_crossing = zeros(1,plotJob.ch);
for chann = 1:size(ber,2)
for realiz = 1:size(ber,1)
ber_series = squeeze(ber(realiz,chann,:)).';
if mean(ber_series) > 0.1
continue
end
a = InterX([hdfec(:)';xAxis],[ber_series;xAxis]);
if ~isempty(a)
S_chann(realiz,chann) = a(2);
% if a(2) > -7 && string(plotJob.pol) == "copolarized"
% continue
% end
S(end+1) = a(2);
wavelength{end+1} = num2str(wl(chann));
else
S(end+1) = 0;
wavelength{end+1} = num2str(wl(chann));
S_chann_no_crossing(realiz,chann) = 1;
S_chann(realiz,chann) = -1;
end
end
end
threshold = -6;
FEC_crossed = sum(S_chann < threshold & ~isnan(S_chann),1);
FEC_not_crossed = sum(S_chann >= threshold & ~isnan(S_chann),1);
failure_rate = FEC_not_crossed ./ (FEC_crossed + FEC_not_crossed) ;
S_chann(S_chann==0) = NaN;
total_avg = mean(S_chann,"all","omitnan");
%figure(2024)
%C = flip(cbrewer2('Spectral',8));
if numel(S) <= numel(wl)
vs = scatter(1:numel(S),S,50,'o','MarkerEdgeColor','black','MarkerFaceColor',plotJob.color,'LineWidth',1,'HandleVisibility','off');
%vs = scatter(1,mean(S),50,'o','MarkerEdgeColor','black','MarkerFaceColor',plotJob.color,'LineWidth',1);
else
vs = violinplot(S,wavelength,...
'ViolinColor',plotJob.color,...
'ViolinAlpha',0.1,...
'MarkerSize',1,...
'ShowMedian',false,...
'EdgeColor',plotJob.color,...
'ShowWhiskers',false,...
'ShowData',false,...
'ShowBox',false,...
'Bandwidth',0.051 ...
);
hold on
partly_failed = boolean(ceil(failure_rate));
avg = mean(S_chann,1,"omitnan");
notfailed = ~partly_failed .* avg;
notfailed(notfailed==0) = NaN;
scatter(1:size(S_chann,2),notfailed,10,'Marker','x','MarkerEdgeColor','black','MarkerFaceColor',plotJob.color,'LineWidth',0.5,'HandleVisibility','off');
hold on
partly_failed = partly_failed.*avg;
partly_failed(partly_failed==0) = NaN;
s=scatter(1:numel(failure_rate),partly_failed,10,'Marker','x','LineWidth',0.5,'HandleVisibility','off','MarkerEdgeColor','red');
s.DataTipTemplate.Interpreter = "latex";
s.DataTipTemplate.DataTipRows(1).Label = "Fail Rate: ";
s.DataTipTemplate.DataTipRows(1).Value = failure_rate;
s.DataTipTemplate.DataTipRows(2) = [];
hold off
end
% ax = gca;
% ax.XTicks
hold on
yline(total_avg,'LineWidth',1,'LineStyle','--','DisplayName','System Avg.')
fig.Position = plotJob.Position;
xticklabels(1:16);
ylabel('Penalty in dB');
xlabel('Channel Number');
ylim([-9.3,-3]);
xlim([0,plotJob.ch+1]);
grid minor;
set(gca, 'color', 'none');
legend = [];
fontsize(AxesMain,8,"points")
fig.Position = plotJob.Position;
fig.Units = "centimeters";
fig.Position = [2 2 8.5 7];
set(AxesMain,'TickLabelInterpreter','latex')
set(AxesMain.Legend,'Interpreter','latex')
if 0
ber_sorted = sort(S);
z1 = [];
penalty = mean(ber_sorted):0.01:mean(ber_sorted)+2;
for i = 1:length(penalty)
l = penalty(i);
if i == 1
z1 = [z1 sum(ber_sorted(1,:)<l ) / length(ber_sorted) ];
else
z1 = [z1 sum(ber_sorted(1,:)<l & ber_sorted(1,:)>l-0.01) / length(ber_sorted) ];
end
end
penalty_higherthan = 0.5;
probability = sum(z1(find(penalty>mean(ber_sorted)+penalty_higherthan)));
disp(['A penalty of more than 0.5 dB has a probability of: ', num2str(probability)]);
end
end
function vec = removeZeros(vec)
% Find rows that contain only zeros
rows_to_remove = all(vec == 0, 2);
% Remove rows with only zeros
vec(rows_to_remove, :) = [];
end

View File

@@ -1,58 +0,0 @@
%automate plots
% [file, path] = uigetfile("C:\Users\Silas\Documents\MATLAB\Raw_Cluster_Simulations\session_januar24\wh_complete_at_1310.mat");
% wh = load([path filesep file]);
% wh = wh.wh;
plotJob = struct();
plotJob.l = 10;
plotJob.ch = 16;
plotJob.d = 3;
plotJob.sgm = 1;
plotJob.pol = "copolarized";
plotJob.p_in = 3;
plotJob.gamma = 0.0023;
plotJob.pmd = 0.1;
plotJob.channelspacing = 400e9;
plotJob.randzdw = 0;
ber_per_chann = [];
% get all xAxis values
xAxis = wh.parameter.p_out.values;
realization = wh.parameter.realization.values(1:end);
% Fetch Data from Warehouse
for xl = 1:numel(xAxis)
p_out = xAxis(xl);
raw_fetch = wh.getStoValue('ber',plotJob.l,plotJob.d,plotJob.sgm,string(plotJob.pol),plotJob.p_in,p_out,plotJob.pmd,plotJob.gamma,realization,plotJob.ch,1310,plotJob.channelspacing,plotJob.randzdw).';
ber_per_chann(:,:,xl) = raw_fetch;
end
%%
figure(2023);
for ch = [1,floor(plotJob.ch/2),ceil(plotJob.ch/2)+1,plotJob.ch] %1:15:size(ber_per_chann,1)
for p = 5%1:size(ber_per_chann,3)
% Extract data for the current row
row_data = squeeze(ber_per_chann(ch,:,p));
[f, xi] = ksdensity(row_data);
% Identify the peak point
[max_density, max_index] = max(f);
peak_x = xi(max_index);
end
% Create a histogram plot for the current row with a unique color
plot(xi, f, 'LineWidth', 2, 'DisplayName', ['Ch. ', num2str(ch)],'LineStyle','--');
%histogram(row_data,100, 'DisplayName', ['Channel ', num2str(ch)], 'EdgeColor', 'none');
hold on; % Hold the plot for the next iteration
text(peak_x, max_density, ['Ch ', num2str(ch)], 'VerticalAlignment', 'bottom', 'HorizontalAlignment', 'left');
end
legend show
%%

View File

@@ -49,22 +49,5 @@ classdef clr
hold off;
end
function showSet(colorStruct)
% Show colors from a structure in a bar plot
names = fieldnames(colorStruct);
colors = cell2mat(struct2cell(colorStruct)');
figure;
hold on;
for i = 1:size(colors, 1)
fill([0 1 1 0], [i-1 i-1 i i], colors(i, :), 'EdgeColor', 'k');
text(1.1, i-0.5, names{i}, 'FontSize', 12, 'Interpreter', 'none');
end
ylim([0, size(colors, 1)]);
xlim([0, 1.5]);
axis off;
title('Color Preview');
hold off;
end
end
end

View File

@@ -2,11 +2,13 @@ classdef equalizer_structure < int32
enumeration
ffe (0)
vnle (1)
vnle_pf_mlse (2)
dfe (1)
vnle (2)
vnle_pf_mlse (3)
% db_precoded (3)
vnle_db_mlse (3)
db_encoded (4)
vnle_db_mlse (4)
db_encoded (5)
ml_mlse (6)
end
end

View File

@@ -1,97 +1,149 @@
function [eq_package] = duobinary_signaling(eq_, mlse_,M ,rx_signal, tx_symbols, tx_bits,options)
%Duobinary Signaling
function [db_results] = duobinary_signaling(eq_, mlse_, M, rx_signal, tx_symbols, tx_bits, options)
% DUOBINARY_SIGNALING Processes signals through duobinary signaling
%
% Inputs:
% eq_ - Equalizer object
% mlse_ - MLSE object
% M - Modulation order
% rx_signal - Received signal
% tx_symbols - Transmitted symbols
% tx_bits - Transmitted bits
% options - Optional parameters
%
% Outputs:
% db_results - Results from duobinary signaling processing
arguments
eq_
mlse_
M
rx_signal
tx_symbols
tx_bits
options.postFFE = [];
eq_
mlse_
M
rx_signal
tx_symbols
tx_bits
options.precode_mode db_mode
options.showAnalysis = 0
options.eth_style_symbol_mapping = 0
options.postFFE = []
options.database = []
end
%% Process signals through equalizer
[eq_signal, eq_noise] = eq_.process(rx_signal, tx_symbols);
[eq_signal, eq_noise] = eq_.process(rx_signal,tx_symbols);
% Apply post-FFE if provided
if ~isempty(options.postFFE)
[eq_signal,eq_noise] = options.postFFE.process(eq_signal,tx_symbols);
[eq_signal, eq_noise] = options.postFFE.process(eq_signal, tx_symbols);
end
eq_signal.eye(eq_signal.fs,M,"fignum",340);
if isa(mlse_,'MLSE_viterbi')
[mlse_signal] = mlse_.process(eq_signal);
else
eq_signal = mlse_.process(eq_signal);
% Aufpassen mit welcher Sequenz man hier vergleicht für LLR stuff...
% gespeichtere "Symbols" sind schon DB codiert, das wollen wir hier
% nicht! Sondern die precoded aber nicht db-encoded müssen als ref in
% die LLR berechnung gehen!
ref_sym = PAMmapper(M,0).map(tx_bits); %ist klar
ref_sym_dpc = Duobinary().precode(ref_sym); % precoded
% ref_sym_dbenc = Duobinary().encode(ref_sym_dpc); %encoded - das wurde gesendet!
% ref_sym_dec = Duobinary().decode(ref_sym_dbenc); %ref_sym wieder zurück!
eq_signal = Duobinary().encode(eq_signal);
eq_signal = Duobinary().decode(eq_signal);
mlse_.trellis_states = PAMmapper(M,0).levels;
mlse_.trellis_state_mode = 1;
[mlse_signal,LLR,GMI_MLSE] = mlse_.process(eq_signal,ref_sym_dpc);
end
% M = numel(unique(eq_signal.signal));
rx_bits = PAMmapper(M,0).demap(eq_signal);
[bits_db,errors_db,ber_db,~] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
eq_package.ber = ber_db;
% tx_symbols_ = Duobinary().decode(tx_symbols);
% [mlse_signal,~,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols);
resultsDBsignaling = struct( ...
'result_id', NaN, ... %
'run_id', NaN, ... % Beispielhafte Run-ID
'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle
'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit
'numBits', bits_db, ... % Beispiel: 1.000.000 Bits
'numBitErr', errors_db, ... % Beispiel: 120 Bitfehler
'BER_precoded', [], ... % BER = 120 / 1.000.000
'numBitErr_precoded', [], ... % Beispiel: 120 Bitfehler
'BER', ber_db, ... % BER = 120 / 1.000.000
'SNR', [], ... % Beispielhafte SNR
'SNR_level', jsonencode([]), ... % SNR-Level als JSON-codiertes Array
'GMI', [], ... % Beispielhafter GMI-Wert
'AIR', [], ... % Beispielhafter AIR-Wert
'EVM', [], ... % Beispielhafte EVM
'EVM_level', jsonencode([]), ... % EVM-Level als JSON-codiertes Array
'Alpha', [] ... % Beispielhafter Alpha-Wert
);
% Apply duobinary encoding and decoding
mlse_signal = Duobinary().encode(mlse_signal);
mlse_signal = Duobinary().decode(mlse_signal);
% Demap symbols to bits
rx_bits = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).demap(mlse_signal);
%% Calculate BER and metrics
[bits_db, errors_db, ber_db, error_pos] = calc_ber(rx_bits.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
% Calculate performance metrics after duobinary FFE!
[snr, snr_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal); %SNR of duobinary sequence - not directly comparable to
[gmi] = calc_air(eq_signal, tx_symbols, "skip_front", 10000, "skip_end", 10000);
air = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi ./ log2(double(M));
[evm_total, evm_lvl] = calc_evm(eq_signal, tx_symbols);
[std_total, std_lvl] = calc_std(eq_signal, tx_symbols);
[std_rxraw_total, std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols);
%% Prepare output structure
% Determine postFFE order
if ~isempty(options.postFFE)
npostFFE = options.postFFE.order;
else
npostFFE = 0;
end
equalizerConfigDBsignaling = struct( ...
'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt
'equalizer_structure', int32(equalizer_structure.db_encoded), ... % Beispiel: 1 (z.B. für vnle)
'M', M, ... % Ordnung der PAM-Konstellation
'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String
'db_target', 1, ... % 0 oder 1
'diff_precode', 1, ... % 0 oder 1
'postFFE', double(~isempty(options.postFFE)), ... % Beispielwert
'NpostFFE', npostFFE, ... % Beispielwert
'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung
'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung
'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung
'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung
'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung
'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung
'K', eq_.K, ... % Samples pro Symbol
'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap
'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1)
'training_length', eq_.training_length, ... % Anzahl Trainingssymbole
'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe
'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung)
'TRmu2', eq_.FFEmu, ... % mu für DD-Modus (2. Ordnung)
'TRmu3', eq_.FFEmu, ... % mu für DD-Modus (3. Ordnung)
'TRmuDFE', eq_.DFEmu, ... % mu für DFE-Modus im DD
'dd_loops', 5, ... % Anzahl Durchläufe im DD-Modus
'DDmu1', eq_.DDmu(1), ... % mu für DD-Modus (1. Ordnung)
'DDmu2', eq_.DDmu(2), ... % mu für DD-Modus (2. Ordnung)
'DDmu3', eq_.DDmu(3), ... % mu für DD-Modus (3. Ordnung)
'DDmuDFE', eq_.DDmu(4), ... % mu für DFE-Modus im DD
'MLSE_mode', 'viterbi', ... % Beispiel: MLSE-Modus als String
'MLSE_trellis_states', jsonencode(mlse_.trellis_states), ... % Trellis-States, z.B. als JSON-String oder kommasepariert
'comment', 'function: duobinary_target.m', ... % Zusätzliche Kommentare
'config_hash', NaN ...
);
% Create results structure
db_results = struct();
db_results.metrics = Metricstruct;
db_results.metrics.result_id = NaN;
db_results.metrics.run_id = NaN;
db_results.metrics.eqParam_id = NaN;
db_results.metrics.date_of_processing = datetime('now');
db_results.metrics.BER = ber_db;
db_results.metrics.numBits = bits_db;
db_results.metrics.numBitErr = errors_db;
db_results.metrics.SNR = snr;
db_results.metrics.SNR_level = snr_lvl;
db_results.metrics.STD = std_total;
db_results.metrics.STD_level = std_lvl;
db_results.metrics.STDrx = std_rxraw_total;
db_results.metrics.STDrx_level = std_rxraw_lvl;
db_results.metrics.GMI = gmi;
db_results.metrics.AIR = air;
db_results.metrics.EVM = evm_total;
db_results.metrics.EVM_level = evm_lvl;
db_results.metrics.MLSE_dir = mlse_.DIR;
eq_package.resultsDBsignaling = resultsDBsignaling;
eq_package.equalizerConfigDBsignaling = equalizerConfigDBsignaling;
% Create configuration structure
eq_.e = [];
eq_.e2 = [];
eq_.e3 = [];
db_results.config = Equalizerstruct();
db_results.config.eq = jsonencode(eq_);
mlse_.DIR = [];
db_results.config.mlse = jsonencode(mlse_);
db_results.config.equalizer_structure = int32(equalizer_structure.db_encoded);
db_results.config.comment = 'function: duobinary_signaling';
%% Display analysis if requested
if options.showAnalysis
displayAnalysis(eq_noise, eq_signal, rx_signal, eq_, tx_symbols, M, options.postFFE);
end
end
%% Helper Function
function displayAnalysis(eq_noise, eq_signal, rx_signal, eq_, tx_symbols, M, postFFE)
% Display analysis plots and metrics
figure(336);
showEQNoisePSD(eq_noise, "fignum", 336, "displayname", 'Residual Noise after Duobinary');
if ~isempty(postFFE)
showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338);
end
showEQfilter(eq_.e, eq_signal.fs.*2);
figure(341); clf;
showLevelHistogram(eq_signal, tx_symbols, "fignum", 341);
warning off
figure(400); clf;
showLevelScatter(eq_signal, tx_symbols, "fignum", 400);
figure(401); clf;
showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 401);
warning on
end

View File

@@ -1,4 +1,4 @@
function [eq_package] = duobinary_target(eq_, mlse_,M, rx_signal, tx_symbols, tx_bits, options)
function [db_results] = duobinary_target(eq_, mlse_,M, rx_signal, tx_symbols, tx_bits, options)
arguments
eq_
@@ -22,8 +22,14 @@ if ~isempty(options.postFFE)
[eq_signal,eq_noise] = options.postFFE.process(eq_signal,db_ref_sequence);
end
% dir = [1,1];
mlse_sig_sd = mlse_.process(eq_signal);
mlse_.DIR = [1,1];
%
if isa(mlse_,'MLSE_viterbi')
mlse_sig_sd = mlse_.process(eq_signal);
else
[mlse_sig_sd,LLR,GMI_MLSE] = mlse_.process(eq_signal,tx_symbols);
end
mlse_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quantize(mlse_sig_sd);
@@ -43,8 +49,8 @@ switch options.precode_mode
tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded);
tx_bits_precoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded);
rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd_precoded);
[~,errors_db_diff_precoded,ber_db_diff_precoded,~] = calc_ber(rx_bits_mlse.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
%B) Just determine BER
@@ -59,96 +65,87 @@ switch options.precode_mode
mlse_sig_hd_decoded = Duobinary().encode(mlse_sig_hd,"M",M);
mlse_sig_hd_decoded = Duobinary().decode(mlse_sig_hd_decoded,"M",M);
rx_bits_mlse_decoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd_decoded);
[~,errors_db_diff_precoded,ber_db_diff_precoded,~] = calc_ber(rx_bits_mlse_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
[~,errors_db_diff_precoded,ber_db_diff_precoded,a] = calc_ber(rx_bits_mlse_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
burst_db_precoded = count_error_bursts(a, 40);
% B) Omit the Coding by comparing with demapped TX symbol sequence
tx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols);
rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd);
[bits_db,errors_db,ber_db,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
[bits_db,errors_db,ber_db,a] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
burst_db = count_error_bursts(a, 40);
cols = linspecer(8);
figure();hold on;
stem(1:40,burst_db,'LineWidth',1,'Color',cols(4,:),'Marker','_','DisplayName','w/o diff. precoder');
stem(1:40,burst_db_precoded,'LineWidth',1,'Color',cols(3,:),'Marker','.','LineStyle','-','DisplayName','w diff. precoder');
xlabel('Bit Error Burst Length')
ylabel('Occurence')
set(gca, 'yscale', 'log');
end
% M = numel(unique(tx_symbols.signal));
rx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd);
[bits_db,errors_db,ber_db,errorIndice_db] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
eq_package.ber = ber_db;
resultsDBtgt = struct( ...
'result_id', NaN, ... %
'run_id', NaN, ... % Beispielhafte Run-ID
'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle
'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit
'numBits', bits_db, ... % Beispiel: 1.000.000 Bits
'numBitErr', errors_db, ... % Beispiel: 120 Bitfehler
'BER_precoded', ber_db_diff_precoded, ... % BER = 120 / 1.000.000
'numBitErr_precoded', errors_db_diff_precoded, ... % Beispiel: 120 Bitfehler
'BER', ber_db, ... % BER = 120 / 1.000.000
'SNR', [], ... % Beispielhafte SNR
'SNR_level', jsonencode([]), ... % SNR-Level als JSON-codiertes Array
'GMI', [], ... % Beispielhafter GMI-Wert
'AIR', [], ... % Beispielhafter AIR-Wert
'EVM', [], ... % Beispielhafte EVM
'EVM_level', jsonencode([]), ... % EVM-Level als JSON-codiertes Array
'Alpha', [] ... % Beispielhafter Alpha-Wert
);
if ~isempty(options.postFFE)
npostFFE = options.postFFE.order;
alpha = arburg(eq_noise.signal,1);%pf_.coefficients(2);
alpha = alpha(2);
if isa(mlse_,'MLSE_viterbi')
gmi_mlse = NaN;
air_mlse = NaN;
else
npostFFE = 0;
gmi_mlse = GMI_MLSE;
air_mlse = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_mlse ./ log2(double(M));
end
equalizerConfigDBtgt = struct( ...
'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt
'equalizer_structure', int32(equalizer_structure.vnle_db_mlse), ... % Beispiel: 1 (z.B. für vnle)
'M', M, ... % Ordnung der PAM-Konstellation
'target_constellation', jsonencode(round(db_ref_constellation,5)), ... % Beispielhafter Target-String
'db_target', 1, ... % 0 oder 1
'diff_precode', int32(options.precode_mode), ... % 0 oder 1
'postFFE', ~isempty(options.postFFE), ... % Beispielwert
'NpostFFE', npostFFE, ... % Beispielwert
'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung
'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung
'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung
'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung
'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung
'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung
'K', eq_.K, ... % Samples pro Symbol
'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap
'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1)
'training_length', eq_.training_length, ... % Anzahl Trainingssymbole
'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe
'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung)
'TRmu2', eq_.FFEmu, ... % mu für DD-Modus (2. Ordnung)
'TRmu3', eq_.FFEmu, ... % mu für DD-Modus (3. Ordnung)
'TRmuDFE', eq_.DFEmu, ... % mu für DFE-Modus im DD
'dd_loops', 5, ... % Anzahl Durchläufe im DD-Modus
'DDmu1', eq_.DDmu(1), ... % mu für DD-Modus (1. Ordnung)
'DDmu2', eq_.DDmu(2), ... % mu für DD-Modus (2. Ordnung)
'DDmu3', eq_.DDmu(3), ... % mu für DD-Modus (3. Ordnung)
'DDmuDFE', eq_.DDmu(4), ... % mu für DFE-Modus im DD
'MLSE_mode', 'viterbi', ... % Beispiel: MLSE-Modus als String
'MLSE_trellis_states', jsonencode(mlse_.trellis_states), ... % Trellis-States, z.B. als JSON-String oder kommasepariert
'comment', 'function: duobinary_target.m', ... % Zusätzliche Kommentare
'config_hash', NaN ...
);
db_results = struct();
db_results.metrics = Metricstruct;
db_results.metrics.result_id = NaN;
db_results.metrics.run_id = NaN;
db_results.metrics.eqParam_id = NaN;
db_results.metrics.date_of_processing = datetime('now');
db_results.metrics.BER = ber_db;
db_results.metrics.numBits = bits_db;
db_results.metrics.numBitErr = errors_db;
db_results.metrics.BER_precoded = ber_db_diff_precoded;
db_results.metrics.numBitErr_precoded = errors_db_diff_precoded;
db_results.metrics.GMI = gmi_mlse;
db_results.metrics.AIR = air_mlse;
db_results.metrics.MLSE_dir = mlse_.DIR;
db_results.metrics.Alpha = alpha;
eq_package.resultsDBtgt = resultsDBtgt;
eq_package.equalizerConfigDBtgt = equalizerConfigDBtgt;
% Create DB results structure
db_results.config = Equalizerstruct();
eq_.e = [];
eq_.e2 = [];
eq_.e3 = [];
db_results.config.eq = jsonencode(eq_);
% mlse_.DIR = [];
db_results.config.mlse = jsonencode(mlse_);
db_results.config.equalizer_structure = int32(equalizer_structure.vnle_db_mlse);
db_results.config.comment = 'function: Duobinary tgt. (VNLE -> MLSE)';
if options.showAnalysis
eq_signal.eye(eq_signal.fs,M,"fignum",249);
eq_noise = eq_noise - mean(eq_noise.signal);
rx_signal.spectrum("normalizeTo0dB",1,"fignum",250,"displayname","Rx Spectrum");
Duobinary().encode(tx_symbols).spectrum("normalizeTo0dB",1,"fignum",250,"displayname","DB encoded reference");
Duobinary().encode(tx_symbols).spectrum("normalizeTo0dB",1,"fignum",10,"displayname","DB encoded reference");
showEQNoisePSD(eq_noise,"fignum",250,"displayname",'Duobinary Target Noise after Equalization');
fprintf('DB tgt BER: %.2e \n',ber);
fprintf('DB tgt BER: %.2e \n',ber_db);
figure(341); clf;
showLevelHistogram(eq_signal, db_ref_sequence, "fignum", 341);
end

View File

@@ -1,129 +0,0 @@
function [eq_package] = vnle(eq_,M,rx_signal,tx_symbols,tx_bits,options)
%VNLE Apply an equalization algorithm to the received signal and calculate BER
% This function takes an equalizer object, a received signal, and the
% transmitted symbols to apply equalization, map the received signal back to bits,
% and compute the bit error rate (BER).
%
% Inputs:
% EQ - Equalizer object that provides the equalization method
% rx_signal - Received signal that needs to be equalized
% tx_symbols - Transmitted symbols used as a reference for BER calculation
%
% Outputs:
% eq_signal - Equalized version of the received signal
% ber - Bit error rate after equalization
% numErrors - Number of bit errors detected
arguments
eq_
M
rx_signal
tx_symbols
tx_bits
options.precode_mode db_mode
options.showAnalysis = 0
options.eth_style = 0;
options.postFFE = [];
end
%FFE or VNLE
if length(rx_signal)/2 == length(tx_symbols)+0.5
rx_signal.signal = rx_signal.signal(1:end-1);
elseif length(rx_signal)/2 == length(tx_symbols)-1
end
[eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols);
if ~isempty(options.postFFE)
[eq_signal_sd,eq_noise] = options.postFFE.process(eq_signal_sd,tx_symbols);
end
eq_signal_hd = PAMmapper(M,0).quantize(eq_signal_sd);
% precoding to mitigate error propagation, most prominently used in
% combination with duobinary signaling to avoid catastrophic error
% behavior (see J.W.M. Bergmans, Digital Baseband Transmission and Recording -> partial response signaling)
% takes:
% -> eq_signal_hd: hard decision signal after eq
% -> tx_symbols: that where used as reference for eq
switch options.precode_mode
case db_mode.db_emulate
% re
eq_signal_hd = Duobinary().encode(eq_signal_hd,"M",M);
eq_signal_hd = Duobinary().decode(eq_signal_hd,"M",M);
tx_symbols_precoded = Duobinary().encode(tx_symbols);
tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded);
tx_bits = PAMmapper(M,0,"eth_style",options.eth_style).demap(tx_symbols_precoded);
case db_mode.db_discard
% normal dsp for precoded sequence == discard/omit/ignore precode
tx_bits = PAMmapper(M,0).demap(tx_symbols);
case db_mode.db_encoded
% normal DB encoded data (only for 10KM)
case db_mode.db_precoded
eq_signal_hd = Duobinary().encode(eq_signal_hd,"M",M);
eq_signal_hd = Duobinary().decode(eq_signal_hd,"M",M);
end
rx_bits = PAMmapper(M,0,"eth_style",options.eth_style).demap(eq_signal_hd);
[~,numErrors,ber,~] = calc_ber(rx_bits.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
[evm_total,evm_lvl] = calc_evm(eq_signal_sd,tx_symbols);
[inf_rate] = calc_air(eq_signal_sd,tx_symbols,"skip_front",10000,"skip_end",10000);
eq_package.ber_vnle = ber;
eq_package.evm_total = evm_total;
eq_package.evm_lvl = evm_lvl;
eq_package.inf_rate_vnle = inf_rate;
eq_package.snr = snr(eq_signal_sd.signal,eq_noise.signal);
eq_package.signal = eq_signal_sd;
if options.showAnalysis
fprintf('SNR: %d dB \n',snr(eq_signal_sd.signal,eq_noise.signal));
if M == 6
logm = 2.5;
else
logm = log2(M);
end
fprintf('NGMI: %.4f \n', inf_rate/logm);
fprintf(['VNLE EVM lvl: ',repmat('%.3f ',1,numel(evm_lvl)),' \n'],evm_lvl);
fprintf('VNLE BER: %.2e \n',ber);
disp("%%%%%%%%%%%%%%%%%%%%%")
% showEQcoefficients('n1',eq_.e,'n2',eq_.e2,'n3',eq_.e3,"displayname",'Coefficients');
%
% if ~isempty(options.postFFE)
% showEQcoefficients('n1',options.postFFE.e,"displayname",'Coefficients');
% end
%
% showEQNoisePSD(eq_noise);
%
% showEQfilter(eq_.e,eq_signal_sd.fs.*2)
% noiselessness(tx_symbols,eq_noise,"displayname",'SNR after VNLE','fignum',301);
% showLevelHistogram(eq_signal_sd,tx_symbols,"fignum",302);
end
end

View File

@@ -1,4 +1,19 @@
function [eq_package] = vnle_postfilter_mlse(eq_,pf_,mlse_,M,rx_signal,tx_symbols,tx_bits,options)
function [ffe_results, mlse_results] = vnle_postfilter_mlse(eq_, pf_, mlse_, M, rx_signal, tx_symbols, tx_bits, options)
% VNLE_POSTFILTER_MLSE Processes signals through VNLE, postfilter, and MLSE
%
% Inputs:
% eq_ - Equalizer object
% pf_ - Postfilter object
% mlse_ - MLSE object
% M - Modulation order
% rx_signal - Received signal
% tx_symbols - Transmitted symbols
% tx_bits - Transmitted bits
% options - Optional parameters
%
% Outputs:
% ffe_results - Results from FFE/VNLE processing
% mlse_results - Results from MLSE processing
arguments
eq_
@@ -15,271 +30,276 @@ arguments
options.database = [];
end
%FFE or VNLE
[eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols);
%% Process signals through equalizers
% FFE or VNLE
[eq_signal_sd, eq_noise] = eq_.process(rx_signal, tx_symbols);
% Apply post-FFE if provided (does not work properly at the moment...very sad)
if ~isempty(options.postFFE)
tic
[eq_signal_sd,eq_noise] = options.postFFE.process(eq_signal_sd,tx_symbols);
[eq_signal_sd, eq_noise] = options.postFFE.process(eq_signal_sd, tx_symbols);
toc
end
eq_signal_hd = PAMmapper(M,0).quantize(eq_signal_sd);
% Hard decision on VNLE output
eq_signal_hd = PAMmapper(M, 0).quantize(eq_signal_sd);
mlse_sig_sd = pf_.process(eq_signal_sd,eq_noise);
% Process through postfilter and MLSE
mlse_.DIR = pf_.coefficients;
% [mlse_sig_hd,mlse_sig_sd] = mlse_.process(mlse_sig_sd,tx_symbols);
mlse_sig_sd = mlse_.process(mlse_sig_sd);
[mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise);
mlse_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quantize(mlse_sig_sd);
% precoding to mitigate error propagation, most prominently used in
% combination with duobinary signaling to avoid catastrophic error
% behavior (see J.W.M. Bergmans, Digital Baseband Transmission and Recording -> partial response signaling)
switch options.precode_mode
case db_mode.no_db
% TX Data is not precoded:
% A) Emulate diff precoding
eq_signal_hd_precoded = Duobinary().encode(eq_signal_hd,"M",M);
eq_signal_hd_precoded = Duobinary().decode(eq_signal_hd_precoded,"M",M);
mlse_sig_hd_precoded = Duobinary().encode(mlse_sig_hd,"M",M);
mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_hd_precoded,"M",M);
tx_symbols_precoded = Duobinary().encode(tx_symbols);
tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded);
tx_bits_precoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols_precoded);
rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd_precoded);
[~,errors_vnle_diff_precoded,ber_vnle_diff_precoded,~] = calc_ber(rx_bits_vnle.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd_precoded);
[~,errors_mlse_diff_precoded,ber_mlse_diff_precoded,~] = calc_ber(rx_bits_mlse.signal,tx_bits_precoded.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
%B) Just determine BER
rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd);
[bits_vnle,errors_vnle,ber_vnle,~] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd);
[bits_mlse,errors_mlse,ber_mlse,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
case db_mode.db_precoded
% Daten SIND TATSÄCHLICH precoded auf TX Seite:
% A) Decode at Rx if no DB targeting was applied (we are in VNLE or MLSE EQ structure here!
eq_signal_hd_decoded = Duobinary().encode(eq_signal_hd,"M",M);
eq_signal_hd_decoded = Duobinary().decode(eq_signal_hd_decoded,"M",M);
rx_bits_vnle_decoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd_decoded);
[~,errors_vnle_diff_precoded,ber_vnle_diff_precoded,~] = calc_ber(rx_bits_vnle_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
mlse_sig_hd_decoded = Duobinary().encode(mlse_sig_hd,"M",M);
mlse_sig_hd_decoded = Duobinary().decode(mlse_sig_hd_decoded,"M",M);
rx_bits_mlse_decoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd_decoded);
[~,errors_mlse_diff_precoded,ber_mlse_diff_precoded,~] = calc_ber(rx_bits_mlse_decoded.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
% B) Omit the Coding by comparing with demapped TX symbol sequence
tx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(tx_symbols);
rx_bits_vnle = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(eq_signal_hd);
[bits_vnle,errors_vnle,ber_vnle,~] = calc_ber(rx_bits_vnle.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
rx_bits_mlse = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd);
[bits_mlse,errors_mlse,ber_mlse,~] = calc_ber(rx_bits_mlse.signal,tx_bits.signal,"skip_front",100,"skip_end",150,"returnErrorLocation",1);
if 0 %tx_symbols.fs > 190e9
if pf_.ncoeff == 1
if pf_.coefficients(2) < 0
% coeff is negative for too high/ bad VNLE convergence
pf_.coefficients(2) = 0.9;
end
else
%long memory / pf respinse - not sure what to set here in a worst
%case :-)
end
%do it again:
pf_.useBurg = 0;
[mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise);
end
% METRICS OF VNLE SD Signal:
[snr_vnle,snr_vnle_lvl] = calc_snr(tx_symbols.signal,eq_noise.signal);
[gmi_vnle] = calc_air(eq_signal_sd,tx_symbols,"skip_front",10000,"skip_end",10000);
air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(double(M));
[evm_vnle_total,evm_vnle_lvl] = calc_evm(eq_signal_sd,tx_symbols);
[std_vnle_total,std_vnle_lvl] = calc_std(eq_signal_sd,tx_symbols);
[std_rxraw_total,std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out",tx_symbols.fs),tx_symbols);
mlse_.DIR = pf_.coefficients;
% METRICS OF MLSE (HD-VITERBI)
pf_.ncoeff = 1;
pf_.process(eq_signal_sd,eq_noise);
alpha = pf_.coefficients(2);
GMI_MLSE = NaN;
if isa(mlse_,'MLSE_viterbi')
[mlse_sig_sd] = mlse_.process(mlse_sig_sd);
else
[mlse_sig_sd,LLR,GMI_MLSE] = mlse_.process(mlse_sig_sd,tx_symbols);
end
eq_package.ber_mlse = ber_mlse;
eq_package.ber_vnle = ber_vnle;
eq_package.evm_vnle_total = evm_vnle_total;
eq_package.evm_vnle_lvl = evm_vnle_lvl;
eq_package.gmi = gmi_vnle;
mlse_sig_hd = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).quantize(mlse_sig_sd);
eq_package.eq = eq_;
eq_package.pf = pf_;
eq_package.mlse = mlse_;
%% Calculate BER based on precoding mode
[numbits, errors, bers, ~] = calculateBER(eq_signal_hd, mlse_sig_hd, tx_symbols, tx_bits, options.precode_mode, M, options.eth_style_symbol_mapping);
resultsVNLE = struct( ...
'result_id', NaN, ... %
'run_id', NaN, ... % Beispielhafte Run-ID
'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle
'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit
'BER', ber_vnle, ... % BER = 120 / 1.000.000
'numBits', bits_vnle, ... % Beispiel: 1.000.000 Bits
'numBitErr', errors_vnle, ... % Beispiel: 120 Bitfehler
'BER_precoded', ber_vnle_diff_precoded, ... % BER = 120 / 1.000.000
'numBitErr_precoded', errors_vnle_diff_precoded, ... % Beispiel: 120 Bitfehler
'SNR', snr_vnle, ... % Beispielhafte SNR
'SNR_level', jsonencode(snr_vnle_lvl), ... % SNR-Level als JSON-codiertes Array
'STD', std_vnle_total, ...
'STD_level', jsonencode(std_vnle_lvl),...
'STDrx' , std_rxraw_total, ...
'STDrx_level', jsonencode(std_rxraw_lvl),...
'GMI', gmi_vnle, ... % Beispielhafter GMI-Wert
'AIR', air_vnle, ... % Beispielhafter AIR-Wert
'EVM', evm_vnle_total, ... % Beispielhafte EVM
'EVM_level', jsonencode(evm_vnle_lvl), ... % EVM-Level als JSON-codiertes Array
'Alpha', [] ... % Beispielhafter Alpha-Wert
);
%% Calculate performance metrics
% VNLE metrics
[snr_vnle, snr_vnle_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal);
% [gmi_vnle] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000);
%calculate bitwise GMI
[gmi_vnle] = calc_ngmi(eq_signal_sd,tx_symbols);
gmi_vnle = max(gmi_vnle,0);
air_vnle = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_vnle ./ log2(double(M));
[evm_vnle_total, evm_vnle_lvl] = calc_evm(eq_signal_sd, tx_symbols);
[std_vnle_total, std_vnle_lvl] = calc_std(eq_signal_sd, tx_symbols);
[std_rxraw_total, std_rxraw_lvl] = calc_std(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols);
% MLSE metrics
alpha = arburg(eq_noise.signal,1);%pf_.coefficients(2);
alpha = alpha(2);
gmi_mlse = max(GMI_MLSE,0);
air_mlse = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_mlse ./ log2(double(M));
%% Display analysis if requested
if options.showAnalysis
displayAnalysis(eq_noise,whitened_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, options.postFFE);
end
%% Prepare output structures
% Determine postFFE order
if ~isempty(options.postFFE)
npostFFE = options.postFFE.order;
else
npostFFE = 0;
end
equalizerConfigVNLE = struct( ...
'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt
'equalizer_structure', int32(equalizer_structure.vnle), ... % Beispiel: 1 (z.B. für vnle)
'M', M, ... % Ordnung der PAM-Konstellation
'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String
'db_target', 0, ... % 0 oder 1
'diff_precode', int32(options.precode_mode), ... % 0 oder 1
'postFFE', double(~isempty(options.postFFE)), ... % Beispielwert
'NpostFFE', npostFFE, ... % Beispielwert
'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung
'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung
'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung
'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung
'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung
'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung
'K', eq_.K, ... % Samples pro Symbol
'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap
'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1)
'training_length', eq_.training_length, ... % Anzahl Trainingssymbole
'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe
'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung)
'TRmu2', eq_.FFEmu, ... % mu für DD-Modus (2. Ordnung)
'TRmu3', eq_.FFEmu, ... % mu für DD-Modus (3. Ordnung)
'TRmuDFE', eq_.DFEmu, ... % mu für DFE-Modus im DD
'dd_loops', 5, ... % Anzahl Durchläufe im DD-Modus
'DDmu1', eq_.DDmu(1), ... % mu für DD-Modus (1. Ordnung)
'DDmu2', eq_.DDmu(2), ... % mu für DD-Modus (2. Ordnung)
'DDmu3', eq_.DDmu(3), ... % mu für DD-Modus (3. Ordnung)
'DDmuDFE', eq_.DDmu(4), ... % mu für DFE-Modus im DD
'comment', 'function: vnle_postfilter_mlse', ... % Zusätzliche Kommentare
'config_hash', NaN ...
);
ffe_results = struct();
ffe_results.metrics = Metricstruct;
ffe_results.metrics.result_id = NaN;
ffe_results.metrics.run_id = NaN;
ffe_results.metrics.eqParam_id = NaN;
ffe_results.metrics.date_of_processing = datetime('now');
ffe_results.metrics.BER = bers.vnle;
ffe_results.metrics.numBits = numbits.vnle;
ffe_results.metrics.numBitErr = errors.vnle;
ffe_results.metrics.BER_precoded = bers.vnle_precoded;
ffe_results.metrics.numBitErr_precoded = errors.vnle_precoded;
ffe_results.metrics.SNR = snr_vnle;
ffe_results.metrics.SNR_level = snr_vnle_lvl;
ffe_results.metrics.STD = std_vnle_total;
ffe_results.metrics.STD_level = std_vnle_lvl;
ffe_results.metrics.STDrx = std_rxraw_total;
ffe_results.metrics.STDrx_level = std_rxraw_lvl;
ffe_results.metrics.GMI = gmi_vnle;
ffe_results.metrics.AIR = air_vnle;
ffe_results.metrics.EVM = evm_vnle_total;
ffe_results.metrics.EVM_level = evm_vnle_lvl;
ffe_results.metrics.Alpha = alpha;
try
eq_.e = [];
eq_.e2 = [];
eq_.e3 = [];
end
resultsMLSE = struct( ...
'result_id', NaN, ... %
'run_id', NaN, ... % Beispielhafte Run-ID
'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle
'date_of_processing', datetime('now'), ... % Aktuelles Datum und Uhrzeit
'BER', ber_mlse, ... % BER = 120 / 1.000.000
'numBits', bits_mlse, ... % Beispiel: 1.000.000 Bits
'numBitErr', errors_mlse, ... % Beispiel: 120 Bitfehler
'BER_precoded', ber_mlse_diff_precoded, ... % BER = 120 / 1.000.000
'numBitErr_precoded', errors_mlse_diff_precoded, ... % Beispiel: 120 Bitfehler
'SNR', [], ... % Beispielhafte SNR
'SNR_level', jsonencode([]), ... % SNR-Level als JSON-codiertes Array
'GMI', [], ... % Beispielhafter GMI-Wert
'AIR', [], ... % Beispielhafter AIR-Wert
'EVM', [], ... % Beispielhafte EVM
'EVM_level', jsonencode([]), ... % EVM-Level als JSON-codiertes Array
'Alpha', alpha, ... % Beispielhafter Alpha-Wert
'MLSE_dir', jsonencode([mlse_.DIR])...
);
ffe_results.config = Equalizerstruct();
ffe_results.config.eq = jsonencode(eq_);
ffe_results.config.equalizer_structure = int32(equalizer_structure.vnle);
ffe_results.config.comment = 'function: vnle_postfilter_mlse - FFE part';
mlse_results = struct();
mlse_results.metrics = Metricstruct;
mlse_results.metrics.result_id = NaN;
mlse_results.metrics.run_id = NaN;
mlse_results.metrics.eqParam_id = NaN;
mlse_results.metrics.date_of_processing = datetime('now');
mlse_results.metrics.BER = bers.mlse;
mlse_results.metrics.numBits = numbits.mlse;
mlse_results.metrics.numBitErr = errors.mlse;
mlse_results.metrics.BER_precoded = bers.mlse_precoded;
mlse_results.metrics.numBitErr_precoded = errors.mlse_precoded;
% mlse_results.metrics.SNR = NaN;
mlse_results.metrics.GMI = gmi_mlse;
mlse_results.metrics.AIR = air_mlse;
% mlse_results.metrics.EVM = NaN;
% mlse_results.metrics.EVM_level = NaN;
mlse_results.metrics.Alpha = alpha;
mlse_results.metrics.MLSE_dir = mlse_.DIR;
equalizerConfigMLSE = struct( ...
'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt
'equalizer_structure', int32(equalizer_structure.vnle_pf_mlse), ... % Beispiel: 1 (z.B. für vnle)
'M', M, ... % Ordnung der PAM-Konstellation
'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String
'db_target', 0, ... % 0 oder 1
'diff_precode', int32(options.precode_mode), ... % 0 oder 1
'postFFE', double(~isempty(options.postFFE)), ... % Beispielwert
'NpostFFE', npostFFE, ... % Beispielwert
'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung
'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung
'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung
'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung
'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung
'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung
'K', eq_.K, ... % Samples pro Symbol
'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap
'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1)
'training_length', eq_.training_length, ... % Anzahl Trainingssymbole
'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe
'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung)
'TRmu2', eq_.FFEmu, ... % mu für DD-Modus (2. Ordnung)
'TRmu3', eq_.FFEmu, ... % mu für DD-Modus (3. Ordnung)
'TRmuDFE', eq_.DFEmu, ... % mu für DFE-Modus im DD
'dd_loops', 5, ... % Anzahl Durchläufe im DD-Modus
'DDmu1', eq_.DDmu(1), ... % mu für DD-Modus (1. Ordnung)
'DDmu2', eq_.DDmu(2), ... % mu für DD-Modus (2. Ordnung)
'DDmu3', eq_.DDmu(3), ... % mu für DD-Modus (3. Ordnung)
'DDmuDFE', eq_.DDmu(4), ... % mu für DFE-Modus im DD
'MLSE_mode', 'viterbi', ... % Beispiel: MLSE-Modus als String
'MLSE_trellis_states', jsonencode(mlse_.trellis_states), ... % Trellis-States, z.B. als JSON-String oder kommasepariert
'comment', 'function: vnle_postfilter_mlse', ... % Zusätzliche Kommentare
'config_hash', NaN ...
);
eq_package.resultsVNLE = resultsVNLE;
eq_package.resultsMLSE = resultsMLSE;
eq_package.equalizerConfigVNLE = equalizerConfigVNLE;
eq_package.equalizerConfigMLSE = equalizerConfigMLSE;
% Create MLSE results structure
mlse_results.config = Equalizerstruct();
mlse_results.config.eq = jsonencode(eq_);
mlse_.DIR = length(mlse_.DIR)-1;
mlse_results.config.mlse = jsonencode(mlse_);
mlse_results.config.equalizer_structure = int32(equalizer_structure.vnle_pf_mlse);
mlse_results.config.comment = 'function: vnle_postfilter_mlse - MLSE part';
% eq_package.vnle_out = eq_signal_sd;
if options.showAnalysis
% fprintf(['VNLE EVM lvl: ',repmat('%.3f ',1,numel(evm_lvl)),' \n'],evm_lvl);
% fprintf('VNLE BER: %.2e \n',ber_vnle);
%
% fprintf('MLSE BER: %.2e \n',ber_mlse);
figure(336);clf;
showEQNoisePSD(eq_noise,"fignum",336,"displayname",'Residual Noise after VNLE','postfilter_taps',pf_.coefficients);
% figure(337);clf;
% rx_signal.spectrum("normalizeTo0dB",1,"fignum",337,"displayname",'Rx Signal');
% figure(338);clf;
% showEQcoefficients('n1',eq_.e,'n2',eq_.e2,'n3',eq_.e3,"displayname",'Coefficients','fignum',338);
if ~isempty(options.postFFE)
showEQcoefficients('n1',options.postFFE.e,"displayname",'Coefficients','fignum',338);
end
showEQfilter(eq_.e,eq_signal_sd.fs.*2);
figure(340);clf;
eq_signal_sd.eye(eq_signal_sd.fs,M,"fignum",340);
figure(341);clf;
showLevelHistogram(eq_signal_sd,tx_symbols,"fignum",341);
showLevelScatter(eq_signal_sd,tx_symbols,"fignum",400);
showLevelScatter(rx_signal.resample("fs_out",tx_symbols.fs),tx_symbols,"fignum",401);
% autoArrangeFigures(3,3,2)
end
%% Helper Functions
function [numbits, errors, ber, error_locations] = calculateBER(eq_signal_hd, mlse_sig_hd, tx_symbols, tx_bits, precode_mode, M, eth_style)
% Initialize output structure
numbits = struct('vnle', 0, 'mlse', 0);
errors = struct('vnle', 0, 'mlse', 0, 'vnle_precoded', 0, 'mlse_precoded', 0);
ber = struct('vnle', 0, 'mlse', 0, 'vnle_precoded', 0, 'mlse_precoded', 0);
error_locations = struct('vnle', [], 'mlse', []);
% PAM mapper for demapping
mapper = PAMmapper(M, 0, "eth_style", eth_style);
switch precode_mode
case db_mode.no_db
% TX Data is not precoded
% A) Emulate diff precoding
eq_signal_hd_precoded = Duobinary().encode(eq_signal_hd, "M", M);
eq_signal_hd_precoded = Duobinary().decode(eq_signal_hd_precoded, "M", M);
mlse_sig_hd_precoded = Duobinary().encode(mlse_sig_hd, "M", M);
mlse_sig_hd_precoded = Duobinary().decode(mlse_sig_hd_precoded, "M", M);
tx_symbols_precoded = Duobinary().encode(tx_symbols);
tx_symbols_precoded = Duobinary().decode(tx_symbols_precoded);
tx_bits_precoded = mapper.demap(tx_symbols_precoded);
rx_bits_vnle = mapper.demap(eq_signal_hd_precoded);
[~, errors.vnle_precoded, ber.vnle_precoded, ~] = calc_ber(rx_bits_vnle.signal, tx_bits_precoded.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
rx_bits_mlse = mapper.demap(mlse_sig_hd_precoded);
[~, errors.mlse_precoded, ber.mlse_precoded, ~] = calc_ber(rx_bits_mlse.signal, tx_bits_precoded.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
% B) Just determine BER
rx_bits_vnle = mapper.demap(eq_signal_hd);
[numbits.vnle, errors.vnle, ber.vnle, error_locations.vnle] = calc_ber(rx_bits_vnle.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
rx_bits_mlse = mapper.demap(mlse_sig_hd);
[numbits.mlse, errors.mlse, ber.mlse, error_locations.mlse] = calc_ber(rx_bits_mlse.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
case db_mode.db_precoded
% Data is precoded on TX side
% A) Decode at Rx if no DB targeting was applied
eq_signal_hd_decoded = Duobinary().encode(eq_signal_hd, "M", M);
eq_signal_hd_decoded = Duobinary().decode(eq_signal_hd_decoded, "M", M);
rx_bits_vnle_decoded = mapper.demap(eq_signal_hd_decoded);
[~, errors.vnle_precoded, ber.vnle_precoded, ~] = calc_ber(rx_bits_vnle_decoded.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
mlse_sig_hd_decoded = Duobinary().encode(mlse_sig_hd, "M", M);
mlse_sig_hd_decoded = Duobinary().decode(mlse_sig_hd_decoded, "M", M);
rx_bits_mlse_decoded = mapper.demap(mlse_sig_hd_decoded);
[~, errors.mlse_precoded, ber.mlse_precoded, err_loc_precoded] = calc_ber(rx_bits_mlse_decoded.signal, tx_bits.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
% B) Omit the Coding by comparing with demapped TX symbol sequence
tx_bits_demapped = mapper.demap(tx_symbols);
rx_bits_vnle = mapper.demap(eq_signal_hd);
[numbits.vnle, errors.vnle, ber.vnle, error_locations.vnle] = calc_ber(rx_bits_vnle.signal, tx_bits_demapped.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
rx_bits_mlse = mapper.demap(mlse_sig_hd);
[numbits.mlse, errors.mlse, ber.mlse, error_locations.mlse] = calc_ber(rx_bits_mlse.signal, tx_bits_demapped.signal, "skip_front", 100, "skip_end", 150, "returnErrorLocation", 1);
end
% cols = linspecer(8);
% burst_precoded = count_error_bursts(err_loc_precoded, 40);
% burst_normal = count_error_bursts(error_locations.mlse, 40);
% figure();hold on;
% stem(1:40,burst_normal,'LineWidth',1,'Color',cols(1,:),'Marker','_','DisplayName','w/o diff. precoder');
% stem(1:40,burst_precoded,'LineWidth',1,'Color',cols(2,:),'Marker','.','LineStyle','-','DisplayName','w diff. precoder');
% xlabel('Bit Error Burst Length')
% ylabel('Occurence')
% set(gca, 'yscale', 'log');
end
function displayAnalysis(eq_noise, whitened_noise, eq_signal_sd, rx_signal, eq_, pf_, mlse_, tx_symbols, M, postFFE)
% rx_signal.spectrum("displayname",'Rx Signal','fignum',100,'normalizeTo0dB',1);
% Display analysis plots and metrics
figure(336);
hold on;
eq_signal_sd.spectrum("displayname",'Equalized Signal','fignum',336,'normalizeTo0dB',0);
eq_noise.spectrum("displayname",'Equalized Signal','fignum',336,'normalizeTo0dB',0);
showEQNoisePSD(eq_noise, "fignum", 338, "displayname", 'Residual Noise after VNLE', 'postfilter_taps', pf_.coefficients);
for t = 1:4
pf_.ncoeff = t;
[~,~] = pf_.process(eq_signal_sd, eq_noise);
showEQNoisePSD(eq_noise, "fignum", 3388, "displayname", 'Residual Noise after VNLE', 'postfilter_taps', pf_.coefficients);
end
tx_symbols.spectrum("displayname",'Equalized Signal','fignum',1234,'normalizeTo0dB',1);
if ~isempty(postFFE)
showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338);
end
showEQcoefficients('n1', eq_.e,'n2', eq_.e2,'n3', eq_.e3, "displayname", 'Coefficients', 'fignum', 339);
showEQfilter(eq_.e, eq_signal_sd.fs.*2);
figure(340); clf;
eq_signal_sd.eye(eq_signal_sd.fs, M, "fignum", 340);
figure(341); clf;
showLevelHistogram(eq_signal_sd, tx_symbols, "fignum", 341);
warning off
showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 400);
% showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 401);
drawnow;
warning on
whitened_noise.spectrum("displayname",'after postfilter','fignum',342);
eq_noise.spectrum("displayname",'before postfilter','fignum',342);
end

View File

@@ -22,7 +22,6 @@ end
% Ensure the figure is ready before calling spectrum
eq_noise.spectrum("displayname", options.displayname, "fignum", fig.Number, "normalizeTo0dB", 1,"color",options.color);
title('Noise of soft decision signal (not MLSE)')
if ~isnan(options.postfilter_taps)
% Hold on to the figure for further plotting
@@ -41,4 +40,8 @@ end
% Ensure a legend is displayed
legend('show');
end
xlim([-eq_noise.fs/2* 1e-9 eq_noise.fs/2* 1e-9]);
% ylim([-15, 0]);
end

View File

@@ -53,8 +53,8 @@ function showEQcoefficients(options)
for i = 1:numSubplots
subplot(1, numSubplots, i);
stem(coeffs{i}, 'Color', options.color, 'LineWidth', 0.1, ...
'Marker', '.', 'MarkerSize', 1);
stem(coeffs{i}, 'Color', options.color, 'LineWidth', 1, ...
'Marker', '.', 'MarkerSize', 10);
title(titles{i});
ylim([-1, 1]); % Set y-axis limits to [-1, 1]
grid on;

View File

@@ -1,5 +1,12 @@
function showEQfilter(coefficients,fs)
function showEQfilter(coefficients,fs,options)
arguments
coefficients
fs
options.fignum (1,1) double = NaN % Default to NaN if not provided
options.displayname (1,:) char = '' % Default to an empty string if not provided
end
% Assuming that obj.e contains the final FFE filter coefficients.
% Set the number of frequency points and sampling frequency.
@@ -12,24 +19,34 @@ function showEQfilter(coefficients,fs)
half_nfft = floor(nfft/2) + 1;
f = f(1:half_nfft);
H = H(1:half_nfft);
% Plot the magnitude and phase responses.
figure(339);
H_mag = abs(H);
H_norm = H_mag ./ max(H_mag);
H_db = 20*log10(H_norm);
H_db = H_db - min(H_db);
% Determine the figure number to use or create a new figure
if isnan(options.fignum)
fig = figure; % Create a new figure and get its handle
else
fig = figure(options.fignum); % Use the specified figure number
end
% Magnitude response (in dB)
subplot(2,1,1);
hold on
plot(f.*1e-9, 20*log10(abs(1./H)));
plot(f.*1e-9, H_db,'DisplayName',options.displayname);
title('(Inverted) Magnitude Response of FFE Filter');
xlabel('Frequency (Hz)');
xlabel('Frequency (GHz)');
ylabel('Magnitude (dB)');
grid on;
% Phase response
subplot(2,1,2);
plot(f.*1e-9, unwrap(angle(H)));
plot(f.*1e-9, unwrap(angle(H)),'DisplayName',options.displayname);
title('Phase Response of FFE Filter');
xlabel('Frequency (Hz)');
xlabel('Frequency (GHz)');
ylabel('Phase');
grid on;

View File

@@ -20,10 +20,15 @@ end
fig = figure(options.fignum); % Use the specified figure number
end
eq_signal = max(min(eq_signal,3),-3);
%%% Separate Classes
constellation = unique(ref_symbols);
received_sd = NaN(numel(constellation),length(ref_symbols));
lvlcol = cbrewer2('Set1',numel(constellation));
lvlcol = cbrewer2('Paired',numel(constellation)*2);
lvlcol = lvlcol(2:2:end,:);
lvlcol = linspecer(numel(constellation));
% lvlcol = cbrewer2('Set1',numel(constellation));
for lvl = 1:numel(constellation)
%Separate the equalized signal into the
%respective levels based on the actually
@@ -40,12 +45,15 @@ end
cnt(lvl) = round(numel(intermediate(~isnan(intermediate)))./length(eq_signal),3).*100;
hold on
warning off
histogram(received_sd(lvl,:),1000,"EdgeAlpha",0,'DisplayName',['Lvl ',num2str(lvl),' | ',num2str(cnt(lvl)),' %'],'FaceColor',lvlcol(lvl,:),'Normalization','pdf');
histogram(received_sd(lvl,:),1000,"EdgeAlpha",0,'DisplayName',['Lvl ',num2str(lvl),' ; ',num2str(cnt(lvl)),' '],'FaceColor',lvlcol(lvl,:),'Normalization','pdf');
warning on
end
xlim([-3 3]);
legend
grid on
% view([90 -90]);
end

View File

@@ -1,31 +1,36 @@
function showLevelScatter(eq_signal,ref_symbols,options)
function [symbols_for_lvl,avg_for_lvl] = showLevelScatter(eq_signal,ref_symbols,options)
arguments
eq_signal
ref_symbols
options.fignum (1,1) double = NaN % Default to NaN if not provided
options.displayname (1,:) char = '' % Default to an empty string if not provided
options.f_sym = [];
options.f_sym =1e6;
end
plot_shit = 1;
if isa(eq_signal,'Signal')
options.f_sym = eq_signal.fs;
eq_signal = eq_signal.signal;
assert(~isempty(options.f_sym),'No fsym given');
end
if isa(ref_symbols,'Signal')
ref_symbols = ref_symbols.signal;
end
% Determine the figure number to use or create a new figure
if isnan(options.fignum)
fig = figure; % Create a new figure and get its handle
else
fig = figure(options.fignum); % Use the specified figure number
clf;
if plot_shit
% Determine the figure number to use or create a new figure
if isnan(options.fignum)
fig = figure; % Create a new figure and get its handle
else
fig = figure(options.fignum); % Use the specified figure number
clf;
end
end
assert(~isempty(options.f_sym),'No fsym given');
rx_symbols = eq_signal ./ rms(eq_signal);
rx_symbols = eq_signal; %./ rms(eq_signal);
correct_symbols = ref_symbols;
f_sym = options.f_sym;
@@ -33,56 +38,54 @@ col = cbrewer2('Paired',numel(unique(correct_symbols))*2);
ccnt = -1;
levels = unique(correct_symbols);
symbols_for_lvl = NaN(numel(levels),length(correct_symbols));
start = 1;
ende = length(correct_symbols);
% start = 30000;
% ende = 40000;
for l = 1:numel(levels)
ccnt = ccnt+2;
level_amplitude = levels(l);
symbols_for_lvl = NaN(1,length(correct_symbols));
symbols_for_lvl(correct_symbols==level_amplitude) = rx_symbols(correct_symbols==level_amplitude);
std_lvl(l) = std(symbols_for_lvl,'omitnan');
symbols_for_lvl(l,correct_symbols==level_amplitude) = rx_symbols(correct_symbols==level_amplitude);
std_lvl(l) = std(symbols_for_lvl(l,:),'omitnan');
xax_in_sec = ((1:length(correct_symbols)) / f_sym) * 1e6;
% xax_in_sec = 1:length(correct_symbols);
scatter(xax_in_sec(start:ende),symbols_for_lvl(start:ende),10,'.','MarkerFaceAlpha',0.5,'MarkerEdgeAlpha',0.5,'MarkerEdgeColor',col(ccnt,:));
hold on;
if plot_shit
scatter(xax_in_sec(start:ende),symbols_for_lvl(l,start:ende),10,'.','MarkerFaceAlpha',0.5,'MarkerEdgeAlpha',0.5,'MarkerEdgeColor',col(ccnt,:));
hold on;
end
end
std_lvl = round(std_lvl,2);
ccnt = 0;
avg_for_lvl = NaN(numel(levels),length(correct_symbols));
% Add the windowed/ smoothed curves
for l = 1:numel(levels)
ccnt = ccnt+2;
level_amplitude = levels(l);
symbols_for_lvl = NaN(1,length(correct_symbols));
L = 500;
movmean = 1/L .* movsum(rx_symbols(correct_symbols==level_amplitude),[L/2,L/2], 'Endpoints', 'fill');
movmean = 1/250 .* movsum(rx_symbols(correct_symbols==level_amplitude),[250/2,250/2]);
avg_for_lvl(l,correct_symbols==level_amplitude) = movmean;
symbols_for_lvl(correct_symbols==level_amplitude) = movmean;
nanx = isnan(symbols_for_lvl);
t = 1:numel(symbols_for_lvl);
symbols_for_lvl(nanx) = interp1(t(~nanx), symbols_for_lvl(~nanx), t(nanx));
nanx = isnan(avg_for_lvl(l,:));
t = 1:numel(avg_for_lvl(l,:));
avg_for_lvl(l,nanx) = interp1(t(~nanx), avg_for_lvl(l,~nanx), t(nanx));
xax_in_sec = ((1:length(correct_symbols)) / f_sym) * 1e6;
% xax_in_sec = 1:length(correct_symbols);
plot(xax_in_sec(start:ende),symbols_for_lvl(start:ende),'Color',col(ccnt,:));
if plot_shit
plot(xax_in_sec(start:ende),avg_for_lvl(l,start:ende),'Color',col(ccnt,:));
end
hold on
end
%yline(max(rx_symbols(correct_symbols==levels(2))))
yline(levels);
if 0
annotation(fig,'textbox',...
@@ -121,9 +124,10 @@ if 0
'FitBoxToText','off');
end
% xlim([0, 2.6])
% ylim([-2 2])
if plot_shit
% yline(levels);
xlabel('Time in $\mu$s');
ylabel('Normalized Amplitude');
ylim([-3 3]);
end
end

View File

@@ -1,5 +1,5 @@
% Define the precomp path
precomp_path = "C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\";
precomp_path = "C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\precomp";
% Step 1: Find all valid files (assume .mat files for ChannelFreqResp)
fileList = dir(fullfile(precomp_path, '*.mat'));

View File

@@ -1,5 +1,5 @@
function [ach_inf_rate] = calc_air(test_signal,reference_signal,options)
% Calculation of AIR acc. to J. Kozesnik, Numerically Computing Achievable Rates of Memoryless Channels, Francisco Javier Garcıa-Gomez, doi: 10.1007/978-94-009-9857-5.
% Numerically Computing Achievable Rates of Memoryless Channels, Francisco Javier Garcıa-Gomez, doi: 10.1007/978-94-009-9857-5.
% Implementation is not accessible, I mailed TUM to get the code...
arguments(Input)
@@ -26,7 +26,7 @@ end
% TRIM
[test_signal,reference_signal]=trimseq(test_signal,reference_signal,options.skip_front,options.skip_end);
% CALC EVM
% CALC AIR
%%% new implementation of AIR
constellation = unique(reference_signal);
reference_idx = arrayfun(@(x) find(constellation == x, 1), reference_signal);

View File

@@ -28,39 +28,39 @@ end
[evm_total,evm_lvl] = calc_evm_(test_signal,reference_signal);
function [evm_total,evm_lvl] = calc_evm_(test_signal,reference_signal)
function [evm_total, evm_lvl] = calc_evm_(test_signal, reference_signal)
% Validate input
assert(length(test_signal) == length(reference_signal), "Sequence length does not match");
assert(length(test_signal) == length(reference_signal),"Sequence length does not match");
% Calculate error vector
error_vector = test_signal - reference_signal;
error_vector = (test_signal-reference_signal);
% EVM (RMS) as percentage, per MathWorks definition
evm_total = sqrt(sum(abs(error_vector).^2) / sum(abs(reference_signal).^2)) * 100;
%%% Overall EVM
evm_total = rms(error_vector);
try
%%% Per Level EVM
% Per-level EVM
k = unique(reference_signal);
evm_lvl = NaN(1, length(k));
for lvl = 1:length(k)
lvl_errors = error_vector(reference_signal==k(lvl));
evm_lvl(lvl) = rms(lvl_errors);
idx = reference_signal == k(lvl);
if any(idx)
lvl_errors = error_vector(idx);
lvl_refs = reference_signal(idx);
evm_lvl(lvl) = sqrt(sum(abs(lvl_errors).^2) / sum(abs(lvl_refs).^2)) * 100;
end
end
catch
evm_lvl = NaN;
warning('No EVM per level calculated')
end
end
function [data_,reference_]=trimseq(data,reference,skipstart,skip_end)
function [data_,reference_]=trimseq(data,reference,skipstart,skip_end)
data_ = data(skipstart+1:end-skip_end,:);
data_ = data(skipstart+1:end-skip_end,:);
delta_bits = length(reference) - length(data);
delta_bits = length(reference) - length(data);
skip_end = delta_bits + skip_end;
skip_end = delta_bits + skip_end;
reference_ = reference(skipstart+1:end-skip_end,:);
reference_ = reference(skipstart+1:end-skip_end,:);
end
end
end

View File

@@ -1,123 +1,181 @@
function [GMI,NGMI] = calc_ngmi(test_signal,reference_signal,options)
% Silas implementation of (N)GMI calculation according to: J. Cho, L. Schmalen, und P. J. Winzer,
% Normalized Generalized Mutual Information as a Forward Error Correction Threshold for Probabilistically Shaped QAM,
% in 2017 European Conference on Optical Communication (ECOC), Sep. 2017, doi: 10.1109/ECOC.2017.8345872.
% This implementation assumes the same normal distributed noise for each
% channel (sigma2 is calculated once for the whole signal)
% (N)GMI for AWGN, supports PAM2/4/8 (per-symbol) and PAM6 (pair-based, 5 bits / 2 symbols)
arguments(Input)
test_signal;
reference_signal;
options.skip_front = 0;
options.skip_end = 0;
options.returnErrorLocation = 0;
test_signal
reference_signal
options.skip_front (1,1) double = 0
options.skip_end (1,1) double = 0
options.returnErrorLocation (1,1) double = 0 %#ok<NASGU>
end
options.skip_end = abs(options.skip_end);
options.skip_end = abs(options.skip_end);
options.skip_front = abs(options.skip_front);
assert((options.skip_end+options.skip_front) < numel(test_signal), ...
"You can not skip more samples than the overall length.");
assert((options.skip_end+options.skip_front)<length(test_signal),"You can not skip more bits than overall length of data! Set skip_front or skip_end to lower value or check data_in");
if isa(reference_signal,'Signal'), reference_signal = reference_signal.signal; end
if isa(test_signal,'Signal'), test_signal = test_signal.signal; end
if isa(reference_signal,'Signal')
reference_signal = reference_signal.signal;
end
% --- Trim head/tail consistently
[test_signal,reference_signal] = trimseq(test_signal,reference_signal,options.skip_front,options.skip_end);
if isa(test_signal,'Signal')
test_signal = test_signal.signal;
end
% --- Noise variance (real AWGN): use mean-square error for robustness
err = test_signal - reference_signal;
sigma2 = mean((err(:)).^2);
% TRIM
[test_signal,reference_signal]=trimseq(test_signal,reference_signal,options.skip_front,options.skip_end);
% --- Constellation (levels observed)
constellation = unique(reference_signal);
M = numel(constellation);
% CALC EVM
[GMI,NGMI] = calc_ngmi_(test_signal,reference_signal);
% --- Empirical P_X (uniform is fine too; keep your original behavior)
N = numel(reference_signal);
counts = arrayfun(@(c) sum(reference_signal==c), constellation);
P_X = counts / N;
% --- Dispatch: PAM6 needs pairwise treatment (5 bits across 2 symbols)
if M==6
% ===== PAM6: 5 bits mapped to two consecutive symbols =====
% Pair the sequence (drop last symbol if odd)
numPairs = floor(N/2);
if numPairs == 0
GMI = 0; NGMI = 0; return;
end
y1 = test_signal(1:2:2*numPairs);
y2 = test_signal(2:2:2*numPairs);
x1 = reference_signal(1:2:2*numPairs);
x2 = reference_signal(2:2:2*numPairs);
function [GMI,NGMI] = calc_ngmi_(test_signal,reference_signal)
% Build the 36 transition table and its 5-bit labels (ETH-style)
symbols = sort(constellation); % keep fixed order
% all pairs using ndgrid (no dependency on combvec)
[S1,S2] = ndgrid(symbols, symbols); % MxM
trans_pairs = [S1(:), S2(:)]; % (M^2) x 2
assert(length(test_signal) == length(reference_signal),"Sequence length does not match");
% Map those pairs to 5-bit labels using your PAMmapper (same as in your BCJR code)
% NOTE: keep your normalization used by PAMmapper (sqrt(10)) if that's what it expects.
bits72 = PAMmapper(6,0,"eth_style",0).demap( reshape(trans_pairs',[],1) );
pam6bits = reshape(bits72', 5, []).'; % (M^2) x 5, row-aligned with trans_pairs
%%% implemented according to [1] J. Cho, L. Schmalen, und P. J. Winzer,
% Normalized Generalized Mutual Information as a Forward Error Correction Threshold for Probabilistically Shaped QAM,
% in 2017 European Conference on Optical Communication (ECOC), Sep. 2017, S. 13. doi: 10.1109/ECOC.2017.8345872.
% Precompute mapping (pair -> bit label) and (symbol -> index)
[ok1, idx_s1] = ismember(trans_pairs(:,1), symbols);
[ok2, idx_s2] = ismember(trans_pairs(:,2), symbols);
assert(all(ok1)&all(ok2), 'Transition level not found in symbols.');
% For fast masking: linear indices of each (i,j) pair in an MxM grid
lin_idx_pairs = sub2ind([M,M], idx_s1, idx_s2); % (M^2) x 1
error_vector = (test_signal-reference_signal);
sigma2 = var(error_vector); %noise variance
% Build pairwise prior P_pair = P_X(i)*P_X(j) on the MxM grid
P_pair = P_X(:) * P_X(:).'; % MxM
logP_pair = log(P_pair); % for numerical stability
%%% Separate Classes
constellation = unique(reference_signal);
received_sd = NaN(numel(constellation),length(reference_signal));
lvlcol = cbrewer2('Set1',numel(constellation));
for lvl = 1:numel(constellation)
%Separate the equalized signal into the
%respective levels based on the actually
%transmitted level!
received_sd(lvl,reference_signal==constellation(lvl)) = test_signal(reference_signal==constellation(lvl));
% Precompute constant term for log-likelihood
logc = -0.5*log(2*pi*sigma2);
% Prepare transmitted 5-bit labels per observed pair (to split llr0/llr1)
% Find each (x1(k),x2(k)) in trans_pairs:
[tf, row_in_table] = ismember([x1(:), x2(:)], trans_pairs, 'rows');
assert(all(tf), 'A reference pair was not found in the transition table.');
tx_bits_pair = pam6bits(row_in_table, :); % numPairs x 5
% Precompute masks for bit=0 / bit=1 on the MxM grid for each bit position
mask1 = false(M,M,5);
mask0 = false(M,M,5);
for b = 1:5
tmp = false(M,M);
tmp(lin_idx_pairs) = pam6bits(:,b) == 1;
mask1(:,:,b) = tmp;
tmp = false(M,M);
tmp(lin_idx_pairs) = pam6bits(:,b) == 0;
mask0(:,:,b) = tmp;
end
% Loop pairs: compute exact LLRs via log-sum-exp over the MxM grid
LLR_exact = zeros(numPairs,5);
for k = 1:numPairs
% log-likelihood vectors along each axis
li = logc - ((y1(k) - symbols).^2) / (2*sigma2); % 1xM
lj = logc - ((y2(k) - symbols).^2) / (2*sigma2); % 1xM
% joint log-weights over all (i,j)
logW = (li(:) + lj(:).') + logP_pair; % MxM
for b = 1:5
% log P(bit=1 | y) logsumexp(logW over mask1)
lnum = logsumexp(logW(mask1(:,:,b)));
% log P(bit=0 | y) logsumexp(logW over mask0)
lden = logsumexp(logW(mask0(:,:,b)));
LLR_exact(k,b) = lnum - lden; % natural-log LLR
end
end
N = length(test_signal); % Number of received samples
% --- Bitwise MI via consistency relation
MI = zeros(1,5);
for b = 1:5
llr_b = LLR_exact(:,b);
idx0 = (tx_bits_pair(:,b)==0);
idx1 = ~idx0;
I0 = mean( log2(1 + exp( llr_b(idx0))) ); % natural LLR inside exp()
I1 = mean( log2(1 + exp(-llr_b(idx1))) );
MI(b) = 1 - 0.5*(I0 + I1);
end
M = length(constellation); %P
m = log2(M); %bits per symbol
% GMI per symbol (5 bits per 2 symbols)
GMI = sum(MI)/2;
NGMI = GMI / 2.5;
entries = sum(~isnan(received_sd),2)';
P_X = entries./N;
else
% ===== Generic PAM2/4/8 etc.: per-symbol bit labeling =====
symbols = constellation; % 1xM
% Gray labels per symbol (your helper)
gray_bits = PAMmapper(M,0).demap_(symbols); % M x log2(M)
m = log2(M);
% Parameters
symbols = constellation'; % PAM-4 symbols
% Likelihood (real AWGN)
q = @(y,x) (1/sqrt(2*pi*sigma2)) * exp(-(y - x).^2/(2*sigma2));
gray_bits = PAMmapper(M,0).demap_(constellation); % Gray coding (bits per symbol)
% Transmitted bits per sample
tx_bits = PAMmapper(M,0,"eth_style",0).demap(reference_signal); % N x m
% Conditional probability function for AWGN
q_Y_given_X = @(y, x) (1 / sqrt(2 * pi * sigma2)) * exp(-(y - x).^2 / (2 * sigma2));
% Entropy term
H_X = -sum(P_X .* log2(P_X)); % Entropy of input distribution
% GMI computation
noise_impact_term = 0;
% Exact bit-LLRs and MI
LLR_exact = zeros(N,m);
for b = 1:m
mask1 = (gray_bits(:,b)==1);
mask0 = ~mask1;
for k = 1:N
y_k = test_signal(k); % Current received sample
[~, closest_symbol_idx] = min(abs(symbols - y_k)); % Closest symbol index
closest_symbol = symbols(closest_symbol_idx); % Closest symbol
for i = 1:m
% Extract i-th bit for each symbol
bit_mask = gray_bits(:, i); % Binary column for i-th bit of all symbols
matching_symbols = symbols(bit_mask == gray_bits(closest_symbol_idx, i));
% Numerator: Sum over x in x_{b_{k, i}}
numerator = sum(q_Y_given_X(y_k, matching_symbols) .* P_X(ismember(symbols, matching_symbols)));
% Denominator: Sum over all x
denominator = sum(q_Y_given_X(y_k, symbols) .* P_X);
% Logarithmic contribution
noise_impact_term = noise_impact_term + log2(numerator / denominator);
end
yk = test_signal(k);
den = sum(q(yk, symbols) .* P_X);
num1 = sum(q(yk, symbols(mask1)) .* P_X(mask1));
num0 = sum(q(yk, symbols(mask0)) .* P_X(mask0));
% Use the ratio of posteriors P(bit=1|y)/P(bit=0|y)
LLR_exact(k,b) = log(num1) - log(num0); % natural log
end
% Normalize the noise impact term by N
noise_impact_term = noise_impact_term / N;
% GMI
GMI = H_X + noise_impact_term;
NGMI = GMI / m;
end
function [data_,reference_]=trimseq(data,reference,skipstart,skip_end)
data_ = data(skipstart+1:end-skip_end,:);
delta_bits = length(reference) - length(data);
skip_end = delta_bits + skip_end;
reference_ = reference(skipstart+1:end-skip_end,:);
% Bitwise MI
MI = zeros(1,m);
for b = 1:m
llr_b = LLR_exact(:,b);
idx0 = (tx_bits(:,b)==0);
idx1 = ~idx0;
I0 = mean( log2(1 + exp( llr_b(idx0))) );
I1 = mean( log2(1 + exp(-llr_b(idx1))) );
MI(b) = 1 - 0.5*(I0 + I1);
end
end
% Per-symbol GMI/NGMI
GMI = sum(MI);
NGMI = GMI / m;
end
% ---------- helpers ----------
function s = logsumexp(a)
if isempty(a), s = -inf; return; end
amax = max(a(:));
s = amax + log(sum(exp(a(:) - amax)));
end
function [data_,reference_] = trimseq(data,reference,skipstart,skip_end)
data_ = data(skipstart+1:end-skip_end,:);
delta = numel(reference) - numel(data_);
reference_ = reference(skipstart+1:end-(skip_end+delta),:);
end
end

View File

@@ -1,36 +1,43 @@
function [snr_all, snr_per_level] = calc_snr(tx_signal, eq_noise)
% CALC_SNR Calculates overall SNR and level-wise SNR for a PAM-M constellation.
%
% [snr_all, snr_per_level] = calc_snr(tx_signal, eq_noise)
%
% Inputs:
% tx_signal - Vector of transmitted signal values.
% eq_noise - Vector of corresponding noise samples.
%
% Outputs:
% snr_all - Overall SNR computed using all signal values.
% snr_per_level - A vector where each element is the SNR computed
% for a unique amplitude level in tx_signal.
%
% The function first computes the overall SNR using the full signal vectors.
% Then it uses the unique levels in tx_signal to calculate the SNR for
% the symbols corresponding to each level separately.
% CALC_SNR Calculates overall SNR and level-wise SNR for a PAM-M constellation.
%
% [snr_all, snr_per_level] = calc_snr(tx_signal, eq_noise)
%
% Inputs:
% tx_signal - Vector of transmitted signal values.
% eq_noise - Vector of corresponding noise samples.
%
% Outputs:
% snr_all - Overall SNR computed using all signal values.
% snr_per_level - A vector where each element is the SNR computed
% for a unique amplitude level in tx_signal.
%
% The function first computes the overall SNR using the full signal vectors.
% Then it uses the unique levels in tx_signal to calculate the SNR for
% the symbols corresponding to each level separately.
if isa(tx_signal,'Signal')
tx_signal = tx_signal.signal;
end
if isa(eq_noise,'Signal')
eq_noise = eq_noise.signal;
end
% Calculate overall SNR using the complete signals
snr_all = snr(tx_signal, eq_noise);
% Get the unique amplitude levels in the transmitted signal
levels = unique(tx_signal);
% Preallocate an array to store the SNR for each unique level
snr_per_level = zeros(size(levels));
% Loop over each unique level to compute the SNR for that level
for i = 1:length(levels)
% Find indices where tx_signal equals the current level
idx = (tx_signal == levels(i));
% Compute the SNR for these indices
snr_per_level(i) = snr(tx_signal(idx), eq_noise(idx));
end
end

View File

@@ -25,34 +25,46 @@ end
[test_signal,reference_signal]=trimseq(test_signal,reference_signal,options.skip_front,options.skip_end);
% CALC EVM
[std_total,std_lvl] = calc_std_(test_signal,reference_signal);
[std_total, std_lvl, nsd_lvl, d_min]= calc_std_(test_signal,reference_signal);
std_lvl = nsd_lvl;
function [std_total, std_lvl, nsd_lvl, d_min] = calc_std_(test_signal, reference_signal)
% NSD (Normalized Standard Deviation) expresses the noise spread at each symbol level
% relative to the minimum distance between levels. A low NSD means low error risk,
% while NSD approaching 0.5 indicates a high chance of symbol errors due to noise.
function [std_total,std_lvl] = calc_std_(test_signal,reference_signal)
assert(length(test_signal) == length(reference_signal),"Sequence length does not match");
error_vector = (test_signal-reference_signal);
%%% Overall EVM
std_total = std(test_signal);
test_signal = test_signal ./ rms(test_signal);
try
%%% Per Level EVM
% Ensure input is column vector
test_signal = test_signal(:);
reference_signal = reference_signal(:);
assert(length(test_signal) == length(reference_signal), "Sequence length does not match");
% Find unique levels and their minimum spacing
k = unique(reference_signal);
d_min = min(diff(k)); % Minimum distance between adjacent levels
% Normalize test signal to RMS=1 (if not already)
test_signal = test_signal / rms(test_signal);
% Overall standard deviation (not normalized)
std_total = std(test_signal);
% Per-level standard deviation and normalized std
std_lvl = zeros(1, length(k));
nsd_lvl = zeros(1, length(k));
for lvl = 1:length(k)
% lvl_errors = error_vector(reference_signal==k(lvl));
std_lvl(lvl) = std(test_signal(reference_signal==k(lvl)));
idx = reference_signal == k(lvl);
if any(idx)
std_lvl(lvl) = std(test_signal(idx));
nsd_lvl(lvl) = std_lvl(lvl) / d_min;
else
std_lvl(lvl) = NaN;
nsd_lvl(lvl) = NaN;
end
end
catch
std_lvl = NaN;
warning('No EVM per level calculated')
end
end
function [data_,reference_] = trimseq(data,reference,skipstart,skip_end)
data_ = data(skipstart+1:end-skip_end,:);

View File

@@ -1,51 +0,0 @@
figure(2)
tiledlayout(1,3)
cols = linspecer(5);
cnt = 1;
for m = [4,6,8]
M = m;
fsym = 112e9;
fdac = 256e9;
[Digi_sig,Symbols,Tx_bits] = PAMsource(...
"fsym",fsym,"M",M,"order",19,"useprbs",1,...
"fs_out",fdac,...
"applyclipping",0,"clipfactor",1.5,...
"applypulseform",0,"pulseformer",NaN,...
"randkey",33,...
"db_precode",1,"db_encode",0,...
"mrds_code",0,"mrds_blocklength",512).process();
Symbols_pre = Duobinary().precode(Symbols);
Symbols_db = Duobinary().encode(Symbols_pre);
if M == 4
Symbols_db.signal = Symbols_db.signal .*sqrt(2.5);
elseif M == 6
Symbols_db.signal = Symbols_db.signal .*sqrt(5.8);
elseif M == 8
Symbols_db.signal = Symbols_db.signal .*sqrt(10.5);
end
% figure(1)
% hold on
% histogram(Symbols_db.signal,"EdgeAlpha",0.3,"Normalization","probability");
% figure(1)
nexttile
hold on
bar(unique(Symbols_db.signal),histcounts(int32(Symbols_db.signal),"Normalization","probability"),"FaceColor",cols(cnt,:),"FaceAlpha",0.6,"BarWidth",1-(0.2*cnt),"LineWidth",0.5,"EdgeColor",'black','DisplayName',['Duobinary PAM-',num2str(M)]);
xticks(unique(Symbols_db.signal));
ylim([0 0.26]);
xlabel("Symbol")
cnt = cnt+1,
end

View File

@@ -1,18 +0,0 @@
% Define the filter taps
h_ = {[1],[1 1],[1 2 1],[1 3 3 1]};
for i = 1:length(h_)
h = h_{i};
[H, w] = freqz(h, 1, 1024, 1);
figure(1);
hold on
plot(w, 10*log10(abs(H)), 'LineWidth', 2,'DisplayName',['$(1+D)^2$']); %todo
xlabel('Normalized Frequency');
ylabel('Amplitude in dB');
grid on;
ylim([-20,10])
end

View File

@@ -1,37 +1,113 @@
function beautifyBERplot()
% BEAUTIFYBERPLOT Enhances a BER plot for publication-quality figures.
function beautifyBERplot(options)
% BEAUTIFYBERPLOT Enhances BER-style plots for publication-quality figures.
% Supports automatic smoothing and trend-line overlay.
%
% Usage examples:
% beautifyBERplot; % default
% beautifyBERplot("polyfit",1); % add polynomial fit
% beautifyBERplot("polyfit",1,"fitmethod","pchip") % piecewise cubic fit
%
% Supported fitmethod options: 'polyfit', 'smoothingspline', 'loess', 'pchip'
arguments
options.logscale (1,1) logical = 1
options.setmarkers (1,1) logical = 1;
options.polyfit (1,1) logical = 0
options.polyorder (1,1) double = 2
options.fitmethod (1,1) string = "polyfit" % choose fit type
end
% --- find all line objects in current axes
lines = findall(gca, 'Type', 'Line');
markers = {'o', 's', 'd', '^', 'v', '>', '<', 'p', 'h'};
num_markers = length(markers);
% --- style all lines consistently
for i = 1:length(lines)
lines(i).LineWidth = 1.2;
% lines(i).LineStyle = '-';
if options.setmarkers == 1
if string(lines(i).Marker) == "none"
lines(i).Marker = markers{mod(i-1, num_markers) + 1};
end
lines(i).MarkerSize = 4;
lines(i).MarkerFaceColor = lines(i).Color;
end
end
% --- optional smoothing/fitting overlay
if options.polyfit
hold on
for i = 1:length(lines)
x = lines(i).XData;
y = lines(i).YData;
valid = isfinite(x) & isfinite(y);
if sum(valid) < options.polyorder + 1
continue;
end
xf = linspace(min(x(valid)), max(x(valid)), 200);
% ----- choose fitting method -----
switch lower(options.fitmethod)
case "polyfit"
p = polyfit(x(valid), y(valid), options.polyorder);
yf = polyval(p, xf);
case "smoothingspline"
try
f = fit(x(valid)', y(valid)', 'smoothingspline');
yf = feval(f, xf);
catch
yf = interp1(x(valid), y(valid), xf, 'pchip');
end
case "loess"
yf = smooth(x(valid), y(valid), 0.2, 'loess');
yf = interp1(x(valid), yf, xf, 'linear', 'extrap');
case "pchip"
yf = interp1(x(valid), y(valid), xf, 'pchip');
otherwise
warning('Unknown fitmethod "%s". Using polyfit.', options.fitmethod);
p = polyfit(x(valid), y(valid), options.polyorder);
yf = polyval(p, xf);
end
% --- lightened color for fit overlay
lightcol = lines(i).Color + 0.4 * (1 - lines(i).Color);
lightcol(lightcol > 1) = 1;
plot(xf, yf, '-', 'Color', lightcol, ...
'LineWidth', 0.7, 'Marker', 'none', ...
'HandleVisibility','off');
end
hold off
end
% --- axis scaling and cosmetics
if options.logscale
set(gca, 'YScale', 'log');
end
% --- Figure size in centimeters ---
% width_pt = 500;
% height_pt = 300;
%
% pt2cm = 0.03514598; % TeX point cm
% width_cm = width_pt * pt2cm; % = 8.85 cm
% height_cm = height_pt * pt2cm; % = 2.81 cm
%
% set(gcf, 'Units', 'centimeters', 'Position', [2 2 width_cm height_cm]);
% set(gcf, 'PaperUnits', 'centimeters', 'PaperPosition', [0 0 width_cm height_cm]);
% --- Formatting ---
set(findall(gca, '-property', 'Interpreter'), 'Interpreter', 'latex');
set(gcf, 'Color', 'w');
set(gca, 'Box', 'on', 'LineWidth', 0.8);
grid on;
set(gca, 'FontSize', 10, 'FontName', 'Latin Modern Roman');
% Set line properties for all current plot lines
lines = findall(gca, 'Type', 'Line');
markers = {'o', 's', 'd', '^', 'v', '>', '<', 'p', 'h'}; % Define marker styles
num_markers = length(markers);
for i = 1:length(lines)
lines(i).LineWidth = 1.3; % Thicker line width
%lines(i).LineStyle = '-'; % Solid lines for simplicity
if string(lines(i).Marker) == "none"
lines(i).Marker = markers{mod(i-1, num_markers) + 1}; % Assign markers cyclically
end
lines(i).MarkerSize = 4; % Marker size
lines(i).MarkerFaceColor = 'auto'; % Use line color for marker face
end
% Change all text interpreters to LaTeX
set(findall(gca, '-property', 'Interpreter'), 'Interpreter', 'latex');
% Set figure background to white
set(gcf, 'Color', 'w');
% Set logarithmic scale for y-axis, but only if it makes sense.
% If this is not always desired, you could condition this on the presence of lines or data.
set(gca, 'YScale', 'log');
% Customize grid and box appearance
set(gca, 'Box', 'on', 'LineWidth', 0.8); % Thicker border
grid on;
% grid minor;
% Adjust font size and style for better readability
set(gca, 'FontSize', 10, 'FontName', 'Times New Roman');
end

View File

@@ -12,13 +12,13 @@ arguments
options.storage_path
end
% database = DBHandler("pathToDB",[options.database_path,options.database_name],"type","sqlite");
database = DBHandler("type","mysql");
database = DBHandler("pathToDB",[options.database_path,options.database_name],"type","sqlite");
% database = DBHandler("type","mysql");
filterParams = database.tables;
filterParams.Configurations = struct('run_id', run_id);
selectedFields = {'Runs.run_id','Runs.tx_bits_path','Runs.tx_signal_path','Runs.tx_symbols_path','Runs.rx_sync_path','Runs.rx_raw_path',...
selectedFields = {'Runs.run_id','Runs.tx_bits_path','Runs.tx_symbols_path','Runs.rx_sync_path','Runs.rx_raw_path',...
'Configurations.db_mode','Configurations.pam_level','Configurations.bitrate','Configurations.symbolrate','Configurations.fiber_length','Configurations.wavelength','Configurations.precomp_amp','Measurements.power_rop','Configurations.v_bias',...
'Configurations.interference_attenuation'};
@@ -28,8 +28,11 @@ dataTable = dataTable(uniqueIdx,:); % Extract unique configurations for each ru
fsym = dataTable.symbolrate;
M = double(dataTable.pam_level);
try
duob_mode = db_mode.(strrep(char(dataTable.db_mode),'"',''));
catch
duob_mode = db_mode(dataTable.db_mode);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
len_tr = 4096*2;
@@ -48,15 +51,14 @@ mu_ffe3 = 0.001;
mu_dfe = 0.0004;
mu_dc = 0.00;
% mu_ffe1 = 0;
% mu_ffe2 = 0;
% mu_ffe3 = 0;
% mu_dfe =0;
% mu_dc = 0.00;
mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3];
vnle_order=[vnle_order1,vnle_order2,vnle_order3];
dc_buffer_len = 224;
ffe_buffer_len = 1;
smoothing_buffer_length = 4096;
smoothing_buffer_update = 224;
% Overwrite default parameters if given in options.parameters
paramStruct = options.parameters;
if ~isempty(paramStruct)
@@ -78,13 +80,11 @@ eq_post = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0
output = struct();
vnle_pf_package = {};
vnle_dfe_package = {};
vnle_package = {};
dbtgt_package = {};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Tx_signal = load([options.storage_path, char(dataTable.tx_signal_path)]);
Tx_signal = Tx_signal.Digi_sig;
Tx_bits = load([options.storage_path, char(dataTable.tx_bits_path)]);
Tx_bits = Tx_bits.Bits;
@@ -99,21 +99,21 @@ found_sync = 0;
try
Scpe_load = load([options.storage_path, char(dataTable.rx_sync_path)]);
Scpe_cell = Scpe_load.S;
[~,~,found_sync] = Scpe_cell{2}.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",0);
[~,~,~,found_sync] = Scpe_cell{2}.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",1);
end
if ~found_sync
Scpe_sig_raw = load([options.storage_path, char(dataTable.rx_raw_path(1))]);
Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw;
Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in",Scpe_sig_raw.fs,"fs_out",2*fsym);
[~,Scpe_cell,found_sync] =Scpe_sig_resampled.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",1);
[~,Scpe_cell,~,found_sync] =Scpe_sig_resampled.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",1);
end
if ~found_sync
if length(Symbols_mapped.signal) == sum(Symbols_mapped.signal == Symbols.signal)
warning('Could not synchronize the received signal with the stored symbols!')
else
[~,Scpe_cell,found_sync] =Scpe_sig_raw.tsynch("reference",Symbols_mapped,"fs_ref",dataTable.symbolrate,"debug_plots",0);
[~,Scpe_cell,~,found_sync] =Scpe_sig_raw.tsynch("reference",Symbols_mapped,"fs_ref",dataTable.symbolrate,"debug_plots",0);
end
if ~found_sync
warning('Could not synchronize the received signal with the stored symbols!')
@@ -142,11 +142,24 @@ for occ = 1:record_realizations
% %%%%% VNLE + DFE %%%%
if 0
eq_vnle_dfe = EQ("Ne",vnle_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
eq_post = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",2001,"sps",1,"decide",0);
eq_ = FFE_DCremoval_adaptive_mu("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0,...
"mu_dc",mu_dc,...
"dc_buffer_len",dc_buffer_len, ...
"ffe_buffer_len",ffe_buffer_len,...
"smoothing_buffer_length",smoothing_buffer_length,...
"smoothing_buffer_update",smoothing_buffer_update);
[result] = vnle(eq_vnle_dfe,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",duob_mode,"showAnalysis",1,"postFFE",[]);
vnle_dfe_package{occ} = result;
eq_ = EQ("Ne",vnle_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
result = vnle(eq_,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",duob_mode,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0);
vnle_package{occ} = result;
fprintf("FFE Results: %.2e\n", result.ber_vnle);
if options.append_to_db
database.addProcessingResult(run_id, result.resultsVNLE, result.equalizerConfigVNLE);
end
end
@@ -176,12 +189,12 @@ for occ = 1:record_realizations
% % fprintf("BER VNLE: %.2e | %.2e; BER MLSE: %.2e | %.2e \n",vnle_pf_package{occ}.resultsVNLE.BER,vnle_pf_package{occ}.resultsVNLE.BER_precoded ,vnle_pf_package{occ}.resultsMLSE.BER,vnle_pf_package{occ}.resultsMLSE.BER_precoded);
if vnle_pf
occ = 1; % or whatever your loop index is
% occ = 1; % or whatever your loop index is
% Extract VNLE results for readability
vnle = vnle_pf_package{occ}.resultsVNLE;
mlse = vnle_pf_package{occ}.resultsMLSE;
vnle_result = vnle_pf_package{occ}.resultsVNLE;
mlse_result = vnle_pf_package{occ}.resultsMLSE;
% Print header
@@ -189,19 +202,19 @@ for occ = 1:record_realizations
% VNLE Results
fprintf(">> VNLE Results:\n");
fprintf(" BER %.2e\n", vnle.BER);
fprintf(" BER (pre-code) %.2e\n", vnle.BER_precoded);
fprintf(" SNR: %.2f dB\n", vnle.SNR);
fprintf(" GMI: %.4f\n", vnle.GMI);
fprintf(" BER %.2e\n", vnle_result.BER);
fprintf(" BER (pre-code) %.2e\n", vnle_result.BER_precoded);
fprintf(" SNR: %.2f dB\n", vnle_result.SNR);
fprintf(" GMI: %.4f\n", vnle_result.GMI);
fprintf(" Linerate: %.2f Gbps\n", Symbols.fs .* floor(log2(M)*10)/10 .*1e-9);
fprintf(" AIR: %.2f Gbps\n", vnle.AIR.*1e-9);
fprintf(" AIR: %.2f Gbps\n", vnle_result.AIR.*1e-9);
fprintf("\n");
% MLSE Results
fprintf(">> MLSE Results:\n");
fprintf(" BER : %.2e\n", mlse.BER);
fprintf(" BER (pre-code): %.2e\n", mlse.BER_precoded);
fprintf(" Channel Alpha : %.2f\n", mlse.Alpha);
fprintf(" BER : %.2e\n", mlse_result.BER);
fprintf(" BER (pre-code): %.2e\n", mlse_result.BER_precoded);
fprintf(" Channel Alpha : %.2f\n", mlse_result.Alpha);
fprintf("\n");
end
@@ -243,6 +256,7 @@ end
output.dataTable = dataTable;
output.vnle_pf_package = vnle_pf_package;
output.vnle_package = vnle_package;
output.dbtgt_package = dbtgt_package;
end

View File

@@ -4,9 +4,89 @@
savePath = 'Z:\2025\ECOC Silas\ecoc_2025\';
databasePath = 'C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
database_name = 'ecoc2025_loops.db';
db = DBHandler("pathToDB", [databasePath, database_name],"type","mysql");
% db = DBHandler("type","mysql");
num_occ = 5;
run_par = false;
run_id = 1958;
[out, future] = submit_dsp(run_id, databasePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ);
db = DBHandler("type","mysql");
% db = DBHandler("pathToDB", [databasePath, database_name],"type","sqlite");
filterParams = db.tables;
% filterParams.Configurations = struct('run_id', 4001);
filterParams.Configurations = struct( ...
'symbolrate', 112e9, ... %[224,336,360,390,420,448]
'fiber_length', 0, ...
'db_mode', '"no_db"', ...
'interference_attenuation', [], ...
'interference_path_length', [], ...
'is_mpi', 1, ...
'pam_level', 4, ...
'wavelength', 1310, ...
'precomp_amp', [], ...
'signal_attenuation', [], ...
'v_awg', [], ...
'v_bias', 2.65 ...
);
selectedFields = {'Runs.run_id','Runs.loop_id','Runs.tx_bits_path','Runs.tx_signal_path','Runs.tx_symbols_path','Runs.rx_sync_path','Runs.rx_raw_path',...
'Configurations.db_mode','Configurations.pam_level','Configurations.bitrate','Configurations.symbolrate','Configurations.fiber_length','Configurations.wavelength','Configurations.precomp_amp','Measurements.power_rop','Configurations.v_bias',...
'Configurations.interference_attenuation', 'Configurations.interference_path_length'};
[dataTable,sql_query] = db.queryDB(filterParams, selectedFields);
% dataTable(dataTable.loop_id<200,:) = [];
num_occ = 15;
run_par = true;
run_id = dataTable.run_id;
params = struct();
% slow DC tracking
params.dc_buffer_len = 224;
params.ffe_buffer_len = 1;
params.smoothing_buffer_length = 0;
params.smoothing_buffer_update = 0;
params.mu_dc = 0.005;
futures_list = parallel.FevalFuture.empty();
for id = 1:length(dataTable.run_id)
run_id = dataTable.run_id(id);
[out, futures_list(id)] = submit_dsp(run_id, databasePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ,'paramstruct',params);
end
% ideal DC tracking
params.dc_buffer_len = 1;
params.ffe_buffer_len = 1;
params.smoothing_buffer_length = 0;
params.smoothing_buffer_update = 0;
params.mu_dc = 0.005;
futures_list = parallel.FevalFuture.empty();
for id = 1:length(dataTable.run_id)
run_id = dataTable.run_id(id);
[out, futures_list(id)] = submit_dsp(run_id, databasePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ,'paramstruct',params);
end
% DC smoothing
params.dc_buffer_len = 1;
params.ffe_buffer_len = 1;
params.smoothing_buffer_length = 4096;
params.smoothing_buffer_update = 224;
params.mu_dc = 0.00;
futures_list = parallel.FevalFuture.empty();
for id = 1:length(dataTable.run_id)
run_id = dataTable.run_id(id);
[out, futures_list(id)] = submit_dsp(run_id, databasePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ,'paramstruct',params);
end
% only FFE
params.dc_buffer_len = 1;
params.ffe_buffer_len = 1;
params.smoothing_buffer_length = 0;
params.smoothing_buffer_update = 0;
params.mu_dc = 0.00;
futures_list = parallel.FevalFuture.empty();
for id = 1:length(dataTable.run_id)
run_id = dataTable.run_id(id);
[out, futures_list(id)] = submit_dsp(run_id, databasePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ,'paramstruct',params);
end

View File

@@ -1,46 +1,89 @@
db = DBHandler("type","mysql","dataBase",'labor');
run_id = 231;
fp = QueryFilter();
% fp.where('Runs', 'run_id','EQUALS', 987);
M = 4;
fp.where('Runs', 'pam_level','EQUALS', M);
fp.where('Runs', 'symbolrate','EQUALS', 112e9);
fp.where('Runs', 'fiber_length','EQUALS', 0);
fp.where('Runs', 'is_mpi','EQUALS', 1);
fp.where('Runs', 'interference_path_length','EQUALS', 70);
% fp.where('Runs', 'loop_id','GREATER_THAN', 11);
fp.where('Runs', 'sir','EQUALS',20);
savePath = 'Z:\2025\ECOC Silas\ecoc_2025\';
databasePath = 'C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
database_name = 'ecoc2025_loops.db';
db = DBHandler("type","mysql");
% db = DBHandler("pathToDB", [databasePath, database_name],"type","sqlite");
filterParams = db.tables;
filterParams.Configurations = struct('run_id', run_id);
selectedFields = {'Runs.run_id','Runs.tx_bits_path','Runs.tx_signal_path','Runs.tx_symbols_path','Runs.rx_sync_path','Runs.rx_raw_path',...
'Configurations.db_mode','Configurations.pam_level','Configurations.bitrate','Configurations.symbolrate','Configurations.fiber_length','Configurations.wavelength','Configurations.precomp_amp','Measurements.power_rop','Configurations.v_bias',...
'Configurations.interference_attenuation'};
[dataTable,sql_query] = db.queryDB(filterParams, selectedFields);
[dataTable,sql_query] = db.queryDB(fp, db.getTableFieldNames('Runs'));
[~, uniqueIdx] = unique(dataTable.run_id); % Get unique run_id indices
dataTable = dataTable(uniqueIdx,:); % Extract unique configurations for each run_id
fsym = dataTable.symbolrate;
M = double(dataTable.pam_level);
duob_mode = db_mode.(strrep(char(dataTable.db_mode),'"',''));
dataTable.SIR = -7 - round(dataTable.power_mpi_interference);
Tx_signal = load([savePath, char(dataTable.tx_signal_path)]);
Tx_signal = Tx_signal.Digi_sig;
%%
for i = 1:size(dataTable,1)
dataTable_ = dataTable(i,:); % Extract unique configurations for each run_id
Tx_bits = load([savePath, char(dataTable.tx_bits_path)]);
Tx_bits = Tx_bits.Bits;
Symbols_mapped = PAMmapper(M,0).map(Tx_bits);
Symbols_mapped.fs = dataTable.symbolrate;
fsym = dataTable_.symbolrate;
M = double(dataTable_.pam_level);
duob_mode = db_mode.(strrep(char(dataTable_.db_mode),'"',''));
Symbols = load([savePath, char(dataTable.tx_symbols_path)]);
Symbols = Symbols.Symbols;
Tx_signal = load([savePath, char(dataTable_.tx_signal_path)]);
Tx_signal = Tx_signal.Digi_sig;
Scpe_sig_raw = load([savePath, char(dataTable.rx_raw_path(1))]);
Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw;
Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in",Scpe_sig_raw.fs,"fs_out",2*fsym);
[~,Scpe_cell,found_sync,~,shifts] = Scpe_sig_resampled.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",1);
Tx_bits = load([savePath, char(dataTable_.tx_bits_path)]);
Tx_bits = Tx_bits.Bits;
Symbols_mapped = PAMmapper(M,0).map(Tx_bits);
Symbols_mapped.fs = dataTable_.symbolrate;
shifts_mus = shifts./Scpe_sig_resampled.fs .*1e6;
Scpe_sig_raw.plot("displayname",['Scope Signal (Run ID: ',num2str(run_id)],"fignum",2024,"clear",1);
hold on;
xline(shifts_mus,'HandleVisibility','off');
Symbols = load([savePath, char(dataTable_.tx_symbols_path)]);
Symbols = Symbols.Symbols;
Scpe_sig_raw = load([savePath, char(dataTable_.rx_raw_path(1))]);
Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw;
Scpe_sig_raw.plot("displayname",['Scope Signal (Run ID: ',num2str(dataTable_.run_id)],"fignum",dataTable_.run_id,"clear",0);
Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in",Scpe_sig_raw.fs,"fs_out",fsym);
[~,Scpe_cell,found_sync,test,shifts] = Scpe_sig_resampled.tsynch("reference",Symbols,"fs_ref",dataTable_.symbolrate,"debug_plots",0);
shifts_mus = shifts./Scpe_sig_resampled.fs .*1e6;
Scpe_sig_resampled = Scpe_sig_resampled.normalize("mode","rms");
% Scpe_sig_resampled.plot("displayname",['Scope Signal (Run ID: ',num2str(dataTable_.run_id)],"fignum",dataTable_.run_id,"clear",0);
hold on;
% xline(shifts_mus,'HandleVisibility','off');
shifts = shifts-shifts(1)+1;
sep_sig = NaN(M,length(Scpe_sig_resampled));
avg_sig = NaN(M,length(Scpe_sig_resampled));
for j = 1:size(Scpe_cell,1)
[sep_sig_,avg_sig_]=showLevelScatter(Scpe_cell{j}.resample("fs_out",Symbols.fs),Symbols,"fignum",400);
s = shifts(j);
sep_sig(:,s+1:s+length(sep_sig_)) = sep_sig_;
avg_sig(:,s+1:s+length(avg_sig_)) = avg_sig_;
end
disp(num2str(filterParams.Configurations.interference_path_length));
var(sep_sig,0,2,'omitnan')
%%
xax_in_sec = ((1:length(avg_sig)) / fsym) * 1e6;
figure();hold on;
cols = cbrewer2('Paired',8);
% for p = 1:size(avg_sig,1)
% sc=scatter(xax_in_sec,sep_sig(p,:),1,'.','MarkerEdgeColor',cols((2*p)-1,:),'MarkerEdgeAlpha',0.1);
% end
for p = 1:size(avg_sig,1)
sc=plot(xax_in_sec,avg_sig(p,:),'LineWidth',1,'Color',cols((2*p),:));
end
yline(unique(Symbols.signal),'HandleVisibility','off');
% xline(shifts./ fsym .*1e6,'HandleVisibility','off');
xlabel('time in $\mu$s');
ylabel('Normalized Amplitude');
xlim([0 25]);
ylim([-2 2]);
drawnow;
end

View File

@@ -1,179 +1,345 @@
local = 1;
if local
databasePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
else
databasePath = '\\ntserver.tf.uni-kiel.de\scratch\sioe\ECOC_2025\';
end
database_name = 'ecoc2025_loops.db';
database = DBHandler("pathToDB", [databasePath, database_name]);
figure();
plotBoundaries = 1;
plotRealizations = 1;
cols = linspecer(3); % Ensure color count matches
% cols = cols(8,:);
filterParams = database.tables;
filterParams.Configurations = struct( ...
'symbolrate', 112e9, ... %[224,336,360,390,420,448]
'fiber_length', 0, ...
'db_mode', '"no_db"', ...
'interference_attenuation', [], ...
'interference_path_length', 10, ...
'is_mpi', 1, ...
'pam_level', 4, ...
'wavelength', 1310, ...
'precomp_amp', [], ...
'signal_attenuation', [], ...
'v_awg', 0.95, ...
'v_bias', 2.5 ...
);
% filterParams.EqualizerParameters.diff_precode = int32(db_mode.db_encoded);
% filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle);
selectedFields = {'Configurations.run_id' 'Runs.loop_id' 'Runs.date_of_run' 'Runs.rx_raw_path' 'Configurations.bitrate' 'Configurations.v_bias' 'Configurations.v_awg' 'Configurations.precomp_amp' 'Configurations.symbolrate' 'Configurations.pam_level'...
'Configurations.db_mode' 'Configurations.rop_attenuation' 'Configurations.is_mpi' 'Configurations.interference_attenuation' 'Configurations.interference_path_length' 'Configurations.signal_attenuation' ...
'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'Measurements.power_pd_in' ...
'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.BER_precoded' 'Results.SNR' 'Results.GMI' 'Results.Alpha' 'Results.date_of_processing'};
[dataTable,sql_query] = database.queryDB(filterParams, selectedFields);
dataTable.SIR = round(-6 - dataTable.power_mpi_interference);
dataTable = cleanUpTable(dataTable);
dataTable(dataTable.eq_id==0,:) = [];
% Filter by time
filter_by_time = 0;
if filter_by_time
startTime = datetime('2025-04-14 13:00:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss');
stopTime = datetime('2025-04-14 19:30:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss');
dataTable.date_of_run = datetime(dataTable.date_of_run, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS');
dataTable.date_of_processing = datetime(dataTable.date_of_processing, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS');
dataTable = dataTable(dataTable.date_of_processing > startTime, :);
dataTable = dataTable(dataTable.date_of_processing < stopTime, :);
end
% Group by smth
y_var = 'BER';
x_var = 'SIR';
loop_var = 'eq_id';
fixedVars = {'eq_id',x_var};
[dataTable, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var);
dataTableGrpd_mean = groupIt(fixedVars, dataTable, @mean);
dataTableGrpd_min = groupIt(fixedVars, dataTable, @min);
dataTableGrpd_max = groupIt(fixedVars, dataTable, @max);
% Create a new figure
mkr = '.';
hold on
% dsp_options.database_type = 'mysql';
% dsp_options.dataBase = 'labor';
% dsp_options.storage_path = 'Z:\2025\ECOC Silas\ecoc_2025\';
% database = DBHandler("dataBase", [dsp_options.dataBase], "type", dsp_options.database_type);
% filterParams = database.tables;
% filterParams.Runs.loop_id = 209;
% % filterParams.Configurations = struct( ...
% % 'symbolrate', 112e9, ... %[224,336,360,390,420,448]
% % 'fiber_length', 0, ...
% % 'db_mode', '"no_db"', ...
% % 'interference_attenuation', [], ...
% % 'interference_path_length', 1000, ...
% % 'is_mpi', 1, ...
% % 'pam_level', 4, ...
% % 'wavelength', 1310, ...
% % 'precomp_amp', [], ...
% % 'signal_attenuation', [], ...
% % 'v_awg', [], ...
% % 'v_bias', [] ...
% % );
%
% % if 1
% % % filterParams.EqualizerParameters.dc_buffer_len = 1;
% % filterParams.EqualizerParameters.ffe_buffer_len = 1;
% % filterParams.EqualizerParameters.smoothing_buffer_len = 4096;
% % filterParams.EqualizerParameters.smoothing_buffer_update = 224;
% % filterParams.EqualizerParameters.DCmu = 0;
% % end
% a = database.getTableFieldNames('Runs');
% b = database.getTableFieldNames('Results');
% c = database.getTableFieldNames('EqualizerParameters');
% d = [a;b;c];
%
% [dataTable,~] = database.queryDB(filterParams, d);
%
% selectedFields = {'Configurations.run_id' 'Runs.loop_id' 'Runs.date_of_run' 'Runs.rx_raw_path' 'Runs.bitrate' 'Runs.v_bias' 'Runs.v_awg' 'Runs.precomp_amp' 'Runs.symbolrate' 'Runs.pam_level'...
% 'Runs.db_mode' 'Runs.rop_attenuation' 'Runs.is_mpi' 'Runs.interference_attenuation' 'Runs.interference_path_length' 'Runs.signal_attenuation' ...
% 'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'EqualizerParameters.dc_buffer_len' 'EqualizerParameters.ffe_buffer_len' 'EqualizerParameters.smoothing_buffer_len' 'EqualizerParameters.smoothing_buffer_update' 'EqualizerParameters.DCmu' 'Measurements.power_pd_in' ...
% 'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.BER_precoded' 'Results.EVM' 'Results.SNR' 'Results.GMI' 'Results.Alpha' 'Results.date_of_processing'};
unique_loop_var = unique(dataTable.(loop_var));
db = DBHandler("type","mysql","dataBase",'labor');
for i = 1%:numel(unique_loop_var)
fp = QueryFilter();
% fp.where('mpi_superview', 'loop_id','EQUALS', 209);
fp.where('mpi_superview', 'symbolrate','EQUALS', 112e9);
fp.where('mpi_superview', 'pam_level','EQUALS', 4);
fn = [db.getTableFieldNames('mpi_superview')];
[dataTable,sql_query] = db.queryDB(fp,fn);
% Prepare filtered data for this loop variable
loopValue = unique_loop_var(i);
loopFiltGrpd = dataTableGrpd_mean.(loop_var) == loopValue;
if ~any(loopFiltGrpd)
continue; % Skip if no data for this loop var
end
%%
% Extract values
x_values = dataTableGrpd_mean.(x_var)(loopFiltGrpd, :);
y_mean = dataTableGrpd_mean.(y_var)(loopFiltGrpd, :);
y_min = dataTableGrpd_min.(y_var)(loopFiltGrpd, :);
y_max = dataTableGrpd_max.(y_var)(loopFiltGrpd, :);
dataTable_clean = dataTable;
dataTable_clean.SIR = -7 - round(dataTable_clean.power_mpi_interference);
dataTable_clean.NGMI = dataTable_clean.GMI ./ log2(dataTable_clean.pam_level);
dataTable_clean = cleanUpTable(dataTable_clean);
% Compute bounds: distance from mean
y_lower = y_mean - y_min;
y_upper = y_max - y_mean;
y_bounds = [y_lower, y_upper];
dataTable_clean(dataTable_clean.BER>0.2,:) = [];
% Display name (optional)
idx = find(dataTable.(loop_var) == loopValue, 1, 'first');
dispname = equalizer_structure(dataTable.equalizer_structure(idx));
dispname = [char(dispname),'; ',num2str(unique(dataTable.interference_path_length)),' m'];
%%
if plotBoundaries
% Plot bounded line
[hl, hp] = boundedline(x_values, y_mean, y_bounds, ...
'alpha', 'transparency', 0.2, ...
'cmap', cols(i,:), ...
'nan', 'fill', ...
'orientation', 'vert');
% Style the main line: thinnest, dotted, no marker
set(hl, 'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ...
'Color', cols(i,:), 'DisplayName', string(dispname));
% Hide patch (shaded area) from legend
set(hp, 'HandleVisibility', 'off','LineStyle',':','LineWidth',0.5,'Marker','none');
% Add invisible scatter for DataTips
plt = scatter(x_values, y_mean, ...
'Marker', 'o', 'MarkerEdgeColor', 'none', 'MarkerFaceColor', 'none', ...
'HandleVisibility', 'off', 'PickableParts', 'all');
else
plt= plot(x_values,y_mean,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ...
'Color', cols(i,:), 'DisplayName', string(dispname));
end
% Add data tips to the invisible scatter
pair_one = {'Run ID', dataTableGrpd_mean.run_id(loopFiltGrpd, :)};
pair_two = {'Rate', dataTableGrpd_mean.bitrate(loopFiltGrpd, :) * 1e-9};
pair_three = {'PD in', round(dataTableGrpd_mean.power_pd_in(loopFiltGrpd, :), 2)};
addDatatips(plt, pair_one, pair_two, pair_three);
cols = linspecer(8); % Ensure color count matches
figure()
tiledlayout(1, 4, 'TileSpacing', 'compact', 'Padding', 'compact');
y_here = 0;
figcnt = 0;
for int_len = [0,50,300,1000]
figcnt = figcnt+1;
% figure(int_len+1);
nexttile;
hold on
mode = 4;
% Optionally: outline bounds for better visibility (optional)
% hnew = outlinebounds(hl, hp);
% Tick marks (x-axis)
xticks(round(unique(x_values),2));
for mode = [1,2]
hold on;
dataTable = dataTable_clean;
% Optional: scatter realizations
if plotRealizations
loopFiltSingle = dataTable.(loop_var) == loopValue;
x_single = double(dataTable.(x_var)(loopFiltSingle, :));
y_single = double(dataTable.(y_var)(loopFiltSingle, :));
sc = scatter(x_single, y_single, 'Marker', mkr, 'MarkerEdgeColor', cols(i, :), ...
'LineWidth', 0.5, 'HandleVisibility', 'on', 'DisplayName', string(dispname));
plotBoundaries = 1;
plotRealizations = 1;
cols = linspecer(8); % Ensure color count matches
if mode == 1
% No compensation method
dataTable = dataTable(dataTable.dc_buffer_len == 1, :);
dataTable = dataTable(dataTable.ffe_buffer_len == 1, :);
dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :);
dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :);
dataTable = dataTable(dataTable.DCmu == 0, :);
cols = cols(1:1+1,:);
method = 'ffe only';
% slow DC smoothing
elseif mode == 2
dataTable = dataTable(dataTable.dc_buffer_len == 1, :);
dataTable = dataTable(dataTable.ffe_buffer_len == 1, :);
dataTable = dataTable(dataTable.smoothing_buffer_len == 4096, :);
dataTable = dataTable(dataTable.smoothing_buffer_update == 224, :);
dataTable = dataTable(dataTable.DCmu == 0, :);
cols = cols(4:4+1,:);
method = 'dc smoothing';
% slow DC tracking
elseif mode == 3
dataTable = dataTable(dataTable.dc_buffer_len == 224, :);
dataTable = dataTable(dataTable.ffe_buffer_len == 1, :);
dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :);
dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :);
dataTable = dataTable(dataTable.DCmu == 0.005, :);
cols = cols(3:3+1,:);
method = 'parallelized dc tracking';
elseif mode == 4
% ideal DC tracking
dataTable = dataTable(dataTable.dc_buffer_len == 1, :);
dataTable = dataTable(dataTable.ffe_buffer_len == 1, :);
dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :);
dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :);
dataTable = dataTable(dataTable.DCmu == 0.005, :);
cols = cols(2:2+1,:);
method = 'ideal dc tracking';
end
% dataTable(dataTable.eq_id==0,:) = [];
dataTable(dataTable.equalizer_structure~=1,:) = [];
% Modify values in 'interference_path_length' where the condition is met
dataTable.interference_path_length(dataTable.interference_path_length < 101 & dataTable.interference_path_length > 1) = 50;
dataTable.interference_path_length(dataTable.interference_path_length == 1) = 0;
dataTable = dataTable(dataTable.interference_path_length == int_len, :);
dataTable(dataTable.run_id == 3866, :) = [];
dataTable(dataTable.run_id == 3865, :) = [];
dataTable(dataTable.run_id == 3796, :) = [];
dataTable(dataTable.run_id == 3797, :) = [];
dataTable(dataTable.run_id == 3798, :) = [];
dataTable(dataTable.run_id == 4002, :) = [];
dataTable(dataTable.run_id == 4200, :) = [];
dataTable(dataTable.run_id == 4199, :) = [];
% 0
% 1
% 10
% 15
% 20
% 50
% 100
% 300
% 1000
% dataTable(dataTable.interference_path_length ~= 50, :) = [];
% dataTable = dataTable(dataTable.interference_path_length < 51, :);
% dataTable(dataTable.loop_id<200,:) = [];
% Filter by time
filter_by_time = 0;
if filter_by_time
startTime = datetime('2025-04-20 18:00:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss');
stopTime = datetime('2025-04-30 19:30:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss');
dataTable.date_of_run = datetime(dataTable.date_of_run, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS');
dataTable.date_of_processing = datetime(dataTable.date_of_processing, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS');
dataTable = dataTable(dataTable.date_of_processing > startTime, :);
dataTable = dataTable(dataTable.date_of_processing < stopTime, :);
end
% Group by smth
y_var = 'BER';
x_var = 'SIR';
loop_var = 'interference_path_length';
fixedVars = {'equalizer_structure','interference_path_length',x_var};
[dataTable, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var);
dataTableGrpd_mean = groupIt(fixedVars, dataTable, @mean);
dataTableGrpd_min = groupIt(fixedVars, dataTable, @min);
dataTableGrpd_max = groupIt(fixedVars, dataTable, @max);
% dataTableGrpd_mean(dataTableGrpd_mean.nRows<50,:) = [];
% dataTableGrpd_min(dataTableGrpd_min.nRows<50,:) = [];
% dataTableGrpd_max(dataTableGrpd_max.nRows<50,:) = [];
% Create a new figure
hold on
unique_loop_var = unique(dataTable.(loop_var));
for i = 1:numel(unique_loop_var)
% Prepare filtered data for this loop variable
loopValue = unique_loop_var(i);
loopFiltGrpd = dataTableGrpd_mean.(loop_var) == loopValue;
if ~any(loopFiltGrpd)
continue; % Skip if no data for this loop var
end
% Extract values
x_values = dataTableGrpd_mean.(x_var)(loopFiltGrpd, :);
y_mean = dataTableGrpd_mean.(y_var)(loopFiltGrpd, :);
y_min = dataTableGrpd_min.(y_var)(loopFiltGrpd, :);
y_max = dataTableGrpd_max.(y_var)(loopFiltGrpd, :);
% Compute bounds: distance from mean
y_lower = y_mean - y_min;
y_upper = y_max - y_mean;
y_bounds = [y_lower, y_upper];
% Display name (optional)
try
idx = find(dataTable.(loop_var) == loopValue, 1, 'first');
% dispname = char(equalizer_structure(dataTable.equalizer_structure(idx)));
dispname = [method];
dispname = [dispname, '/ ',num2str(unique_loop_var(i)) ,' m'];
% dispname = [dispname,'; ',num2str(unique(dataTable.interference_path_length)),' m'];
% dispname = [dispname, '/ PAM ', num2str(filterParams.Configurations.pam_level)];
% dispname = [dispname, '/ ', num2str(filterParams.Configurations.symbolrate.*1e-9),' GBd'];
end
if plotBoundaries
% Plot bounded line
[hl, hp] = boundedline(x_values, y_mean, y_bounds, ...
'alpha', 'transparency', 0.1, ...
'cmap', cols(i,:), ...
'nan', 'fill', ...
'orientation', 'vert');
% % Style the main line: thinnest, dotted, no marker
set(hl, 'LineWidth', 0.5, 'LineStyle', ':', 'Marker', 'none', ...
'Color', cols(i,:), 'DisplayName', string(dispname));
plt = errorbar(x_values,y_mean,y_lower,y_upper,'LineWidth', 0.9, 'LineStyle', 'none', 'Marker', 'none','Color', cols(i,:), 'DisplayName', string(dispname),'HandleVisibility','off');
% Hide patch (shaded area) from legend
set(hp, 'HandleVisibility', 'off','LineStyle',':','LineWidth',0.5,'Marker','none');
% Fit a 4th-order polynomial to log10(BER)
p = polyfit(x_values, log10(y_mean), 3); % 4 is fitting order, adjust as needed
% Evaluate the fitted polynomial
x_fit = linspace(min(x_values), max(x_values), 300); % Fine points
y_fit_log = polyval(p, x_fit); % Still in log10 domain
y_fit = 10.^y_fit_log; % Back to BER domain
plot(x_fit,y_fit,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ...
'Color', cols(i,:), 'DisplayName', string(dispname),'HandleVisibility','off');
% % Add invisible scatter for DataTips
% plt = scatter(x_values, y_mean, ...
% 'Marker', 'o', 'MarkerEdgeColor', 'none', 'MarkerFaceColor', 'none', ...
% 'HandleVisibility', 'off', 'PickableParts', 'all');
else
plt= plot(x_values,y_mean,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ...
'Color', cols(i,:), 'DisplayName', string(dispname));
% plt= errorbar(x_values,y_mean,y_lower,y_upper,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none','Color', cols(i,:), 'DisplayName', string(dispname));
end
% Add data tips to the invisible scatter
pair_one = {'Run ID', dataTableGrpd_mean.run_id(loopFiltGrpd, :)};
pair_two = {'Rate', dataTableGrpd_mean.bitrate(loopFiltGrpd, :) * 1e-9};
pair_three = {'PD in', round(dataTableGrpd_mean.power_pd_in(loopFiltGrpd, :), 2)};
addDatatips(plt, pair_one, pair_two, pair_three);
% Optionally: outline bounds for better visibility (optional)
% hnew = outlinebounds(hl, hp);
% Tick marks (x-axis)
% Optional: scatter realizations
if plotRealizations
loopFiltSingle = dataTable.(loop_var) == loopValue;
x_single = double(dataTable.(x_var)(loopFiltSingle, :));
y_single = double(dataTable.(y_var)(loopFiltSingle, :));
mkr = '.';
sc = scatter(x_single+(mode*0.1)-0.2, y_single,15, 'Marker', mkr, 'MarkerEdgeColor', cols(i, :), ...
'LineWidth', 0.5, 'HandleVisibility', 'off', 'DisplayName', string(dispname));
pair_one = {'Run ID', dataTable.run_id(loopFiltSingle, :)};
pair_two = {'Baud', dataTable.symbolrate(loopFiltSingle, :) * 1e-9};
pair_three = {'PD in', round(dataTable.power_pd_in(loopFiltSingle, :), 2)};
pair_four = {'#bits', round(dataTable.numBits(loopFiltSingle, :), 2)};
addDatatips(sc, pair_one, pair_two, pair_three,pair_four);
end
end
% Label axes and title
legend('Interpreter', 'latex');
xlabel(x_var);
if ~y_here
ylabel(y_var);
yticklabels = [];
y_here = 1;
end
if int_len ~= 0
set(gca, 'YTick', []);
end
% title([x_var, ' vs. ', y_var]);
if string(y_var) == "BER"
yline(2.2e-4, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off');
yline(3.8e-3, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off');
yline(2e-2, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off');
ylim([1e-5, 0.1]);
end
xlim([15,35]);
ylim([9e-5 0.1 ]);
xticks([13:2:35]);
% Enable grid and beautify
grid on;
beautifyBERplot;
pair_one = {'Run ID', dataTable.run_id(loopFiltSingle, :)};
pair_two = {'Rate', dataTable.bitrate(loopFiltSingle, :) * 1e-9};
pair_three = {'PD in', round(dataTable.power_pd_in(loopFiltSingle, :), 2)};
addDatatips(sc, pair_one, pair_two, pair_three);
end
end
% Label axes and title
legend('Interpreter', 'latex');
xlabel(x_var);
ylabel(y_var);
title([x_var, ' vs. ', y_var]);
if y_var == 'BER'
yline(4e-4, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off');
ylim([1e-5, 0.1]);
end
xlim([15,50]);
% Enable grid and beautify
grid on;
beautifyBERplot;
function resultTable = groupIt(fixedVars, dataTable, aggregationFunction)
% groupIt Groups data in a table based on fixedVars and applies aggregationFunction to numeric data.
@@ -243,7 +409,6 @@ resultTable.nRows = groupCount;
end
function addDatatips(sc, varargin)
% addDatatips Adds custom data tip rows to a scatter plot.
%
@@ -283,7 +448,6 @@ end
end
function cleanedTable = cleanUpTable(inputTable)
% cleanUpTable Cleans a MATLAB table where numbers and NaNs are stored as strings or structs.
%
@@ -335,7 +499,7 @@ for i = 1:numel(varNames)
else
% Try convert to datetime
try
cleanedTable.(varNames{i}) = datetime(col, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS');
cleanedTable.(varNames{i}) = datetime(col, 'InputFormat', 'yyyy-MM-dd HH:mm:ss');
catch
% Leave as string
end
@@ -391,8 +555,9 @@ for groupIdx = 1:height(groupKeys)
continue;
end
% Detect outliers
outlierMask = isoutlier(y_values, 'quartiles');
% Detect outliers in log space
y_log = log10(y_values);
outlierMask = isoutlier(y_log, 'quartiles',1); % or 'median', 'grubbs', etc.
% If any outliers found, collect their data
if any(outlierMask)

View File

@@ -12,6 +12,7 @@ arguments
savePath
options.parallel (1,1) logical = true
options.max_occurences = 1;
options.paramstruct = struct();
end
if options.parallel
@@ -29,7 +30,8 @@ if options.parallel
"database_name", database_name, ...
'storage_path', savePath, ...
'append_to_db', 1, ...
'max_occurences', options.max_occurences ...
'max_occurences', options.max_occurences, ...
'parameters', options.paramstruct ...
);
output = [];
@@ -46,7 +48,8 @@ else
"database_name", database_name, ...
'storage_path', savePath, ...
'append_to_db', 1, ...
'max_occurences', options.max_occurences ...
'max_occurences', options.max_occurences,...
'parameters', options.paramstruct ...
);
future = []; % No future since it's synchronous

View File

@@ -2,17 +2,18 @@
% 1) Find RUN ID's
basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\';
database = DBHandler("pathToDB",[basePath,'silas_labor.db']);
% database = DBHandler("pathToDB",[basePath,'silas_labor.db']);
database = DBHandler("type",'mysql');
filterParams = database.tables;
filterParams.Configurations = struct( ...
'bitrate', [], ... %[224,336,360,390,420,448]
'bitrate', 112e9, ... %[224,336,360,390,420,448]
'db_mode', [], ...
'fiber_length', [], ...
'interference_attenuation',[], ...
'is_mpi', 0, ...
'pam_level', [], ...
'rop_attenuation', 0 ...
'interference_path_length',300, ...
'is_mpi', 1, ...
'pam_level', 4 ...
);
selectedFields = {'Runs.run_id',...

View File

@@ -124,7 +124,7 @@ for occ = 1:proc_occ
% %%%%% VNLE + DFE %%%%
if 0
eq_vnle_dfe = EQ("Ne",vnle_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
eq_vnle_dfe = EQ("Ne",vnle_order,"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.001,"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
eq_post = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"mu_dd",1e-4,"mu_tr",0,"order",2001,"sps",1,"decide",0);
[result] = vnle(eq_vnle_dfe,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",duob_mode,"showAnalysis",1,"postFFE",[]);

View File

@@ -1,26 +1,27 @@
basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\';
database = DBHandler("pathToDB",[basePath,'silas_labor.db']);
database = DBHandler("pathToDB",[basePath,'silas_labor_plain.db'],"type",'sqlite');
filterParams = database.tables;
filterParams.Configurations = struct( ...
'bitrate', [], ... %[224,336,360,390,420,448]
'db_mode', int32(db_mode.no_db), ...
'db_mode', [], ...
'fiber_length', 1, ...
'interference_attenuation', [], ...
'interference_path_length', [], ...
'is_mpi', 1, ...
'is_mpi', 0, ...
'pam_level', 4, ...
'rop_attenuation', 0, ...
'wavelength', 1310 ...
);
% filterParams.EqualizerParameters.diff_precode = int32(db_mode.no_db);
filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle);
% filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle);
% filterParams.EqualizerParameters.DCmu = 0.005;
selectedFields = {'Configurations.run_id' 'Runs.rx_raw_path' 'Configurations.bitrate' 'Configurations.symbolrate' 'Configurations.pam_level' 'Configurations.db_mode' 'Configurations.rop_attenuation' 'Configurations.is_mpi' 'Configurations.interference_attenuation' 'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'Measurements.power_pd_in' 'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.SNR' 'Results.GMI' 'Results.Alpha'};
% selectedFields = {'Configurations.run_id'};
[dataTable,sql_query] = database.queryDB(filterParams, selectedFields);

View File

@@ -5,85 +5,118 @@ precomp_mode = 2; %0=do nothing ; 1= measure; 2=precomp active
db_precode = 0;
db_coding_approach = 0;
fsym = 224e9;
fsym = 160e9;
fdac = 256e9;
random_key = 0;
M = 4;
pams = [4];
if (db_precode==1)&&(db_coding_approach==0)
cols = cbrewer2('Paired',6);
for i = 1:length(pams)
M = pams(i);
if (db_precode==1)&&(db_coding_approach==0)
if M == 4
pulsef=1;
precomp_amp_max = -50;
elseif M == 6
pulsef=0;
precomp_amp_max = -50;
elseif M == 8
pulsef=0;
precomp_amp_max = -50;
if M == 4
pulsef=1;
precomp_amp_max = -50;
fsym = 196e9;
elseif M == 6
pulsef=0;
precomp_amp_max = -50;
fsym = 180e9;
elseif M == 8
pulsef=0;
precomp_amp_max = -50;
fsym = 160e9;
end
elseif (db_precode==1)&&(db_coding_approach==1)
if M == 4
pulsef=1;
precomp_amp_max = -38;
pulsef = 1;
elseif M == 6
pulsef=0;
precomp_amp_max = -38;
pulsef = 1;
elseif M == 8
pulsef=0;
precomp_amp_max = -38;
pulsef = 1;
end
elseif (db_precode==0)&&(db_coding_approach==0)
if M == 4
pulsef=1;
precomp_amp_max = -37;
pulsef = 1;
fsym = 196e9;
elseif M == 6
pulsef=0;
precomp_amp_max = -34;
pulsef = 1;
fsym = 180e9;
elseif M == 8
pulsef=0;
precomp_amp_max = -34;
pulsef = 0;
fsym = 160e9;
end
end
elseif (db_precode==1)&&(db_coding_approach==1)
if M == 4
pulsef=1;
precomp_amp_max = -38;
pulsef = 1;
elseif M == 6
pulsef=0;
precomp_amp_max = -38;
pulsef = 1;
elseif M == 8
pulsef=0;
precomp_amp_max = -38;
pulsef = 1;
rcalpha = 0.05;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha);
Pamsource = PAMsource(...
"fsym",fsym,"M",M,"order",19,"useprbs",0,...
"fs_out",fdac,...
"applyclipping",0,"clipfactor",1.2,...
"applypulseform",pulsef,"pulseformer",Pform,...
"randkey",random_key,...
"db_precode",db_precode,"db_encode",db_coding_approach,...
"mrds_code",0,"mrds_blocklength",512);
[Digi_sig,Symbols,Bits] = Pamsource.process();
Digi_sig = Digi_sig.normalize("mode","rms");
precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',Digi_sig.fs);
% maxampdb = [-30:-3:-50,precomp_amp_max];
maxampdb = precomp_amp_max;%sort(maxampdb);
cols_ = cbrewer2('spectral',15);
for j = 1:length(maxampdb)
if maxampdb(j) == precomp_amp_max
color=clr.Set1.green;
else
color=cols_(j,:);
end
Digi_sig_pre = precomp_est.precomp(Digi_sig,'maxampdb',maxampdb(j),'loadPath',precomp_path,'fileName',precomp_fn);
% Digi_sig_pre = Digi_sig_pre.normalize("mode","rms");
Digi_sig_pre = Digi_sig_pre.resample("fs_out",fdac);
Digi_sig_pre= Digi_sig_pre.normalize("mode","rms");
Digi_sig_pre.spectrum("displayname","Strong Precomp","fignum",2223,"normalizeToNyquist",0,"normalizeTo0dB",0,"color",color,"linestyle",'-','addDCoffset',27);
end
elseif (db_precode==0)&&(db_coding_approach==0)
Digi_sig.spectrum("displayname","No Precomp","fignum",2223,"normalizeToNyquist",0,"normalizeTo0dB",0,"color",cols(2*i,:),"linestyle",'-','addDCoffset',27);
if M == 4
pulsef=1;
precomp_amp_max = -37;
pulsef = 1;
elseif M == 6
pulsef=0;
precomp_amp_max = -34;
pulsef = 1;
elseif M == 8
pulsef=0;
precomp_amp_max = -34;
pulsef = 0;
end
end
ylim([-25,10]);
xlim([0,105]);
xticks(0:20:110);
yticks(-30:10:10);
rcalpha = 0.05;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"rrcalpha",rcalpha);
Pamsource = PAMsource(...
"fsym",fsym,"M",M,"order",19,"useprbs",1,...
"fs_out",fdac,...
"applyclipping",0,"clipfactor",1.2,...
"applypulseform",pulsef,"pulseformer",Pform,...
"randkey",random_key,...
"db_precode",db_precode,"db_encode",db_coding_approach,...
"mrds_code",0,"mrds_blocklength",512);
[Digi_sig,Symbols,Bits] = Pamsource.process();
Digi_sig = Digi_sig.normalize("mode","rms");
Digi_sig.spectrum("displayname","No Precomp","fignum",2223,"normalizeToNyquist",0,"normalizeTo0dB",0);
precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',Digi_sig.fs);
Digi_sig = precomp_est.precomp(Digi_sig,'maxampdb',precomp_amp_max,'loadPath',precomp_path,'fileName',precomp_fn);
Digi_sig = Digi_sig.normalize("mode","rms");
Digi_sig = Digi_sig.resample("fs_out",fdac);
Digi_sig= Digi_sig.normalize("mode","rms");
Digi_sig.spectrum("displayname","Strong Precomp","fignum",2223,"normalizeToNyquist",0,"normalizeTo0dB",0);
fig = gcf;
pos = [536.3333 879 450 222];
set(fig, 'Position', pos);

View File

@@ -1,5 +1,5 @@
filename = "C:\Users\sioe\Documents\High_Speed_Measurement_2024\bias_5km\PAMX_5km_20241025_204334_wh.mat";
filename = "Z:\2024\sioe\High Speed Messungen Oktober\bias_5km\PAMX_5km_20241025_204334_wh.mat";
a = load(filename);
wh = a.obj;
@@ -26,11 +26,14 @@ clf
hold on
cols = cbrewer2('Set1',3);
for l = 1:numel(lambda_vals)
figure()
for m = 1:numel(M_vals)
ber_ffe = wh.getStoValue('ber_ffe',v_bias_vals,awg_vpp_vals(1),precomp_amp_max_vals(1),rop_atten_vals(1),M_vals(m),lambda_vals(l));
ber = wh.getStoValue('ber_collect',v_bias_vals,awg_vpp_vals(1),precomp_amp_max_vals(1),rop_atten_vals(1),M_vals(m),lambda_vals(l));
ber = wh.getStoValue('ber_ffe',v_bias_vals,awg_vpp_vals(1),precomp_amp_max_vals(1),rop_atten_vals(1),M_vals(m),lambda_vals(l));
exfo = wh.getStoValue('exfo',v_bias_vals,awg_vpp_vals(1),precomp_amp_max_vals(1),rop_atten_vals(1),M_vals(m),lambda_vals(l));
lb = wh.getStoValue('exfo',v_bias_vals,awg_vpp_vals(1),precomp_amp_max_vals(1),rop_atten_vals(1),M_vals(m),lambda_vals(l));
for e = 1:numel(exfo)
laser_pow(e) = exfo{e}.cur_power;
@@ -42,7 +45,7 @@ for l = 1:numel(lambda_vals)
rx_logbook = wh.getStoValue('rx_logbook',v_bias_vals(1),awg_vpp_vals(1),precomp_amp_max_vals(1),rop_atten_vals(1),M_vals(1),lambda_vals(1));
subplot(1,3,l)
hold on
a = scatter(v_bias_vals,min(ber,[],2),40,'LineWidth',2,'Marker','.','DisplayName',['PAM ',num2str(M_vals(m))],'MarkerEdgeColor',cols(m,:));
title([num2str(lambda_vals(l)),'nm'])
@@ -66,7 +69,7 @@ for l = 1:numel(lambda_vals)
% Polynomial fit (e.g., second-order polynomial)
[woutliers,n] = rmoutliers( min(ber,[],2) );
p = polyfit( v_bias_vals(~n), log10(woutliers), 4); % Adjust order as needed
p = polyfit( v_bias_vals(~n), log10(woutliers), 3); % Adjust order as needed
BER_fit = polyval(p, v_bias_vals);
@@ -93,9 +96,10 @@ for l = 1:numel(lambda_vals)
end
end
%%
filename = "C:\Users\sioe\Documents\High_Speed_Measurement_2024\bias_testing_and_b2b\PAM4_b2b_bias_sweep_20241023_191202_wh_BB_BIAS_FINAL.mat";
filename = "Z:\2024\sioe\High Speed Messungen Oktober\bias_testing_and_b2b\PAM4_b2b_bias_sweep_20241023_191202_wh_BB_BIAS_FINAL.mat";
a = load(filename);
wh = a.obj;

View File

@@ -1,13 +1,13 @@
basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\';
database = DBHandler("pathToDB",[basePath,'silas_labor.db']);
database = DBHandler("dataBase",[basePath,'silas_labor.db'],"type",'sqlite');
filterParams = database.tables;
filterParams.Configurations = struct( ...
'bitrate', [], ... %[224,336,360,390,420,448]
'db_mode', int32(db_mode.db_encoded), ...
'fiber_length', 10, ...
'bitrate', [390e9], ... %[224,336,360,390,420,448]
'db_mode', 1, ...
'fiber_length', 1, ...
'interference_attenuation', [], ...
'interference_path_length', [], ...
'is_mpi', 0, ...
@@ -18,11 +18,11 @@ filterParams.Configurations = struct( ...
% filterParams.EqualizerParameters.diff_precode = int32(db_mode.db_encoded);
% filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle);
filterParams.EqualizerParameters.DCmu = 0.00;
% filterParams.EqualizerParameters.DCmu = 0.00;
selectedFields = {'Configurations.run_id' 'Runs.rx_raw_path' 'Configurations.bitrate' 'Configurations.symbolrate' 'Configurations.pam_level'...
'Configurations.db_mode' 'Configurations.rop_attenuation' 'Configurations.is_mpi' 'Configurations.interference_attenuation' ...
'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'Measurements.power_pd_in' ...
'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'EqualizerParameters.DCmu' 'Measurements.power_pd_in' ...
'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.BER_precoded' 'Results.SNR' 'Results.GMI' 'Results.Alpha' 'Results.date_of_processing'};
[dataTable,sql_query] = database.queryDB(filterParams, selectedFields);

View File

@@ -1,11 +1,11 @@
basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\';
database = DBHandler("pathToDB",[basePath,'silas_labor.db']);
database = DBHandler("pathToDB",[basePath,'silas_labor.db'],"type",'sqlite');
filterParams = database.tables;
filterParams.Configurations = struct( ...
'bitrate', 450e9, ... %[224,336,360,390,420,448]
'bitrate', 420e9, ... %[224,336,360,390,420,448]
'db_mode', int32(db_mode.no_db), ...
'fiber_length', 10, ...
'interference_attenuation', [], ...

View File

@@ -15,7 +15,7 @@ if 1
wh.addStorage("ber");
% wh = submit_simulations(wh,"parallel",0,"simulation_mode",0);
wh = submit_handle(@imdd_model,wh,"parallel",1);
wh = submit_handle(@imdd_model,wh,"parallel",0);
end

View File

@@ -1,6 +1,6 @@
function [output] = imdd_model(varargin)
simulation_mode = 0;
simulation_mode = 1;
%%% Change folder
curFolder = pwd;
@@ -134,6 +134,7 @@ if fsym_ ~= fsym
fsym = fsym_;
% fprintf('Adapted symbolrate to %d GBd, to match provided bitrate of %d GBit/s using PAM %d \n',fsym.*1e-9,bitrate.*1e-9, M);
end
f_nyquist = fsym/2;
%%% run the simulation or measurement or ...

View File

@@ -1,6 +1,6 @@
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"rrcalpha",rcalpha);
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"rrcalpha",rcalpha);
[Digi_sig,Symbols,Tx_bits] = PAMsource(...
"fsym",fsym,"M",M,"order",19,"useprbs",1,...

View File

@@ -6,3 +6,11 @@ precomp_filename = "lab_high_speed";
freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',92e9);
freqresp.load('loadPath',precomp_path,'fileName',precomp_filename);
freqresp.plot();
freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',256e9);
precomp_path = "C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\HighSpeedExperiment_2024\Auswertung_JLT";
precomp_fn = "precomp_simulated.mat";
freqresp.load('loadPath',precomp_path,'fileName',precomp_fn);
freqresp.plot();

View File

@@ -1,28 +1,19 @@
measure = 0;
measure = 1;
freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",70,"f_ref",256e9);
%
Digi_sig = freqresp.buildOFDM();
Digi_sig.spectrum("fignum",1112,"displayname",['maxamp:',num2str(maxamp)]);
Digi_sig = Filter('filtdegree',3,"f_cutoff",70e9,"fs",256e9,"filterType",filtertypes.butterworth,"active",true).process(Digi_sig);
Digi_sig = Filter('filtdegree',3,"f_cutoff",70e9,"fs",256e9,"filterType",filtertypes.bessel_inp,"active",true).process(Digi_sig);
if measure
freqresp = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",70,"f_ref",256e9);
%
Digi_sig = freqresp.buildOFDM();
else
[Digi_sig,Symbols,Bits] = PAMsource("fsym",fsym,"M",M,"order",18,"useprbs",0,...
"fs_out",M8199.fdac,"applyclipping",1,"clipfactor",1.7,"applypulseform",1,"pulseformer",Pform,"randkey",pn_key,"mrds_code",usemrds,"mrds_blocklength",512).process();
end
freqresp.estimate(Digi_sig,"fileName",'','save',false);
Digi_sig.spectrum("fignum",1112,"displayname",['Signal']);
freqresp.plot()
maxamp = -1;
El_sig = freqresp.precomp(Digi_sig,"maxampdb",maxamp);
El_sig.spectrum("fignum",1112,"displayname",['maxamp:',num2str(maxamp)]);
El_sig = Filter('filtdegree',2,"f_cutoff",60e9,"fs",256e9,"filterType",filtertypes.butterworth,"active",true).process(El_sig);
if measure
freqresp.estimate(El_sig,"fileName",'','save',false);
end
El_sig.spectrum("fignum",1112,"displayname",['after filter; maxamp:',num2str(maxamp)]);
a = gca;
a.YTick = [-30,-20,-10,0];

View File

@@ -1,65 +1,49 @@
useprbs = 1;
M = 6;
randkey = 1;
datarate = 224e9;
fsym = round(datarate / log2(M)) ;
apply_precode = 1;
db_pre = 1;
bitpattern = [];
s = RandStream('twister','Seed',1);
for i = 1:log2(M)
N = 2^(17-1); %length of prbs
bitpattern(:,i) = randi(s,[0 1], N, 1);
end
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"rrcalpha",0.05);
if M == 6
bitpattern = reshape(bitpattern',[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
[d,Symbols,Bits] = PAMsource(...
"fsym",fsym,"M",M,"order",17,"useprbs",1,...
"fs_out",fsym,...
"applyclipping",0,"clipfactor",1.5,...
"applypulseform",0,"pulseformer",Pform,...
"randkey",1,...
"db_precode",db_pre,"db_encode",0,...
"mrds_code",0,"mrds_blocklength",512).process();
bits = Informationsignal(bitpattern);
%%%CHANNEL
symbols = PAMmapper(M,0).map(bits);
% s = RandStream('twister','Seed',2);
% start = 10000;
% burstwidth = 100;
% d_burst = d;
% for pos = start:start+burstwidth
% lvls = 1.5 .* PAMmapper(M,0).levels / rms(PAMmapper(M,0).levels);
% d_burst.signal(pos) = d.signal(pos)+randn(s,1,1);
% end
bits_rx = PAMmapper(M,0).demap(symbols);
[~,~,ber_direct,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
d_resample = d.resample("fs_out",2.*fsym);
if apply_precode
symbols_tx = Duobinary().precode(symbols);
else
symbols_tx = symbols;
end
eq_ffe = FFE("epochs_tr",5,"epochs_dd",5,"len_tr",1024,"mu_dd",0.0004,"mu_tr",0,"order",25,"sps",2,"decide",1);
d_eq = eq_ffe.process(d_resample,Symbols);
show2Dconstellation(symbols_tx,symbols_tx,"displayname",'VNLE Out','fignum',2241);
% s = RandStream('twister','Seed',2);
% start = 10000;
% burstwidth = 100;
% d_burst = d_eq;
% for pos = start:start+burstwidth
% lvls = 1.5 .* PAMmapper(M,0).levels / rms(PAMmapper(M,0).levels);
% d_burst.signal(pos) = d_eq.signal(pos)+randn(s,1,1);
% end
%
% d_burst = PAMmapper(M,0).decide_pamlevel(d_burst);
if db_pre
if apply_precode
% Entschiedene Symbole codieren: d_DB(n) = d(n) + d(n-1) (im Fall von PAM4 7 level [0 1 2 3 4 5 6])
d_db = Duobinary().encode(d);
symbols_db = Duobinary().encode(symbols_tx);
% Entschiedene codierte Symbole decodieren: d_dec(n) = d_DB(n) mod4
d_dec = Duobinary().decode(d_db);
symbols_rx = Duobinary().decode(symbols_db);
else
d_dec = d_burst;
symbols_rx = symbols_tx;
end
% Vergleichen von b(n) und d_dec(n)
Rx_bits = PAMmapper(M,0).demap(d_dec);
Tx_bits = Bits;
[~,error_num,ber,error_pos] = calc_ber(Tx_bits.signal,Rx_bits.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
bits_rx = PAMmapper(M,0).demap(symbols_rx);
[~,~,ber,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",10,"skip_end",10,"returnErrorLocation",1);
disp(['BER: ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]);

View File

@@ -46,66 +46,55 @@ end
Tx_bits = Informationsignal(bitpattern);
%%%%% Duobinary %%%%%%
precode = 1;
db_encode = 0;
close all
Symbols_tx = PAMmapper(M,0).map(Tx_bits);
Symbols_tx = PAMmapper(M,0,"eth_style",0).map(Tx_bits);
Symbols_tx.fs = fsym;
precode = db_mode.db_precoded;
%%% precode
if precode
Symbols0 = Duobinary().precode(Symbols_tx);
else
Symbols0 = Symbols_tx;
switch precode
case db_mode.db_precoded
Symbols_tx = Duobinary().precode(Symbols_tx);
case db_mode.db_encoded
Symbols_tx = Duobinary().precode(Symbols_tx);
Symbols_tx = Duobinary().encode(Symbols_tx);
case db_mode.no_db
end
figure;histogram(Symbols0.signal);
for n = 10
for n = 0:200
Symbols_rx = Symbols_tx;
if db_encode
Symbols1 = Duobinary().encode(Symbols0);
else
Symbols1 = Symbols0;
end
Symbols2 = Symbols1;
pos = 1;
if n~=0
for pos = 1:n
po = randi(100);
a = Symbols2.signal(100+pos) == Symbols1.signal(100+po);
a = Symbols_rx.signal(100+pos) == Symbols_tx.signal(100+po);
while a == 1
po = po+1;
po = randi(100);
a = Symbols2.signal(100+pos) == Symbols1.signal(100+po);
a = Symbols_rx.signal(100+pos) == Symbols_tx.signal(100+po);
end
Symbols2.signal(100+pos) = Symbols1.signal(100+po);
Symbols_rx.signal(100+pos) = Symbols_tx.signal(100+po);
end
end
% disp(Symbols2.signal(100:100+pos)==Symbols1.signal(100:100+pos))
error_positions = ~(Symbols_rx.signal == Symbols_tx.signal);
error_positions = find(error_positions==1);
%%% encode
switch precode
case db_mode.db_precoded
Symbols_rx = Duobinary().encode(Symbols_rx);
Symbols_rx = Duobinary().decode(Symbols_rx);
case db_mode.db_encoded
Symbols_rx = Duobinary().decode(Symbols_rx);
if db_encode
% figure;histogram(Symbols2.signal);
Symbols3 = Duobinary().decode(Symbols2);
% figure;histogram(Symbols3.signal);
% autoArrangeFigures;
elseif precode
Symbols3 = Duobinary().encode(Symbols2);
Symbols3 = Duobinary().decode(Symbols3);
% figure;histogram(Symbols3.signal);
else
Symbols3 = Symbols2;
end
Rx_bits = PAMmapper(M,0).demap(Symbols3);
Rx_bits = PAMmapper(M,0).demap(Symbols_rx);
%%%%% Check BER of Bit Sequence %%%%%%
@@ -113,14 +102,10 @@ for n = 0:200
% disp(['BER: ',sprintf('%.1E',ber),sprintf(' - Num. Err: %.1d',error_num(n+1)-2),' - - PAM-',num2str(M)]);
fprintf('n: %d - Num. Err: %.1d \n',n,error_num(n+1));
end
figure()
hold on
scatter(1:length(Symbols3),Symbols3.signal,1,'.');
scatter(error_pos,Symbols3.signal(error_pos),14,'o');
figure(3);
clf
@@ -128,8 +113,8 @@ clf
subplot(2,2,1)
hold on
title('First Bits')
stairs(Tx_bits.signal(1:100,1),'LineStyle','-','LineWidth',2,'DisplayName','Tx Bits');
stairs(Rx_bits.signal(1:100,1),'LineWidth',2,'DisplayName','Rx Bits','LineStyle',':')
stairs(Tx_bits.signal(100:150,1),'LineStyle','-','LineWidth',2,'DisplayName','Tx Bits');
stairs(Rx_bits.signal(100:150,1),'LineWidth',2,'DisplayName','Rx Bits','LineStyle',':')
legend
subplot(2,2,2)
@@ -143,12 +128,12 @@ subplot(2,2,3)
hold on
title('First Symbols Compare')
stairs(Symbols_tx.signal(1:100,1),'LineWidth',2,'DisplayName','Tx Symbols','LineStyle','-')
stairs(Symbols.signal(1:100,1),'LineStyle',':','LineWidth',2,'DisplayName','Rx Symbols');
stairs(Symbols_rx.signal(1:100,1),'LineStyle',':','LineWidth',2,'DisplayName','Rx Symbols');
legend
subplot(2,2,4)
hold on
title('Last Symbols Compare')
stairs(Symbols_tx.signal(end-50:end,1),'LineWidth',2,'DisplayName','Tx Symbols','LineStyle','-')
stairs(Symbols.signal(end-50:end,1),'LineStyle',':','LineWidth',2,'DisplayName','Rx Symbols');
stairs(Symbols_rx.signal(end-50:end,1),'LineStyle',':','LineWidth',2,'DisplayName','Rx Symbols');
legend