# 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 properties
nase nase
lambda lambda
polrot
end end
@@ -18,6 +19,7 @@ classdef Opticalsignal < Signal
options.logbook options.logbook
options.lambda options.lambda
options.nase options.nase
options.polrot
end end
obj = obj@Signal(signal); obj = obj@Signal(signal);

View File

@@ -108,6 +108,7 @@ classdef Signal
options.logbook options.logbook
options.nase options.nase
options.lambda options.lambda
options.polrot
end end
fn = fieldnames(options); fn = fieldnames(options);
@@ -122,7 +123,7 @@ classdef Signal
%convert to optical %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') elseif isa(obj,'Informationsignal')
@@ -141,7 +142,7 @@ classdef Signal
arguments arguments
obj obj
options.fignum options.fignum = randi(1000)
options.displayname = []; options.displayname = [];
options.timeframe = 0; options.timeframe = 0;
options.clear = 0; options.clear = 0;
@@ -269,6 +270,7 @@ classdef Signal
SignalPower = [obj.power]; SignalPower = [obj.power];
Nase = [0]; Nase = [0];
SignalCopy = obj.signal; SignalCopy = obj.signal;
SignalCopy = [];
ModifierName = class(CallingModifier); ModifierName = class(CallingModifier);
@@ -342,83 +344,151 @@ classdef Signal
arguments arguments
obj obj
options.fignum options.fignum = 2025
options.displayname = ""; options.displayname = "";
options.color = []; options.color = [];
options.linestyle = '-';
options.normalizeToNyquist = 0; options.normalizeToNyquist = 0;
options.addDCoffset = 0;
options.normalizeToDC = 0;
options.normalizeTo0dB = 0; options.normalizeTo0dB = 0;
options.max_num_lines = []; % Leave empty or omit to disable line rotation options.max_num_lines = []; % Leave empty or omit to disable line rotation
options.fft_length = []; 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 end
if isempty(options.fft_length) if isempty(options.fft_length)
options.fft_length = 2^(nextpow2(length(obj.signal))-9); options.fft_length = 2^(nextpow2(length(obj.signal))-9);
end end
if options.normalizeToNyquist == 0 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"); [p_lin,f_Hz] = pwelch(obj.signal, hanning(options.fft_length), ...
w = w.*1e-9; 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 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 end
% p_lin = movmean(p_lin,4);
if options.normalizeTo0dB 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 p_dbm = 10*log10(p_lin); % normalized to 0 dB
ylab = "normalized to 0 dB"; ylab = "Normalized PSD";
else else
p_dbm = 10*log10(p_lin); p_dbm = 10*log10(p_lin);
ylab = "Power (dB/Hz)"; ylab = "Power (dB/Hz)";
end 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); figure(options.fignum);
ax = gca; ax = gca;
hold on hold on
if isempty(options.color) p_dbm = p_dbm+options.addDCoffset;
hLine = plot(w,p_dbm,'DisplayName',options.displayname,'LineWidth',1);
else if options.normalizeToDC
hLine = plot(w,p_dbm,'DisplayName',options.displayname,'LineWidth',1,'Color',options.color); [~,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 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 if ~isempty(options.max_num_lines) && options.max_num_lines > 0
allLines = findall(ax, 'Type', 'Line'); allLines = findall(ax, 'Type', 'Line');
if length(allLines) > options.max_num_lines 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; numToRemove = length(allLines) - options.max_num_lines;
delete(allLines(1:numToRemove)); delete(allLines(1:numToRemove));
end end
end end
if options.normalizeToNyquist == 0 % Axis labels and limits
xlabel("Frequency in GHz"); xlabel(x_label);
edgetick = 2^(nextpow2(obj.fs*1e-9));
xticks(-edgetick:16:edgetick); if options.useWavelengthAxis && options.normalizeToNyquist == 0
xlim([100*round(min(w)/100,1)-10, 100*round(max(w)/100,1)+10]) xlim([min(x_vec) max(x_vec)]);
xlim([-128 128]);%256GSa/s
else else
xlabel("Normalized Frequency"); if options.normalizeToNyquist == 0
xlim([-pi, pi]); % 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 end
ylabel(ylab); ylabel(ylab);
try % --- Y-Axis scaling (auto with margin) ---
ylim([max(min(floor(min(p_dbm))-3, ax.YLim(1)),-40), min(max(ceil(max(p_dbm))+3, ax.YLim(2)),10)]); y_min = min(p_dbm(:));
catch y_max = max(p_dbm(:));
ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]);
% 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 end
ylim([floor(min(p_dbm))-3, ceil(max(p_dbm))+3]);
yticks(-200:10:10); y_margin = 0.05 * y_range;
grid on; grid minor; ylim([y_min - y_margin, y_max + y_margin]);
legend
% legend('Interpreter','none'); % 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 end
function move_it_spectrum(obj,options) function move_it_spectrum(obj,options)
arguments arguments
obj obj
@@ -546,7 +616,7 @@ classdef Signal
options.unit power_notation = power_notation.dBm options.unit power_notation = power_notation.dBm
end end
pow = mean(abs(obj.signal).^2); pow = sum(mean(abs(obj.signal).^2));
switch options.unit switch options.unit
case power_notation.dBm case power_notation.dBm
@@ -693,10 +763,10 @@ classdef Signal
return return
end end
if mean(w) > 10 pkpos = sort(pkpos);
warning('No sync possible, check code of findpeaks! Look at correlation')
return
if mean(w) > 10 || mean(p) > 10
return
else else
sequenceFound = 1; sequenceFound = 1;
end end
@@ -711,7 +781,7 @@ classdef Signal
shifts = lags(pkpos); shifts = lags(pkpos);
sequenceStarts = shifts; sequenceStarts = shifts;
% shifts = shifts(shifts>=0); shifts = shifts(shifts>=0);
if numel(shifts) > 0 if numel(shifts) > 0
@@ -722,17 +792,17 @@ classdef Signal
for c = shifts for c = shifts
sig = obj.delay(-c,'mode','samples'); 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; S{end+1,1} = sig;
end end
%return/keep the sinal with the highest correlation (only within positive shifts) % %return/keep the sinal with the highest correlation (only within positive shifts)
[~,idx]=max(pks); % [~,idx]=max(pks);
obj.signal = S{idx}.signal; % obj.signal = S{idx}.signal;
%put signal with highest corr. to first index in S array % %put signal with highest corr. to first index in S array
swap = S{1}; % swap = S{1};
S{1} = S{idx}; % S{1} = S{idx};
S{idx} = swap; % S{idx} = swap;
for c = 1:numel(shifts) for c = 1:numel(shifts)
S{c}.logbook = []; S{c}.logbook = [];
@@ -762,10 +832,6 @@ classdef Signal
end end
%% %%
function obj = filter(obj,a,b) function obj = filter(obj,a,b)
@@ -912,11 +978,11 @@ classdef Signal
elseif mode == 1 elseif mode == 1
% generate eye diagram using histogram % generate eye diagram using histogram
maxA = max(sig(100:end-100))*2; maxA = max(sig(100:end-100))*1.3;
minA = min(sig(100:end-100))*2; minA = min(sig(100:end-100))*1.3;
% maxA = 0.0015; maxA = 0.12;
% minA = 0; minA = -0.08;
difference= maxA-minA; difference= maxA-minA;
@@ -927,7 +993,7 @@ classdef Signal
hist_data(:,n)=flip(nn.'); %without flip, the eye is upside down :-( hist_data(:,n)=flip(nn.'); %without flip, the eye is upside down :-(
end end
ax = gca;
plot_data = 20*log10(hist_data); plot_data = 20*log10(hist_data);
plot_data(plot_data==-Inf) = 0; plot_data(plot_data==-Inf) = 0;
@@ -944,27 +1010,29 @@ classdef Signal
if isa(obj,'Opticalsignal') if isa(obj,'Opticalsignal')
title(['Optical Eye ',options.displayname]) title(['Optical Eye ',options.displayname])
ylabel("Power in mW"); 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); min_ = min(abs(obj.signal(100:end-100)).^2);
max_ = abs(max(obj.signal(100:end-100)).^2); max_ = abs(max(obj.signal(100:end-100)).^2);
elseif isa(obj,'Electricalsignal') elseif isa(obj,'Electricalsignal')
title(['Electrical Eye ',options.displayname]) title(['Electrical Eye ',options.displayname])
ylabel("Voltage in V"); 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)); min_ = min(obj.signal(100:end-100));
max_ = abs(max(obj.signal(100:end-100))); max_ = abs(max(obj.signal(100:end-100)));
else else
title(['Digital Eye ',options.displayname]) title(['Digital Eye ',options.displayname])
ylabel("Digital Signal Amplitude"); 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)); min_ = min(obj.signal(100:end-100));
max_ = abs(max(obj.signal(100:end-100))); max_ = abs(max(obj.signal(100:end-100)));
end end
xlabel('Time in ps') xlabel('Time in ps')
% add information % add information
if 1 if 0
pwr_dbm = round(obj.power,3); pwr_dbm = round(obj.power,3);
pwr_lin = obj.power("unit",power_notation.W); pwr_lin = obj.power("unit",power_notation.W);
@@ -1073,18 +1141,20 @@ classdef Signal
end end
yticks(linspace(0,histpoints,16)); grid off
y_tickstring = sprintfc('%.2f', y_tickstring);
yticklabels(y_tickstring);
xticks(linspace(0,histpoints_horizontal,8))
x_tickstring = sprintfc('%.2f', linspace(0, 2/fsym, 8) .* 1e12);
xticklabels(x_tickstring);
end 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 end
% disp('h'); % disp('h');

View File

@@ -139,6 +139,8 @@ classdef ChannelFreqResp < handle
fnew = linspace(0,fstarget/2,length(Target)/2+1); fnew = linspace(0,fstarget/2,length(Target)/2+1);
fnew = fnew(2:end-1); fnew = fnew(2:end-1);
% Old frequency axis (should be much coarser) % 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 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 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) % 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'); 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 % set all NaN values to the fist/ last non-NaN value
nH = find(~isnan(iH),1,'first'); nH = find(~isnan(iH),1,'first');
iH(1:nH)=iH(nH); iH(1:nH)=iH(nH);
@@ -171,6 +177,8 @@ classdef ChannelFreqResp < handle
% five frequencies -> should be the vaue at f=0=DC component? % five frequencies -> should be the vaue at f=0=DC component?
iH = iH./mean(abs(iH)); %why 1:5?? iH = iH./mean(abs(iH)); %why 1:5??
dc = 20*log10(abs(mean(abs(iH(1:5)))));
% set maximum amplification % set maximum amplification
% set als values higher than hmax to hmax and keep the % set als values higher than hmax to hmax and keep the
% phase information by multiplication with respective % phase information by multiplication with respective
@@ -206,7 +214,7 @@ classdef ChannelFreqResp < handle
function plot(obj) function plot(obj)
figure(55); figure();
clf; clf;
Havg = obj.H; Havg = obj.H;
@@ -225,7 +233,7 @@ classdef ChannelFreqResp < handle
xlim([0.2 .5*max(obj.faxis)*1e-9]); xlim([0.2 .5*max(obj.faxis)*1e-9]);
grid on; grid on;
figure(56); figure();
clf; clf;
@@ -246,7 +254,7 @@ classdef ChannelFreqResp < handle
xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on; xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on;
%%% plot for publication %%% plot for publication
figure(1234); figure(101);
hold all; hold all;
box on; box on;
title('Magnitude Freq. Response'); title('Magnitude Freq. Response');
@@ -269,6 +277,25 @@ classdef ChannelFreqResp < handle
legend('Interpreter','latex') legend('Interpreter','latex')
grid on; 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 end

View File

@@ -17,7 +17,7 @@ classdef M8199B < AWG
fdac = 256e9; 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,... 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"); "bit_resolution",5.5,"normalize2dac",1,"upsampling_method","samplehold");

View File

@@ -29,7 +29,7 @@ classdef PAMmapper
obj.levels = obj.get_levels(); 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; obj.eth_style = options.eth_style;
@@ -45,7 +45,7 @@ classdef PAMmapper
signal_in = signal_in.logbookentry(lbdesc,obj); signal_in = signal_in.logbookentry(lbdesc,obj);
out = signal_in; out = signal_in;
else else
out = signal_in; out = obj.map_(signal_in);
end end
end end
@@ -283,6 +283,21 @@ classdef PAMmapper
end end
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) function [data_out] = demap_(obj,data_in)
data_in= data_in'; data_in= data_in';
@@ -328,8 +343,9 @@ classdef PAMmapper
if ~obj.eth_style 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(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 if size(data_in,2) > 1
data_in = data_in.'; data_in = data_in.';
@@ -449,7 +465,7 @@ classdef PAMmapper
function [Signal_out] = quantize(obj,Signal_in) function [Signal_out] = quantize(obj,Signal_in)
constellation = obj.get_levels(); constellation = obj.get_levels();
constellation = constellation ./ rms(constellation); constellation = constellation ./ obj.scaling;
issignalclass = 0; issignalclass = 0;
if isa(Signal_in,'Signal') if isa(Signal_in,'Signal')
@@ -480,7 +496,9 @@ classdef PAMmapper
end end
function bitmap = showBitMapping(obj) function bitmap = showBitMapping(obj)
bitmap = obj.demap([obj.levels ./ obj.scaling]');
bitmap = obj.demap((obj.levels ./ obj.scaling)');
end end
end end

View File

@@ -136,6 +136,13 @@ classdef PAMsource
%%%%%% Duobinary %%%%%%%%%%% %%%%%% 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 switch obj.duobinary_mode
case db_mode.no_db case db_mode.no_db
@@ -164,7 +171,7 @@ classdef PAMsource
%%%%% Pulse-forming %%%%%% %%%%% Pulse-forming %%%%%%
if obj.applypulseform if obj.applypulseform
%%% MY CODE %%% MY CODE
if 0 if 1
digi_sig = obj.pulseformer.process(symbols); digi_sig = obj.pulseformer.process(symbols);
else else

View File

@@ -108,7 +108,7 @@ classdef Amplifier
if obj.gain_mode == gain_mode.output_power if obj.gain_mode == gain_mode.output_power
%get linear gain for output power mode %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 pow_out = 10^(obj.amplification_db/10 - 3) ; % dBm to lin
a_lin = sqrt(pow_out/pow_in) ; a_lin = sqrt(pow_out/pow_in) ;

View File

@@ -70,7 +70,7 @@ classdef EML
[signalclass_in.signal,obj] = obj.process_(signalclass_in.signal); [signalclass_in.signal,obj] = obj.process_(signalclass_in.signal);
% cast the inform. signal to electrical 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 % 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) ]; 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 classdef Fiber
%FIBER Summary of this class goes here % Fiber: Simulate optical fiber signal propagation using
% Detailed explanation goes here % the split-step Fourier method (SSFM).
properties properties
% Simulation sampling frequency [Hz]
fsimu fsimu
% Fiber length [m]
fiber_length fiber_length
% Attenuation coefficient [dB/km]
alpha alpha
% Dispersion parameter D [s/m^2]
D D
% Dispersion slope [s/m^3]
Dslope Dslope
% Reference wavelength [m]
lambda0 lambda0
% Nonlinear coefficient [1/(W·m)]
gamma gamma
% Maximum allowed nonlinear phase change per step [rad]
dphimax dphimax
% Derived parameters:
% Second-order dispersion coefficient β2 [s^2/m]
b2 b2
% Third-order dispersion coefficient β3 [s^3/m]
b3 b3
% Linear attenuation constant [1/m]
alpha_lin alpha_lin
% Frequency-domain linear operator per unit length
linstep linstep
end end
methods methods
function obj = Fiber(options) function obj = Fiber(options)
%FIBER Construct an instance of this class % Constructor: initialize fiber physical and simulation parameters.
% Detailed explanation goes here
arguments arguments
options.fsimu options.fsimu % Sampling frequency [Hz]
options.fiber_length = 0 options.fiber_length = 0 % Fiber length [km]
options.alpha = 0.2 options.alpha = 0.2 % Attenuation [dB/km]
options.D = 17 options.D = 17 % Dispersion D parameter [ps/(nm·km)]
options.Dslope = 0.06 options.Dslope = 0.06 % Dispersion slope [ps/(nm^2·km)]
options.lambda0 = 1550 options.lambda0 = 1550 % Reference wavelength [nm]
options.gamma = 0 options.gamma = 0 % Nonlinear coefficient [1/(W·km)]
options.dphimax = 5e-3 options.dphimax = 5e-3 % Max phase step [rad]
end end
obj.fsimu = options.fsimu; % Assign inputs to object properties, converting units to SI.
obj.fiber_length = options.fiber_length*1000; %km obj.fsimu = options.fsimu;
obj.alpha = options.alpha; obj.fiber_length = options.fiber_length * 1e3; % km m
obj.D = options.D*1e-6; obj.alpha = options.alpha;
obj.Dslope = options.Dslope*1e3; obj.D = options.D * 1e-6; % ps/(nm·km) s/(m·m)
obj.lambda0 = options.lambda0*1e-9; obj.Dslope = options.Dslope * 1e3; % ps/(nm^2·km) s/m^2
obj.gamma = options.gamma; obj.lambda0 = options.lambda0 * 1e-9; % nm m
obj.dphimax = options.dphimax; obj.gamma = options.gamma; % 1/(W·km) (assumed 1/(W·m) internally)
obj.dphimax = options.dphimax;
end 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.) % Run internal split-step propagation
signalclass_in = obj.process_(signalclass_in); signalclass = obj.process_(signalclass_in);
% append to logbook % Add entry to logbook for tracking
lbdesc = 'Fiber '; signalclass = signalclass.logbookentry('Fiber ');
signalclass_in = signalclass_in.logbookentry(lbdesc);
% write to output
signalclass_out = signalclass_in;
% Return processed signal class
signalclass_out = signalclass;
end end
function opt_sig = process_(obj,opt_sig) function opt_sig = process_(obj, opt_sig)
%METHOD1 Summary of this method goes here % process_: Internal routine for one-step fiber propagation.
% Detailed explanation goes here % 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; lambda_signal = opt_sig.lambda;
obj.D = obj.D + (lambda_signal-obj.lambda0)*obj.Dslope; % Update dispersion parameter D for current wavelength
obj.D = obj.D + (lambda_signal - obj.lambda0) * obj.Dslope;
obj.b2 = -obj.D*lambda_signal^2/(2*pi*Constant.LightSpeed); % Compute dispersion coefficients (β2, β3)
obj.b3 = ((lambda_signal.^2/(2*pi*Constant.LightSpeed)).^2*obj.Dslope); obj.b2 = -obj.D * lambda_signal^2 / (2 * pi * Constant.LightSpeed);
obj.alpha_lin = obj.alpha/10*log(10)/1000; 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); N = length(signal);
faxis = linspace(-obj.fsimu/2,obj.fsimu/2,N+1); faxis = linspace(-obj.fsimu/2, obj.fsimu/2, N+1);
faxis = ifftshift(faxis(:,1:end-1)); faxis = faxis(1:end-1); % drop redundant endpoint
faxis = faxis'; faxis = ifftshift(faxis)'; % center zero frequency
obj.linstep = -obj.alpha_lin/2 - 2*1j*pi^2*obj.b2*faxis.^2 - 4/3*1j*pi^3*obj.b3*faxis.^3; % Define linear operator per meter in frequency domain
obj.linstep = -obj.alpha_lin/2 ... % half-step loss
if 0 - 1j*2*pi^2*obj.b2 .* faxis.^2 ... % second-order dispersion
H = exp((obj.linstep)*obj.fiber_length); - 1j*(4/3)*pi^3*obj.b3 .* faxis.^3; % third-order dispersion
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
% Choose linear-only or nonlinear SSFM based on gamma
if obj.gamma ~= 0 if obj.gamma ~= 0
% Full adaptive SSFM with nonlinear Schrödinger solver
opt_out = obj.NLSE(signal); opt_out = obj.NLSE(signal);
else 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 end
%TODO: attenuate nase ... % Update output field in signal class
opt_sig.signal = opt_out; opt_sig.signal = opt_out;
end end
function [yout] = NLSE(obj, xin) function yout = NLSE(obj, xin)
% NLSE: Solve nonlinear Schrödinger equation by adaptive split-step
maxPow = obj.gamma.*max(abs(xin).^2); % xin: input time-domain field
% Returns yout: output time-domain field after propagation
Leff = obj.dphimax / maxPow ;
dz = Leff;
% Initial estimate of effective length per step
maxPow = obj.gamma * max(abs(xin).^2);
Leff = obj.dphimax / maxPow;
dz = Leff;
z_prop = 0; z_prop = 0;
yout = fft(xin); % Apply initial half-step linear operator
yout = fft(xin) .* exp(obj.linstep * dz/2);
yout = ((yout).*exp(obj.linstep*dz/2));
% Loop until full fiber length is reached
while true while true
% Inverse FFT to time domain for nonlinear phase shift
yout = ifft(yout); yout = ifft(yout);
Leff = dz; % Nonlinear phase rotation per segment
power = abs(yout).^2; power = abs(yout).^2;
Hnl = exp( -1j*obj.gamma*power*Leff); Hnl = exp(-1j * obj.gamma * power * dz);
yout = yout .* Hnl; yout = yout .* Hnl;
% Accumulate propagation distance
z_prop = z_prop + dz; z_prop = z_prop + dz;
maxPow = obj.gamma*max(abs(yout).^2); % Compute adaptive step for next interval
Leff = obj.dphimax/maxPow; maxPow = obj.gamma * max(abs(yout).^2);
dz_new = obj.dphimax / maxPow;
dz_new = Leff;
% If remaining length shorter than new step, finish loop
if z_prop + dz_new > obj.fiber_length if z_prop + dz_new > obj.fiber_length
dz_new = obj.fiber_length - z_prop; dz_new = obj.fiber_length - z_prop;
break break;
end end
yout = fft(yout); % Half-step linear operator bridging segments
yout = fft(yout) .* exp(obj.linstep * ((dz/2) + (dz_new/2)));
yout = ((yout).*exp(obj.linstep*(dz/2+dz_new/2))); dz = dz_new;
dz = dz_new;
end end
yout = fft(yout); % Final propagation segment: combine half-steps and nonlinear
yout = fft(yout) .* exp(obj.linstep * ((dz/2) + (dz_new/2)));
yout = ((yout).*exp(obj.linstep*(dz/2+dz_new/2)));
yout = ifft(yout); yout = ifft(yout);
Leff = dz; % Last nonlinear phase shift
power = abs(yout).^2; power = abs(yout).^2;
Hnl = exp(-1j * obj.gamma * power * dz_new);
yout = yout .* Hnl;
Hnl = exp( -1j*obj.gamma*power*Leff); % Final half-step linear operator to complete SSFM
yout = fft(yout) .* exp(obj.linstep * (dz_new/2));
yout = yout .* Hnl;
yout = fft(yout);
yout = ((yout).*exp(obj.linstep*(dz/2)));
yout = ifft(yout); yout = ifft(yout);
end end
end end
end end

View File

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

View File

@@ -3,24 +3,34 @@ classdef FFE < handle
% 1) Training mode (stable performance when you use NLMS) % 1) Training mode (stable performance when you use NLMS)
% 2) Decision directed mode % 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 properties
sps % usually 2 sps % usually 2
order order
e e
e_tr
error error
debug_struct
len_tr len_tr
mu_tr mu_tr
epochs_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 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 end
methods methods
@@ -34,6 +44,8 @@ classdef FFE < handle
options.mu_tr = 0; options.mu_tr = 0;
options.epochs_tr = 5; options.epochs_tr = 5;
options.adaption_technique adaption_method = adaption_method.lms;
options.dd_mode = 1;
options.mu_dd = 1e-5; options.mu_dd = 1e-5;
options.epochs_dd = 5; options.epochs_dd = 5;
@@ -59,10 +71,15 @@ classdef FFE < handle
obj.constellation = unique(D.signal); obj.constellation = unique(D.signal);
delta = 0.05;
obj.P = (1/delta) * eye(obj.order);
% Training Mode % Training Mode
training = 1; training = 1;
showviz = 0; showviz = 0;
obj.equalize(X.signal, D.signal,obj.mu_tr,obj.epochs_tr,obj.len_tr,training,showviz); 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 % Decision Directed Mode
n = X.length; n = X.length;
@@ -87,13 +104,13 @@ classdef FFE < handle
end 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 arguments
obj obj
x x
d d
mio mu
epochs epochs
N N
training training
@@ -101,6 +118,7 @@ classdef FFE < handle
end end
x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)]; x = [zeros(floor(obj.order/2),1); x; zeros(obj.order,1)];
lambda = mu;
if training if training
mask = ones(obj.order,1); mask = ones(obj.order,1);
@@ -111,7 +129,16 @@ classdef FFE < handle
end end
mask = ones(obj.order,1); 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 for epoch = 1 : epochs
symbol = 0; symbol = 0;
@@ -126,28 +153,73 @@ classdef FFE < handle
if training if training
d_hat(symbol,1) = d(symbol); d_hat(symbol,1) = d(symbol);
else else
[~,symbol_idx] = min(abs(y(symbol) - obj.constellation)); % decision for closest constellation point if ~always_ideal_decision
d_hat(symbol,1) = obj.constellation(symbol_idx); [~,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 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 true_err(symbol) = y(symbol) - d(symbol); % Instantaneous error
if mio ~= 0 if training || obj.dd_mode
obj.e = obj.e - (mio * err(symbol) * U) ; % Weight update rule of LMS switch obj.adaption_technique
else
normalizationfactor = (U.' * U); case adaption_method.lms
obj.e = obj.e - err(symbol) * U / normalizationfactor; % Weight update rule of NLMS
% 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 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
end end
end end

View File

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

View File

@@ -60,7 +60,7 @@ classdef FFE_DFE < handle
end end
function [X] = process(obj, X, D) function [X,Noi] = process(obj, X, D)
% actual processing of the signal (steps 1. - 3.) % actual processing of the signal (steps 1. - 3.)
% 1 normalize RMS % 1 normalize RMS
@@ -89,6 +89,9 @@ classdef FFE_DFE < handle
lbdesc = [num2str(obj.ffe_order),' tap FFE']; lbdesc = [num2str(obj.ffe_order),' tap FFE'];
X = X.logbookentry(lbdesc); % append to logbook X = X.logbookentry(lbdesc); % append to logbook
Noi = X;
Noi = X - D;
end end
@@ -185,8 +188,6 @@ classdef FFE_DFE < handle
obj.e = coeff(1:obj.ffe_order); obj.e = coeff(1:obj.ffe_order);
obj.b = coeff(obj.ffe_order+1:end); obj.b = coeff(obj.ffe_order+1:end);
end end
end end

View File

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

View File

@@ -6,6 +6,10 @@ classdef MLSE < handle
DIR DIR
trellis_states trellis_states
duobinary_output duobinary_output
trellis_state_mode
trellis_exclusion
debug
scale_mode
end end
methods (Access=public) methods (Access=public)
@@ -19,7 +23,10 @@ classdef MLSE < handle
options.DIR double = [1]; options.DIR double = [1];
options.trellis_states double = [-3 -1 1 3]; options.trellis_states double = [-3 -1 1 3];
options.duobinary_output logical = false; options.duobinary_output logical = false;
options.trellis_state_mode = 2;
options.trellis_exclusion = 0;
options.scale_mode = 2;
options.debug = 0;
end end
% %
@@ -29,29 +36,65 @@ classdef MLSE < handle
obj.(fn{n}) = options.(fn{n}); obj.(fn{n}) = options.(fn{n});
end end
end end
% do more stuff
end 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; data_in = signalclass.signal;
shape_in = size(data_in);
data_ref = ref_symbolclass.signal; 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 = signalclass;
signalclass_hd.signal = data_out_hd; signalclass_hd.signal = data_out_hd;
signalclass_sd = signalclass; % signalclass_sd = signalclass;
signalclass_sd.signal = data_out_sd; % signalclass_sd.signal = data_out_sd;
end 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 % remove unnecessary zeros at start of impulse response to keep
% number of trellis states minimal % number of trellis states minimal
DIR_nonzero = find(obj.DIR ~= 0); DIR_nonzero = find(obj.DIR ~= 0);
@@ -63,399 +106,557 @@ classdef MLSE < handle
obj.DIR = [0 obj.DIR]; obj.DIR = [0 obj.DIR];
end end
% impulse respnse to remove from signal
obj.DIR = flip(obj.DIR); %i.e. -0.2676 -0.0478 1.0000
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% tx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(data_ref);
%%%%%% PREPARATIONS %%%%%%%%
%%%% Separate the equalized signal into the respective levels based on the actually transmitted level % Trellis States
constellation = unique(data_ref); if trellis_state_mode == 1 % Normalize the Trellis states to =1 RMS
decisionLevels = (constellation(1:end-1) + constellation(2:end)) / 2;
tx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(data_ref);
obj.trellis_states = obj.trellis_states ./ rms(obj.trellis_states);
% impulse respnse i.e. [0.5, 1.0000] elseif trellis_state_mode == 2 %simply use the states from the ref signal (should be a robust option)
obj.DIR = flip(obj.DIR);
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 % seems to be the only way to use combvec for a flexible amount
% of vectors. 'combs' contains all trellis states % of vectors. 'combs' contains all trellis states
pre_comb_mat = repmat(obj.trellis_states,length(obj.DIR)-1,1); 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)); 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{:}).'); 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); states = sum(combs,2);
nStates = length(last_sym);
% Calculate all possible input symbols for the desired impulse % % Calculate all possible input symbols for the desired impulse
% response. Row number is the index of the previous state, % % response. Row number is the index of the previous state,
% column number is the index of the next state % % column number is the index of the next state
% noise free received == branch metrics % % noise free received == branch metrics
noise_free_received = zeros(length(states),length(states)); % assumes: last_sym = combs(:,end); % already defined earlier
count_row = 1; levels = sort(unique(obj.trellis_states(:)).');
count_col = 1; edges = [levels(1) levels(end)]; % edge levels (0 and 5 in PAM6)
for l1 = 1:length(states)
for l2 = 1:length(states) noise_free_received = inf(nStates,nStates); % rows: to, cols: from
if sum(combs(l2,2:end) == combs(l1,1:end-1)) == size(combs,2)-1 edge_edge_mask = false(nStates,nStates); % rows: to, cols: from
noise_free_received(count_row,count_col) = sum(combs(l2,:).*obj.DIR(end:-1:2)) + last_sym(l1)*obj.DIR(1);
else for from = 1:nStates
noise_free_received(count_row,count_col) = inf; 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 end
count_row = count_row + 1;
end end
count_col = count_col + 1;
count_row = 1;
end end
% match amplitude levels of input signal to those of the calculated ideal symbols h = flip(obj.DIR(:)).';
% i.e. match the rms values of data_in to noise_free_received data_in = data_in(:);
if isreal(data_in) y_ideal = conv(data_ref(:), h, "same");
if obj.M == round(obj.M)
data_in = data_in * rms(noise_free_received(noise_free_received ~= inf),'all','omitnan'); switch scale_mode
end 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 end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% FORWARD PASS %%%%% %%%%% FORWARD PASS (VITERBI -Alpha's) %%%%%
% Initialize the output vector % Initialize the output vector
pm = zeros(length(states),length(states)); pm = zeros(nStates,nStates);
bm_fw = zeros(length(states),length(states),length(data_in)); 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) % 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 = pm + bm;
[pm_survivor_fw(:,n),pm_survivor_fw_idx(:,n)] = min(pm,[],2); % choose lowest path metric as new 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(pm_survivor_fw(:,n).',length(states),1); % update pm (chosen state to 2nd dimension -> FROM state) pm = repmat(alpha(:,n).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state)
bm_fw(:,:,n) = bm; bm_fw(:,:,n) = bm;
end end
% we can now get the best path as min % 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 % 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 % Initialize the output vector
pm = zeros(length(states),length(states)); pm = zeros(nStates,nStates);
pm_survivor_bw = zeros(length(states),length(data_in)+1); beta = zeros(nStates,length(data_in));
pm_survivor_bw_idx = zeros(length(states),length(data_in)+1); pm_survivor_bw_idx = zeros(nStates,length(data_in));
bm_bw = zeros(length(states),length(states),length(data_in)+1); bm_bw = zeros(nStates,nStates,length(data_in));
% starting with the state that has the lowest sum path % starting with the state that has the lowest sum path
% metric, follow the stored information about the % metric, follow the stored information about the
% predecessor % 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 = pm + bm.';
[pm_survivor_bw(:,h),pm_survivor_bw_idx(:,h)] = min(pm,[],2); % choose lowest path metric as new state [beta(:,h),pm_survivor_bw_idx(:,h)] = max(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) pm = repmat(beta(:,h).',nStates,1); % update pm (chosen state to 2nd dimension -> FROM state)
bm_bw(:,:,h) = bm; bm_bw(:,:,h) = bm;
end 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) %calc the log probabilities (llp's)
for k = 2:length(data_in) for k = 1:length(data_in)
fsm = repmat(pm_survivor_fw(:,k-1)',[length(states),1]); %pm_survivor_fw size: length(states)xlength(sequence) if k == 1
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
llp(:,k) = min(fsm+bm+bsm,[],2); 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
end end
% Compute soft output PAM4 stream from the metric_sym %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
soft_output = zeros(length(data_in),1); % Expected symbol value per stage %%%%% FORWARD PASS PAM2,4,8 %%%%%
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');
end
% Number of symbols and bits per symbol % These are interchangeable... second is chatgpt:
num_symbols = constellation; nml_LLP = LLP - max(LLP); %subtract highest value for better numerical stability, LLP's are not always close to zero
num_bits = 2; % 2 bits per symbol expLLP = exp(nml_LLP);
state_prob = expLLP ./ sum(expLLP); % sums to one (or numerically close to one)
bit_mapping = PAMmapper(4,0,"eth_style",1).showBitMapping; % Each row corresponds to the symbol above % 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
% Initialize LLR storage
llr = zeros(num_bits, length(data_in));
% Compute bit-wise LLRs if obj.M == 6
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) num_bits = 5;
llr(:,bit_idx) = min(llp(:,idx_bit_1), [], 1) - min(llp(:,idx_bit_0), [], 1);
% 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 end
% Convert LLR values to a hard-decision bit stream VITERBI_ESTIMATION_SYMBOLS = VITERBI_ESTIMATION_SYMBOLS./rms(VITERBI_ESTIMATION_SYMBOLS);
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 if debug
[~,llp_based_state_seq]=min(llp); %%% DEBUG PLOT LIKELIHOOD RATIOS %%%
LLP_EST(1:length(data_in)) = constellation(llp_based_state_seq); figure(115);clf
rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(LLP_EST'); subplot(2,1,1)
[~,~,ber_llp,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); for bit = 1:num_bits
fprintf('LLP BER : %.2e \n',ber_llp); hold on;
% [~,~,ber_llp,~] = calc_ber(circshift(rx_bits,1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); histogram(LLR_exact(:,bit),1000,"DisplayName",sprintf('Actual LLR of Bit Pos %d',bit),'LineStyle','none','FaceAlpha',0.4);
% fprintf('LLP BER +1: %.2e \n',ber_llp); end
% [~,~,ber_llp,~] = calc_ber(circshift(rx_bits,-1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); legend
% fprintf('LLP BER -1: %.2e \n',ber_llp);
%%%% DECIDE based on Viterbi traceback subplot(2,1,2)
rx_bits = PAMmapper(numel(constellation),0,"eth_style",1).demap(VITERBI_ESTIMATION_SYMBOLS'); for bit = 1:num_bits
[~,~,ber_viterbi,~] = calc_ber(rx_bits,tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); hold on;
fprintf('Viterbi BER: %.2e \n',ber_viterbi); histogram(LLR_maxlogmap(:,bit),1000,"DisplayName",sprintf('Max Log LLR of Bit Pos %d',bit),'LineStyle','none','FaceAlpha',0.4);
% [~,~,ber_viterbi,~] = calc_ber(circshift(rx_bits,1),tx_bits,"skip_front",100,"skip_end",150,"returnErrorLocation",1); end
% fprintf('Viterbi BER: %.2e \n',ber_viterbi); legend
end
% 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)); %%%%% CHECK BER's %%%%%
est_symbolpos = zeros(numel(constellation),length(data_ref));
for lvl = 1:numel(constellation) if debug
tx_symbolpos(lvl,data_ref==constellation(lvl)) = 1; tx_bits = reshape(tx_bits',[],1);
est_symbolpos(lvl,VITERBI_ESTIMATION_IDX==first_sym(lvl)) = 1; fprintf('\n')
est_symbolpos_llm(lvl,llp_based_state_seq==lvl) = 1; disp('Start DEBUG MLSE:')
% DECIDE based on Viterbi traceback
VITERBI_ESTIMATION_SYMBOLS = VITERBI_ESTIMATION_SYMBOLS./rms(VITERBI_ESTIMATION_SYMBOLS);
rx_bits = PAMmapper(obj.M,0,"eth_style",0).demap(VITERBI_ESTIMATION_SYMBOLS');
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 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
end
methods (Access=private)
end end
end end

View File

@@ -42,6 +42,7 @@ classdef TransmissionPerformance
0.8733, 0.8790, 0.8848, 0.8905, 0.8962, ... 0.8733, 0.8790, 0.8848, 0.8905, 0.8962, ...
0.9019, 0.9077, 0.9134, 0.9191, 0.9248, ... 0.9019, 0.9077, 0.9134, 0.9191, 0.9248, ...
0.9306, 0.9363, 0.9420, 0.9477, 0.9535]; 0.9306, 0.9363, 0.9420, 0.9477, 0.9535];
NGMITHRESHOLDS_SDHD = [0.8116, 0.8167, 0.8241, 0.8317, 0.8401, ... NGMITHRESHOLDS_SDHD = [0.8116, 0.8167, 0.8241, 0.8317, 0.8401, ...
0.8459, 0.8512, 0.8574, 0.8685, 0.8746, ... 0.8459, 0.8512, 0.8574, 0.8685, 0.8746, ...
0.8829, 0.8892, 0.8958, 0.9022, 0.9090,... 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 %% 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; BERTHRESHOLDS_KP4_AND_INNER = 4.85e-3;
% https://www.ieee802.org/3/bs/public/14_11/parthasarathy_3bs_01a_1114.pdf % 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); netrates.SDHD.Threshold = NaN(1, numMeasurements);
end end
if ~isempty(ber) 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.GrossRate = NaN(1, numMeasurements);
netrates.HD.NetRate = NaN(1, numMeasurements); netrates.HD.NetRate = NaN(1, numMeasurements);
netrates.HD.CodeRate = NaN(1, numMeasurements); netrates.HD.CodeRate = NaN(1, numMeasurements);
netrates.HD.Threshold = 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.GrossRate = NaN(1, numMeasurements);
netrates.KP4_hamming.NetRate = NaN(1, numMeasurements); netrates.KP4_hamming.NetRate = NaN(1, numMeasurements);
netrates.KP4_hamming.CodeRate = NaN(1, numMeasurements); netrates.KP4_hamming.CodeRate = NaN(1, numMeasurements);
@@ -200,11 +211,47 @@ classdef TransmissionPerformance
end end
if ~isempty(idxBER) if ~isempty(idxBER)
codeRate = obj.CODE_RATES_HD(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.NetRate(i) = grossRate(i) * codeRate;
netrates.HD.GrossRate(i) = grossRate(i) ; netrates.HD.GrossRate(i) = grossRate(i) ;
netrates.HD.CodeRate(i) = codeRate; netrates.HD.CodeRate(i) = codeRate;
netrates.HD.Threshold(i) = obj.BERTHRESHOLDS_HD(idxBER); netrates.HD.Threshold(i) = obj.CODE_RATE_HDFEC(idxBER);
end 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 = []; idxBER = [];
for j = length(obj.BERTHRESHOLDS_KP4_AND_INNER):-1:1 for j = length(obj.BERTHRESHOLDS_KP4_AND_INNER):-1:1
if ber(i) <= obj.BERTHRESHOLDS_KP4_AND_INNER(j) 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); netrates.KP4_hamming.Threshold(i) = obj.BERTHRESHOLDS_KP4_AND_INNER(idxBER);
end end
% O FEC
idxBER = []; idxBER = [];
for j = length(obj.BERTHRESHOLDS_O_FEC):-1:1 for j = length(obj.BERTHRESHOLDS_O_FEC):-1:1
if ber(i) <= obj.BERTHRESHOLDS_O_FEC(j) if ber(i) <= obj.BERTHRESHOLDS_O_FEC(j)

View File

@@ -5,7 +5,7 @@ classdef DBHandler < handle
properties properties
conn % Database connection object 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 tableNames % Cell array containing names of all tables in the database
tables = struct(); % Structure containing MATLAB tables for each database table tables = struct(); % Structure containing MATLAB tables for each database table
distinctValues distinctValues
@@ -21,7 +21,7 @@ classdef DBHandler < handle
% obj = DBHandler('pathToDB', 'path/to/database.db'); % obj = DBHandler('pathToDB', 'path/to/database.db');
arguments arguments
options.pathToDB = ""; % Default value for pathToDB if not provided options.dataBase = ""; % Default value for pathToDB if not provided
options.type = "mysql"; options.type = "mysql";
end end
@@ -37,12 +37,15 @@ classdef DBHandler < handle
try try
if options.type == "sqlite" if options.type == "sqlite"
obj.conn = sqlite(obj.pathToDB);
obj.conn = sqlite(obj.dataBase);
elseif options.type == "mysql" 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( ... obj.conn = database( ...
"labor", ... % Database name string(obj.dataBase), ... % Database name
"silas", ... % Username "silas", ... % Username
"silas", ... % Password (or getSecret) "silas", ... % Password (or getSecret)
"Vendor", "MySQL", ... "Vendor", "MySQL", ...
@@ -60,7 +63,9 @@ classdef DBHandler < handle
obj.refresh(); obj.refresh();
else else
error('DB seems to be corrupt') error('DB seems to be corrupt')
end end
end end
@@ -68,9 +73,11 @@ classdef DBHandler < handle
function obj = refresh(obj) function obj = refresh(obj)
% Get table names and the first rows of each table to understand the structure % Get table names and the first rows of each table to understand the structure
warning off
obj.getTableNames(); obj.getTableNames();
obj.getTables(); obj.getTables();
obj.getDistinctValues(); warning on
% obj.getDistinctValues();
end end
function obj = getTableNames(obj) function obj = getTableNames(obj)
@@ -169,14 +176,6 @@ classdef DBHandler < handle
function healthyDB = dbIsHealthy(obj) function healthyDB = dbIsHealthy(obj)
healthyDB = false; 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 %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_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"); 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)); fprintf('Raw Rx Paths: Found %d duplictaes of %s \n',duplictae_raw.occurrences(i),duplictae_raw.rx_raw_path(i));
end end
end end
healthyDB = true; healthyDB = true;
end end
function lastID = appendToTable(obj, tableName, newRow) function lastID = appendToTable(obj, tableName, newRow)
% appendToTable Appends a new row to the specified table % 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); error('Table %s does not exist in the database or has not been fetched.', tableName);
end end
% Convert newRow to a table if it is a struct % Handle struct preprocessing before table conversion
if isstruct(newRow) if isstruct(newRow)
fields = fieldnames(newRow); fields = fieldnames(newRow);
emptyFields = structfun(@isempty,newRow);
if sum(emptyFields) > 0 % % Handle empty fields
emptyFieldNames = fields(emptyFields); % use () to get a cell array % emptyFields = structfun(@isempty, newRow);
for idx = 1:numel(emptyFieldNames) % if sum(emptyFields) > 0
newRow.(emptyFieldNames{idx}) = NaN; % 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
end end
% Convert to table
newRow = struct2table(newRow); newRow = struct2table(newRow);
end end
% Ensure the new row matches the structure of the existing table % Ensure the new row matches the structure of the existing table
existingTableStructure = obj.tables.(tableName); existingTableStructure = obj.tables.(tableName);
% Perform data type checks and conversions % Perform remaining data type checks and conversions
for colName = newRow.Properties.VariableNames for colName = newRow.Properties.VariableNames
% Extract the value and its intended column type
value = newRow.(colName{1}); value = newRow.(colName{1});
existingValue = existingTableStructure.(colName{1});
% If the value is a class object, convert it to JSON format if iscell(value) && ~isempty(value)
if isobject(value) && ~isdatetime(value) && ~isa(value,"string") 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)); newRow.(colName{1}) = string(jsonencode(value));
% If the value is a character array, convert it to a string
elseif ischar(value) elseif ischar(value)
newRow.(colName{1}) = string(value); newRow.(colName{1}) = string(value);
elseif isdatetime(value)
% newRow.(colName{1}) = string(value);
end end
end end
@@ -247,7 +256,6 @@ classdef DBHandler < handle
% Parameters for retry logic % Parameters for retry logic
maxRetries = 50; maxRetries = 50;
basePause = 0.05; % seconds basePause = 0.05; % seconds
attempt = 0; attempt = 0;
success = false; success = false;
@@ -256,37 +264,41 @@ classdef DBHandler < handle
if obj.type == "mysql" if obj.type == "mysql"
newRow_ = obj.convertTableToCellStrings(newRow); newRow_ = obj.convertTableToCellStrings(newRow);
end end
sqlwrite(obj.conn, tableName, newRow); sqlwrite(obj.conn, tableName, newRow,"Catalog",obj.dataBase);
success = true; % Write successful success = true;
catch e catch e
if contains(e.message, 'database is locked') || contains(e.message, 'cannot rollback transaction') if contains(e.message, 'database is locked') || contains(e.message, 'cannot rollback transaction')
attempt = attempt + 1; attempt = attempt + 1;
pauseTime = basePause * (1 + rand()); % Add random jitter to reduce collision chance pauseTime = basePause * (1 + rand());
fprintf('Database locked. Retry %d/%d after %.3f seconds...\n', attempt, maxRetries, pauseTime); fprintf('Database locked. Retry %d/%d after %.3f seconds...\n', ...
attempt, maxRetries, pauseTime);
pause(pauseTime); pause(pauseTime);
else else
fprintf('Error details:\n');
fprintf('Column values:\n');
disp(newRow);
error('Failed to append to the table %s: %s', tableName, e.message); error('Failed to append to the table %s: %s', tableName, e.message);
end end
end end
end end
if ~success 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 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" if obj.type == "mysql"
queue = "SELECT LAST_INSERT_ID()"; query = "SELECT LAST_INSERT_ID()";
elseif obj.type == "sqlite" elseif obj.type == "sqlite"
queue = "SELECT last_insert_rowid()"; query = "SELECT last_insert_rowid()";
end end
result = fetch(obj.conn, queue); result = fetch(obj.conn, query);
lastID = result{1, 1}; % Access the value directly from the table lastID = result{1, 1};
end 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 % checkIfRunExists Checks if a specific value exists in a specified column of a table
% %
% Usage: % Usage:
@@ -311,7 +323,11 @@ classdef DBHandler < handle
end end
% Construct the query to check for the value in the specified column % 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 % Execute the query and pass the value2check to avoid SQL injection issues
try try
@@ -325,12 +341,21 @@ classdef DBHandler < handle
exists = count > 0; exists = count > 0;
if exists 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 else
% disp(['The value "', value2check, '" does not exist in the column "', column2check, '" of the table "', table2check, '".']); % disp(['The value "', value2check, '" does not exist in the column "', column2check, '" of the table "', table2check, '".']);
end end
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) function resultID = addProcessingResult(obj, run_id, resultData, eqParamsData)
% addProcessingResult Adds a processing result and links it to an EqualizerParameters entry. % 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. % resultID: The result_id of the newly inserted ProcessingResults entry.
% 1. Compute hash for equalizer parameters % 1. Compute hash for equalizer parameters
jsonStr = jsonencode(eqParamsData); % jsonStr = jsonencode(eqParamsData);
md = java.security.MessageDigest.getInstance('MD5'); % md = java.security.MessageDigest.getInstance('MD5');
md.update(uint8(jsonStr)); % md.update(uint8(jsonStr));
hashBytes = typecast(md.digest, 'uint8'); % hashBytes = typecast(md.digest, 'uint8');
hashStr = lower(dec2hex(hashBytes)'); % hashStr = lower(dec2hex(hashBytes)');
hashStr = lower(strtrim(hashStr(:)')); % Convert to a lowercase string % hashStr = lower(strtrim(hashStr(:)')); % Convert to a lowercase string
hashStr = obj.calcHash(eqParamsData);
% Add hash to equalizer parameters % Add hash to equalizer parameters
eqParamsData.config_hash = hashStr; eqParamsData.hash = hashStr;
% 2. Check if an equalizer configuration with the same hash exists % 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); existingEntry = obj.fetch(queryStr);
if ~isempty(existingEntry) if ~isempty(existingEntry)
@@ -367,27 +393,30 @@ classdef DBHandler < handle
eq_id = existingEntry{1,1}; eq_id = existingEntry{1,1};
else else
% Insert the new equalizer configuration and get its eq_id % 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 end
% 3. Add the equalizer configuration reference and run_id to resultData % 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; resultData.run_id = run_id;
% 4. Compute hash for the processing result % 4. Compute hash for the processing result
tempResultData = rmfield(resultData, 'date_of_processing'); tempResultData = rmfield(resultData, 'date_of_processing');
resultJsonStr = jsonencode(tempResultData); % resultJsonStr = jsonencode(tempResultData);
md2 = java.security.MessageDigest.getInstance('MD5'); % Create a new MD5 instance % md2 = java.security.MessageDigest.getInstance('MD5'); % Create a new MD5 instance
md2.update(uint8(resultJsonStr)); % md2.update(uint8(resultJsonStr));
resultHashBytes = typecast(md2.digest, 'uint8'); % resultHashBytes = typecast(md2.digest, 'uint8');
resultHashStr = lower(dec2hex(resultHashBytes)'); % resultHashStr = lower(dec2hex(resultHashBytes)');
resultHashStr = lower(strtrim(resultHashStr(:)')); % Convert to a lowercase string % resultHashStr = lower(strtrim(resultHashStr(:)')); % Convert to a lowercase string
resultHashStr = obj.calcHash(tempResultData);
% Add the result hash to resultData % Add the result hash to resultData
resultData.result_hash = resultHashStr; resultData.hash = resultHashStr;
% 5. Check if an identical processing result already exists % 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); existingResult = obj.fetch(queryStr2);
if ~isempty(existingResult) if ~isempty(existingResult)
@@ -397,7 +426,66 @@ classdef DBHandler < handle
return; return;
end 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); resultID = obj.appendToTable('Results', resultData);
end end
@@ -486,17 +574,32 @@ classdef DBHandler < handle
end end
function executeSQL(obj, query) function executeSQL(obj, query)
% This method executes an SQL statement using MATLAB's execute function. % This method executes an SQL statement using MATLAB's execute function.
execute(obj.conn, query); execute(obj.conn, query);
end 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 end
function [result,query] = queryDB(obj, filterParams, selectedFields) function [result,query] = queryDB(obj, filterParams, selectedFields)
% getPathsWithFlexibleFilter Retrieves values from Runs table with flexible filtering % getPathsWithFlexibleFilter Retrieves values from Runs table with flexible filtering
% and lets the user select which fields to include in the SELECT statement. % and lets the user select which fields to include in the SELECT statement.
@@ -518,22 +621,6 @@ classdef DBHandler < handle
selectedFields = []; selectedFields = [];
end 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 % Step 3: Construct the SQL query based on the inputs
query = obj.constructSQLQuery(filterParams, selectedFields); query = obj.constructSQLQuery(filterParams, selectedFields);
@@ -558,7 +645,7 @@ classdef DBHandler < handle
cleanedTable.(varNames{i}) = numCol; cleanedTable.(varNames{i}) = numCol;
else else
% Clean double-quoted SQL literals (e.g., ""no_db"") % Clean double-quoted SQL literals (e.g., ""no_db"")
cleanedTable.(varNames{i}) = strrep(string(col), '""', '"'); cleanedTable.(varNames{i}) = strrep(string(col), '"', '');
end end
end end
end end
@@ -619,132 +706,238 @@ classdef DBHandler < handle
end end
end end
function query = constructSQLQuery(obj, filterParams, selectedFields) function query = constructSQLQuery(obj, filterParams, selectedFields)
% constructSQLQuery Constructs the SQL query based on filter parameters and selected fields. % 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. arguments
if isstruct(selectedFields) 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 = {}; newFields = {};
tableNames = fieldnames(selectedFields); tableNames = fieldnames(selectedFields);
for t = 1:numel(tableNames) for t = 1:numel(tableNames)
tableStruct = selectedFields.(tableNames{t}); tableStruct = selectedFields.(tableNames{t});
fieldNames = fieldnames(tableStruct); fns = fieldnames(tableStruct);
for f = 1:numel(fieldNames) for f = 1:numel(fns)
if isequal(tableStruct.(fieldNames{f}), 1) if isequal(tableStruct.(fns{f}), 1)
newFields{end+1} = sprintf('%s.%s', tableNames{t}, fieldNames{f}); newFields{end+1} = sprintf('%s.%s', tableNames{t}, fns{f}); %#ok<AGROW>
end end
end end
end end
selectedFields = newFields; selectedFields = newFields;
end end
% Construct the SELECT clause dynamically based on user selection. % Parse the table names actually referenced by the SELECT
% (Assuming that when provided as a cell array, each entry is of the form reqTables = unique(cellfun(@(s) extractBefore(s, '.'), selectedFields, ...
% 'TableName.fieldName' or, in our conversion case, 'selectedFields.fieldName'.) 'UniformOutput', false));
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
% Decide on COALESCE depending on the field type. % -------- Step 2: Build SELECT with COALESCE wrapper as you already do ----
% If the table is known in obj.tables and the field is numeric, use 'NaN'. selectClause = obj.generateCoalesceString(selectedFields);
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
if i < numel(selectedFields) % -------- Step 3: FROM and minimal JOIN plan ------------------------------
selectClause = [selectClause, ', ']; % Decide main table: prefer the first explicitly referenced table, else 'Runs'
else if ~isempty(reqTables)
selectClause = [selectClause, ' ']; 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
end end
% Ensure main table is included
if ~ismember(mainTable, reqTables)
reqTables = unique([mainTable; reqTables]); %#ok<AGROW>
end
% --- Adaptive FROM Clause --- % Well build joins only for the required tables (minus the main)
mainTable = 'Runs'; otherTables = setdiff(reqTables, {mainTable});
fromClause = ['FROM ', mainTable, ' '];
% Prepare join buffers % Keep track of whats already in the FROM graph (start with main)
normalJoins = ''; present = string(mainTable);
equalizerJoin = ''; joins = strings(0,1);
tableNamesAll = fieldnames(obj.tables); % Helper lambdas
for t = 1:numel(tableNamesAll) hasField = @(tbl, fld) isfield(obj.tables.(char(tbl)), char(fld));
tableName = tableNamesAll{t}; 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 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') % First pass: if WHERE uses Runs.* and mainTable isnt Runs, ensure Runs is in the graph
% Direct join to Runs if contains(string(whereClause), "Runs.") && ~any(present == "Runs")
normalJoins = [normalJoins, 'LEFT JOIN ', tableName, ' ON ', mainTable, '.run_id = ', tableName, '.run_id ']; % Try to join Runs to whatever has run_id (mainTable ideally)
elseif isfield(obj.tables.(tableName), 'eq_id') if hasField(mainTable, 'run_id') && hasField('Runs', 'run_id')
% Equalizer depends on Results, collect this join separately joins(end+1,1) = "LEFT JOIN Runs ON " + string(mainTable) + ".run_id = Runs.run_id";
equalizerJoin = ['LEFT JOIN ', tableName, ' ON Results.eqParam_id = ', tableName, '.eq_id ']; present(end+1,1) = "Runs";
end end
end end
% Combine joins: normal joins first, Equalizer last % Join the required tables with minimal edges
fromClause = [fromClause, normalJoins, equalizerJoin]; 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
function whereClause = generateWhereClause(obj, filterParams)
% --- WHERE Clause Construction ---
baseQuery = [selectClause, ' ', fromClause, 'WHERE '];
filterClauses = []; filterClauses = [];
tableNames_ = fieldnames(filterParams);
for t = 1:numel(tableNames_) % If filterParams is a DbFilterParameter object, get its internal structure
tableName = tableNames_{t}; 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); tableParams = filterParams.(tableName);
fieldNames = fieldnames(tableParams); fieldNames = fieldnames(tableParams);
for i = 1:numel(fieldNames) for i = 1:numel(fieldNames)
fieldName = fieldNames{i}; fieldName = fieldNames{i};
value = tableParams.(fieldName); value = tableParams.(fieldName);
fullName = sprintf('%s.%s', tableName, fieldName); fullName = sprintf('%s.%s', tableName, fieldName);
% Handle various types of values for SQL query construction % Skip empty values
if isempty(value) if isempty(value) || (isa(value, 'QueryFilter') && isempty(value.value))
continue; 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 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
end end
% Remove trailing ' AND ' if any filters were added. % Remove trailing ' AND ' if any filters were added
if ~isempty(filterClauses) if ~isempty(filterClauses)
filterClauses = filterClauses(1:end-5); whereClause = filterClauses(1:end-5);
query = [selectClause, ' ', fromClause, 'WHERE ', filterClauses];
else else
query = [selectClause, ' ', fromClause]; whereClause = '';
end end
end end
@@ -980,5 +1173,59 @@ classdef DBHandler < handle
end 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
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) function save(obj,path)
try try
save(path,"obj"); save(path,"obj");
catch catch e
disp(e.message)
disp('Provide save path')
end end
end end
@@ -136,7 +137,21 @@ classdef DataStorage < handle
if ~isempty(tmp) if ~isempty(tmp)
if isa(tmp,'double') 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') elseif isa(tmp,'Signal') || isa(tmp,'struct') || isa(tmp,'Exfo_laser') || isa(tmp,'DC_supply')
if i == 1 if i == 1
value = {}; value = {};
@@ -150,7 +165,7 @@ classdef DataStorage < handle
end end
value{i} = tmp{1} ; value{i} = tmp{1} ;
else else
value{i} = tmp{1} ; value{i} = tmp ;
end end
else 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; hold off;
end 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
end end

View File

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

View File

@@ -1,5 +1,18 @@
function [eq_package] = duobinary_signaling(eq_, mlse_,M ,rx_signal, tx_symbols, tx_bits,options) function [db_results] = duobinary_signaling(eq_, mlse_, M, rx_signal, tx_symbols, tx_bits, options)
%Duobinary Signaling % 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 arguments
eq_ eq_
mlse_ mlse_
@@ -7,91 +20,130 @@ arguments
rx_signal rx_signal
tx_symbols tx_symbols
tx_bits tx_bits
options.postFFE = []; options.precode_mode db_mode
options.showAnalysis = 0
options.eth_style_symbol_mapping = 0
options.postFFE = []
options.database = []
end 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) 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 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); mlse_.trellis_states = PAMmapper(M,0).levels;
eq_signal = Duobinary().decode(eq_signal); 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( ... % Apply duobinary encoding and decoding
'result_id', NaN, ... % mlse_signal = Duobinary().encode(mlse_signal);
'run_id', NaN, ... % Beispielhafte Run-ID mlse_signal = Duobinary().decode(mlse_signal);
'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
);
% 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) if ~isempty(options.postFFE)
npostFFE = options.postFFE.order; npostFFE = options.postFFE.order;
else else
npostFFE = 0; npostFFE = 0;
end end
equalizerConfigDBsignaling = struct( ... % Create results structure
'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt db_results = struct();
'equalizer_structure', int32(equalizer_structure.db_encoded), ... % Beispiel: 1 (z.B. für vnle) db_results.metrics = Metricstruct;
'M', M, ... % Ordnung der PAM-Konstellation db_results.metrics.result_id = NaN;
'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String db_results.metrics.run_id = NaN;
'db_target', 1, ... % 0 oder 1 db_results.metrics.eqParam_id = NaN;
'diff_precode', 1, ... % 0 oder 1 db_results.metrics.date_of_processing = datetime('now');
'postFFE', double(~isempty(options.postFFE)), ... % Beispielwert db_results.metrics.BER = ber_db;
'NpostFFE', npostFFE, ... % Beispielwert db_results.metrics.numBits = bits_db;
'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung db_results.metrics.numBitErr = errors_db;
'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung db_results.metrics.SNR = snr;
'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung db_results.metrics.SNR_level = snr_lvl;
'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung db_results.metrics.STD = std_total;
'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung db_results.metrics.STD_level = std_lvl;
'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung db_results.metrics.STDrx = std_rxraw_total;
'K', eq_.K, ... % Samples pro Symbol db_results.metrics.STDrx_level = std_rxraw_lvl;
'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap db_results.metrics.GMI = gmi;
'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1) db_results.metrics.AIR = air;
'training_length', eq_.training_length, ... % Anzahl Trainingssymbole db_results.metrics.EVM = evm_total;
'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe db_results.metrics.EVM_level = evm_lvl;
'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung) db_results.metrics.MLSE_dir = mlse_.DIR;
'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 ...
);
eq_package.resultsDBsignaling = resultsDBsignaling; % Create configuration structure
eq_package.equalizerConfigDBsignaling = equalizerConfigDBsignaling; 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 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 arguments
eq_ eq_
@@ -22,8 +22,14 @@ if ~isempty(options.postFFE)
[eq_signal,eq_noise] = options.postFFE.process(eq_signal,db_ref_sequence); [eq_signal,eq_noise] = options.postFFE.process(eq_signal,db_ref_sequence);
end end
% dir = [1,1]; mlse_.DIR = [1,1];
mlse_sig_sd = mlse_.process(eq_signal); %
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); 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_symbols_precoded = Duobinary().decode(tx_symbols_precoded);
tx_bits_precoded = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(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); 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); [~,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 %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().encode(mlse_sig_hd,"M",M);
mlse_sig_hd_decoded = Duobinary().decode(mlse_sig_hd_decoded,"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); 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 % 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); 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); 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 end
% M = numel(unique(tx_symbols.signal)); % M = numel(unique(tx_symbols.signal));
rx_bits = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).demap(mlse_sig_hd); 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); [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 else
npostFFE = 0; gmi_mlse = GMI_MLSE;
air_mlse = tx_symbols.fs .* floor(log2(double(M))*10)/10 .* gmi_mlse ./ log2(double(M));
end end
equalizerConfigDBtgt = struct( ... db_results = struct();
'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt db_results.metrics = Metricstruct;
'equalizer_structure', int32(equalizer_structure.vnle_db_mlse), ... % Beispiel: 1 (z.B. für vnle) db_results.metrics.result_id = NaN;
'M', M, ... % Ordnung der PAM-Konstellation db_results.metrics.run_id = NaN;
'target_constellation', jsonencode(round(db_ref_constellation,5)), ... % Beispielhafter Target-String db_results.metrics.eqParam_id = NaN;
'db_target', 1, ... % 0 oder 1 db_results.metrics.date_of_processing = datetime('now');
'diff_precode', int32(options.precode_mode), ... % 0 oder 1 db_results.metrics.BER = ber_db;
'postFFE', ~isempty(options.postFFE), ... % Beispielwert db_results.metrics.numBits = bits_db;
'NpostFFE', npostFFE, ... % Beispielwert db_results.metrics.numBitErr = errors_db;
'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung db_results.metrics.BER_precoded = ber_db_diff_precoded;
'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung db_results.metrics.numBitErr_precoded = errors_db_diff_precoded;
'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung db_results.metrics.GMI = gmi_mlse;
'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung db_results.metrics.AIR = air_mlse;
'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung db_results.metrics.MLSE_dir = mlse_.DIR;
'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung db_results.metrics.Alpha = alpha;
'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 ...
);
eq_package.resultsDBtgt = resultsDBtgt; % Create DB results structure
eq_package.equalizerConfigDBtgt = equalizerConfigDBtgt; 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 if options.showAnalysis
eq_signal.eye(eq_signal.fs,M,"fignum",249);
eq_noise = eq_noise - mean(eq_noise.signal); eq_noise = eq_noise - mean(eq_noise.signal);
rx_signal.spectrum("normalizeTo0dB",1,"fignum",250,"displayname","Rx Spectrum"); 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'); 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 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 arguments
eq_ eq_
@@ -15,271 +30,276 @@ arguments
options.database = []; options.database = [];
end end
%FFE or VNLE %% Process signals through equalizers
[eq_signal_sd,eq_noise] = eq_.process(rx_signal,tx_symbols); % 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) if ~isempty(options.postFFE)
tic 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 toc
end 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_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise);
% [mlse_sig_hd,mlse_sig_sd] = mlse_.process(mlse_sig_sd,tx_symbols);
mlse_sig_sd = mlse_.process(mlse_sig_sd);
mlse_sig_hd = PAMmapper(M,0,"eth_style",options.eth_style_symbol_mapping).quantize(mlse_sig_sd); 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;
% precoding to mitigate error propagation, most prominently used in end
% combination with duobinary signaling to avoid catastrophic error else
% behavior (see J.W.M. Bergmans, Digital Baseband Transmission and Recording -> partial response signaling) %long memory / pf respinse - not sure what to set here in a worst
%case :-)
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);
end
%do it again:
pf_.useBurg = 0;
[mlse_sig_sd,whitened_noise] = pf_.process(eq_signal_sd, eq_noise);
end end
% METRICS OF VNLE SD Signal: mlse_.DIR = pf_.coefficients;
[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);
% METRICS OF MLSE (HD-VITERBI) GMI_MLSE = NaN;
pf_.ncoeff = 1; if isa(mlse_,'MLSE_viterbi')
pf_.process(eq_signal_sd,eq_noise); [mlse_sig_sd] = mlse_.process(mlse_sig_sd);
alpha = pf_.coefficients(2); else
[mlse_sig_sd,LLR,GMI_MLSE] = mlse_.process(mlse_sig_sd,tx_symbols);
end
eq_package.ber_mlse = ber_mlse; mlse_sig_hd = PAMmapper(M, 0, "eth_style", options.eth_style_symbol_mapping).quantize(mlse_sig_sd);
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;
eq_package.eq = eq_; %% Calculate BER based on precoding mode
eq_package.pf = pf_; [numbits, errors, bers, ~] = calculateBER(eq_signal_hd, mlse_sig_hd, tx_symbols, tx_bits, options.precode_mode, M, options.eth_style_symbol_mapping);
eq_package.mlse = mlse_;
resultsVNLE = struct( ... %% Calculate performance metrics
'result_id', NaN, ... % % VNLE metrics
'run_id', NaN, ... % Beispielhafte Run-ID [snr_vnle, snr_vnle_lvl] = calc_snr(tx_symbols.signal, eq_noise.signal);
'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle % [gmi_vnle] = calc_air(eq_signal_sd, tx_symbols, "skip_front", 10000, "skip_end", 10000);
'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 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) if ~isempty(options.postFFE)
npostFFE = options.postFFE.order; npostFFE = options.postFFE.order;
else else
npostFFE = 0; npostFFE = 0;
end end
equalizerConfigVNLE = struct( ... ffe_results = struct();
'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt ffe_results.metrics = Metricstruct;
'equalizer_structure', int32(equalizer_structure.vnle), ... % Beispiel: 1 (z.B. für vnle) ffe_results.metrics.result_id = NaN;
'M', M, ... % Ordnung der PAM-Konstellation ffe_results.metrics.run_id = NaN;
'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String ffe_results.metrics.eqParam_id = NaN;
'db_target', 0, ... % 0 oder 1 ffe_results.metrics.date_of_processing = datetime('now');
'diff_precode', int32(options.precode_mode), ... % 0 oder 1 ffe_results.metrics.BER = bers.vnle;
'postFFE', double(~isempty(options.postFFE)), ... % Beispielwert ffe_results.metrics.numBits = numbits.vnle;
'NpostFFE', npostFFE, ... % Beispielwert ffe_results.metrics.numBitErr = errors.vnle;
'Ne1', eq_.Ne(1), ... % Feedforward Koeffizienten 1. Ordnung ffe_results.metrics.BER_precoded = bers.vnle_precoded;
'Ne2', eq_.Ne(2), ... % Feedforward Koeffizienten 2. Ordnung ffe_results.metrics.numBitErr_precoded = errors.vnle_precoded;
'Ne3', eq_.Ne(3), ... % Feedforward Koeffizienten 3. Ordnung ffe_results.metrics.SNR = snr_vnle;
'Nb1', eq_.Nb(1), ... % Decision Feedback Koeffizienten 1. Ordnung ffe_results.metrics.SNR_level = snr_vnle_lvl;
'Nb2', eq_.Nb(2), ... % Decision Feedback Koeffizienten 2. Ordnung ffe_results.metrics.STD = std_vnle_total;
'Nb3', eq_.Nb(3), ... % Decision Feedback Koeffizienten 3. Ordnung ffe_results.metrics.STD_level = std_vnle_lvl;
'K', eq_.K, ... % Samples pro Symbol ffe_results.metrics.STDrx = std_rxraw_total;
'DCmu', eq_.DCmu, ... % Anpassungsrate für DC-Tap ffe_results.metrics.STDrx_level = std_rxraw_lvl;
'ideal_dfe', eq_.ideal_dfe, ... % Flag für ideal DFE (0 oder 1) ffe_results.metrics.GMI = gmi_vnle;
'training_length', eq_.training_length, ... % Anzahl Trainingssymbole ffe_results.metrics.AIR = air_vnle;
'training_loops', eq_.training_loops, ... % Anzahl Trainingsdurchläufe ffe_results.metrics.EVM = evm_vnle_total;
'TRmu1', eq_.FFEmu, ... % mu für DD-Modus (1. Ordnung) ffe_results.metrics.EVM_level = evm_vnle_lvl;
'TRmu2', eq_.FFEmu, ... % mu für DD-Modus (2. Ordnung) ffe_results.metrics.Alpha = alpha;
'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 ...
);
try
eq_.e = [];
eq_.e2 = [];
eq_.e3 = [];
end
resultsMLSE = struct( ... ffe_results.config = Equalizerstruct();
'result_id', NaN, ... % ffe_results.config.eq = jsonencode(eq_);
'run_id', NaN, ... % Beispielhafte Run-ID ffe_results.config.equalizer_structure = int32(equalizer_structure.vnle);
'eqParam_id', NaN, ... % Beispielhafter Fremdschlüssel zur EqualizerParameters-Tabelle ffe_results.config.comment = 'function: vnle_postfilter_mlse - FFE part';
'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])...
);
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( ... % Create MLSE results structure
'eq_id', NaN, ... % Auto-Inkrement, wird in der DB gesetzt mlse_results.config = Equalizerstruct();
'equalizer_structure', int32(equalizer_structure.vnle_pf_mlse), ... % Beispiel: 1 (z.B. für vnle) mlse_results.config.eq = jsonencode(eq_);
'M', M, ... % Ordnung der PAM-Konstellation mlse_.DIR = length(mlse_.DIR)-1;
'target_constellation', jsonencode(round(unique(tx_symbols.signal),5)), ... % Beispielhafter Target-String mlse_results.config.mlse = jsonencode(mlse_);
'db_target', 0, ... % 0 oder 1 mlse_results.config.equalizer_structure = int32(equalizer_structure.vnle_pf_mlse);
'diff_precode', int32(options.precode_mode), ... % 0 oder 1 mlse_results.config.comment = 'function: vnle_postfilter_mlse - MLSE part';
'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;
% 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 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 end

View File

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

View File

@@ -53,8 +53,8 @@ function showEQcoefficients(options)
for i = 1:numSubplots for i = 1:numSubplots
subplot(1, numSubplots, i); subplot(1, numSubplots, i);
stem(coeffs{i}, 'Color', options.color, 'LineWidth', 0.1, ... stem(coeffs{i}, 'Color', options.color, 'LineWidth', 1, ...
'Marker', '.', 'MarkerSize', 1); 'Marker', '.', 'MarkerSize', 10);
title(titles{i}); title(titles{i});
ylim([-1, 1]); % Set y-axis limits to [-1, 1] ylim([-1, 1]); % Set y-axis limits to [-1, 1]
grid on; 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. % Assuming that obj.e contains the final FFE filter coefficients.
% Set the number of frequency points and sampling frequency. % Set the number of frequency points and sampling frequency.
@@ -13,23 +20,33 @@ function showEQfilter(coefficients,fs)
f = f(1:half_nfft); f = f(1:half_nfft);
H = H(1:half_nfft); H = H(1:half_nfft);
% Plot the magnitude and phase responses. H_mag = abs(H);
figure(339); 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) % Magnitude response (in dB)
subplot(2,1,1); subplot(2,1,1);
hold on 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'); title('(Inverted) Magnitude Response of FFE Filter');
xlabel('Frequency (Hz)'); xlabel('Frequency (GHz)');
ylabel('Magnitude (dB)'); ylabel('Magnitude (dB)');
grid on; grid on;
% Phase response % Phase response
subplot(2,1,2); 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'); title('Phase Response of FFE Filter');
xlabel('Frequency (Hz)'); xlabel('Frequency (GHz)');
ylabel('Phase'); ylabel('Phase');
grid on; grid on;

View File

@@ -20,10 +20,15 @@ end
fig = figure(options.fignum); % Use the specified figure number fig = figure(options.fignum); % Use the specified figure number
end end
eq_signal = max(min(eq_signal,3),-3);
%%% Separate Classes %%% Separate Classes
constellation = unique(ref_symbols); constellation = unique(ref_symbols);
received_sd = NaN(numel(constellation),length(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) for lvl = 1:numel(constellation)
%Separate the equalized signal into the %Separate the equalized signal into the
%respective levels based on the actually %respective levels based on the actually
@@ -40,12 +45,15 @@ end
cnt(lvl) = round(numel(intermediate(~isnan(intermediate)))./length(eq_signal),3).*100; cnt(lvl) = round(numel(intermediate(~isnan(intermediate)))./length(eq_signal),3).*100;
hold on hold on
warning off 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 warning on
end end
xlim([-3 3]);
legend legend
grid on grid on
% view([90 -90]);
end 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 arguments
eq_signal eq_signal
ref_symbols ref_symbols
options.fignum (1,1) double = NaN % Default to NaN if not provided 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.displayname (1,:) char = '' % Default to an empty string if not provided
options.f_sym = []; options.f_sym =1e6;
end end
plot_shit = 1;
if isa(eq_signal,'Signal') if isa(eq_signal,'Signal')
options.f_sym = eq_signal.fs; options.f_sym = eq_signal.fs;
eq_signal = eq_signal.signal; eq_signal = eq_signal.signal;
assert(~isempty(options.f_sym),'No fsym given');
end end
if isa(ref_symbols,'Signal') if isa(ref_symbols,'Signal')
ref_symbols = ref_symbols.signal; ref_symbols = ref_symbols.signal;
end end
% Determine the figure number to use or create a new figure
if isnan(options.fignum) if plot_shit
fig = figure; % Create a new figure and get its handle % Determine the figure number to use or create a new figure
else if isnan(options.fignum)
fig = figure(options.fignum); % Use the specified figure number fig = figure; % Create a new figure and get its handle
clf; else
fig = figure(options.fignum); % Use the specified figure number
clf;
end
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; correct_symbols = ref_symbols;
f_sym = options.f_sym; f_sym = options.f_sym;
@@ -33,56 +38,54 @@ col = cbrewer2('Paired',numel(unique(correct_symbols))*2);
ccnt = -1; ccnt = -1;
levels = unique(correct_symbols); levels = unique(correct_symbols);
symbols_for_lvl = NaN(numel(levels),length(correct_symbols));
start = 1; start = 1;
ende = length(correct_symbols); ende = length(correct_symbols);
% start = 30000;
% ende = 40000;
for l = 1:numel(levels) for l = 1:numel(levels)
ccnt = ccnt+2; ccnt = ccnt+2;
level_amplitude = levels(l); level_amplitude = levels(l);
symbols_for_lvl = NaN(1,length(correct_symbols)); symbols_for_lvl(l,correct_symbols==level_amplitude) = rx_symbols(correct_symbols==level_amplitude);
symbols_for_lvl(correct_symbols==level_amplitude) = rx_symbols(correct_symbols==level_amplitude); std_lvl(l) = std(symbols_for_lvl(l,:),'omitnan');
std_lvl(l) = std(symbols_for_lvl,'omitnan');
xax_in_sec = ((1:length(correct_symbols)) / f_sym) * 1e6; 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,:)); if plot_shit
hold on; 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 end
std_lvl = round(std_lvl,2); std_lvl = round(std_lvl,2);
ccnt = 0; ccnt = 0;
avg_for_lvl = NaN(numel(levels),length(correct_symbols));
% Add the windowed/ smoothed curves % Add the windowed/ smoothed curves
for l = 1:numel(levels) for l = 1:numel(levels)
ccnt = ccnt+2; ccnt = ccnt+2;
level_amplitude = levels(l); 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(avg_for_lvl(l,:));
t = 1:numel(avg_for_lvl(l,:));
nanx = isnan(symbols_for_lvl); avg_for_lvl(l,nanx) = interp1(t(~nanx), avg_for_lvl(l,~nanx), t(nanx));
t = 1:numel(symbols_for_lvl);
symbols_for_lvl(nanx) = interp1(t(~nanx), symbols_for_lvl(~nanx), t(nanx));
xax_in_sec = ((1:length(correct_symbols)) / f_sym) * 1e6; xax_in_sec = ((1:length(correct_symbols)) / f_sym) * 1e6;
% xax_in_sec = 1:length(correct_symbols); % 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 hold on
end end
%yline(max(rx_symbols(correct_symbols==levels(2))))
yline(levels);
if 0 if 0
annotation(fig,'textbox',... annotation(fig,'textbox',...
@@ -121,9 +124,10 @@ if 0
'FitBoxToText','off'); 'FitBoxToText','off');
end end
% xlim([0, 2.6]) if plot_shit
% ylim([-2 2]) % yline(levels);
xlabel('Time in $\mu$s'); xlabel('Time in $\mu$s');
ylabel('Normalized Amplitude'); ylabel('Normalized Amplitude');
ylim([-3 3]);
end
end end

View File

@@ -1,5 +1,5 @@
% Define the precomp path % 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) % Step 1: Find all valid files (assume .mat files for ChannelFreqResp)
fileList = dir(fullfile(precomp_path, '*.mat')); fileList = dir(fullfile(precomp_path, '*.mat'));

View File

@@ -1,5 +1,5 @@
function [ach_inf_rate] = calc_air(test_signal,reference_signal,options) 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... % Implementation is not accessible, I mailed TUM to get the code...
arguments(Input) arguments(Input)
@@ -26,7 +26,7 @@ end
% TRIM % TRIM
[test_signal,reference_signal]=trimseq(test_signal,reference_signal,options.skip_front,options.skip_end); [test_signal,reference_signal]=trimseq(test_signal,reference_signal,options.skip_front,options.skip_end);
% CALC EVM % CALC AIR
%%% new implementation of AIR %%% new implementation of AIR
constellation = unique(reference_signal); constellation = unique(reference_signal);
reference_idx = arrayfun(@(x) find(constellation == x, 1), 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); [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 % Per-level EVM
evm_total = rms(error_vector);
try
%%% Per Level EVM
k = unique(reference_signal); k = unique(reference_signal);
evm_lvl = NaN(1, length(k));
for lvl = 1:length(k) for lvl = 1:length(k)
lvl_errors = error_vector(reference_signal==k(lvl)); idx = reference_signal == k(lvl);
evm_lvl(lvl) = rms(lvl_errors); 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 end
catch end
evm_lvl = NaN;
warning('No EVM per level calculated') 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,:);
end end
end 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,:);
end
end

View File

@@ -1,123 +1,181 @@
function [GMI,NGMI] = calc_ngmi(test_signal,reference_signal,options) 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, % (N)GMI for AWGN, supports PAM2/4/8 (per-symbol) and PAM6 (pair-based, 5 bits / 2 symbols)
% 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)
arguments(Input) arguments(Input)
test_signal; test_signal
reference_signal; reference_signal
options.skip_front = 0; options.skip_front (1,1) double = 0
options.skip_end = 0; options.skip_end (1,1) double = 0
options.returnErrorLocation = 0; options.returnErrorLocation (1,1) double = 0 %#ok<NASGU>
end end
options.skip_end = abs(options.skip_end); options.skip_end = abs(options.skip_end);
options.skip_front = abs(options.skip_front); 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') % --- Trim head/tail consistently
reference_signal = reference_signal.signal; [test_signal,reference_signal] = trimseq(test_signal,reference_signal,options.skip_front,options.skip_end);
end
if isa(test_signal,'Signal') % --- Noise variance (real AWGN): use mean-square error for robustness
test_signal = test_signal.signal; err = test_signal - reference_signal;
end sigma2 = mean((err(:)).^2);
% TRIM % --- Constellation (levels observed)
[test_signal,reference_signal]=trimseq(test_signal,reference_signal,options.skip_front,options.skip_end); constellation = unique(reference_signal);
M = numel(constellation);
% CALC EVM % --- Empirical P_X (uniform is fine too; keep your original behavior)
[GMI,NGMI] = calc_ngmi_(test_signal,reference_signal); 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, % Precompute mapping (pair -> bit label) and (symbol -> index)
% Normalized Generalized Mutual Information as a Forward Error Correction Threshold for Probabilistically Shaped QAM, [ok1, idx_s1] = ismember(trans_pairs(:,1), symbols);
% in 2017 European Conference on Optical Communication (ECOC), Sep. 2017, S. 13. doi: 10.1109/ECOC.2017.8345872. [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); % Build pairwise prior P_pair = P_X(i)*P_X(j) on the MxM grid
sigma2 = var(error_vector); %noise variance P_pair = P_X(:) * P_X(:).'; % MxM
logP_pair = log(P_pair); % for numerical stability
%%% Separate Classes % Precompute constant term for log-likelihood
constellation = unique(reference_signal); logc = -0.5*log(2*pi*sigma2);
received_sd = NaN(numel(constellation),length(reference_signal));
lvlcol = cbrewer2('Set1',numel(constellation)); % Prepare transmitted 5-bit labels per observed pair (to split llr0/llr1)
for lvl = 1:numel(constellation) % Find each (x1(k),x2(k)) in trans_pairs:
%Separate the equalized signal into the [tf, row_in_table] = ismember([x1(:), x2(:)], trans_pairs, 'rows');
%respective levels based on the actually assert(all(tf), 'A reference pair was not found in the transition table.');
%transmitted level! tx_bits_pair = pam6bits(row_in_table, :); % numPairs x 5
received_sd(lvl,reference_signal==constellation(lvl)) = test_signal(reference_signal==constellation(lvl));
% 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
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 % GMI per symbol (5 bits per 2 symbols)
m = log2(M); %bits per symbol GMI = sum(MI)/2;
NGMI = GMI / 2.5;
entries = sum(~isnan(received_sd),2)'; else
P_X = entries./N; % ===== 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 % Likelihood (real AWGN)
symbols = constellation'; % PAM-4 symbols 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 % Exact bit-LLRs and MI
q_Y_given_X = @(y, x) (1 / sqrt(2 * pi * sigma2)) * exp(-(y - x).^2 / (2 * sigma2)); LLR_exact = zeros(N,m);
for b = 1:m
% Entropy term mask1 = (gray_bits(:,b)==1);
H_X = -sum(P_X .* log2(P_X)); % Entropy of input distribution mask0 = ~mask1;
% GMI computation
noise_impact_term = 0;
for k = 1:N for k = 1:N
y_k = test_signal(k); % Current received sample yk = test_signal(k);
[~, closest_symbol_idx] = min(abs(symbols - y_k)); % Closest symbol index den = sum(q(yk, symbols) .* P_X);
closest_symbol = symbols(closest_symbol_idx); % Closest symbol num1 = sum(q(yk, symbols(mask1)) .* P_X(mask1));
num0 = sum(q(yk, symbols(mask0)) .* P_X(mask0));
for i = 1:m % Use the ratio of posteriors P(bit=1|y)/P(bit=0|y)
% Extract i-th bit for each symbol LLR_exact(k,b) = log(num1) - log(num0); % natural log
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
end 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 end
function [data_,reference_]=trimseq(data,reference,skipstart,skip_end) % Bitwise MI
MI = zeros(1,m);
data_ = data(skipstart+1:end-skip_end,:); for b = 1:m
llr_b = LLR_exact(:,b);
delta_bits = length(reference) - length(data); idx0 = (tx_bits(:,b)==0);
idx1 = ~idx0;
skip_end = delta_bits + skip_end; I0 = mean( log2(1 + exp( llr_b(idx0))) );
I1 = mean( log2(1 + exp(-llr_b(idx1))) );
reference_ = reference(skipstart+1:end-skip_end,:); 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 end

View File

@@ -1,20 +1,28 @@
function [snr_all, snr_per_level] = calc_snr(tx_signal, eq_noise) 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. % 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) % [snr_all, snr_per_level] = calc_snr(tx_signal, eq_noise)
% %
% Inputs: % Inputs:
% tx_signal - Vector of transmitted signal values. % tx_signal - Vector of transmitted signal values.
% eq_noise - Vector of corresponding noise samples. % eq_noise - Vector of corresponding noise samples.
% %
% Outputs: % Outputs:
% snr_all - Overall SNR computed using all signal values. % snr_all - Overall SNR computed using all signal values.
% snr_per_level - A vector where each element is the SNR computed % snr_per_level - A vector where each element is the SNR computed
% for a unique amplitude level in tx_signal. % for a unique amplitude level in tx_signal.
% %
% The function first computes the overall SNR using the full signal vectors. % 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 % Then it uses the unique levels in tx_signal to calculate the SNR for
% the symbols corresponding to each level separately. % 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 % Calculate overall SNR using the complete signals
snr_all = snr(tx_signal, eq_noise); snr_all = snr(tx_signal, eq_noise);
@@ -23,8 +31,6 @@ function [snr_all, snr_per_level] = calc_snr(tx_signal, eq_noise)
levels = unique(tx_signal); levels = unique(tx_signal);
% Preallocate an array to store the SNR for each unique level % 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 % Loop over each unique level to compute the SNR for that level
for i = 1:length(levels) for i = 1:length(levels)
% Find indices where tx_signal equals the current level % Find indices where tx_signal equals the current level
@@ -32,5 +38,6 @@ function [snr_all, snr_per_level] = calc_snr(tx_signal, eq_noise)
% Compute the SNR for these indices % Compute the SNR for these indices
snr_per_level(i) = snr(tx_signal(idx), eq_noise(idx)); snr_per_level(i) = snr(tx_signal(idx), eq_noise(idx));
end end
end end

View File

@@ -25,33 +25,45 @@ end
[test_signal,reference_signal]=trimseq(test_signal,reference_signal,options.skip_front,options.skip_end); [test_signal,reference_signal]=trimseq(test_signal,reference_signal,options.skip_front,options.skip_end);
% CALC EVM % 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)
function [std_total,std_lvl] = 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.
assert(length(test_signal) == length(reference_signal),"Sequence length does not match"); % Ensure input is column vector
test_signal = test_signal(:);
reference_signal = reference_signal(:);
error_vector = (test_signal-reference_signal); assert(length(test_signal) == length(reference_signal), "Sequence length does not match");
%%% Overall EVM % Find unique levels and their minimum spacing
std_total = std(test_signal);
test_signal = test_signal ./ rms(test_signal);
try
%%% Per Level EVM
k = unique(reference_signal); k = unique(reference_signal);
for lvl = 1:length(k) d_min = min(diff(k)); % Minimum distance between adjacent levels
% lvl_errors = error_vector(reference_signal==k(lvl));
std_lvl(lvl) = std(test_signal(reference_signal==k(lvl)));
end
catch
std_lvl = NaN;
warning('No EVM per level calculated')
end
end % 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)
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
end
function [data_,reference_] = trimseq(data,reference,skipstart,skip_end) function [data_,reference_] = trimseq(data,reference,skipstart,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() function beautifyBERplot(options)
% BEAUTIFYBERPLOT Enhances a BER plot for publication-quality figures. % 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 end

View File

@@ -12,13 +12,13 @@ arguments
options.storage_path options.storage_path
end end
% database = DBHandler("pathToDB",[options.database_path,options.database_name],"type","sqlite"); database = DBHandler("pathToDB",[options.database_path,options.database_name],"type","sqlite");
database = DBHandler("type","mysql"); % database = DBHandler("type","mysql");
filterParams = database.tables; filterParams = database.tables;
filterParams.Configurations = struct('run_id', run_id); 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.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_attenuation'};
@@ -28,8 +28,11 @@ dataTable = dataTable(uniqueIdx,:); % Extract unique configurations for each ru
fsym = dataTable.symbolrate; fsym = dataTable.symbolrate;
M = double(dataTable.pam_level); M = double(dataTable.pam_level);
try
duob_mode = db_mode.(strrep(char(dataTable.db_mode),'"','')); duob_mode = db_mode.(strrep(char(dataTable.db_mode),'"',''));
catch
duob_mode = db_mode(dataTable.db_mode);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
len_tr = 4096*2; len_tr = 4096*2;
@@ -48,15 +51,14 @@ mu_ffe3 = 0.001;
mu_dfe = 0.0004; mu_dfe = 0.0004;
mu_dc = 0.00; 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]; mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3];
vnle_order=[vnle_order1,vnle_order2,vnle_order3]; 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 % Overwrite default parameters if given in options.parameters
paramStruct = options.parameters; paramStruct = options.parameters;
if ~isempty(paramStruct) 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(); output = struct();
vnle_pf_package = {}; vnle_pf_package = {};
vnle_dfe_package = {}; vnle_package = {};
dbtgt_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 = load([options.storage_path, char(dataTable.tx_bits_path)]);
Tx_bits = Tx_bits.Bits; Tx_bits = Tx_bits.Bits;
@@ -99,21 +99,21 @@ found_sync = 0;
try try
Scpe_load = load([options.storage_path, char(dataTable.rx_sync_path)]); Scpe_load = load([options.storage_path, char(dataTable.rx_sync_path)]);
Scpe_cell = Scpe_load.S; 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 end
if ~found_sync if ~found_sync
Scpe_sig_raw = load([options.storage_path, char(dataTable.rx_raw_path(1))]); Scpe_sig_raw = load([options.storage_path, char(dataTable.rx_raw_path(1))]);
Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw; 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_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 end
if ~found_sync if ~found_sync
if length(Symbols_mapped.signal) == sum(Symbols_mapped.signal == Symbols.signal) if length(Symbols_mapped.signal) == sum(Symbols_mapped.signal == Symbols.signal)
warning('Could not synchronize the received signal with the stored symbols!') warning('Could not synchronize the received signal with the stored symbols!')
else 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 end
if ~found_sync if ~found_sync
warning('Could not synchronize the received signal with the stored symbols!') warning('Could not synchronize the received signal with the stored symbols!')
@@ -142,11 +142,24 @@ for occ = 1:record_realizations
% %%%%% VNLE + DFE %%%% % %%%%% VNLE + DFE %%%%
if 0 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_ = FFE_DCremoval_adaptive_mu("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
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); 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",[]); 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);
vnle_dfe_package{occ} = result;
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 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); % % 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 if vnle_pf
occ = 1; % or whatever your loop index is % occ = 1; % or whatever your loop index is
% Extract VNLE results for readability % Extract VNLE results for readability
vnle = vnle_pf_package{occ}.resultsVNLE; vnle_result = vnle_pf_package{occ}.resultsVNLE;
mlse = vnle_pf_package{occ}.resultsMLSE; mlse_result = vnle_pf_package{occ}.resultsMLSE;
% Print header % Print header
@@ -189,19 +202,19 @@ for occ = 1:record_realizations
% VNLE Results % VNLE Results
fprintf(">> VNLE Results:\n"); fprintf(">> VNLE Results:\n");
fprintf(" BER %.2e\n", vnle.BER); fprintf(" BER %.2e\n", vnle_result.BER);
fprintf(" BER (pre-code) %.2e\n", vnle.BER_precoded); fprintf(" BER (pre-code) %.2e\n", vnle_result.BER_precoded);
fprintf(" SNR: %.2f dB\n", vnle.SNR); fprintf(" SNR: %.2f dB\n", vnle_result.SNR);
fprintf(" GMI: %.4f\n", vnle.GMI); fprintf(" GMI: %.4f\n", vnle_result.GMI);
fprintf(" Linerate: %.2f Gbps\n", Symbols.fs .* floor(log2(M)*10)/10 .*1e-9); 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"); fprintf("\n");
% MLSE Results % MLSE Results
fprintf(">> MLSE Results:\n"); fprintf(">> MLSE Results:\n");
fprintf(" BER : %.2e\n", mlse.BER); fprintf(" BER : %.2e\n", mlse_result.BER);
fprintf(" BER (pre-code): %.2e\n", mlse.BER_precoded); fprintf(" BER (pre-code): %.2e\n", mlse_result.BER_precoded);
fprintf(" Channel Alpha : %.2f\n", mlse.Alpha); fprintf(" Channel Alpha : %.2f\n", mlse_result.Alpha);
fprintf("\n"); fprintf("\n");
end end
@@ -243,6 +256,7 @@ end
output.dataTable = dataTable; output.dataTable = dataTable;
output.vnle_pf_package = vnle_pf_package; output.vnle_pf_package = vnle_pf_package;
output.vnle_package = vnle_package;
output.dbtgt_package = dbtgt_package; output.dbtgt_package = dbtgt_package;
end end

View File

@@ -4,9 +4,89 @@
savePath = 'Z:\2025\ECOC Silas\ecoc_2025\'; savePath = 'Z:\2025\ECOC Silas\ecoc_2025\';
databasePath = 'C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\'; databasePath = 'C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
database_name = 'ecoc2025_loops.db'; database_name = 'ecoc2025_loops.db';
db = DBHandler("pathToDB", [databasePath, database_name],"type","mysql"); db = DBHandler("type","mysql");
% db = DBHandler("type","mysql"); % db = DBHandler("pathToDB", [databasePath, database_name],"type","sqlite");
num_occ = 5;
run_par = false; filterParams = db.tables;
run_id = 1958; % filterParams.Configurations = struct('run_id', 4001);
[out, future] = submit_dsp(run_id, databasePath, database_name, savePath,"parallel",run_par,'max_occurences',num_occ); 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; [dataTable,sql_query] = db.queryDB(fp, db.getTableFieldNames('Runs'));
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);
[~, uniqueIdx] = unique(dataTable.run_id); % Get unique run_id indices [~, uniqueIdx] = unique(dataTable.run_id); % Get unique run_id indices
dataTable = dataTable(uniqueIdx,:); % Extract unique configurations for each run_id
fsym = dataTable.symbolrate; dataTable.SIR = -7 - round(dataTable.power_mpi_interference);
M = double(dataTable.pam_level);
duob_mode = db_mode.(strrep(char(dataTable.db_mode),'"',''));
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)]); fsym = dataTable_.symbolrate;
Tx_bits = Tx_bits.Bits; M = double(dataTable_.pam_level);
Symbols_mapped = PAMmapper(M,0).map(Tx_bits); duob_mode = db_mode.(strrep(char(dataTable_.db_mode),'"',''));
Symbols_mapped.fs = dataTable.symbolrate;
Symbols = load([savePath, char(dataTable.tx_symbols_path)]); Tx_signal = load([savePath, char(dataTable_.tx_signal_path)]);
Symbols = Symbols.Symbols; Tx_signal = Tx_signal.Digi_sig;
Scpe_sig_raw = load([savePath, char(dataTable.rx_raw_path(1))]); Tx_bits = load([savePath, char(dataTable_.tx_bits_path)]);
Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw; Tx_bits = Tx_bits.Bits;
Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in",Scpe_sig_raw.fs,"fs_out",2*fsym); Symbols_mapped = PAMmapper(M,0).map(Tx_bits);
[~,Scpe_cell,found_sync,~,shifts] = Scpe_sig_resampled.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",1); Symbols_mapped.fs = dataTable_.symbolrate;
shifts_mus = shifts./Scpe_sig_resampled.fs .*1e6; Symbols = load([savePath, char(dataTable_.tx_symbols_path)]);
Scpe_sig_raw.plot("displayname",['Scope Signal (Run ID: ',num2str(run_id)],"fignum",2024,"clear",1); Symbols = Symbols.Symbols;
hold on;
xline(shifts_mus,'HandleVisibility','off'); 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 % dsp_options.database_type = 'mysql';
mkr = '.'; % dsp_options.dataBase = 'labor';
hold on % 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 dataTable_clean = dataTable;
x_values = dataTableGrpd_mean.(x_var)(loopFiltGrpd, :); dataTable_clean.SIR = -7 - round(dataTable_clean.power_mpi_interference);
y_mean = dataTableGrpd_mean.(y_var)(loopFiltGrpd, :); dataTable_clean.NGMI = dataTable_clean.GMI ./ log2(dataTable_clean.pam_level);
y_min = dataTableGrpd_min.(y_var)(loopFiltGrpd, :); dataTable_clean = cleanUpTable(dataTable_clean);
y_max = dataTableGrpd_max.(y_var)(loopFiltGrpd, :);
% Compute bounds: distance from mean dataTable_clean(dataTable_clean.BER>0.2,:) = [];
y_lower = y_mean - y_min;
y_upper = y_max - y_mean;
y_bounds = [y_lower, y_upper];
% 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 cols = linspecer(8); % Ensure color count matches
set(hl, 'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ... figure()
'Color', cols(i,:), 'DisplayName', string(dispname)); 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;
% Hide patch (shaded area) from legend
set(hp, 'HandleVisibility', 'off','LineStyle',':','LineWidth',0.5,'Marker','none');
% Add invisible scatter for DataTips for mode = [1,2]
plt = scatter(x_values, y_mean, ...
'Marker', 'o', 'MarkerEdgeColor', 'none', 'MarkerFaceColor', 'none', ... hold on;
'HandleVisibility', 'off', 'PickableParts', 'all'); dataTable = dataTable_clean;
else
plt= plot(x_values,y_mean,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ...
'Color', cols(i,:), 'DisplayName', string(dispname)); plotBoundaries = 1;
end plotRealizations = 1;
% Add data tips to the invisible scatter cols = linspecer(8); % Ensure color count matches
pair_one = {'Run ID', dataTableGrpd_mean.run_id(loopFiltGrpd, :)};
pair_two = {'Rate', dataTableGrpd_mean.bitrate(loopFiltGrpd, :) * 1e-9}; if mode == 1
pair_three = {'PD in', round(dataTableGrpd_mean.power_pd_in(loopFiltGrpd, :), 2)}; % No compensation method
addDatatips(plt, pair_one, pair_two, pair_three); 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');
% Optionally: outline bounds for better visibility (optional) % Fit a 4th-order polynomial to log10(BER)
% hnew = outlinebounds(hl, hp); p = polyfit(x_values, log10(y_mean), 3); % 4 is fitting order, adjust as needed
% Tick marks (x-axis) % Evaluate the fitted polynomial
xticks(round(unique(x_values),2)); 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
% 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));
pair_one = {'Run ID', dataTable.run_id(loopFiltSingle, :)}; plot(x_fit,y_fit,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ...
pair_two = {'Rate', dataTable.bitrate(loopFiltSingle, :) * 1e-9}; 'Color', cols(i,:), 'DisplayName', string(dispname),'HandleVisibility','off');
pair_three = {'PD in', round(dataTable.power_pd_in(loopFiltSingle, :), 2)};
addDatatips(sc, pair_one, pair_two, pair_three); % % 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;
end end
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) function resultTable = groupIt(fixedVars, dataTable, aggregationFunction)
% groupIt Groups data in a table based on fixedVars and applies aggregationFunction to numeric data. % groupIt Groups data in a table based on fixedVars and applies aggregationFunction to numeric data.
@@ -243,7 +409,6 @@ resultTable.nRows = groupCount;
end end
function addDatatips(sc, varargin) function addDatatips(sc, varargin)
% addDatatips Adds custom data tip rows to a scatter plot. % addDatatips Adds custom data tip rows to a scatter plot.
% %
@@ -283,7 +448,6 @@ end
end end
function cleanedTable = cleanUpTable(inputTable) function cleanedTable = cleanUpTable(inputTable)
% cleanUpTable Cleans a MATLAB table where numbers and NaNs are stored as strings or structs. % 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 else
% Try convert to datetime % Try convert to datetime
try 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 catch
% Leave as string % Leave as string
end end
@@ -391,8 +555,9 @@ for groupIdx = 1:height(groupKeys)
continue; continue;
end end
% Detect outliers % Detect outliers in log space
outlierMask = isoutlier(y_values, 'quartiles'); y_log = log10(y_values);
outlierMask = isoutlier(y_log, 'quartiles',1); % or 'median', 'grubbs', etc.
% If any outliers found, collect their data % If any outliers found, collect their data
if any(outlierMask) if any(outlierMask)

View File

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

View File

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

View File

@@ -124,7 +124,7 @@ for occ = 1:proc_occ
% %%%%% VNLE + DFE %%%% % %%%%% VNLE + DFE %%%%
if 0 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); 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",[]); [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\'; 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 = database.tables;
filterParams.Configurations = struct( ... filterParams.Configurations = struct( ...
'bitrate', [], ... %[224,336,360,390,420,448] 'bitrate', [], ... %[224,336,360,390,420,448]
'db_mode', int32(db_mode.no_db), ... 'db_mode', [], ...
'fiber_length', 1, ... 'fiber_length', 1, ...
'interference_attenuation', [], ... 'interference_attenuation', [], ...
'interference_path_length', [], ... 'interference_path_length', [], ...
'is_mpi', 1, ... 'is_mpi', 0, ...
'pam_level', 4, ... 'pam_level', 4, ...
'rop_attenuation', 0, ... 'rop_attenuation', 0, ...
'wavelength', 1310 ... 'wavelength', 1310 ...
); );
% filterParams.EqualizerParameters.diff_precode = int32(db_mode.no_db); % 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; % 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' '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); [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_precode = 0;
db_coding_approach = 0; db_coding_approach = 0;
fsym = 224e9; fsym = 160e9;
fdac = 256e9; fdac = 256e9;
random_key = 0; 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 if M == 4
pulsef=1; pulsef=1;
precomp_amp_max = -50; precomp_amp_max = -50;
elseif M == 6 fsym = 196e9;
pulsef=0; elseif M == 6
precomp_amp_max = -50; pulsef=0;
elseif M == 8 precomp_amp_max = -50;
pulsef=0; fsym = 180e9;
precomp_amp_max = -50; 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 end
elseif (db_precode==1)&&(db_coding_approach==1)
if M == 4 rcalpha = 0.05;
pulsef=1; Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha);
precomp_amp_max = -38;
pulsef = 1; Pamsource = PAMsource(...
elseif M == 6 "fsym",fsym,"M",M,"order",19,"useprbs",0,...
pulsef=0; "fs_out",fdac,...
precomp_amp_max = -38; "applyclipping",0,"clipfactor",1.2,...
pulsef = 1; "applypulseform",pulsef,"pulseformer",Pform,...
elseif M == 8 "randkey",random_key,...
pulsef=0; "db_precode",db_precode,"db_encode",db_coding_approach,...
precomp_amp_max = -38; "mrds_code",0,"mrds_blocklength",512);
pulsef = 1;
[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 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 end
ylim([-25,10]);
xlim([0,105]);
xticks(0:20:110);
yticks(-30:10:10);
rcalpha = 0.05; fig = gcf;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"rrcalpha",rcalpha); pos = [536.3333 879 450 222];
set(fig, 'Position', pos);
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);

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); a = load(filename);
wh = a.obj; wh = a.obj;
@@ -26,12 +26,15 @@ clf
hold on hold on
cols = cbrewer2('Set1',3); cols = cbrewer2('Set1',3);
for l = 1:numel(lambda_vals) for l = 1:numel(lambda_vals)
figure()
for m = 1:numel(M_vals) 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_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)); 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) for e = 1:numel(exfo)
laser_pow(e) = exfo{e}.cur_power; laser_pow(e) = exfo{e}.cur_power;
end end
@@ -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)); 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 hold on
a = scatter(v_bias_vals,min(ber,[],2),40,'LineWidth',2,'Marker','.','DisplayName',['PAM ',num2str(M_vals(m))],'MarkerEdgeColor',cols(m,:)); 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']) title([num2str(lambda_vals(l)),'nm'])
@@ -66,7 +69,7 @@ for l = 1:numel(lambda_vals)
% Polynomial fit (e.g., second-order polynomial) % Polynomial fit (e.g., second-order polynomial)
[woutliers,n] = rmoutliers( min(ber,[],2) ); [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); BER_fit = polyval(p, v_bias_vals);
@@ -93,9 +96,10 @@ for l = 1:numel(lambda_vals)
end end
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); a = load(filename);
wh = a.obj; wh = a.obj;

View File

@@ -1,13 +1,13 @@
basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; 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 = database.tables;
filterParams.Configurations = struct( ... filterParams.Configurations = struct( ...
'bitrate', [], ... %[224,336,360,390,420,448] 'bitrate', [390e9], ... %[224,336,360,390,420,448]
'db_mode', int32(db_mode.db_encoded), ... 'db_mode', 1, ...
'fiber_length', 10, ... 'fiber_length', 1, ...
'interference_attenuation', [], ... 'interference_attenuation', [], ...
'interference_path_length', [], ... 'interference_path_length', [], ...
'is_mpi', 0, ... 'is_mpi', 0, ...
@@ -18,11 +18,11 @@ filterParams.Configurations = struct( ...
% filterParams.EqualizerParameters.diff_precode = int32(db_mode.db_encoded); % filterParams.EqualizerParameters.diff_precode = int32(db_mode.db_encoded);
% filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle); % 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'... 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' ... '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'}; '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,sql_query] = database.queryDB(filterParams, selectedFields);

View File

@@ -1,11 +1,11 @@
basePath = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\'; 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 = database.tables;
filterParams.Configurations = struct( ... 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), ... 'db_mode', int32(db_mode.no_db), ...
'fiber_length', 10, ... 'fiber_length', 10, ...
'interference_attenuation', [], ... 'interference_attenuation', [], ...

View File

@@ -15,7 +15,7 @@ if 1
wh.addStorage("ber"); wh.addStorage("ber");
% wh = submit_simulations(wh,"parallel",0,"simulation_mode",0); % 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 end

View File

@@ -1,6 +1,6 @@
function [output] = imdd_model(varargin) function [output] = imdd_model(varargin)
simulation_mode = 0; simulation_mode = 1;
%%% Change folder %%% Change folder
curFolder = pwd; curFolder = pwd;
@@ -134,6 +134,7 @@ if fsym_ ~= fsym
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); % 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 end
f_nyquist = fsym/2; f_nyquist = fsym/2;
%%% run the simulation or measurement or ... %%% 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(... [Digi_sig,Symbols,Tx_bits] = PAMsource(...
"fsym",fsym,"M",M,"order",19,"useprbs",1,... "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 = ChannelFreqResp("Nacq",1024,"Navg",64,"Ncp",63,'f_ref',92e9);
freqresp.load('loadPath',precomp_path,'fileName',precomp_filename); freqresp.load('loadPath',precomp_path,'fileName',precomp_filename);
freqresp.plot(); 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.estimate(Digi_sig,"fileName",'','save',false);
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
Digi_sig.spectrum("fignum",1112,"displayname",['Signal']); freqresp.plot()
maxamp = -1; a = gca;
El_sig = freqresp.precomp(Digi_sig,"maxampdb",maxamp); a.YTick = [-30,-20,-10,0];
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)]);

View File

@@ -1,65 +1,49 @@
useprbs = 1;
M = 6; M = 6;
randkey = 1; apply_precode = 1;
datarate = 224e9;
fsym = round(datarate / log2(M)) ;
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(... bits = Informationsignal(bitpattern);
"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();
%%%CHANNEL symbols = PAMmapper(M,0).map(bits);
% s = RandStream('twister','Seed',2); bits_rx = PAMmapper(M,0).demap(symbols);
% start = 10000; [~,~,ber_direct,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
% 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
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); show2Dconstellation(symbols_tx,symbols_tx,"displayname",'VNLE Out','fignum',2241);
d_eq = eq_ffe.process(d_resample,Symbols);
% 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]) % 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 % Entschiedene codierte Symbole decodieren: d_dec(n) = d_DB(n) mod4
d_dec = Duobinary().decode(d_db); symbols_rx = Duobinary().decode(symbols_db);
else else
d_dec = d_burst; symbols_rx = symbols_tx;
end end
% Vergleichen von b(n) und d_dec(n) % Vergleichen von b(n) und d_dec(n)
Rx_bits = PAMmapper(M,0).demap(d_dec); bits_rx = PAMmapper(M,0).demap(symbols_rx);
[~,~,ber,~] = calc_ber(bits.signal,bits_rx.signal,"skip_front",10,"skip_end",10,"returnErrorLocation",1);
Tx_bits = Bits;
[~,error_num,ber,error_pos] = calc_ber(Tx_bits.signal,Rx_bits.signal,"skip_front",0,"skip_end",0,"returnErrorLocation",1);
disp(['BER: ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]); disp(['BER: ',sprintf('%.1E',ber),' - - PAM-',num2str(M)]);

View File

@@ -46,66 +46,55 @@ end
Tx_bits = Informationsignal(bitpattern); Tx_bits = Informationsignal(bitpattern);
%%%%% Duobinary %%%%%% %%%%% Duobinary %%%%%%
precode = 1;
db_encode = 0;
close all 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; Symbols_tx.fs = fsym;
precode = db_mode.db_precoded;
%%% precode %%% precode
if precode switch precode
Symbols0 = Duobinary().precode(Symbols_tx); case db_mode.db_precoded
else Symbols_tx = Duobinary().precode(Symbols_tx);
Symbols0 = 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 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; pos = 1;
if n~=0 if n~=0
for pos = 1:n for pos = 1:n
po = randi(100); 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 while a == 1
po = po+1; po = po+1;
po = randi(100); po = randi(100);
a = Symbols2.signal(100+pos) == Symbols1.signal(100+po); a = Symbols_rx.signal(100+pos) == Symbols_tx.signal(100+po);
end end
Symbols2.signal(100+pos) = Symbols1.signal(100+po); Symbols_rx.signal(100+pos) = Symbols_tx.signal(100+po);
end end
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
if db_encode 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);
% 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 end
Rx_bits = PAMmapper(M,0).demap(Symbols3); Rx_bits = PAMmapper(M,0).demap(Symbols_rx);
%%%%% Check BER of Bit Sequence %%%%%% %%%%% 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)]); % 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)); fprintf('n: %d - Num. Err: %.1d \n',n,error_num(n+1));
end end
figure()
hold on
scatter(1:length(Symbols3),Symbols3.signal,1,'.');
scatter(error_pos,Symbols3.signal(error_pos),14,'o');
figure(3); figure(3);
clf clf
@@ -128,8 +113,8 @@ clf
subplot(2,2,1) subplot(2,2,1)
hold on hold on
title('First Bits') title('First Bits')
stairs(Tx_bits.signal(1:100,1),'LineStyle','-','LineWidth',2,'DisplayName','Tx Bits'); stairs(Tx_bits.signal(100:150,1),'LineStyle','-','LineWidth',2,'DisplayName','Tx Bits');
stairs(Rx_bits.signal(1:100,1),'LineWidth',2,'DisplayName','Rx Bits','LineStyle',':') stairs(Rx_bits.signal(100:150,1),'LineWidth',2,'DisplayName','Rx Bits','LineStyle',':')
legend legend
subplot(2,2,2) subplot(2,2,2)
@@ -143,12 +128,12 @@ subplot(2,2,3)
hold on hold on
title('First Symbols Compare') title('First Symbols Compare')
stairs(Symbols_tx.signal(1:100,1),'LineWidth',2,'DisplayName','Tx Symbols','LineStyle','-') 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 legend
subplot(2,2,4) subplot(2,2,4)
hold on hold on
title('Last Symbols Compare') title('Last Symbols Compare')
stairs(Symbols_tx.signal(end-50:end,1),'LineWidth',2,'DisplayName','Tx Symbols','LineStyle','-') 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 legend