FSO analysis:

- minimal changes in several files
- new Timing Rec from Magnus
This commit is contained in:
Silas Oettinghaus
2026-01-20 10:49:59 +01:00
parent ad14cd738f
commit 719e5508e7
14 changed files with 511 additions and 130 deletions

View File

@@ -971,8 +971,8 @@ classdef Signal
maxA = max(sig(100:end-100))*1.3; maxA = max(sig(100:end-100))*1.3;
minA = min(sig(100:end-100))*1.3; minA = min(sig(100:end-100))*1.3;
% maxA = 0.12; maxA = 0.12;
% minA = -0.08; minA = -0.08;
difference= maxA-minA; difference= maxA-minA;
@@ -993,7 +993,9 @@ classdef Signal
% beautify % beautify
cm=flip(cbrewer2("RdYlBu",4096)); cm=flip(cbrewer2("RdYlBu",4096));
% cm=flip(cbrewer2("RdBu",4096)); % cm=flip(cbrewer2("RdBu",4096));
% cm=flip(cbrewer2("Blues",4096));
cm(1,:) = [1,1,1]; % set zeros to white => clean background cm(1,:) = [1,1,1]; % set zeros to white => clean background
colormap(cm); colormap(cm);
% colormap('turbo'); % colormap('turbo');
% ax.CLim = [0 50]; % ax.CLim = [0 50];

View File

@@ -9,7 +9,7 @@ classdef Timing_Recovery < handle
end end
methods(Access=public) methods(Access=public)
function obj = FFE(options) function obj = Timing_Recovery(options)
arguments(Input) arguments(Input)
options.timing_error_detector = 'Gardner'; options.timing_error_detector = 'Gardner';
@@ -25,12 +25,11 @@ classdef Timing_Recovery < handle
obj.(fn{n}) = options.(fn{n}); obj.(fn{n}) = options.(fn{n});
end end
obj.e = zeros(obj.order,1);
obj.error = 0;
end end
function data_out = process(obj, data_in) function [data_out,timing_error] = process(obj, data_in)
timing_synchronization = comm.SymbolSynchronizer( ... timing_synchronization = comm.SymbolSynchronizer( ...
"TimingErrorDetector", obj.timing_error_detector, ... "TimingErrorDetector", obj.timing_error_detector, ...
@@ -39,7 +38,8 @@ classdef Timing_Recovery < handle
"NormalizedLoopBandwidth", obj.normalized_loop_bandwidth, ... "NormalizedLoopBandwidth", obj.normalized_loop_bandwidth, ...
"DetectorGain", obj.detector_gain); "DetectorGain", obj.detector_gain);
data_out.signal = timing_synchronization(data_in.signal); data_out = data_in;
[data_out.signal,timing_error] = timing_synchronization(data_in.signal);
end end
end end

View File

@@ -95,7 +95,7 @@ try
adaption= 1; adaption= 1;
use_dd_mode = 1; use_dd_mode = 1;
use_ffe = 0; use_ffe = 1;
use_dfe = 0; use_dfe = 0;
use_vnle_mlse = 0; use_vnle_mlse = 0;
use_dbtgt = 0; use_dbtgt = 0;
@@ -149,6 +149,9 @@ try
% Scpe_sig.spectrum("fignum",22233,"normalizeTo0dB",0,"displayname",'Rx'); % Scpe_sig.spectrum("fignum",22233,"normalizeTo0dB",0,"displayname",'Rx');
% Scpe_sig.eye(fsym,M,"fignum",1024); % Scpe_sig.eye(fsym,M,"fignum",1024);
Scpe_sig.signal = Scpe_sig.signal(1:2*Symbols.length);
Scpe_sig.signal = real(Scpe_sig.signal);
if duob_mode ~= db_mode.db_encoded if duob_mode ~= db_mode.db_encoded
if use_ffe if use_ffe
@@ -246,7 +249,7 @@ try
%ML-based MLSE (L=2) %ML-based MLSE (L=2)
mu_ml = 0.01; training_epochs = 100; mu_ml = 0.01; training_epochs = 100;
ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ... ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ...
"len_tr",length(Scpe_sig),"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ... "len_tr",length(Scpe_sig)/4,"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",2, ...
"traceback_depth",128,"L",1,"delta",4,"adaptive_mu",0); "traceback_depth",128,"L",1,"delta",4,"adaptive_mu",0);
[ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Scpe_sig, Symbols, Tx_bits,"precode_mode",duob_mode); [ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Scpe_sig, Symbols, Tx_bits,"precode_mode",duob_mode);

View File

@@ -157,7 +157,7 @@ function displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, tx_symbols, M,
% Initialize figure handles % Initialize figure handles
% Corrected line - added tx_symbols as second positional argument % Corrected line - added tx_symbols as second positional argument
showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 100); % showLevelScatter(rx_signal.resample("fs_out", tx_symbols.fs), tx_symbols, "fignum", 100);
warning off warning off
showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 101); showLevelScatter(eq_signal_sd, tx_symbols, "fignum", 101);
@@ -174,19 +174,20 @@ function displayAnalysis(eq_noise, eq_signal_sd, rx_signal, eq_, tx_symbols, M,
end end
try try
figure(339); figure(339);hold on
showEQfilter(eq_.e_tr, eq_signal_sd.fs.*2,"displayname",'training','fignum',339); showEQfilter(eq_.e, eq_signal_sd.fs.*2,"displayname",'training','fignum',339);
showEQfilter(eq_.e, eq_signal_sd.fs.*2,"displayname",'dec. directed','fignum',339); % showEQfilter(eq_.e, eq_signal_sd.fs.*2,"displayname",'dec. directed','fignum',339);
legend on legend on
end end
try % try
figure(240); hold on; plot(pow2db(movmean(eq_.debug_struct.error_tr',100)));ylim([-30,3]);title('error training'); % figure(240); hold on; plot(pow2db(movmean(eq_.debug_struct.error_tr',100)));ylim([-30,3]);title('error training');
%
% figure(241); hold on; plot(pow2db(movmean(eq_.debug_struct.update_tr',100)));title('update step training');
%
% figure(242); hold on; plot(pow2db(movmean(eq_.debug_struct.update',1000)));title('update step dd');
% end
figure(241); hold on; plot(pow2db(movmean(eq_.debug_struct.update_tr',100)));title('update step training'); eq_signal_sd.eye(eq_signal_sd.fs,M,"displayname",'Eye','fignum',105);
figure(242); hold on; plot(pow2db(movmean(eq_.debug_struct.update',1000)));title('update step dd');
end
% eq_signal_sd.eye(eq_signal_sd.fs,M,"displayname",'Eye','fignum',105);
end end

View File

@@ -45,8 +45,7 @@ end
ml_mlse_results.config = Equalizerstruct(); ml_mlse_results.config = Equalizerstruct();
eq_small = strip_eq(eq_, 10); eq_ = strip_eq(eq_, 10);
json_str = jsonencode(eq_small);
ml_mlse_results.config.eq = jsonencode(eq_); ml_mlse_results.config.eq = jsonencode(eq_);
ml_mlse_results.config.equalizer_structure = int32(equalizer_structure.ml_mlse); ml_mlse_results.config.equalizer_structure = int32(equalizer_structure.ml_mlse);
@@ -135,6 +134,9 @@ function eq_out = strip_eq(eq_, max_elems)
eq_.(props{i}) = []; eq_.(props{i}) = [];
end end
end end
if issparse(val)
eq_.(props{i}) = find(eq_.(props{i}));
end
end end
eq_out = eq_; eq_out = eq_;
end end

View File

@@ -283,7 +283,7 @@ if ~isempty(postFFE)
showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338); showEQcoefficients('n1', postFFE.e, "displayname", 'Coefficients', 'fignum', 338);
end end
showEQcoefficients('n1', eq_.e,'n2', eq_.e2,'n3', eq_.e3, "displayname", 'Coefficients', 'fignum', 339); % showEQcoefficients('n1', eq_.e1,'n2', eq_.e2,'n3', eq_.e3, "displayname", 'Coefficients', 'fignum', 339);
showEQfilter(eq_.e, eq_signal_sd.fs.*2); showEQfilter(eq_.e, eq_signal_sd.fs.*2);
figure(340); clf; figure(340); clf;

View File

@@ -1,15 +1,15 @@
base = "C:\Users\Silas\Nextcloud\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC"; base = "C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC";
mode = 0; %0 oder 1 mode = 0; %0 oder 1
M = 2; M = 2;
all_files = dir(fullfile(base, "**/*.mat")); all_files = dir(fullfile(base, "**/*.mat"));
if M == 2 if M == 2
tx_data = load("C:\Users\Silas\Nextcloud\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC\14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat"); tx_data = load("C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC\14G_PAM2\tx_info\tx_info_PAM2_14Gbd0.75RRC.mat");
filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat"); filename = fullfile(base, "14G_PAM2\M=2_Rs=1.4e10_Fs=8e10_I=265mA_RoP=46.3mW_L=31m_PS=RRC_rolloff=0.75_Mode=Rise.mat");
elseif M == 4 elseif M == 4
tx_data = load("C:\Users\Silas\Nextcloud\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC\6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat"); tx_data = load("C:\Users\Silas\Nextcloud4\Dokumente\02_Ablage_Office\FSO_FP_QCL_60umUTC\6G_PAM4\tx_info\tx_info_PAM4_6Gbd0.6RRC.mat");
filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat"); filename = fullfile(base, "6G_PAM4\M=4_Rs=6e9_Fs=8e10_I=255mA_RoP=42.3mW_L=31m_PS=RRC_rolloff=0.6_Mode=Rise.mat");
end end
@@ -53,10 +53,14 @@ Bits_ = PM.demap(Symbols);
assert(ber == 0); assert(ber == 0);
%% For comparison, apply pulsef on Tx Symbols %% For comparison, apply pulsef on Tx Symbols
Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rc","pulselength",16,"alpha",rolloff); Pform = Pulseformer("fsym",fsym,"fdac",fs,"pulse","rrc","pulselength",16,"alpha",rolloff);
Digi_sig_compare = Pform.process(Symbols); Digi_sig_tx_compare = Pform.process(Symbols);
MF = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
Rx_sig_compare = MF.process(Digi_sig_compare); Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
Digi_sync = Pform.process(Symbols);
MF = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff);
Rx_sig_compare = MF.process(Digi_sig_tx_compare);
%% %%
@@ -68,53 +72,47 @@ scoperead_volts = (traceData.RawData - traceData.YReference) * traceData.YIncrem
demystified = isequal(traceData.YData,scoperead_volts); demystified = isequal(traceData.YData,scoperead_volts);
assert(demystified); assert(demystified);
timesig_compare = [0:1:datas.tr.lastData(1).trace.ch3.Points-1] ./ fs;
timesig = datas.tr.lastData(1).trace.ch3.XData;
assert(isequal(traceData.YData,scoperead_volts));
Scope_sig = Electricalsignal(traceData.YData,"fs",fs); Scope_sig = Electricalsignal(traceData.YData,"fs",fs);
Scope_sig.plot("displayname",'raw','fignum',100); %%
Scope_sig.spectrum("displayname",'raw','fignum',101)
% 1) matched filter % 1) matched filter
% pulse is symmetric, hence we can use pulsef firectly as matched filter. % pulse is symmetric, hence we can use pulsef directly as matched filter.
% It feels off (bit I think correct) that the fsym is now the output freq.!! % It feels off (bit I think correct) that the fsym is now the output freq.!!
% -> output 2 sps to omit timing recovery!? % -> output 2 sps to omit timing recovery!?
Pform = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1); apply_matched_filter = 0;
Rx_matched = Pform.process(Scope_sig); k = 4;
Rx_matched.spectrum("displayname",'Signal after matched filter','fignum',1); if apply_matched_filter
Pform = Pulseformer("fsym",fsym,"fdac",k*fsym,"pulse","rrc","pulselength",16,"alpha",rolloff,"matched",1);
Rx_matched = Pform.process(Scope_sig);
else
%% Rx_matched = Filter('filtdegree',4,"f_cutoff",fsym*0.5,"fs",Scope_sig.fs,"filterType",filtertypes.gaussian,"active",true).process(Scope_sig);
Rx_matched = Rx_matched.resample("fs_out",k*fsym);
end
Rx_matched.spectrum();
sys = comm.SymbolSynchronizer('TimingErrorDetector', 'Gardner (non-data-aided)', ... %% Timing Rec
'SamplesPerSymbol', 2, ... apply_timing_rec = 1;
'DampingFactor', 0.7, ... if apply_timing_rec
'NormalizedLoopBandwidth', 0.01); [Rx_symbolsync, timing_error] = Timing_Recovery("timing_error_detector",'Gardner (non-data-aided)','sps',k,'damping_factor',0.1,'normalized_loop_bandwidth',0.1,'detector_gain',2.7).process(Rx_matched);
Rx_symbolsync = Rx_matched; figure();plot(timing_error);
[Rx_symbolsync.signal, timing_error] = sys(Rx_matched.signal); Rx_symbolsync.fs = fsym;
% Tsynch
plot(timing_error); % If this is a ramp, you have drift! [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_symbolsync.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 0);
Rx_synced = Rx_synced_cell{1};
%% timing sync -> at this point we still have no symbol timing recovery, we sps = 1;
% % try to do this with 2sps EQ! else
% Tsynch
[~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_symbolsync.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 1); [~,Rx_synced_cell,inverted,sequenceFound,sequenceStarts] = Rx_matched.tsynch("reference", Symbols, "fs_ref", fsym, "debug_plots", 0);
Rx_synced = Rx_synced_cell{1};
Rx_synced = Rx_synced.resample("fs_out",2*fsym);
%% not working.. sps = 2;
Rx_synced = Rx_synced_cell{1}; end
len_tr = 4096*2;
mu_ffe1 = 0.0001;
mu_ffe2 = 0.0008;
mu_ffe3 = 0.001;
mu_dc = 0.005;
mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
mu_dfe = 0.0004;
duob_mode = db_mode.no_db;
Rx_synced.plot("displayname",'RX: Matched+Sync+2sps','fignum',2);
Digi_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped','fignum',1,'normalizeTo0dB',0);
Rx_sig_compare.normalize("mode","rms").spectrum("displayname",'Tx: RC-shaped + matched filtered ','fignum',1,'normalizeTo0dB',0);
Rx_synced.normalize("mode","rms").spectrum("displayname",'RX: matched filtered + synced','fignum',1,'normalizeTo0dB',0);
if M == 2 if M == 2
ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature ber_in_paper = 10^(-2.6); %fig 3a) 4 Gb/s MWIR FSO Transmission using Directly Modulated QCL and an Uncooled UTC-PD at Room-Temperature
@@ -122,50 +120,121 @@ elseif M == 4
ber_in_paper = 10^(-2.5); ber_in_paper = 10^(-2.5);
end end
%% -------------------- FFE --------------------
%%
Rx_synced = Rx_synced_cell{1};
% -------------------- FFE --------------------
% requires some more digging what is going on :-) % requires some more digging what is going on :-)
eq_ffe = EQ("Ne",[50, 5, 5],"Nb",[2,0,0], ... eq_ffe = EQ("Ne",[50, 1, 1],"Nb",[2,0,0], ...
"training_length",len_tr,"training_loops",5,"dd_loops",5, ... "training_length",512,"training_loops",5,"dd_loops",5, ...
"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ... "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
"FFEmu",0,"plotfinal",0,"ideal_dfe",1); "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
ffe_results = ffe(eq_ffe,M,Rx_synced,Symbols,Bits, ... vars = logspace(-4,-3,36);
"precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ...
"eth_style_symbol_mapping",mapping_style);
% ffe_results.metrics.print parfor i = 1:numel(vars)
fprintf('My EQ: %.1e \n',ffe_results.metrics.BER);
len_tr = 4096;
mu_ffe1 = 0.01;% mus(i);%0.0001;
mu_ffe2 = 0.0008;
mu_ffe3 = 0.001;
mu_dc = 0.005;
mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3];
mu_dfe = vars(i);
duob_mode = db_mode.no_db;
% requires some more digging what is going on :-)
eq_ffe_1 = EQ("Ne",[150, 1, 0],"Nb",[50,0,0], ...
"training_length",len_tr,"training_loops",5,"dd_loops",5, ...
"K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
"FFEmu",0,"plotfinal",0,"ideal_dfe",0);
eq_ffe_2 = FFE("epochs_tr",1,"epochs_dd",vars(i),"len_tr",4096,"mu_dd",vars(i),"mu_tr",vars(i),"order",999,"sps",1,"decide",0, "adaption",adaption_method.nlms,"dd_mode",0);
% eq_ffe_2 = FFE_DFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"ffe_mu_dd",1e-5,"dfe_mu_dd",mus(i),"ffe_mu_tr",0,"dfe_mu_tr",0,"ffe_order",50,"dfe_order",10,"sps",1,"decide",1);
ffe_results = ffe(eq_ffe_1,M,Rx_synced,Symbols,Bits, ...
"precode_mode",duob_mode,'showAnalysis',1,"postFFE",[], ...
"eth_style_symbol_mapping",mapping_style);
ffe_results.metrics.BER
bers(i) = ffe_results.metrics.BER;
end
figure();
plot(vars,bers);
yline(ber_in_paper)
beautifyBERplot();
%
fprintf('Paper: %.1e \n \n',ber_in_paper); fprintf('Paper: %.1e \n \n',ber_in_paper);
ffe_results.metrics.print("description",'FFE');
fprintf('FFE: %.1e \n',ffe_results.metrics.BER);
%% -------------------- VNLE + MLSE -------------------- %% -------------------- VNLE + MLSE --------------------
len_tr = 4096;
mu_ffe1 = 0.0001;% mus(i);%0.0001;
mu_ffe2 = 0.0008;
mu_ffe3 = 0.001;
mu_dc = 0.005;
mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3];
mu_dfe = 0.0004;
duob_mode = db_mode.no_db;
pf_ncoeffs = 1; pf_ncoeffs = 4;
eq_v = EQ("Ne",[100, 5, 5],"Nb",[0, 0, 0], ...
vars = 1:7;
bers = zeros(size(vars));
parfor i = 1:numel(vars)
eqv = EQ("Ne",[200, 1, 0],"Nb",[2, 0, 0], ...
"training_length",len_tr,"training_loops",5,"dd_loops",5, ... "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ... "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
"FFEmu",0,"plotfinal",0,"ideal_dfe",1); "FFEmu",0,"plotfinal",0,"ideal_dfe",1);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1); pf_ncoeffs = vars(i);
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels); pf = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0,"eth_style",mapping_style).levels);
[vnle_results, mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Rx_synced, Symbols, Bits, ... [vnle_results, mlse_results] = vnle_postfilter_mlse(eqv, pf, mlse_, M, Rx_synced, Symbols, Bits, ...
"precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style); "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", mapping_style);
fprintf('Paper: %.1e \n \n',ber_in_paper);
mlse_results.metrics.print("description",'MLSE') vnle_results.metrics.print("description",'VNLE');
fprintf('My EQ: %.1e \n',mlse_results.metrics.BER); mlse_results.metrics.print("description",'MLSE');
fprintf('Paper: %.1e \n \n',ber_in_paper); bers(i) = mlse_results.metrics.BER;
end
%%
figure();hold on
plot(vars,bers_ffe,'DisplayName','FFE [200,0,0] + PF + MLSE');
plot(vars,bers_vnle,'DisplayName','VNLE [200,1,0] + PF + MLSE');
plot(vars,bers_vnledfe,'DisplayName','VNLE [200,1,0] + DFE [2] + PF + MLSE');
plot(vars,bers_vnledfe_ideal,'DisplayName','VNLE [200,1,0] + ideal DFE [2] + PF + MLSE');
yline(ber_in_paper);
yline([2e-2, 4.85e-3, 3.8e-3, 2,2e-4],'LineWidth',2,'Color',[0.8,0.8,0.8],'LineStyle',':','HandleVisibility','off');
ylim([1e-5,0.1]);
beautifyBERplot();
%% -------------------- DB target -------------------- %% -------------------- DB target --------------------
mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels); mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels);
eq_ = EQ("Ne",[50, 5, 5],"Nb",[0,0,0],"training_length",len_tr,"training_loops",5,"dd_loops",5, ... eq_ = EQ("Ne",[50, 5, 5],"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",1); "K",sps,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ... dbt_results = duobinary_target(eq_,mlse_db_, M, Rx_synced, Symbols, Bits, ...
"precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style); "precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [],"eth_style_symbol_mapping",mapping_style);
dbt_results.metrics.print("description",'Duobinary');
mlse_results.metrics.print
fprintf('My EQ: %.1e \n',dbt_results.metrics.BER);
fprintf('Paper: %.1e \n \n',ber_in_paper); fprintf('Paper: %.1e \n \n',ber_in_paper);
dbt_results.metrics.print("description",'Duobinary');
%%
%ML-based MLSE (L=2)
mu_ml = 0.01; training_epochs = 100;
ml_mlse_equalizer = ML_MLSE("epochs_tr",training_epochs,"epochs_dd",1, ...
"len_tr",len_tr,"mu_dd",mu_ml,"mu_tr",mu_ml,"order",11,"sps",1, ...
"traceback_depth",128,"L",3,"delta",4,"adaptive_mu",0);
[ml_mlse_results] = ml_mlse(ml_mlse_equalizer, M, Rx_synced, Symbols, Bits,"precode_mode",duob_mode);
ml_mlse_results.metrics.print("description",'ML ');

View File

@@ -2,7 +2,7 @@ dsp_options.storage_path = 'Z:\2024\sioe_labor\';
dsp_options.max_occurences = 1; dsp_options.max_occurences = 1;
database = DBHandler("dataBase", 'labor_highspeed', "type", 'mysql' ); database = DBHandler("dataBase", 'labor_highspeed', "type", 'mysql' );
rate = [300e9];
cols = cbrewer2('BuPu',25); cols = cbrewer2('BuPu',25);
cols = [cols(end-10:2:end,:)]; cols = [cols(end-10:2:end,:)];
cols = cbrewer2('Set1',6); cols = cbrewer2('Set1',6);
@@ -12,10 +12,10 @@ fig=figure(fignum);clf;
dbmode = 0; dbmode = 0;
% 1 - PAM 4 with preemphasis % 1 - PAM 4 with preemphasis
fp = QueryFilter(); fp = QueryFilter();
M = 6; M = 8;
rate = [360e9];
fp.where('Runs', 'pam_level','EQUALS', M); fp.where('Runs', 'pam_level','EQUALS', M);
fp.where('Runs', 'bitrate','EQUALS', rate);%360,390 fp.where('Runs', 'bitrate','EQUALS', rate);%360,390
fp.where('Runs', 'fiber_length','EQUALS', 2); fp.where('Runs', 'fiber_length','EQUALS', 2);
@@ -23,7 +23,7 @@ fp.where('Runs', 'wavelength','EQUALS', 1310);
fp.where('Runs', 'db_mode','EQUALS', dbmode); fp.where('Runs', 'db_mode','EQUALS', dbmode);
fp.where('Runs', 'rop_attenuation','EQUAL', 0); fp.where('Runs', 'rop_attenuation','EQUAL', 0);
[dataTable,~] = db.queryDB(fp, database.getTableFieldNames('Runs')); [dataTable,~] = database.queryDB(fp, database.getTableFieldNames('Runs'));
dataTable = queryRunid(dataTable.run_id, database); dataTable = queryRunid(dataTable.run_id, database);
fsym = dataTable.symbolrate; fsym = dataTable.symbolrate;
@@ -33,14 +33,36 @@ duob_mode = db_mode(strrep(dataTable.db_mode,'"',''));
% Load and Sync signal data from DB % Load and Sync signal data from DB
[Tx_bits, Symbols, Scpe_cell, ~] = loadAndSyncSignalDataFromDb(dataTable, dsp_options); [Tx_bits, Symbols, Scpe_cell, ~] = loadAndSyncSignalDataFromDb(dataTable, dsp_options);
Scpe_sig_syncd = Scpe_cell{1};
Scpe_sig_syncd.eye(fsym,M,"fignum",rate.*1e-9*M+1,"displayname",' Eye of Signal');
%%%%%% SNR CHEAT - Avges the measured signal occurences found after correlation in "tsynch" %%%%%%
average_signals = 1;
if average_signals
Scpe_sig_avg = Scpe_sig_syncd;
scope_mean = zeros(size(Scpe_cell{1}.signal));
for n=1:numel(Scpe_cell)
scope_mean = scope_mean + Scpe_cell{n}.signal;
end
scope_mean = scope_mean ./ n;
Scpe_sig_avg.signal = scope_mean;
Scpe_sig_avg.spectrum("displayname","Scope PSD","fignum",20,"normalizeTo0dB",1);
Scpe_sig_avg.plot("displayname","Scope raw signal","fignum",27,"clear",1);
Scpe_sig_avg = Scpe_sig_avg.*1.25;
Scpe_sig_avg.eye(fsym,M,"fignum",rate.*1e-9*M,"displayname",' Eye of AVG Signal');
end
% Preprocess signal % Preprocess signal
Scpe_sig = preprocessSignal(Scpe_cell{1}, Symbols, fsym); Scpe_sig = preprocessSignal(Scpe_sig_avg, Symbols, fsym);
Scpe_sig.eye(fsym,M,"fignum",M*10); Scpe_sig.eye(fsym,M,"fignum",M*10);
%% === EXPORT TO TIKZ === %% === EXPORT TO TIKZ ===
% outfile = ['C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\eye_pam_',num2str(M),'.tikz'];
% outfile = ['C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\vnle_optimization.tikz']; % outfile = ['C:\Users\Silas\Documents\latex\JLT_400G_submission\media\matlab2tikz\eye_pam_',num2str(M),'-2.tikz'];
% % outfile = ['C:\Users\Silas\Documents\latex\JLT_400G copy\media\matlab2tikz\vnle_optimization.tikz'];
% matlab2tikz(outfile, ... % matlab2tikz(outfile, ...
% 'width','\fwidth', ... % 'width','\fwidth', ...
% 'height','\fheight', ... % 'height','\fheight', ...

View File

@@ -42,14 +42,14 @@ fp = QueryFilter();
% fp.where('Runs', 'run_id','EQUALS', 2776); % fp.where('Runs', 'run_id','EQUALS', 2776);
M = 6; M = 6;
fp.where('Runs', 'pam_level','EQUALS', M); fp.where('Runs', 'pam_level','EQUALS', M);
% fp.where('Runs', 'bitrate','EQUALS', 390e9);%360,390 fp.where('Runs', 'bitrate','EQUALS', 360e9);%360,390
% fp.where('Runs', 'symbolrate','EQUALS', 195e9); % fp.where('Runs', 'symbolrate','EQUALS', 195e9);
fp.where('Runs', 'fiber_length','EQUALS', 10); fp.where('Runs', 'fiber_length','EQUALS', 2);
fp.where('Runs', 'is_mpi','EQUALS', 0); fp.where('Runs', 'is_mpi','EQUALS', 0);
% fp.where('Runs', 'interference_path_length','EQUALS', 1000); % fp.where('Runs', 'interference_path_length','EQUALS', 1000);
% fp.where('Runs', 'loop_id','GREATER_THAN', 11); % fp.where('Runs', 'loop_id','GREATER_THAN', 11);
% fp.where('Runs', 'sir','EQUALS',18); % fp.where('Runs', 'sir','EQUALS',18);
% fp.where('Runs', 'wavelength','EQUALS', 1310); fp.where('Runs', 'wavelength','EQUALS', 1310);
fp.where('Runs', 'db_mode','EQUALS', 0); fp.where('Runs', 'db_mode','EQUALS', 0);
fp.where('Runs', 'rop_attenuation','EQUAL', 0); fp.where('Runs', 'rop_attenuation','EQUAL', 0);
% fp.where('Runs', 'power_pd_in','LESS_THAN', 7); % fp.where('Runs', 'power_pd_in','LESS_THAN', 7);
@@ -70,7 +70,7 @@ wh.addStorage("mlmlse_package");
%% === RUN IT === %% === RUN IT ===
[results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "parallel", 'wh', wh, 'waitbar', true); [results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "serial", 'wh', wh, 'waitbar', true);

View File

@@ -1,5 +1,5 @@
filename = "C:\Users\sioe\Documents\High_Speed_Measurement_2024\baudrate_sweep_b2b\PAMX_b2b_baudrate20241024_210648_wh.mat"; filename = "F:\2024\sioe\High Speed Messungen Oktober\baudrate_sweep_b2b\PAMX_b2b_baudrate20241024_210648_wh_final.mat";
a = load(filename); a = load(filename);
wh = a.obj; wh = a.obj;

View File

@@ -8,7 +8,7 @@ fdac = 256e9;
fadc = 256e9; fadc = 256e9;
random_key = 1; random_key = 1;
rcalpha = 0.05; rcalpha = 0.6;
kover = 16; kover = 16;
duob_mode = db_mode.no_db; duob_mode = db_mode.no_db;
@@ -25,7 +25,7 @@ tx_bw_nyquist = 0.8;
link_length = 1; link_length = 1;
% RX % RX
rop = -8; rop = -9;
rx_bw_nyquist = 0.8; rx_bw_nyquist = 0.8;
vnle_order1 = 50; vnle_order1 = 50;
@@ -51,7 +51,7 @@ mu_ffe = [mu_ffe1 mu_ffe3 mu_ffe3];
mu_dfe = 0.0004; mu_dfe = 0.0004;
Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",16,"alpha",rcalpha); Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha);
[Digi_sig,Symbols,Tx_bits] = PAMsource(... [Digi_sig,Symbols,Tx_bits] = PAMsource(...
"fsym",fsym,"M",M,"order",18,"useprbs",0,... "fsym",fsym,"M",M,"order",18,"useprbs",0,...
@@ -65,7 +65,7 @@ Pform = Pulseformer("fsym",fsym,"fdac",4*fsym,"pulse","rc","pulselength",1
%%%%% AWG %%%%% AWG
El_sig = M8199A("kover",kover).process(Digi_sig); El_sig = M8199A("kover",kover).process(Digi_sig);
% El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig); % El_sig = AWG("fdac",fdac,"f_cutoff",fsym,"lpf_active",0,"kover",kover,"bit_resolution",12,"upsampling_method","samplehold","precomp_sinc_rolloff",1).process(Digi_sig);
El_sig.spectrum("displayname",'Digi Spectrum','fignum',100,'normalizeTo0dB',0); El_sig.spectrum("displayname",'Digi Spectrum','fignum',1,'normalizeTo0dB',1);
% El_sig = El_sig.setPower(0,"dBm"); % El_sig = El_sig.setPower(0,"dBm");
%%%%% Electrical Driver Amplifier %%%%%% %%%%% Electrical Driver Amplifier %%%%%%
@@ -101,32 +101,53 @@ Scpe_sig = Scope("fsimu",fdac*kover,"fadc",fadc,...
"delay",0,"fixed_delay",0,"filtertype",filtertypes.butterworth,... "delay",0,"fixed_delay",0,"filtertype",filtertypes.butterworth,...
"samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0,... "samplingdelay",0,"rand_samplingdelay",0,"freq_offset",0,"samp_jitter",0,...
"adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',1,'H_lpf',Lp_scpe).process(Rx_sig); "adcresolution",8,"quantbuffer",0.1,'block_dc',1,'lpf_active',1,'H_lpf',Lp_scpe).process(Rx_sig);
%%
%%%%%% Sample to 2x fsym %%%%%% % 1) matched filter
Scpe_sig = Scpe_sig.resample("fs_out",2*fsym); % pulse is symmetric, hence we can use pulsef firectly as matched filter.
Scpe_sig.signal = Scpe_sig.signal(1:2*length(Symbols)); % It feels off (bit I think correct) that the fsym is now the output freq.!!
% -> output 2 sps to omit timing recovery!?
Pform = Pulseformer("fsym",fsym,"fdac",2*fsym,"pulse","rrc","pulselength",16,"alpha",rcalpha,"matched",1);
Scpe_sig = Pform.process(Scpe_sig);
Scpe_sig.spectrum("displayname",'Signal after matched filter','fignum',1,'normalizeTo0dB',1);
%
% %%
% %%%%%% Sample to 2x fsym %%%%%%
% Scpe_sig = Scpe_sig.resample("fs_out",2*fsym);
% Scpe_sig.signal = Scpe_sig.signal(1:2*length(Symbols));
%%
%%%%%% Sync Rx signal with reference %%%%%% %%%%%% Sync Rx signal with reference %%%%%%
[Scpe_sig,~] = Scpe_sig.tsynch("reference",Symbols,"fs_ref",fsym,"debug_plots",0); [Scpe_sig,~] = Scpe_sig.tsynch("reference",Symbols,"fs_ref",fsym,"debug_plots",1);
Scpe_sig.spectrum("displayname",'Opt Spectrum','fignum',11,'normalizeTo0dB',1); Scpe_sig.spectrum("displayname",'Opt Spectrum','fignum',11,'normalizeTo0dB',1);
Scpe_sig = Filter('filtdegree',4,"f_cutoff",Symbols.fs.*0.5,"fs",Scpe_sig.fs,"filterType",filtertypes.gaussian,"active",true).process(Scpe_sig); % Scpe_sig = Filter('filtdegree',4,"f_cutoff",Symbols.fs.*0.5,"fs",Scpe_sig.fs,"filterType",filtertypes.gaussian,"active",true).process(Scpe_sig);
Scpe_sig = Scpe_sig - mean(Scpe_sig.signal); Scpe_sig = Scpe_sig - mean(Scpe_sig.signal);
Scpe_sig.signal = Scpe_sig.signal(1:2*length(Symbols));
%%
% -------------------- FFE -------------------- % -------------------- FFE --------------------
ffe_order = [50, 0, 0]; ffe_order = [50, 0, 0];
eq_ffe = EQ("Ne",ffe_order,"Nb",[0,0,0], ... eq_ = EQ("Ne",ffe_order,"Nb",[2,0,0], ...
"training_length",len_tr,"training_loops",5,"dd_loops",5, ... "training_length",len_tr,"training_loops",5,"dd_loops",5, ...
"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ... "K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005, ...
"FFEmu",0,"plotfinal",0,"ideal_dfe",0); "FFEmu",0,"plotfinal",0,"ideal_dfe",0);
output.ffe_results = ffe(eq_ffe,M,Scpe_sig,Symbols,Tx_bits, ... % eq_ = FFE("epochs_tr",4,"epochs_dd",5,"len_tr",4096,"mu_dd",0.01,"mu_tr",0.01,"order",50,"sps",2,"decide",0, "adaption",adaption_method.nlms,"dd_mode",1);
"precode_mode",duob_mode,'showAnalysis',0,"postFFE",[], ... eq_ = FFE_DFE("epochs_tr",5,"epochs_dd",5,"len_tr",512,"ffe_mu_dd",1e-4,"dfe_mu_dd",5e-4,"ffe_mu_tr",0,"dfe_mu_tr",0,"ffe_order",99,"dfe_order",99,"sps",2,"decide",0);
output.ffe_results = ffe(eq_,M,Scpe_sig,Symbols,Tx_bits, ...
"precode_mode",duob_mode,'showAnalysis',1,"postFFE",[], ...
"eth_style_symbol_mapping",0); "eth_style_symbol_mapping",0);
output.ffe_results.metrics.print output.ffe_results.metrics.print
%%
% -------------------- VNLE + MLSE -------------------- % -------------------- VNLE + MLSE --------------------
pf_ncoeffs = 1; pf_ncoeffs = 1;
ffe_order3 = [50, 5, 5]; ffe_order3 = [50, 5, 5];
@@ -139,8 +160,11 @@ pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels); mlse_ = MLSE("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
[output.vnle_results, output.mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ... [output.vnle_results, output.mlse_results] = vnle_postfilter_mlse(eq_v, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ...
"precode_mode", duob_mode, 'showAnalysis', 0, "postFFE", [], "eth_style_symbol_mapping", 0); "precode_mode", duob_mode, 'showAnalysis', 1, "postFFE", [], "eth_style_symbol_mapping", 0);
output.mlse_results.metrics.print
%%
% -------------------- DB target -------------------- % -------------------- DB target --------------------
mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels); mlse_db_ = MLSE("DIR",[1,1],"duobinary_output",0,"M",M,'trellis_states',PAMmapper(M,0).levels);

View File

@@ -0,0 +1,88 @@
wh_aeon = load("C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\Lab_analysis\aeon_soa_measurement_lambda_plaser_pump.mat");
wh_aeon = wh_aeon.wh;
wh_thor = load("C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\Lab_analysis\thorlabs_pdfa_measurement_lambda_plaser_pump.mat");
wh_thor = wh_thor.wh;
% Silas' custom "warehouse" datatype
wh_aeon.showInfo;
wh_thor.showInfo;
%% PLOT OSA SPECTRA
figure();hold on
cols = linspecer(4);
cols = cbrewer2('Paired',8);
subplot(1,2,1);hold on;
wh = wh_thor;
ccnt = 1;
pumps = wh.parameter.pump.values;
plasers = wh.parameter.laserpower.values;
scnt = 1;
for p = 1:numel(plasers)
for pmp = 1:numel(pumps)
subplot(numel(plasers),numel(pumps),scnt);hold on;
title(sprintf('Laser: %d dBm; Pump: %d %',plasers(p),pumps(pmp)),"Interpreter","tex")
for lambda = wh.parameter.lambda.values
spectrum_osa = wh_thor.getStoValue('spectrum_osa',plasers(p),lambda,pumps(pmp));
wavelength_osa = wh_thor.getStoValue('wavelength_osa',plasers(p),lambda,pumps(pmp));
plot(wavelength_osa,spectrum_osa,'DisplayName',sprintf('P_{in}: %d dB ',plasers(p)),'Color',cols(ccnt,:));
spectrum_osa = wh_aeon.getStoValue('spectrum_osa',plasers(p),lambda,pumps(pmp));
wavelength_osa = wh_aeon.getStoValue('wavelength_osa',plasers(p),lambda,pumps(pmp));
plot(wavelength_osa,spectrum_osa,'DisplayName',sprintf('P_{in}: %d dB ',plasers(p)),'Color',cols(ccnt+1,:));
ylim([-60, 20]);
beautifyBERplot("logscale",false,"setmarkers",0,"setcolors",0);
end
scnt = scnt +1;
end
ccnt = ccnt+2;
end
xlabel('Wavelength [nm]');
ylabel('OSNR [dB]')
%%
figure();hold on
cols = linspecer(4);
subplot(1,2,1);hold on;
wh = wh_thor;
ccnt = 1;
pumps = wh.parameter.pump.values;
plasers = wh.parameter.laserpower.values;
scnt = 1;
for p = 1:numel(plasers)
for pmp = 1:numel(pumps)
subplot(numel(plasers),numel(pumps),scnt);hold on;
ase_noise = wh_thor.getStoValue('pase_osa',plasers(p),wh.parameter.lambda.values,pumps(pmp));
signal = wh_thor.getStoValue('psig_osa',plasers(p),wh.parameter.lambda.values,pumps(pmp));
plot(wh.parameter.lambda.values,ase_noise,'DisplayName',sprintf('P_{in}: %d dB ',plasers(p)),'Color',cols(ccnt,:));
ase_noise = wh_aeon.getStoValue('pase_osa',plasers(p),wh.parameter.lambda.values,pumps(pmp));
signal = wh_aeon.getStoValue('psig_osa',plasers(p),wh.parameter.lambda.values,pumps(pmp));
plot(wh.parameter.lambda.values,ase_noise,'DisplayName',sprintf('P_{in}: %d dB ',plasers(p)),'Color',cols(ccnt,:),'LineStyle','-');
ylim([-60, 20]);
beautifyBERplot("logscale",false,"setmarkers",0,"setcolors",0);
scnt = scnt +1;
end
ccnt = ccnt+1;
end
xlabel('Wavelength [nm]');
ylabel('OSNR [dB]')

View File

@@ -1,5 +1,5 @@
base = "C:\Users\Silas\Nextcloud\Cluster"; base = "C:\Users\Silas\Nextcloud4\Cluster";
all_files = dir(fullfile(base, "**/*.mat")); all_files = dir(fullfile(base, "**/*.mat"));
schemes = ["co","pair","alt","seg"]; schemes = ["co","pair","alt","seg"];
@@ -13,8 +13,8 @@ T = table('Size',[0 10], ...
rx = "^WDM_(?<date>\d{8})_(?<time>\d{6})_n(?<node>\d+)_(?<jobid>\d+)_" + ... rx = "^WDM_(?<date>\d{8})_(?<time>\d{6})_n(?<node>\d+)_(?<jobid>\d+)_" + ...
"(?<L>\d+)km_(?<Nch>\d+)ch_(?<df>\d+)ghz_(?<scheme>[a-z]+)_alpha(?<alpha>\d+(?:_\d+)?)\.mat$"; "(?<L>\d+)km_(?<Nch>\d+)ch_(?<df>\d+)ghz_(?<scheme>[a-z]+)_alpha(?<alpha>\d+(?:_\d+)?)\.mat$";
for k = 1:numel(all_files) for i = 1:numel(all_files)
f = all_files(k); f = all_files(i);
folder = string(f.folder); folder = string(f.folder);
file = string(f.name); file = string(f.name);
@@ -44,6 +44,8 @@ for k = 1:numel(all_files)
end end
%% %%
idx = strcmp(T.scheme,"co") & ... idx = strcmp(T.scheme,"co") & ...
T.alpha == 0.4 & ... T.alpha == 0.4 & ...
T.date >= datetime(2026,1,9) & ... T.date >= datetime(2026,1,9) & ...
@@ -51,13 +53,181 @@ idx = strcmp(T.scheme,"co") & ...
T_sel = T(idx,:); T_sel = T(idx,:);
i = 2; % ---- Load all res ----
res = load(fullfile(T_sel.folder(i),T_sel.file(i)),'res'); R = cell(numel(T_sel.file),1);
res = res.res; for i = 1:numel(T_sel.file)
S = load(fullfile(T_sel.folder(i),T_sel.file(i)),'res');
S.res = strip_config_from_res(S.res);
R{i} = S.res;
end
res_all = combine_res_list(R);
res_all = drop_empty_realizations(res_all);
%%
%%
%% %%
% Routine A: plot BER curves and compute crossings % Routine A: plot BER curves and compute crossings
S = plot_BER_vs_ROP(res, 'fec', 3.8e-3); S = plot_BER_vs_ROP(res_all, 'fec', 3.8e-3);
%% Routine B: violin plot (independent) %% Routine B: violin plot (independent)
plot_FEC_violin(res, 'tech','VNLE', 'fec',3.8e-3, 'ylim',[-10 0]); plot_FEC_violin(res_all, 'tech','VNLE', 'fec',3.8e-3, 'ylim',[-10 0],'eval_ptr',5);
%%
function res = strip_config_from_res(res)
% Remove the "config" payload from every non-empty result cell
% Works whether entries are structs-with-field or objects-with-property.
techs = {'ffe','dfe','vnle','mlse','dbt'};
for t = 1:numel(techs)
fn = techs{t};
if ~isfield(res, fn) || isempty(res.(fn)), continue; end
C = res.(fn);
if ~iscell(C), continue; end
idx = find(~cellfun('isempty', C)); % fast builtin
for k = 1:numel(idx)
x = C{idx(k)};
% Case 1: struct entry with field "config"
if isstruct(x) && isfield(x,'config')
x = rmfield(x,'config');
% Case 2: object entry with property "config"
elseif isobject(x) && isprop(x,'config')
try
x.config = []; % lighter than keeping Equalizerstruct JSON
catch
% ignore if class forbids assignment
end
end
C{idx(k)} = x;
end
res.(fn) = C;
end
end
% ===================== Local helper functions =====================
function res_out = combine_res_list(R)
% Concatenate along realization dimension (dim=3) for all techniques.
% Requires consistent sizes in dims 1,2,4 and consistent eval_dist_km.
fieldsTech = ["ffe","dfe","vnle","mlse","dbt"];
% Start with first
res_out = R{1};
% --- Build common distance axis (union, stable) ---
dist_all = res_out.eval_dist_km(:).';
for k = 2:numel(R)
dist_all = unique([dist_all, R{k}.eval_dist_km(:).'], 'stable');
end
% --- If res_out not already on dist_all, expand it ---
if ~isequal(res_out.eval_dist_km(:).', dist_all)
res_out = expand_res_dist(res_out, dist_all);
end
% --- For each file: expand to dist_all, then merge realizations (your logic) ---
for k = 2:numel(R)
R{k} = expand_res_dist(R{k}, dist_all);
end
% Concatenate technique cell arrays along dim=3 (realizations)
for f = fieldsTech
A = res_out.(f);
for k = 2:numel(R)
B = R{k}.(f);
A = cat(3, A, B);
end
res_out.(f) = A;
end
% Update num_realiz in settings to match concatenated dim
dims = size(res_out.ffe); if numel(dims)<3, dims(3)=1; end
res_out.settings.num_realiz = dims(3);
end
function res = expand_res_dist(res, dist_all)
techs = {'ffe','dfe','vnle','mlse','dbt'};
old = res.eval_dist_km(:).';
new = dist_all(:).';
[~,loc] = ismember(old, new); % mapping old -> new positions
if any(loc==0)
error('expand_res_dist: internal: old distances not found in union.');
end
% sizes from an existing field (prefer ffe)
Cref = res.ffe;
sz = size(Cref); sz(end+1:4) = 1; % ensure 4 dims
Nch=sz(1); Nrop=sz(2); Nreal=sz(3); Nnew=numel(new);
for t = 1:numel(techs)
fn = techs{t};
if ~isfield(res,fn) || isempty(res.(fn)), continue; end
Cold = res.(fn);
sz2 = size(Cold); sz2(end+1:4) = 1;
Cnew = cell(sz2(1), sz2(2), sz2(3), Nnew);
Cnew(:,:,:,loc) = Cold; % place old distances into new axis
res.(fn) = Cnew;
end
res.eval_dist_km = new;
end
function res_out = drop_empty_realizations(res_in)
% Removes realizations where ALL entries are empty across ALL techniques
% (across channels, rop, distances).
res_out = res_in;
fieldsTech = ["ffe","dfe","vnle","mlse","dbt"];
% Determine sizes from one field
dims = size(res_in.ffe);
if numel(dims) < 4, dims(end+1:4) = 1; end
Nreal = dims(3);
% For each realization, check if there is at least one non-empty result anywhere
keep = false(1, Nreal);
for r = 1:Nreal
hasAny = false;
for f = fieldsTech
C = res_in.(f); % cell array
% Slice all ch,rop,dist for this realization
slice = C(:,:,r,:); % still a cell array
hasAny = any(~cellfun(@isempty, slice(:)));
if hasAny, break; end
end
keep(r) = hasAny;
end
fprintf('drop_empty_realizations: keeping %d/%d realizations (dropping %d)\n', ...
sum(keep), Nreal, sum(~keep));
% Apply keep mask
for f = fieldsTech
res_out.(f) = res_out.(f)(:,:,keep,:);
end
% Update settings
res_out.settings.num_realiz = sum(keep);
end