CLEANUP - changes to folder structure

This commit is contained in:
Silas Oettinghaus
2026-03-25 10:57:48 +01:00
parent 0c5ad28f0a
commit 0ae846d3c3
351 changed files with 405 additions and 1294 deletions

View File

@@ -0,0 +1,60 @@
load("ffe_debug_snapshot.mat");
dc_buffer_len = logspace(0,3,12);
dc_buffer_len = 1024;
mu_dc = logspace(-3,0,24);
parfor d = 1:length(mu_dc)
eq_lin = FFE_DCremoval_adaptive_mu("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0,...
"mu_dc",mu_dc(d),...
"dc_buffer_len",1024, ...
"ffe_buffer_len",1,...
"smoothing_buffer_length",0,...
"smoothing_buffer_update",1,...
"adaptive_mu_mode",0);
%
% eq_lin = FFE("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
% 0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0);
ffe_results = ffe(eq_lin,M,Scpe_sig,Symbols,Tx_bits,...
"precode_mode",duob_mode,...
'showAnalysis',0,...
"postFFE",[],...
"eth_style_symbol_mapping",0);
ffe_results.metrics.print
% % eq_lin = FFE_DFE("epochs_tr",5,"epochs_dd",5,"len_tr",4096*2,"ffe_mu_dd",1e-4,"dfe_mu_dd",5e-4,"ffe_mu_tr",0,"dfe_mu_tr",0,"ffe_order",21,"dfe_order",2,"sps",2,"decide",0);
%
% eq_lin = FFE("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
% 0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0);
% pf_ = Postfilter("ncoeff",2,"useBurg",1);
% mlse_ = MLSE_viterbi("duobinary_output",0,'M',4,'trellis_states',PAMmapper(4,0).levels);
%
% [ffe_results2, mlse_results] = vnle_postfilter_mlse(eq_lin, pf_, mlse_, M, Scpe_sig, Symbols, Tx_bits, ...
% "precode_mode", duob_mode,...
% 'showAnalysis', 1, ...
% "postFFE", [],...
% "eth_style_symbol_mapping", 0);
ber(d) = ffe_results.metrics.BER;
end
figure(10)
hold on
plot(mu_dc,ber,'LineWidth',1,'DisplayName',sprintf('DC buffer len = 1024'),'Marker','.','MarkerSize',10);
xlabel('BER');
xlabel('MU DC');
title('BER Optimization over dc\_buffer\_len');
yline([4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--');
ylim([9e-4, 0.5]);
set(gca, 'YScale', 'log'); % BER is usually plotted log-scale
legend('show', 'Location', 'best');
grid on;

View File

@@ -0,0 +1,118 @@
% === SETTINGS ===
dsp_options.append_to_db = 0;
dsp_options.max_occurences = 15;
dsp_options.database_path = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\';
dsp_options.database_name = 'silas_labor_newdsp_newstructure.db';
dsp_options.storage_path = 'Z:\2024\sioe_labor\';
dsp_options.parameters = struct();
dsp_options.parameters.mu_dc = [0.005];
% === Get Run ID's ===
db = DBHandler("pathToDB", [dsp_options.database_path, dsp_options.database_name], "type", "sqlite");
fp = QueryFilter();
% fp.where('Runs', 'run_id','EQUALS', 5108);
fp.where('Runs', 'is_mpi','EQUALS', 0);
fp.where('Runs', 'fiber_length','EQUALS', 1);
fp.where('Runs', 'wavelength','EQUALS', 1310);
fp.where('Runs', 'db_mode','EQUALS', 1);
fp.where('Runs', 'rop_attenuation','EQUALS', 0);
fp.where('Runs', 'pam_level','EQUALS', 4);
fp.where('Runs', 'bitrate','EQUALS', 360e9);
% fp.where('Runs', 'power_pd_in','GREATER_THAN', 7);
[dataTable,~] = db.queryDB(fp, db.getTableFieldNames('Runs'));
% === Initialize DataStorage ===
wh = DataStorage(dsp_options.parameters);
wh.addStorage("ffe_package");
wh.addStorage("mlse_package");
wh.addStorage("vnle_package");
wh.addStorage("dbtgt_package");
wh.addStorage("dbenc_package");
% === RUN IT ===
% [results,wh] = submitJobs(dataTable.run_id(:), dsp_options, "serial", 'wh', wh, 'waitbar', true);
% wh.getStoValue('ffe_package',0.005);
% wh.getStoValue('mlse_package',0.005);
[dataTable,~] = db.queryDB(fp, [db.getTableFieldNames('Runs');db.getTableFieldNames('Results');db.getTableFieldNames('Equalizer')]);
dataTable = cleanUpTable(dataTable);
% === Look at it ===
y_var = 'BER_precoded';
x_var = 'bitrate';
fixedVars = {'equalizer_structure', x_var};
[dataTableClean, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var);
% --- Group and aggregate ---
dataTableGrpd_mean = groupIt(fixedVars, dataTableClean, @mean);
dataTableGrpd_min = groupIt(fixedVars, dataTableClean, @min);
dataTableGrpd_max = groupIt(fixedVars, dataTableClean, @max);
% Choose a color map
cols = linspecer(numel(unique(dataTableGrpd_mean.equalizer_structure)));
figure;
hold on;
% Get unique equalizer structures for grouping
unique_eq = unique(dataTableGrpd_mean.equalizer_structure);
for i = 1:numel(unique_eq)
eq_val = unique_eq(i);
% Filter grouped data for this equalizer structure
filt = dataTableGrpd_mean.equalizer_structure == eq_val;
x = dataTableGrpd_mean.(x_var)(filt);
y_mean = dataTableGrpd_mean.(y_var)(filt);
y_min = dataTableGrpd_min.(y_var)(filt);
y_max = dataTableGrpd_max.(y_var)(filt);
% Bounds for boundedline (distance from mean)
y_lower = y_mean - y_min;
y_upper = y_max - y_mean;
y_bounds = [y_lower, y_upper];
% --- Bounded line (mean ± min/max) ---
if exist('boundedline', 'file')
[hl, hp] = boundedline(x, y_mean, y_bounds, ...
'alpha', 'transparency', 0.1, ...
'cmap', cols(i,:), ...
'nan', 'fill', ...
'orientation', 'vert');
set(hl, 'LineWidth', 1.2, 'DisplayName', sprintf('Eq %s', eq_val));
set(hp, 'HandleVisibility', 'off');
else
% If boundedline is not available, use errorbar
errorbar(x, y_mean, y_lower, y_upper, ...
'o-', 'Color', cols(i,:), 'LineWidth', 1.2, ...
'DisplayName', sprintf('Eq %d', eq_val),'HandleVisibility', 'off');
end
% --- Normal line (mean only) ---
plot(x, y_mean, '-', 'Color', cols(i,:), 'LineWidth', 1.5, ...
'DisplayName', sprintf('Mean Eq %s', eq_val),'HandleVisibility', 'off');
% --- Scatter plot for individual points (from original data) ---
% Filter original data for this group
orig_filt = dataTableClean.equalizer_structure == eq_val;
x_scatter = dataTableClean.(x_var)(orig_filt);
y_scatter = dataTableClean.(y_var)(orig_filt);
scatter(x_scatter, y_scatter, 10,cols(i,:), 'filled', ...
'MarkerFaceAlpha', 0.5, 'DisplayName', sprintf('Scatter Eq %s', eq_val),'HandleVisibility', 'off');
end
yline([2.2e-4,4.85e-3,2e-2],'HandleVisibility', 'off','LineWidth',1,'LineStyle','--');
set(gca, 'YScale', 'log'); % BER is usually plotted log-scale
xlabel(x_var, 'Interpreter', 'none');
ylabel(y_var, 'Interpreter', 'none');
legend('show', 'Location', 'best');
grid on;
title(sprintf('%s vs. %s', y_var, x_var), 'Interpreter', 'none');
hold off;

View File

@@ -0,0 +1,68 @@
% === SETTINGS ===
savePath = 'Z:\2025\ECOC Silas\ecoc_2025\';
% databasePath = '\\ntserver.tf.uni-kiel.de\scratch\sioe\ECOC_2025\';
databasePath = 'Z:\2025\ECOC Silas\';
databasePath = 'C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
database_name = 'ecoc2025_loops.db';
db = DBHandler("pathToDB", [databasePath, database_name],"type","mysql");
num_occ = 30;
run_par = true;
refreshInterval = 300; % seconds
% In-memory list of currently processing run_ids
% currentlyProcessing = [];
alreadyProcessing = [];
fprintf('--- DSP WATCHDOG STARTED ---\n');
while true
try
% Query incomplete run_ids
sql_query = [
'SELECT ru.run_id ' ...
'FROM Runs ru ' ...
'LEFT JOIN Results r ON ru.run_id = r.run_id ' ...
'LEFT JOIN EqualizerParameters e ON r.eqParam_id = e.eq_id ' ...
'WHERE ru.loop_id IN ( ' ...
' SELECT loop_id FROM Runs GROUP BY loop_id HAVING COUNT(*) > 2 ' ...
') ' ...
'GROUP BY ru.run_id ' ...
'HAVING COUNT(CASE WHEN e.equalizer_structure = 1 THEN 1 END) < 5;'
];
runIdsFiltered = db.fetch(sql_query);
newRunIds = setdiff(runIdsFiltered.run_id, alreadyProcessing);
fprintf('[%s] Found %d run(s) needing processing, %d new.\n', ...
datestr(now, 'yyyy-mm-dd HH:MM:SS'), numel(runIdsFiltered.run_id), numel(newRunIds));
for i = 1:numel(newRunIds)
cur_id = newRunIds(i);
% Add to in-memory processing list
alreadyProcessing(end+1) = cur_id;
% Submit processing
try
[output, future] = submit_dsp(cur_id, databasePath, database_name, savePath, ...
"parallel", run_par, "max_occurences", num_occ);
% Add a listener for future completion (success or failure)
catch err
warning('Error processing run_id %d: %s', cur_id, err.message);
% Remove from in-memory list even on error (optional: you could leave it for retry)
alreadyProcessing(alreadyProcessing == cur_id) = [];
end
end
catch err
warning('Error in DSP queue watchdog: %s', err.message);
end
fprintf('Sleeping for %.0f seconds...\n\n', refreshInterval);
pause(refreshInterval);
end

View File

@@ -0,0 +1,262 @@
function [output] = dsp_run_id(run_id,options)
arguments
run_id
options.append_to_db = 0;
options.max_occurences = 4;
options.parameters = struct();
options.database_path
options.database_name
options.storage_path
end
database = DBHandler("pathToDB",[options.database_path,options.database_name],"type","sqlite");
% database = DBHandler("type","mysql");
filterParams = database.tables;
filterParams.Configurations = struct('run_id', run_id);
selectedFields = {'Runs.run_id','Runs.tx_bits_path','Runs.tx_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] = database.queryDB(filterParams, selectedFields);
[~, uniqueIdx] = unique(dataTable.run_id); % Get unique run_id indices
dataTable = dataTable(uniqueIdx,:); % Extract unique configurations for each run_id
fsym = dataTable.symbolrate;
M = double(dataTable.pam_level);
try
duob_mode = db_mode.(strrep(char(dataTable.db_mode),'"',''));
catch
duob_mode = db_mode(dataTable.db_mode);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
len_tr = 4096*2;
vnle_order1 = 50;
vnle_order2 = 5;
vnle_order3 = 5;
dfe_order = [0 0 0];
pf_ncoeffs = 1;
mu_ffe1 = 0.0001;
mu_ffe2 = 0.0008;
mu_ffe3 = 0.001;
mu_dfe = 0.0004;
mu_dc = 0.00;
mu_ffe = [mu_ffe1 mu_ffe2 mu_ffe3];
vnle_order=[vnle_order1,vnle_order2,vnle_order3];
dc_buffer_len = 224;
ffe_buffer_len = 1;
smoothing_buffer_length = 4096;
smoothing_buffer_update = 224;
% Overwrite default parameters if given in options.parameters
paramStruct = options.parameters;
if ~isempty(paramStruct)
paramNames = fieldnames(paramStruct);
for i = 1:numel(paramNames)
thisName = paramNames{i};
thisValue = paramStruct.(thisName);
eval([thisName ' = thisValue;']);
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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);
pf_ = Postfilter("ncoeff",pf_ncoeffs,"useBurg",1);
mlse_ = MLSE_viterbi("duobinary_output",0,'M',M,'trellis_states',PAMmapper(M,0).levels);
mlse_db_ = MLSE_viterbi("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels);
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);
output = struct();
vnle_pf_package = {};
vnle_package = {};
dbtgt_package = {};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Tx_bits = load([options.storage_path, char(dataTable.tx_bits_path)]);
Tx_bits = Tx_bits.Bits;
Symbols_mapped = PAMmapper(M,0).map(Tx_bits);
Symbols_mapped.fs = dataTable.symbolrate;
Symbols = load([options.storage_path, char(dataTable.tx_symbols_path)]);
Symbols = Symbols.Symbols;
found_sync = 0;
try
Scpe_load = load([options.storage_path, char(dataTable.rx_sync_path)]);
Scpe_cell = Scpe_load.S;
[~,~,~,found_sync] = Scpe_cell{2}.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",1);
end
if ~found_sync
Scpe_sig_raw = load([options.storage_path, char(dataTable.rx_raw_path(1))]);
Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw;
Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in",Scpe_sig_raw.fs,"fs_out",2*fsym);
[~,Scpe_cell,~,found_sync] =Scpe_sig_resampled.tsynch("reference",Symbols,"fs_ref",dataTable.symbolrate,"debug_plots",1);
end
if ~found_sync
if length(Symbols_mapped.signal) == sum(Symbols_mapped.signal == Symbols.signal)
warning('Could not synchronize the received signal with the stored symbols!')
else
[~,Scpe_cell,~,found_sync] =Scpe_sig_raw.tsynch("reference",Symbols_mapped,"fs_ref",dataTable.symbolrate,"debug_plots",0);
end
if ~found_sync
warning('Could not synchronize the received signal with the stored symbols!')
end
end
record_realizations = min(options.max_occurences,length(Scpe_cell));
for occ = 1:record_realizations
Scpe_sig = Scpe_cell{occ};
%%%%%% Sample to 2x fsym %%%%%%
Scpe_sig = Scpe_sig.resample("fs_out",2*fsym);
%%%%%% Sync Rx signal with reference %%%%%%
[Scpe_sig,~] = Scpe_sig.tsynch("reference",Symbols,"fs_ref",fsym,"debug_plots",0);
Scpe_sig = Filter('filtdegree',4,"f_cutoff",Symbols.fs.*0.6,"fs",Scpe_sig.fs,"filterType",filtertypes.gaussian,"active",true).process(Scpe_sig);
Scpe_sig = Scpe_sig - mean(Scpe_sig.signal);
if duob_mode ~= db_mode.db_encoded
vnle_pf = 1;
dbtgt = 0;
% %%%%% VNLE + DFE %%%%
if 0
eq_ = FFE_DCremoval_adaptive_mu("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0,...
"mu_dc",mu_dc,...
"dc_buffer_len",dc_buffer_len, ...
"ffe_buffer_len",ffe_buffer_len,...
"smoothing_buffer_length",smoothing_buffer_length,...
"smoothing_buffer_update",smoothing_buffer_update);
eq_ = EQ("Ne",vnle_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
result = vnle(eq_,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",duob_mode,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0);
vnle_package{occ} = result;
fprintf("FFE Results: %.2e\n", result.ber_vnle);
if options.append_to_db
database.addProcessingResult(run_id, result.resultsVNLE, result.equalizerConfigVNLE);
end
end
%%%%% VNLE + PF + MLSE %%%%
if vnle_pf
[result] = vnle_postfilter_mlse(eq_,pf_,mlse_,M,Scpe_sig,Symbols,Tx_bits,"precode_mode",duob_mode,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0);
vnle_pf_package{occ} = result;
if options.append_to_db
database.addProcessingResult(run_id,result.resultsMLSE, result.equalizerConfigMLSE);
database.addProcessingResult(run_id,result.resultsVNLE, result.equalizerConfigVNLE);
end
end
%%%%% Duobinary Targeting %%%%
if dbtgt
[result] = duobinary_target(eq_, mlse_db_, M, Scpe_sig, Symbols, Tx_bits, "precode_mode", duob_mode,'showAnalysis',0,"postFFE",[]);
dbtgt_package{occ} = result;
if options.append_to_db
database.addProcessingResult(run_id, result.resultsDBtgt, result.equalizerConfigDBtgt);
end
end
% fprintf("BER VNLE: %.2e | %.2e; BER MLSE: %.2e | %.2e; BER DB tgt: %.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,dbtgt_package{occ}.resultsDBtgt.BER,dbtgt_package{occ}.resultsDBtgt.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
% occ = 1; % or whatever your loop index is
% Extract VNLE results for readability
vnle_result = vnle_pf_package{occ}.resultsVNLE;
mlse_result = vnle_pf_package{occ}.resultsMLSE;
% Print header
fprintf("==== EQUALIZATION RUN-ID %d | PAM-%d | %.2f GBd ====\n\n", run_id, M, Symbols.fs.*1e-9);
% VNLE Results
fprintf(">> VNLE Results:\n");
fprintf(" BER %.2e\n", vnle_result.BER);
fprintf(" BER (pre-code) %.2e\n", vnle_result.BER_precoded);
fprintf(" SNR: %.2f dB\n", vnle_result.SNR);
fprintf(" GMI: %.4f\n", vnle_result.GMI);
fprintf(" Linerate: %.2f Gbps\n", Symbols.fs .* floor(log2(M)*10)/10 .*1e-9);
fprintf(" AIR: %.2f Gbps\n", vnle_result.AIR.*1e-9);
fprintf("\n");
% MLSE Results
fprintf(">> MLSE Results:\n");
fprintf(" BER : %.2e\n", mlse_result.BER);
fprintf(" BER (pre-code): %.2e\n", mlse_result.BER_precoded);
fprintf(" Channel Alpha : %.2f\n", mlse_result.Alpha);
fprintf("\n");
end
if dbtgt
dbtgt = dbtgt_package{occ}.resultsDBtgt;
% DB Target Results
fprintf(">> DB Target Results:\n");
fprintf(" BER: %.2e\n", dbtgt.BER);
fprintf(" BER (pre-code): %.2e\n", dbtgt.BER_precoded);
fprintf("\n");
end
else
%%%%%% %db signaling => db encoded %%%%%
if 1
mlse_db_enc = MLSE_viterbi("DIR",[1,1],"duobinary_output",0,"M",M,"trellis_states",PAMmapper(M,0).levels);
eq_db_enc = EQ("Ne",vnle_order,"Nb",dfe_order,"training_length",len_tr,"training_loops",5,"dd_loops",5,"K",2,"DCmu",mu_dc,"DDmu",[mu_ffe mu_dfe],"DFEmu",0.005,"FFEmu",0,"plotfinal",0,"ideal_dfe",1);
[result] = duobinary_signaling(eq_db_enc, mlse_db_enc,M, Scpe_sig ,Symbols, Tx_bits);
dbenc_package{occ} = result;
if options.append_to_db
database.addProcessingResult(run_id, result.resultsDBsignaling, result.equalizerConfigDBsignaling);
end
end
fprintf("BER DB: %.2e \n",dbenc_package{occ}.resultsDBsignaling.BER);
end
% autoArrangeFigures;
disp('- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ')
fprintf('\n')
end
output.dataTable = dataTable;
output.vnle_pf_package = vnle_pf_package;
output.vnle_package = vnle_package;
output.dbtgt_package = dbtgt_package;
end

View File

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

View File

@@ -0,0 +1,63 @@
savePath = 'Z:\2025\ECOC Silas\ecoc_2025\';
databasePath = 'C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
database_name = 'ecoc2025_loops.db';
db = DBHandler("type","mysql");
% db = DBHandler("pathToDB", [databasePath, database_name],"type","sqlite");
filterParams = db.tables;
% filterParams.Configurations = struct('run_id', run_id);
filterParams.Configurations = struct( ...
'symbolrate', 112e9, ... %[224,336,360,390,420,448]
'fiber_length', 0, ...
'db_mode', '"no_db"', ...
'interference_attenuation', [], ...
'interference_path_length', 0, ...
'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~=217,:) = [];
num_occ = 10;
run_par = true;
run_id = dataTable.run_id;
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
% Extract all ber_mlse values from the vnle_pf_package using cellfun
ber_mlse = cellfun(@(pkg) pkg.ber_mlse, future.OutputArguments{1,1}.vnle_pf_package);
ber_vnle = cellfun(@(pkg) pkg.ber_vnle, future.OutputArguments{1,1}.vnle_pf_package);
figure(101)
hold on;
scatter(1:num_occ,ber_mlse,15,'Marker','*');
scatter(1:num_occ,ber_vnle,15,'Marker','*');
legend('Interpreter', 'latex');
xlabel('Occurences');
ylabel('BER');
grid on;
beautifyBERplot;

View File

@@ -0,0 +1,58 @@
Scpe_sig = load("imdd_simulation\projects\ECOC_2025\dsp_test\pam4_scopesignal.mat");Scpe_sig = Scpe_sig.Scpe_sig;
Tx_bits = load("imdd_simulation\projects\ECOC_2025\dsp_test\pam4_bits.mat");Tx_bits = Tx_bits.Tx_bits;
Symbols = load("imdd_simulation\projects\ECOC_2025\dsp_test\pam4_symbols.mat");Symbols = Symbols.Symbols;
eq_ = FFE_DCremoval("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",1e-5,"mu_tr",0,"order",50,"sps",2,"decide",0,"mu_dc",0.005,"dc_buffer_len",1);
% 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");
params = (logspace(-6,-2,20));
params = floor((logspace(2,3,20)));
params = 224;
for i = 1:length(params)
eq_ = FFE_DCremoval_adaptive_mu("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
0.0002,"mu_tr",0,"order",25,"sps",2,"decide",0,...
"mu_dc",0.005,"dc_buffer_len",1, ...
"ffe_buffer_len",1,...
"smoothing_buffer_length",0,...
"smoothing_buffer_update",1);
result = ffe(eq_,4,Scpe_sig,Symbols,Tx_bits,"precode_mode",db_mode.no_db,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0);
ber_ffe(i) = result.metrics;
fprintf(" FFE Results: %.2e\n", ber_ffe(i));
% db.addProcessingResult(run_id, result.resultsVNLE, result.equalizerConfigVNLE);
% eq_ = FFE_adaptive_decision("epochs_tr",5,"epochs_dd",3,"len_tr",4096*2,"mu_dd",...
% 0.0003,"mu_tr",0,"order",50,"sps",2,"decide",1,"buffer_length",params(i));
%
% result = vnle(eq_,4,Scpe_sig,Symbols,Tx_bits,"precode_mode",db_mode.no_db,'showAnalysis',1,"postFFE",[],"eth_style_symbol_mapping",0);
% ber_dc(i) = result.ber_vnle;
%
% fprintf(" FFE+dc tr. Results: %.2e\n", ber_dc(i));
end
figure(103435)
hold on;
% scatter(params,ber_dc,15,'Marker','o','LineWidth',1,'DisplayName','DC tracking');
% [a,b]=min(ber_dc);
% scatter(params(b),a,45,'Marker','x','MarkerEdgeColor','r','LineWidth',1);
scatter(params,ber_ffe,15,'Marker','square','LineWidth',1);
[a,b]=min(ber_ffe);
scatter(params(b),a,45,'Marker','x','MarkerEdgeColor','r','LineWidth',1,'DisplayName','FFE');
legend('Interpreter', 'latex');
xlabel('Occurences');
ylabel('BER');
grid on;
beautifyBERplot;
ylim([1e-4,0.1 ])

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,44 @@
% Parameters
reclen = 133169152;
Scp_long = ScopeKeysight("model","DSAZ634A",'autoscale',0,"fadc","GSa_160","channel",[0,0,1,0],"recordLen",reclen,"removeDC",1,"extRef",1);
Scpe_sig_raw = Scp_long.read("channel",[0,0,1,0]);
Scpe_sig_raw = Scpe_sig_raw{3};
signal = Scpe_sig_raw.signal;
Fs = Scpe_sig_raw.fs; % Sampling rate [Hz]
N = length(signal); % Number of samples
f_center = 20e9; % Frequency of interest (~20 GHz)
% Create time vector
t = (0:N-1)' / Fs;
% Mix down to baseband
signal_mix = signal .* exp(-1j * 2 * pi * f_center * t);
% Low-pass filter (optional but recommended)
% Design a filter with bandwidth ~ linewidth * safety margin
BW = 1000e6; % 100 MHz bandwidth for example
% Decimate (reduce sampling rate)
decimation_factor = 10;%floor(Fs / (2 * BW));
signal_decim = downsample(signal_mix, decimation_factor);
Fs_decim = Fs / decimation_factor;
% FFT of downmixed signal
Nfft = 2^nextpow2(length(signal_decim) * 4); % Zero-pad
spectrum = fft(signal_decim .* blackmanharris(length(signal_decim)), Nfft);
freq = (0:Nfft-1) * (Fs_decim / Nfft);
% Only positive frequencies
halfIdx = 1:floor(Nfft/2);
spectrum_half = abs(spectrum(halfIdx));
freq_half = freq(halfIdx);
% Plot
figure;
plot(freq_half / 1e6, 20*log10(spectrum_half));
xlabel('Frequency (MHz)');
ylabel('Magnitude (dB)');
title('Zoomed Spectrum around 20 GHz');
grid on;

View File

@@ -0,0 +1,89 @@
db = DBHandler("type","mysql","dataBase",'labor');
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);
[dataTable,sql_query] = db.queryDB(fp, db.getTableFieldNames('Runs'));
[~, uniqueIdx] = unique(dataTable.run_id); % Get unique run_id indices
dataTable.SIR = -7 - round(dataTable.power_mpi_interference);
%%
for i = 1:size(dataTable,1)
dataTable_ = dataTable(i,:); % Extract unique configurations for each run_id
fsym = dataTable_.symbolrate;
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;
Tx_bits = load([savePath, char(dataTable_.tx_bits_path)]);
Tx_bits = Tx_bits.Bits;
Symbols_mapped = PAMmapper(M,0).map(Tx_bits);
Symbols_mapped.fs = dataTable_.symbolrate;
Symbols = load([savePath, char(dataTable_.tx_symbols_path)]);
Symbols = Symbols.Symbols;
Scpe_sig_raw = load([savePath, char(dataTable_.rx_raw_path(1))]);
Scpe_sig_raw = Scpe_sig_raw.Scpe_sig_raw;
Scpe_sig_raw.plot("displayname",['Scope Signal (Run ID: ',num2str(dataTable_.run_id)],"fignum",dataTable_.run_id,"clear",0);
Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in",Scpe_sig_raw.fs,"fs_out",fsym);
[~,Scpe_cell,found_sync,test,shifts] = Scpe_sig_resampled.tsynch("reference",Symbols,"fs_ref",dataTable_.symbolrate,"debug_plots",0);
shifts_mus = shifts./Scpe_sig_resampled.fs .*1e6;
Scpe_sig_resampled = Scpe_sig_resampled.normalize("mode","rms");
% Scpe_sig_resampled.plot("displayname",['Scope Signal (Run ID: ',num2str(dataTable_.run_id)],"fignum",dataTable_.run_id,"clear",0);
hold on;
% xline(shifts_mus,'HandleVisibility','off');
shifts = shifts-shifts(1)+1;
sep_sig = NaN(M,length(Scpe_sig_resampled));
avg_sig = NaN(M,length(Scpe_sig_resampled));
for j = 1:size(Scpe_cell,1)
[sep_sig_,avg_sig_]=showLevelScatter(Scpe_cell{j}.resample("fs_out",Symbols.fs),Symbols,"fignum",400);
s = shifts(j);
sep_sig(:,s+1:s+length(sep_sig_)) = sep_sig_;
avg_sig(:,s+1:s+length(avg_sig_)) = avg_sig_;
end
disp(num2str(filterParams.Configurations.interference_path_length));
var(sep_sig,0,2,'omitnan')
%%
xax_in_sec = ((1:length(avg_sig)) / fsym) * 1e6;
figure();hold on;
cols = cbrewer2('Paired',8);
% for p = 1:size(avg_sig,1)
% sc=scatter(xax_in_sec,sep_sig(p,:),1,'.','MarkerEdgeColor',cols((2*p)-1,:),'MarkerEdgeAlpha',0.1);
% end
for p = 1:size(avg_sig,1)
sc=plot(xax_in_sec,avg_sig(p,:),'LineWidth',1,'Color',cols((2*p),:));
end
yline(unique(Symbols.signal),'HandleVisibility','off');
% xline(shifts./ fsym .*1e6,'HandleVisibility','off');
xlabel('time in $\mu$s');
ylabel('Normalized Amplitude');
xlim([0 25]);
ylim([-2 2]);
drawnow;
end

View File

@@ -0,0 +1,207 @@
function measure_run(options)
arguments
options.parallel (1,1) logical = false
options.max_occurences = 1;
options.config = [];
options.only_record = 0;
end
savePath = 'Z:\2025\ECOC Silas\ecoc_2025\';
current_folder = 'precomp_optimization_friday';
fullFolderPath = fullfile(savePath, current_folder);
if ~exist(fullFolderPath, 'dir')
mkdir(fullFolderPath);
end
basePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
database_name='ecoc2025.db';
db = DBHandler("pathToDB",[basePath,database_name]);
precomp_path = "C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\";
precomp_fn = "precomp_1km_1mm_cable_70ghz_pd_shf_t850_2p8_bias_shot2";
precomp_mode = 2; %0=do nothing ; 1= measure; 2=precomp active
run_lab_automation = 1;
%% Configuration
conf = db.tables.Configurations;
referenceFields = fieldnames(conf);
conf.unique_elab_id = "20250408-842b0a3078172b48dd032795226fbe683190afc4";
conf.symbolrate = 112e9;
conf.bitrate = conf.symbolrate .* floor(log2(6)*10)/10;
conf.pam_level = 4;
conf.db_mode = db_mode.no_db;
conf.pulsef_alpha = 0.3;
conf.v_bias = 2.8;
conf.v_awg = 0.8;
conf.precomp_amp = -64;
conf.wavelength = 1310;
conf.laser_power = 10;
conf.fiber_length = 0;
conf.pd_in_desired = 7;
conf.is_mpi = 1;
conf.signal_attenuation = 0;
conf.interference_path_length = 0.001; %m
conf.interference_attenuation = 40;
conf.pam_source = [];
% === Optional Override of Conf ===
if ~isempty(options.config)
paramStruct = options.config;
paramNames = fieldnames(paramStruct);
for i = 1:numel(paramNames)
thisName = paramNames{i};
thisValue = paramStruct.(thisName);
if isfield(conf, thisName)
conf.(thisName) = thisValue;
else
warning('Unknown parameter: %s', thisName);
end
end
end
assert(isequal(fieldnames(conf), referenceFields), 'Fieldnames do not match the reference structure!');
currentTime = datetime('now', 'Format', 'yyyyMMdd_HHmmss');
timeStr = char(currentTime);
filename = sprintf('%s_PAM_%d_R_%d',timeStr,conf.pam_level,conf.symbolrate.*1e-9);
%% Lab Automation
if run_lab_automation
% DC Source
dcs = DC_supply("active",[1,1],"voltage",[conf.v_bias, 5]);
dcs.set("voltage",[conf.v_bias, 5]);
[v_bias_meas,i_bias_meas]=dcs.readVals();
% Laser
laser = Exfo_laser("mainframe_channel",1,"safety_mode",0,"connection_id",'10','lab_interface','gpib');
laser.setWavelength(conf.wavelength);
laser.setPower(conf.laser_power);
laser.enableLaser();
% Optical Attenuator
voa = OptAtten("active",[0,2,1,1],"value",[0,conf.pd_in_desired,conf.signal_attenuation,conf.interference_attenuation],"wavelength",[conf.wavelength,conf.wavelength,conf.wavelength,conf.wavelength]);
voa.set('value',[0,conf.pd_in_desired,conf.signal_attenuation,conf.interference_attenuation]);
% PDFA
pdfa = Thor_PDFA("safety_mode",0,"serialport_number",'COM15');
% Construct AWG and Scope Modules %%%%%%
fdac = 256e9;
fadc = 160e9;
Scp = ScopeKeysight("model","DSAZ634A",'autoscale',1,"fadc","GSa_160","channel",[0,0,1,0],"recordLen",2000000,"removeDC",1,"extRef",1);
Awg = AwgKeysight("model","M8199A_ILV","fdac",fdac,"scaletodac",[1,1],"skews",[0,0],"voltages",[0,conf.v_awg]);
A2S = Awg2Scope(Awg,Scp,[0,0,3,0],"waitUntilClick",0);
end
%% Signal generation and transmission
%%%%% Symbol Generation %%%%%%
Pform = Pulseformer("fsym",conf.symbolrate,"fdac",8*conf.symbolrate,"pulse","rc","pulselength",16,"alpha",conf.pulsef_alpha);
Pamsource = PAMsource(...
"fsym",conf.symbolrate,"M",conf.pam_level,"order",18,"useprbs",1,...
"fs_out",fdac,...
"applyclipping",0,...
"clipfactor",4,...
"applypulseform",1,...
"pulseformer",Pform,...
"randkey",1,...
"duobinary_mode", conf.db_mode);
conf.pam_source = Pamsource;
[Digi_sig,Symbols,Bits] = Pamsource.process();
%%%%% Precompensation Routine %%%%%%
if precomp_mode == 1 % measure channel
precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',fdac);
Digi_sig = precomp_est.buildOFDM();
elseif precomp_mode == 2 % apply precomp
precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',Digi_sig.fs);
Digi_sig = precomp_est.precomp(Digi_sig,'maxampdb',conf.precomp_amp,'loadPath',precomp_path,'fileName',precomp_fn);
end
Digi_sig.spectrum("displayname",'Tx Spectrum','fignum',10,'normalizeTo0dB',0);
%%%%% Resample to DAC rate %%%%%%
Digi_sig = Digi_sig.resample("fs_out",Awg.fdac);
[~,~,Scpe_sig_raw,~] = A2S.process("signal2",Digi_sig);
% Awg.upload("signal2",Digi_sig);
% Scpe_sig_raw = Scp.read("channel",[0,0,1,0]);
% Scpe_sig_raw = Scpe_sig_raw{[0,0,1,0]==1};
%%%%% Precompensation Routine %%%%%%
if precomp_mode == 1
% Scpe_sig_resampled = Scpe_sig_raw.resample("fs_in",fadc,"fs_out",2*fsym);
precomp_est.estimate(Scpe_sig_raw,"save",true,"savePath",precomp_path,"fileName",precomp_fn);
precomp_est.plot();
return;
end
Scpe_sig_raw.spectrum("displayname",'Rx Spectrum','fignum',10,'normalizeTo0dB',0);
Scpe_sig_raw.plot("clear",1,"displayname",'Rx Signal','fignum',11);
%% %%%%% Save Routine %%%%%%%%%%%%%%%%%%%%%%%%%
tx_bits_path=[filesep,current_folder,filesep,filename,'_bits'];
save([savePath,tx_bits_path],"Bits");
tx_symbols_path=[filesep,current_folder,filesep,filename,'_symbols'];
save([savePath,tx_symbols_path],"Symbols");
tx_signal_path=[filesep,current_folder,filesep,filename,'_signal'];
save([savePath,tx_signal_path],"Digi_sig");
rx_raw_path = [filesep,current_folder,filesep,filename,'_rx_signal_raw'];
save([savePath,rx_raw_path],"Scpe_sig_raw");
% Table 1: Append to Runs
newRun = db.tables.Runs;
newRun.run_id = NaN;
newRun.date_of_run = datetime(currentTime, 'InputFormat', 'yyyyMMdd_HHmmss');
newRun.tx_bits_path = tx_bits_path;
newRun.tx_symbols_path = tx_symbols_path;
newRun.rx_sync_path = [];
newRun.rx_raw_path = rx_raw_path;
newRun.filename = filename;
newRun.tx_signal_path = tx_signal_path;
run_id = db.appendToTable('Runs', newRun);
% Table 2: Append to Configurations
conf.configuration_id = NaN;
conf.run_id = run_id;
db.appendToTable('Configurations', conf);
% Table 3: Append to Measurements
meas = db.tables.Measurements;
meas.measurement_id = NaN; % Auto-increment, leave empty
meas.run_id = run_id;
meas.power_laser = laser.cur_power;
meas.power_rop = [];
meas.power_pd_in = voa.power_state(2);
meas.power_mpi_interference = voa.power_state(4);
meas.power_mpi_signal = voa.power_state(3);
meas.voa_class = voa;
meas.pdfa_class = pdfa;
meas.laser_class = laser;
assert(isequal(fieldnames(meas), fieldnames(db.tables.Measurements)), 'Fieldnames of "meas" do not match the reference structure!');
db.appendToTable('Measurements', meas);
%% %%%%% DSP Routine %%%%%%%%%%%%%%%%%%%%%%%%%
[out, ~] = submit_dsp(run_id, basePath, database_name, savePath,"parallel",options.parallel,'max_occurences',options.max_occurences);
end

View File

@@ -0,0 +1,272 @@
fullFolderPath = fullfile(savePath, current_folder);
if ~exist(fullFolderPath, 'dir')
mkdir(fullFolderPath);
end
precomp_path = "C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\";
precomp_fn = "precomp_1km_1mm_cable_70ghz_pd_shf_t850_2p8_bias_shot2";
precomp_mode = 2; %0=do nothing ; 1= measure; 2=precomp active
run_lab_automation = 0;
%% Configuration
conf = confRequest;
referenceFields = fieldnames(db.tables.Configurations);
% assert(isequal(fieldnames(conf), referenceFields), 'Fieldnames do not match the reference structure!');
%% Lab Automation
if run_lab_automation
% DC Source
if (any(confPrev.v_bias ~= conf.v_bias)) || (~exist('dcs','var')) || (isempty(confPrev.v_bias))
dcs = DC_supply("active",[1,1],"voltage",[conf.v_bias, 5]);
dcs.set("voltage",[conf.v_bias, 5]);
[v_bias_meas,i_bias_meas]=dcs.readVals();
end
% Laser
if (any(confPrev.laser_power ~= conf.laser_power)) || (~exist('laser','var')) || (isempty(confPrev.laser_power))
laser = Exfo_laser("mainframe_channel",1,"safety_mode",0,"connection_id",'10','lab_interface','gpib');
laser.setWavelength(conf.wavelength);
laser.setPower(conf.laser_power);
laser.enableLaser();
end
% Optical Attenuator
if ~exist('voa','var')
voa = OptAtten("active",[1,2,1,1],"value",[0,conf.pd_in_desired,conf.signal_attenuation,conf.interference_attenuation],"wavelength",[conf.wavelength,conf.wavelength,conf.wavelength,conf.wavelength]);
end
voa.set('value',[0,conf.pd_in_desired,conf.signal_attenuation,conf.interference_attenuation]);
if voa.power_state(3) > -24 || voa.power_state(1)+ voa.atten_state(4) > -37
holdAndShowValue
end
% PDFA
if ~exist('pdfa','var')
pdfa = Thor_PDFA("safety_mode",0,"serialport_number",'COM15');
end
end
% if conf.symbolrate == 72e9
% if conf.pam_level == 2
% conf.v_awg = 0.55;
% conf.pulsef_alpha = 0.6;
% elseif conf.pam_level == 4
% conf.v_awg = 0.55;
% conf.pulsef_alpha =0.6;
% elseif conf.pam_level == 6
% conf.v_awg = 0.55;
% conf.pulsef_alpha = 0.6;
% elseif conf.pam_level == 8
% conf.v_awg = 0.55;
% conf.pulsef_alpha = 0.6;
% end
% elseif conf.symbolrate == 96e9
% if conf.pam_level == 2
% conf.v_awg = 0.8;
% conf.pulsef_alpha = 0.2;
% elseif conf.pam_level == 4
% conf.v_awg = 0.8;
% conf.pulsef_alpha = 0.2;
% elseif conf.pam_level == 6
% conf.v_awg = 0.8;
% conf.pulsef_alpha = 0.2;
% elseif conf.pam_level == 8
% conf.v_awg = 0.8;
% conf.pulsef_alpha = 0.2;
% end
% elseif conf.symbolrate == 112e9
% if conf.pam_level == 2
% conf.v_awg = 0.85;
% conf.pulsef_alpha = 0.2;
% elseif conf.pam_level == 4
% conf.v_awg = 0.85;
% conf.pulsef_alpha = 0.2;
% elseif conf.pam_level == 6
% conf.v_awg = 0.85;
% conf.pulsef_alpha = 0.2;
% elseif conf.pam_level == 8
% conf.v_awg = 0.85;
% conf.pulsef_alpha = 0.2;
% end
% end
awg_upload_required = 1;
if any(confPrev.pam_level == conf.pam_level) && any(confPrev.symbolrate == conf.symbolrate)
awg_upload_required = 0;
end
%% Signal generation and transmission
if awg_upload_required || isempty(loop_id)
%%%%% Symbol Generation %%%%%%
Pform = Pulseformer("fsym",conf.symbolrate,"fdac",8*conf.symbolrate,"pulse","rc","pulselength",16,"alpha",conf.pulsef_alpha);
if conf.symbolrate < 64e9
sequence_order = min(18,sequence_order);
end
% Construct AWG and Scope Modules %%%%%%
fdac = 224e9;
fadc = 160e9;
Scp = ScopeKeysight("model","DSAZ634A",'autoscale',0,"fadc","GSa_160","channel",[0,0,1,0],"recordLen",recordlen,"removeDC",1,"extRef",1);
Awg = AwgKeysight("model","M8199A_ILV","fdac",fdac,"scaletodac",[1,1],"skews",[0,0],"voltages",[0,conf.v_awg]);
A2S = Awg2Scope(Awg,Scp,[0,0,3,0],"waitUntilClick",0);
Pamsource = PAMsource(...
"fsym",conf.symbolrate,"M",conf.pam_level,"order",sequence_order,"useprbs",1,...
"fs_out",fdac,...
"applyclipping",0,...
"clipfactor",4,...
"applypulseform",1,...
"pulseformer",Pform,...
"randkey",1,...
"duobinary_mode", conf.db_mode);
[Digi_sig,Symbols,Bits] = Pamsource.process();
figure(10);clf;
Digi_sig.spectrum("displayname",'Tx Pulse Shaped','fignum',10,'normalizeTo0dB',1);
%%%%% Precompensation Routine %%%%%%
if precomp_mode == 1 % measure channel
precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',fdac);
Digi_sig = precomp_est.buildOFDM();
elseif precomp_mode == 2 % apply precomp
precomp_est = ChannelFreqResp("Nacq",2048,"Navg",100,"Ncp",63,'f_ref',Digi_sig.fs);
Digi_sig = precomp_est.precomp(Digi_sig,'maxampdb',conf.precomp_amp,'loadPath',precomp_path,'fileName',precomp_fn);
end
Digi_sig.spectrum("displayname",'Tx Pre-Emphasized','fignum',10,'normalizeTo0dB',1);
%%%%% Resample to DAC rate %%%%%%
Digi_sig = Digi_sig.resample("fs_out",Awg.fdac);
Digi_sig.spectrum("displayname",'Tx Resampled','fignum',10,'normalizeTo0dB',1);
% [~,~,Scpe_sig_raw,~] = A2S.process("signal2",Digi_sig);
Awg.upload("signal2",Digi_sig);
end
% Precompensation Routine (optional)
if precomp_mode == 1
Scpe_sig_raw = Scp.read("channel",[0,0,1,0]);
Scpe_sig_raw = Scpe_sig_raw{[0,0,1,0]==1};
precomp_est.estimate(Scpe_sig_raw, "save", true, "savePath", precomp_path, "fileName", precomp_fn);
precomp_est.plot();
return; % End routine if precomp mode is active
end
confPrev = conf;
%% Save static TX data (only once)
currentTime = datetime('now', 'Format', 'yyyyMMdd_HHmmss');
timeStr = char(currentTime);
base_filename = sprintf('%s_PAM_%d_R_%d', timeStr, conf.pam_level, round(conf.symbolrate.*1e-9));
tx_bits_path = [filesep, current_folder, filesep, base_filename, '_bits'];
save([savePath, tx_bits_path], "Bits");
tx_symbols_path = [filesep, current_folder, filesep, base_filename, '_symbols'];
save([savePath, tx_symbols_path], "Symbols");
tx_signal_path = [filesep, current_folder, filesep, base_filename, '_signal'];
save([savePath, tx_signal_path], "Digi_sig");
for recIdx = 1:n_recording
fprintf('Recording %d / %d\n', recIdx, n_recording);
%% Acquire Scope Signal
Scpe_sig_raw = Scp.read("channel",[0,0,1,0]);
Scpe_sig_raw = Scpe_sig_raw{[0,0,1,0]==1};
% Precompensation Routine (optional)
if precomp_mode == 1
precomp_est.estimate(Scpe_sig_raw, "save", true, "savePath", precomp_path, "fileName", precomp_fn);
precomp_est.plot();
return; % End routine if precomp mode is active
end
% Plot spectrum and time domain (optional)
Scpe_sig_raw.spectrum("displayname", 'Rx Spectrum', 'fignum', 10, 'normalizeTo0dB', 0);
Scpe_sig_raw.plot("clear", 1, "displayname", 'Rx Signal', 'fignum', 11);
%% Save Routine
currentTime = datetime('now', 'Format', 'yyyyMMdd_HHmmss');
timeStr = char(currentTime);
filename = sprintf('%s_PAM_%d_R_%d_rec%02d', timeStr, conf.pam_level, round(conf.symbolrate.*1e-9), recIdx); % Added recIdx
rx_raw_path = [filesep, current_folder, filesep, filename, '_rx_signal_raw'];
save([savePath, rx_raw_path], "Scpe_sig_raw");
% Check if loop_id is already created
if isempty(loop_id)
newLoop = db.tables.LoopControl;
newLoop.loop_id = NaN;
newLoop.date_of_loop = datetime('now', 'Format', 'yyyy-MM-dd HH:mm:ss');
newLoop.description = 'Automated parameter sweep';
loop_id = db.appendToTable('LoopControl', newLoop);
fprintf('LoopControl entry created: loop_id = %d\n', loop_id);
end
%% Append to Runs Table
newRun = db.tables.Runs;
newRun.run_id = NaN;
newRun.loop_id = loop_id;
newRun.date_of_run = datetime(currentTime, 'InputFormat', 'yyyyMMdd_HHmmss');
newRun.tx_bits_path = tx_bits_path;
newRun.tx_symbols_path = tx_symbols_path;
newRun.rx_sync_path = ""; % Leave empty for now
newRun.rx_raw_path = rx_raw_path;
newRun.filename = filename;
newRun.tx_signal_path = tx_signal_path;
run_id = db.appendToTable('Runs', newRun);
%% Append to Configurations Table
conf.configuration_id = NaN;
conf.run_id = run_id;
conf.bitrate = conf.symbolrate .* floor(log2(6)*10)/10;
conf.pam_source = Pamsource;
db.appendToTable('Configurations', conf);
%% Append to Measurements Table
laser.getLaserInfo;
meas = db.tables.Measurements;
meas.measurement_id = NaN;
meas.run_id = run_id;
meas.power_laser = laser.cur_power;
meas.power_rop = [];
meas.power_pd_in = voa.power_state(2);
meas.power_mpi_interference = voa.power_state(4);
meas.power_mpi_signal = voa.power_state(3);
meas.voa_class = voa;
meas.pdfa_class = pdfa;
meas.laser_class = laser;
% Ensure consistency
assert(isequal(fieldnames(meas), fieldnames(db.tables.Measurements)), 'Fieldnames of "meas" do not match the reference structure!');
db.appendToTable('Measurements', meas);
%% Submit DSP Routine
if recIdx == 1
if subimt_DSP
[out, a] = submit_dsp(run_id, databasePath, database_name, savePath, ...
"parallel", parallel_dsp, "max_occurences", max_occurences);
end
if awg_upload_required
[out, a] = submit_dsp(run_id, databasePath, database_name, savePath, ...
"parallel", 0, "max_occurences", 1);
end
end
end

View File

@@ -0,0 +1,269 @@
basePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
database_name = 'ecoc2025.db';
database = DBHandler("pathToDB", [basePath, database_name],"type",'mysql');
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', [], ...
'is_mpi', 1, ...
'pam_level', 4, ...
'wavelength', 1310, ...
'precomp_amp', -64, ...
'signal_attenuation', '0', ...
'v_awg', [], ...
'v_bias', [] ...
);
% filterParams.EqualizerParameters.diff_precode = int32(db_mode.db_encoded);
% filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle);
selectedFields = {'Configurations.run_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.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 = cleanUpTable(dataTable);
% Filter by time
startTime = datetime('2025-04-11 13:00:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss');
stopTime = datetime('2025-04-11 14:00: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, :);
% Group by smth
y_var = 'SNR';
x_var = 'v_bias';
loop_var = 'v_awg';
fixedVars = {'eq_id',loop_var};
dataTableGrpd = groupIt(fixedVars,dataTable);
plotRealizations = 1;
% Create a new figure
mkr = 'x';
figure(10);
hold on
unique_loop_var = unique(dataTable.(loop_var));
cols = linspecer(8);
for i = 1:numel(unique_loop_var)
idx = find(dataTable.(loop_var)== unique_loop_var(i), 1, 'first');
% dispname = equalizer_structure(dataTable.equalizer_structure(idx));
dispname = num2str(unique_loop_var(i));
% Plot SCATTERS: timestamp vs. interference_attenuation
loop_filt_2 = dataTable.(loop_var)==unique_loop_var(i);
if plotRealizations
y_values = dataTable.(y_var)(loop_filt_2,:);
x_values = dataTable.(x_var)(loop_filt_2,:);
y_values = double(y_values);
x_values = double(x_values);
sc = scatter(x_values, y_values, 'LineWidth', 0.5,'Marker',mkr,'MarkerEdgeColor',cols(i,:),'HandleVisibility','on','DisplayName',string(dispname));
pair_one = {'Run ID', dataTable.run_id(loop_filt_2,:)};
pair_two = {'Rate', dataTable.bitrate(loop_filt_2,:).*1e-9};
pair_three = {'PD in', round(dataTable.power_pd_in(loop_filt_2,:),2)};
addDatatips(sc, pair_one, pair_two,pair_three);
xticks(unique(x_values));
end
end
% Label the axes and add a title
legend('Interpreter','latex');
xlabel(x_var);
ylabel(y_var);
title([x_var,' vs. ',y_var]);
if y_var == 'BER'
yline(3.8e-3,'LineWidth',1,'LineStyle','--','HandleVisibility','off');
ylim([1e-4 0.5]);
end
% Enable grid for better readability
grid on;
beautifyBERplot;
function resultTable = groupIt(fixedVars,dataTable)
% Group by run_id and eq_id (adjust grouping keys as needed)
[G, groupKeys] = findgroups(dataTable(:, fixedVars));
% Preallocate a cell array for aggregated data.
varNames = dataTable.Properties.VariableNames;
nVars = numel(varNames);
aggData = cell(height(groupKeys), nVars);
groupCount = zeros(height(groupKeys), 1); % To store the size of each group
% Loop over each group.
for i = 1:height(groupKeys)
idx = (G == i); % Logical index for group i
groupCount(i) = sum(idx); % Count number of rows in this group
% For each variable in the table:
for j = 1:nVars
colData = dataTable.(varNames{j});
if isnumeric(colData)
% For numeric data, compute the mean.
aggData{i, j} = min(colData(idx));
else
% For non-numeric data, take the first entry.
if iscell(colData)
aggData{i, j} = colData{find(idx, 1)};
else
aggData{i, j} = colData(find(idx, 1));
end
end
end
end
% Convert the aggregated cell array into a table.
resultTable = cell2table(aggData, 'VariableNames', varNames);
% Append the group count as a new column.
resultTable.nRows = groupCount;
end
function addDatatips(sc, varargin)
% addDatatips Adds custom data tip rows to a scatter plot.
%
% addDatatips(sc, pair1, pair2, ...) adds one or more custom rows to the
% data tip display of the scatter plot identified by sc.
%
% Each pair should be provided as a 1x2 cell array: {label, value}.
% The value can be a scalar or a vector. If a vector is provided, its length
% must match the number of scatter plot points.
%
% Example:
% sc = scatter(x, y, 'LineWidth', 1.5, 'Marker', 'o');
% pair_one = {'Attenuation', attenuationVector};
% addDatatips(sc, pair_one);
numPoints = numel(sc.XData);
for k = 1:length(varargin)
pair = varargin{k};
if ~iscell(pair) || numel(pair) ~= 2
error('Each pair must be a 1x2 cell array: {label, value}.');
end
label = pair{1};
value = pair{2};
% If value is a vector, ensure its length is either 1 or equal to the number of scatter points.
if isvector(value) && numel(value) ~= 1 && numel(value) ~= numPoints
error('The vector for "%s" must be a scalar or have %d elements matching the scatter data points.', label, numPoints);
end
% Create a new data tip row using the provided label and vector.
newRow = dataTipTextRow(label, value);
sc.DataTipTemplate.DataTipRows(end+1) = newRow;
end
end
function cleanedTable = cleanUpTable(inputTable)
% cleanUpTable Cleans a MATLAB table where numbers and NaNs are stored as strings or structs.
%
% cleanedTable = cleanUpTable(inputTable)
%
% This function goes through all columns of the input table:
% - Converts strings of numbers to numeric values
% - Converts string 'NaN' and struct NaNs to real NaN
% - Converts date strings to datetime (if possible)
%
% Input:
% inputTable - MATLAB table with mixed types
%
% Output:
% cleanedTable - Cleaned MATLAB table with proper numeric types
cleanedTable = inputTable;
varNames = cleanedTable.Properties.VariableNames;
for i = 1:numel(varNames)
col = cleanedTable.(varNames{i});
% Case 1: If it's a cell array (likely mixed strings/struct)
if iscell(col)
% Convert struct 'NaN' entries to string 'NaN'
col = cellfun(@(x) convertStructToString(x), col, 'UniformOutput', false);
% Try to convert string numbers to actual numbers
numericCol = str2double(col);
if all(isnan(numericCol) == strcmpi(col, 'NaN') | cellfun(@isempty, col))
% If conversion is successful (NaNs correspond to 'NaN' strings), use it
cleanedTable.(varNames{i}) = numericCol;
else
% Else, try to convert to datetime
try
cleanedTable.(varNames{i}) = datetime(col, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS');
catch
% If it fails, leave as cell array of strings
cleanedTable.(varNames{i}) = string(col);
end
end
% Case 2: If it's already a string array
elseif isstring(col)
numericCol = str2double(col);
if all(isnan(numericCol) == strcmpi(col, "NaN"))
cleanedTable.(varNames{i}) = numericCol;
else
% Try convert to datetime
try
cleanedTable.(varNames{i}) = datetime(col, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS');
catch
% Leave as string
end
end
% Case 3: If it's already numeric, keep as is
elseif isnumeric(col)
continue;
% Case 4: If it's datetime, keep as is
elseif isdatetime(col)
continue;
else
% Catch-all for unexpected types, convert to string
cleanedTable.(varNames{i}) = string(col);
end
end
end
function out = convertStructToString(x)
% Helper function to convert struct NaN to string 'NaN'
if isstruct(x)
out = "NaN";
elseif isstring(x) || ischar(x)
out = string(x);
else
out = x;
end
end

View File

@@ -0,0 +1,271 @@
basePath = 'C:\Users\sioe\Documents\MATLAB\imdd_simulation\projects\ECOC_2025\';
database_name = 'ecoc2025.db';
database = DBHandler("pathToDB", [basePath, database_name]);
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', [], ...
'is_mpi', 0, ...
'pam_level', 4, ...
'wavelength', 1310, ...
'precomp_amp', [], ...
'signal_attenuation', [], ...
'v_awg', 0.8, ...
'v_bias', 2.8 ...
);
% filterParams.EqualizerParameters.diff_precode = int32(db_mode.db_encoded);
% filterParams.EqualizerParameters.equalizer_structure = int32(equalizer_structure.vnle);
selectedFields = {'Configurations.run_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.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 = cleanUpTable(dataTable);
% Filter by time
startTime = datetime('2025-04-11 13:00:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss');
stopTime = datetime('2025-04-11 14:00: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, :);
% Group by smth
y_var = 'BER';
x_var = 'interference_attenuation';
loop_var = 'eq_id';
fixedVars = {'eq_id',loop_var};
dataTableGrpd = groupIt(fixedVars,dataTable);
plotRealizations = 1;
% Create a new figure
mkr = '*';
figure(10);
hold on
unique_loop_var = unique(dataTable.(loop_var));
cols = linspecer(8);
for i = 1:numel(unique_loop_var)
idx = find(dataTable.(loop_var)== unique_loop_var(i), 1, 'first');
dispname = equalizer_structure(dataTable.equalizer_structure(idx));
% dispname = num2str(unique_loop_var(i));
% Plot SCATTERS: timestamp vs. interference_attenuation
loop_filt_2 = dataTable.(loop_var)==unique_loop_var(i);
if plotRealizations
y_values = dataTable.(y_var)(loop_filt_2,:);
x_values = dataTable.(x_var)(loop_filt_2,:);
y_values = double(y_values);
x_values = double(x_values);
sc = scatter(x_values, y_values, 'LineWidth', 0.5,'Marker',mkr,'MarkerEdgeColor',cols(i,:),'HandleVisibility','on','DisplayName',string(dispname));
pair_one = {'Run ID', dataTable.run_id(loop_filt_2,:)};
pair_two = {'Rate', dataTable.bitrate(loop_filt_2,:).*1e-9};
pair_three = {'PD in', round(dataTable.power_pd_in(loop_filt_2,:),2)};
addDatatips(sc, pair_one, pair_two,pair_three);
xticks(unique(x_values));
end
end
% Label the axes and add a title
legend('Interpreter','latex');
xlabel(x_var);
ylabel(y_var);
title([x_var,' vs. ',y_var]);
if y_var == 'BER'
yline(3.8e-3,'LineWidth',1,'LineStyle','--','HandleVisibility','off');
ylim([1e-5 0.1]);
end
% Enable grid for better readability
grid on;
beautifyBERplot;
function resultTable = groupIt(fixedVars,dataTable)
% Group by run_id and eq_id (adjust grouping keys as needed)
[G, groupKeys] = findgroups(dataTable(:, fixedVars));
% Preallocate a cell array for aggregated data.
varNames = dataTable.Properties.VariableNames;
nVars = numel(varNames);
aggData = cell(height(groupKeys), nVars);
groupCount = zeros(height(groupKeys), 1); % To store the size of each group
% Loop over each group.
for i = 1:height(groupKeys)
idx = (G == i); % Logical index for group i
groupCount(i) = sum(idx); % Count number of rows in this group
% For each variable in the table:
for j = 1:nVars
colData = dataTable.(varNames{j});
if isnumeric(colData)
% For numeric data, compute the mean.
aggData{i, j} = min(colData(idx));
else
% For non-numeric data, take the first entry.
if iscell(colData)
aggData{i, j} = colData{find(idx, 1)};
else
aggData{i, j} = colData(find(idx, 1));
end
end
end
end
% Convert the aggregated cell array into a table.
resultTable = cell2table(aggData, 'VariableNames', varNames);
% Append the group count as a new column.
resultTable.nRows = groupCount;
end
function addDatatips(sc, varargin)
% addDatatips Adds custom data tip rows to a scatter plot.
%
% addDatatips(sc, pair1, pair2, ...) adds one or more custom rows to the
% data tip display of the scatter plot identified by sc.
%
% Each pair should be provided as a 1x2 cell array: {label, value}.
% The value can be a scalar or a vector. If a vector is provided, its length
% must match the number of scatter plot points.
%
% Example:
% sc = scatter(x, y, 'LineWidth', 1.5, 'Marker', 'o');
% pair_one = {'Attenuation', attenuationVector};
% addDatatips(sc, pair_one);
numPoints = numel(sc.XData);
for k = 1:length(varargin)
pair = varargin{k};
if ~iscell(pair) || numel(pair) ~= 2
error('Each pair must be a 1x2 cell array: {label, value}.');
end
label = pair{1};
value = pair{2};
% If value is a vector, ensure its length is either 1 or equal to the number of scatter points.
if isvector(value) && numel(value) ~= 1 && numel(value) ~= numPoints
error('The vector for "%s" must be a scalar or have %d elements matching the scatter data points.', label, numPoints);
end
% Create a new data tip row using the provided label and vector.
newRow = dataTipTextRow(label, value);
sc.DataTipTemplate.DataTipRows(end+1) = newRow;
end
end
function cleanedTable = cleanUpTable(inputTable)
% cleanUpTable Cleans a MATLAB table where numbers and NaNs are stored as strings or structs.
%
% cleanedTable = cleanUpTable(inputTable)
%
% This function goes through all columns of the input table:
% - Converts strings of numbers to numeric values
% - Converts string 'NaN' and struct NaNs to real NaN
% - Converts date strings to datetime (if possible)
%
% Input:
% inputTable - MATLAB table with mixed types
%
% Output:
% cleanedTable - Cleaned MATLAB table with proper numeric types
cleanedTable = inputTable;
varNames = cleanedTable.Properties.VariableNames;
for i = 1:numel(varNames)
col = cleanedTable.(varNames{i});
% Case 1: If it's a cell array (likely mixed strings/struct)
if iscell(col)
% Convert struct 'NaN' entries to string 'NaN'
col = cellfun(@(x) convertStructToString(x), col, 'UniformOutput', false);
% Try to convert string numbers to actual numbers
numericCol = str2double(col);
if all(isnan(numericCol) == strcmpi(col, 'NaN') | cellfun(@isempty, col))
% If conversion is successful (NaNs correspond to 'NaN' strings), use it
cleanedTable.(varNames{i}) = numericCol;
else
% Else, try to convert to datetime
try
cleanedTable.(varNames{i}) = datetime(col, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS');
catch
% If it fails, leave as cell array of strings
cleanedTable.(varNames{i}) = string(col);
end
end
% Case 2: If it's already a string array
elseif isstring(col)
numericCol = str2double(col);
if all(isnan(numericCol) == strcmpi(col, "NaN"))
cleanedTable.(varNames{i}) = numericCol;
else
% Try convert to datetime
try
cleanedTable.(varNames{i}) = datetime(col, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS');
catch
% Leave as string
end
end
% Case 3: If it's already numeric, keep as is
elseif isnumeric(col)
continue;
% Case 4: If it's datetime, keep as is
elseif isdatetime(col)
continue;
else
% Catch-all for unexpected types, convert to string
cleanedTable.(varNames{i}) = string(col);
end
end
end
function out = convertStructToString(x)
% Helper function to convert struct NaN to string 'NaN'
if isstruct(x)
out = "NaN";
elseif isstring(x) || ischar(x)
out = string(x);
else
out = x;
end
end

View File

@@ -0,0 +1,599 @@
% dsp_options.database_type = 'mysql';
% dsp_options.dataBase = 'labor';
% dsp_options.storage_path = 'Z:\2025\ECOC Silas\ecoc_2025\';
% database = DBHandler("dataBase", [dsp_options.dataBase], "type", dsp_options.database_type);
% filterParams = database.tables;
% filterParams.Runs.loop_id = 209;
% % filterParams.Configurations = struct( ...
% % 'symbolrate', 112e9, ... %[224,336,360,390,420,448]
% % 'fiber_length', 0, ...
% % 'db_mode', '"no_db"', ...
% % 'interference_attenuation', [], ...
% % 'interference_path_length', 1000, ...
% % 'is_mpi', 1, ...
% % 'pam_level', 4, ...
% % 'wavelength', 1310, ...
% % 'precomp_amp', [], ...
% % 'signal_attenuation', [], ...
% % 'v_awg', [], ...
% % 'v_bias', [] ...
% % );
%
% % if 1
% % % filterParams.EqualizerParameters.dc_buffer_len = 1;
% % filterParams.EqualizerParameters.ffe_buffer_len = 1;
% % filterParams.EqualizerParameters.smoothing_buffer_len = 4096;
% % filterParams.EqualizerParameters.smoothing_buffer_update = 224;
% % filterParams.EqualizerParameters.DCmu = 0;
% % end
% a = database.getTableFieldNames('Runs');
% b = database.getTableFieldNames('Results');
% c = database.getTableFieldNames('EqualizerParameters');
% d = [a;b;c];
%
% [dataTable,~] = database.queryDB(filterParams, d);
%
% selectedFields = {'Configurations.run_id' 'Runs.loop_id' 'Runs.date_of_run' 'Runs.rx_raw_path' 'Runs.bitrate' 'Runs.v_bias' 'Runs.v_awg' 'Runs.precomp_amp' 'Runs.symbolrate' 'Runs.pam_level'...
% 'Runs.db_mode' 'Runs.rop_attenuation' 'Runs.is_mpi' 'Runs.interference_attenuation' 'Runs.interference_path_length' 'Runs.signal_attenuation' ...
% 'EqualizerParameters.equalizer_structure' 'EqualizerParameters.diff_precode' 'EqualizerParameters.eq_id' 'EqualizerParameters.dc_buffer_len' 'EqualizerParameters.ffe_buffer_len' 'EqualizerParameters.smoothing_buffer_len' 'EqualizerParameters.smoothing_buffer_update' 'EqualizerParameters.DCmu' 'Measurements.power_pd_in' ...
% 'Measurements.power_mpi_interference' 'Measurements.power_mpi_signal' 'Results.BER' 'Results.BER_precoded' 'Results.EVM' 'Results.SNR' 'Results.GMI' 'Results.Alpha' 'Results.date_of_processing'};
db = DBHandler("type","mysql","dataBase",'labor');
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);
%%
dataTable_clean = dataTable;
dataTable_clean.SIR = -7 - round(dataTable_clean.power_mpi_interference);
dataTable_clean.NGMI = dataTable_clean.GMI ./ log2(dataTable_clean.pam_level);
dataTable_clean = cleanUpTable(dataTable_clean);
dataTable_clean(dataTable_clean.BER>0.2,:) = [];
%%
cols = linspecer(8); % Ensure color count matches
figure()
tiledlayout(1, 4, 'TileSpacing', 'compact', 'Padding', 'compact');
y_here = 0;
figcnt = 0;
for int_len = [0,50,300,1000]
figcnt = figcnt+1;
% figure(int_len+1);
nexttile;
hold on
mode = 4;
for mode = [1,2]
hold on;
dataTable = dataTable_clean;
plotBoundaries = 1;
plotRealizations = 1;
cols = linspecer(8); % Ensure color count matches
if mode == 1
% No compensation method
dataTable = dataTable(dataTable.dc_buffer_len == 1, :);
dataTable = dataTable(dataTable.ffe_buffer_len == 1, :);
dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :);
dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :);
dataTable = dataTable(dataTable.DCmu == 0, :);
cols = cols(1:1+1,:);
method = 'ffe only';
% slow DC smoothing
elseif mode == 2
dataTable = dataTable(dataTable.dc_buffer_len == 1, :);
dataTable = dataTable(dataTable.ffe_buffer_len == 1, :);
dataTable = dataTable(dataTable.smoothing_buffer_len == 4096, :);
dataTable = dataTable(dataTable.smoothing_buffer_update == 224, :);
dataTable = dataTable(dataTable.DCmu == 0, :);
cols = cols(4:4+1,:);
method = 'dc smoothing';
% slow DC tracking
elseif mode == 3
dataTable = dataTable(dataTable.dc_buffer_len == 224, :);
dataTable = dataTable(dataTable.ffe_buffer_len == 1, :);
dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :);
dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :);
dataTable = dataTable(dataTable.DCmu == 0.005, :);
cols = cols(3:3+1,:);
method = 'parallelized dc tracking';
elseif mode == 4
% ideal DC tracking
dataTable = dataTable(dataTable.dc_buffer_len == 1, :);
dataTable = dataTable(dataTable.ffe_buffer_len == 1, :);
dataTable = dataTable(dataTable.smoothing_buffer_len == 0, :);
dataTable = dataTable(dataTable.smoothing_buffer_update == 0, :);
dataTable = dataTable(dataTable.DCmu == 0.005, :);
cols = cols(2:2+1,:);
method = 'ideal dc tracking';
end
% dataTable(dataTable.eq_id==0,:) = [];
dataTable(dataTable.equalizer_structure~=1,:) = [];
% Modify values in 'interference_path_length' where the condition is met
dataTable.interference_path_length(dataTable.interference_path_length < 101 & dataTable.interference_path_length > 1) = 50;
dataTable.interference_path_length(dataTable.interference_path_length == 1) = 0;
dataTable = dataTable(dataTable.interference_path_length == int_len, :);
dataTable(dataTable.run_id == 3866, :) = [];
dataTable(dataTable.run_id == 3865, :) = [];
dataTable(dataTable.run_id == 3796, :) = [];
dataTable(dataTable.run_id == 3797, :) = [];
dataTable(dataTable.run_id == 3798, :) = [];
dataTable(dataTable.run_id == 4002, :) = [];
dataTable(dataTable.run_id == 4200, :) = [];
dataTable(dataTable.run_id == 4199, :) = [];
% 0
% 1
% 10
% 15
% 20
% 50
% 100
% 300
% 1000
% dataTable(dataTable.interference_path_length ~= 50, :) = [];
% dataTable = dataTable(dataTable.interference_path_length < 51, :);
% dataTable(dataTable.loop_id<200,:) = [];
% Filter by time
filter_by_time = 0;
if filter_by_time
startTime = datetime('2025-04-20 18:00:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss');
stopTime = datetime('2025-04-30 19:30:00', 'InputFormat', 'yyyy-MM-dd HH:mm:ss');
dataTable.date_of_run = datetime(dataTable.date_of_run, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS');
dataTable.date_of_processing = datetime(dataTable.date_of_processing, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS');
dataTable = dataTable(dataTable.date_of_processing > startTime, :);
dataTable = dataTable(dataTable.date_of_processing < stopTime, :);
end
% Group by smth
y_var = 'BER';
x_var = 'SIR';
loop_var = 'interference_path_length';
fixedVars = {'equalizer_structure','interference_path_length',x_var};
[dataTable, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var);
dataTableGrpd_mean = groupIt(fixedVars, dataTable, @mean);
dataTableGrpd_min = groupIt(fixedVars, dataTable, @min);
dataTableGrpd_max = groupIt(fixedVars, dataTable, @max);
% dataTableGrpd_mean(dataTableGrpd_mean.nRows<50,:) = [];
% dataTableGrpd_min(dataTableGrpd_min.nRows<50,:) = [];
% dataTableGrpd_max(dataTableGrpd_max.nRows<50,:) = [];
% Create a new figure
hold on
unique_loop_var = unique(dataTable.(loop_var));
for i = 1:numel(unique_loop_var)
% Prepare filtered data for this loop variable
loopValue = unique_loop_var(i);
loopFiltGrpd = dataTableGrpd_mean.(loop_var) == loopValue;
if ~any(loopFiltGrpd)
continue; % Skip if no data for this loop var
end
% Extract values
x_values = dataTableGrpd_mean.(x_var)(loopFiltGrpd, :);
y_mean = dataTableGrpd_mean.(y_var)(loopFiltGrpd, :);
y_min = dataTableGrpd_min.(y_var)(loopFiltGrpd, :);
y_max = dataTableGrpd_max.(y_var)(loopFiltGrpd, :);
% Compute bounds: distance from mean
y_lower = y_mean - y_min;
y_upper = y_max - y_mean;
y_bounds = [y_lower, y_upper];
% Display name (optional)
try
idx = find(dataTable.(loop_var) == loopValue, 1, 'first');
% dispname = char(equalizer_structure(dataTable.equalizer_structure(idx)));
dispname = [method];
dispname = [dispname, '/ ',num2str(unique_loop_var(i)) ,' m'];
% dispname = [dispname,'; ',num2str(unique(dataTable.interference_path_length)),' m'];
% dispname = [dispname, '/ PAM ', num2str(filterParams.Configurations.pam_level)];
% dispname = [dispname, '/ ', num2str(filterParams.Configurations.symbolrate.*1e-9),' GBd'];
end
if plotBoundaries
% Plot bounded line
[hl, hp] = boundedline(x_values, y_mean, y_bounds, ...
'alpha', 'transparency', 0.1, ...
'cmap', cols(i,:), ...
'nan', 'fill', ...
'orientation', 'vert');
% % Style the main line: thinnest, dotted, no marker
set(hl, 'LineWidth', 0.5, 'LineStyle', ':', 'Marker', 'none', ...
'Color', cols(i,:), 'DisplayName', string(dispname));
plt = errorbar(x_values,y_mean,y_lower,y_upper,'LineWidth', 0.9, 'LineStyle', 'none', 'Marker', 'none','Color', cols(i,:), 'DisplayName', string(dispname),'HandleVisibility','off');
% Hide patch (shaded area) from legend
set(hp, 'HandleVisibility', 'off','LineStyle',':','LineWidth',0.5,'Marker','none');
% Fit a 4th-order polynomial to log10(BER)
p = polyfit(x_values, log10(y_mean), 3); % 4 is fitting order, adjust as needed
% Evaluate the fitted polynomial
x_fit = linspace(min(x_values), max(x_values), 300); % Fine points
y_fit_log = polyval(p, x_fit); % Still in log10 domain
y_fit = 10.^y_fit_log; % Back to BER domain
plot(x_fit,y_fit,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ...
'Color', cols(i,:), 'DisplayName', string(dispname),'HandleVisibility','off');
% % Add invisible scatter for DataTips
% plt = scatter(x_values, y_mean, ...
% 'Marker', 'o', 'MarkerEdgeColor', 'none', 'MarkerFaceColor', 'none', ...
% 'HandleVisibility', 'off', 'PickableParts', 'all');
else
plt= plot(x_values,y_mean,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none', ...
'Color', cols(i,:), 'DisplayName', string(dispname));
% plt= errorbar(x_values,y_mean,y_lower,y_upper,'LineWidth', 1, 'LineStyle', '-', 'Marker', 'none','Color', cols(i,:), 'DisplayName', string(dispname));
end
% Add data tips to the invisible scatter
pair_one = {'Run ID', dataTableGrpd_mean.run_id(loopFiltGrpd, :)};
pair_two = {'Rate', dataTableGrpd_mean.bitrate(loopFiltGrpd, :) * 1e-9};
pair_three = {'PD in', round(dataTableGrpd_mean.power_pd_in(loopFiltGrpd, :), 2)};
addDatatips(plt, pair_one, pair_two, pair_three);
% Optionally: outline bounds for better visibility (optional)
% hnew = outlinebounds(hl, hp);
% Tick marks (x-axis)
% Optional: scatter realizations
if plotRealizations
loopFiltSingle = dataTable.(loop_var) == loopValue;
x_single = double(dataTable.(x_var)(loopFiltSingle, :));
y_single = double(dataTable.(y_var)(loopFiltSingle, :));
mkr = '.';
sc = scatter(x_single+(mode*0.1)-0.2, y_single,15, 'Marker', mkr, 'MarkerEdgeColor', cols(i, :), ...
'LineWidth', 0.5, 'HandleVisibility', 'off', 'DisplayName', string(dispname));
pair_one = {'Run ID', dataTable.run_id(loopFiltSingle, :)};
pair_two = {'Baud', dataTable.symbolrate(loopFiltSingle, :) * 1e-9};
pair_three = {'PD in', round(dataTable.power_pd_in(loopFiltSingle, :), 2)};
pair_four = {'#bits', round(dataTable.numBits(loopFiltSingle, :), 2)};
addDatatips(sc, pair_one, pair_two, pair_three,pair_four);
end
end
% Label axes and title
legend('Interpreter', 'latex');
xlabel(x_var);
if ~y_here
ylabel(y_var);
yticklabels = [];
y_here = 1;
end
if int_len ~= 0
set(gca, 'YTick', []);
end
% title([x_var, ' vs. ', y_var]);
if string(y_var) == "BER"
yline(2.2e-4, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off');
yline(3.8e-3, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off');
yline(2e-2, 'LineWidth', 1, 'LineStyle', '--', 'HandleVisibility', 'off');
ylim([1e-5, 0.1]);
end
xlim([15,35]);
ylim([9e-5 0.1 ]);
xticks([13:2:35]);
% Enable grid and beautify
grid on;
beautifyBERplot;
end
end
function resultTable = groupIt(fixedVars, dataTable, aggregationFunction)
% groupIt Groups data in a table based on fixedVars and applies aggregationFunction to numeric data.
%
% resultTable = groupIt(fixedVars, dataTable, aggregationFunction)
%
% Inputs:
% fixedVars - Cell array of variable names to group by
% dataTable - Input MATLAB table
% aggregationFunction - Function handle (e.g., @mean, @min, @max)
%
% Output:
% resultTable - Grouped and aggregated table
% Group data
[G, groupKeys] = findgroups(dataTable(:, fixedVars));
% Prepare aggregation
varNames = dataTable.Properties.VariableNames;
nVars = numel(varNames);
aggData = cell(height(groupKeys), nVars);
groupCount = zeros(height(groupKeys), 1); % Store number of rows in each group
% Loop over groups
for i = 1:height(groupKeys)
idx = (G == i); % Logical index for group i
groupCount(i) = sum(idx); % Count rows in group
% Loop over each variable
for j = 1:nVars
colData = dataTable.(varNames{j});
if isnumeric(colData)
% Numeric: apply aggregation function (skip empty groups safely)
if any(idx)
% aggData{i, j} = rmoutliers(double(colData(idx)));
aggData{i, j} = aggregationFunction(colData(idx));
else
aggData{i, j} = NaN;
end
else
% Non-numeric: take first non-empty value
if iscell(colData)
nonEmptyIdx = find(idx & ~cellfun(@isempty, colData), 1);
if ~isempty(nonEmptyIdx)
aggData{i, j} = colData{nonEmptyIdx};
else
aggData{i, j} = [];
end
else
nonEmptyIdx = find(idx, 1);
if ~isempty(nonEmptyIdx)
aggData{i, j} = colData(nonEmptyIdx);
else
aggData{i, j} = [];
end
end
end
end
end
% Convert aggregated data to table
resultTable = cell2table(aggData, 'VariableNames', varNames);
% Add group size as new column
resultTable.nRows = groupCount;
end
function addDatatips(sc, varargin)
% addDatatips Adds custom data tip rows to a scatter plot.
%
% addDatatips(sc, pair1, pair2, ...) adds one or more custom rows to the
% data tip display of the scatter plot identified by sc.
%
% Each pair should be provided as a 1x2 cell array: {label, value}.
% The value can be a scalar or a vector. If a vector is provided, its length
% must match the number of scatter plot points.
%
% Example:
% sc = scatter(x, y, 'LineWidth', 1.5, 'Marker', 'o');
% pair_one = {'Attenuation', attenuationVector};
% addDatatips(sc, pair_one);
numPoints = numel(sc.XData);
for k = 1:length(varargin)
pair = varargin{k};
if ~iscell(pair) || numel(pair) ~= 2
error('Each pair must be a 1x2 cell array: {label, value}.');
end
label = pair{1};
value = pair{2};
% If value is a vector, ensure its length is either 1 or equal to the number of scatter points.
if isvector(value) && numel(value) ~= 1 && numel(value) ~= numPoints
error('The vector for "%s" must be a scalar or have %d elements matching the scatter data points.', label, numPoints);
end
% Create a new data tip row using the provided label and vector.
newRow = dataTipTextRow(label, value);
sc.DataTipTemplate.DataTipRows(end+1) = newRow;
end
end
function cleanedTable = cleanUpTable(inputTable)
% cleanUpTable Cleans a MATLAB table where numbers and NaNs are stored as strings or structs.
%
% cleanedTable = cleanUpTable(inputTable)
%
% This function goes through all columns of the input table:
% - Converts strings of numbers to numeric values
% - Converts string 'NaN' and struct NaNs to real NaN
% - Converts date strings to datetime (if possible)
%
% Input:
% inputTable - MATLAB table with mixed types
%
% Output:
% cleanedTable - Cleaned MATLAB table with proper numeric types
cleanedTable = inputTable;
varNames = cleanedTable.Properties.VariableNames;
for i = 1:numel(varNames)
col = cleanedTable.(varNames{i});
% Case 1: If it's a cell array (likely mixed strings/struct)
if iscell(col)
% Convert struct 'NaN' entries to string 'NaN'
col = cellfun(@(x) convertStructToString(x), col, 'UniformOutput', false);
% Try to convert string numbers to actual numbers
numericCol = str2double(col);
if all(isnan(numericCol) == strcmpi(col, 'NaN') | cellfun(@isempty, col))
% If conversion is successful (NaNs correspond to 'NaN' strings), use it
cleanedTable.(varNames{i}) = numericCol;
else
% Else, try to convert to datetime
try
cleanedTable.(varNames{i}) = datetime(col, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS');
catch
% If it fails, leave as cell array of strings
cleanedTable.(varNames{i}) = string(col);
end
end
% Case 2: If it's already a string array
elseif isstring(col)
numericCol = str2double(col);
if all(isnan(numericCol) == strcmpi(col, "NaN"))
cleanedTable.(varNames{i}) = numericCol;
else
% Try convert to datetime
try
cleanedTable.(varNames{i}) = datetime(col, 'InputFormat', 'yyyy-MM-dd HH:mm:ss');
catch
% Leave as string
end
end
% Case 3: If it's already numeric, keep as is
elseif isnumeric(col)
continue;
% Case 4: If it's datetime, keep as is
elseif isdatetime(col)
continue;
else
% Catch-all for unexpected types, convert to string
cleanedTable.(varNames{i}) = string(col);
end
end
end
function out = convertStructToString(x)
% Helper function to convert struct NaN to string 'NaN'
if isstruct(x)
out = "NaN";
elseif isstring(x) || ischar(x)
out = string(x);
else
out = x;
end
end
function [cleanedTable, outliersTable] = removeGroupOutliers(dataTable, fixedVars, y_var)
% Group the data
[G, groupKeys] = findgroups(dataTable(:, fixedVars));
% Initialize logical index to keep rows
keepIdx = true(height(dataTable), 1);
% Prepare storage for outliers
outlierRecords = [];
% Loop over each group
for groupIdx = 1:height(groupKeys)
% Find indices of current group
groupRows = (G == groupIdx);
% Extract y-values of this group
y_values = dataTable.(y_var)(groupRows);
% Skip groups with fewer than 3 points (optional)
if sum(groupRows) < 3
continue;
end
% Detect outliers in log space
y_log = log10(y_values);
outlierMask = isoutlier(y_log, 'quartiles',1); % or 'median', 'grubbs', etc.
% If any outliers found, collect their data
if any(outlierMask)
groupData = dataTable(groupRows, :);
% Prepare table for current group outliers
outlierGroupTable = groupData(outlierMask, :);
% Add group key values for traceability
for k = 1:numel(fixedVars)
outlierGroupTable.(['Group_', fixedVars{k}]) = repmat(groupKeys{groupIdx, k}, height(outlierGroupTable), 1);
end
% Append to collection
outlierRecords = [outlierRecords; outlierGroupTable]; %#ok<AGROW>
end
% Mark outliers for removal
groupRowIdx = find(groupRows);
keepIdx(groupRowIdx(outlierMask)) = false;
end
% Apply mask to dataTable
cleanedTable = dataTable(keepIdx, :);
% Prepare output: if no outliers, return empty table
if isempty(outlierRecords)
outliersTable = table();
else
outliersTable = outlierRecords;
end
nRemoved = sum(~keepIdx);
nTotalOriginal = height(dataTable) + nRemoved;
percentageRemoved = (nRemoved / nTotalOriginal) * 100;
fprintf('Removed %d outliers from the data table (%.2f%% of total %d entries).\n', ...
nRemoved, percentageRemoved, nTotalOriginal);
end

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,163 @@
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "Configurations" (
"configuration_id" INTEGER,
"run_id" INTEGER,
"unique_elab_id" TEXT,
"bitrate" REAL,
"symbolrate" REAL,
"pam_level" INTEGER,
"db_mode" TEXT,
"pulsef_alpha" INTEGER,
"v_bias" REAL,
"v_awg" REAL,
"precomp_amp" REAL,
"rop_attenuation" REAL,
"wavelength" REAL,
"laser_power" REAL,
"fiber_length" REAL,
"pd_in_desired" REAL,
"is_mpi" BIT,
"signal_attenuation" REAL,
"interference_path_length" REAL,
"interference_attenuation" REAL,
"pam_source" TEXT,
PRIMARY KEY("configuration_id" AUTOINCREMENT),
FOREIGN KEY("run_id") REFERENCES "Runs"("run_id")
);
CREATE TABLE IF NOT EXISTS "EqualizerParameters" (
"eq_id" INTEGER,
"equalizer_structure" REAL,
"M" INTEGER,
"target_constellation" TEXT,
"db_target" INTEGER,
"diff_precode" INTEGER,
"postFFE" INTEGER,
"NpostFFE" INTEGER,
"Ne1" INTEGER,
"Ne2" INTEGER,
"Ne3" INTEGER,
"Nb1" INTEGER,
"Nb2" INTEGER,
"Nb3" INTEGER,
"K" INTEGER,
"DCmu" REAL,
"ideal_dfe" INTEGER,
"training_length" INTEGER,
"training_loops" INTEGER,
"TRmu1" REAL,
"TRmu2" REAL,
"TRmu3" REAL,
"TRmuDFE" REAL,
"dd_loops" INTEGER,
"DDmu1" REAL,
"DDmu2" REAL,
"DDmu3" REAL,
"DDmuDFE" REAL,
"MLSE_mode" TEXT,
"MLSE_trellis_states" TEXT,
"comment" TEXT,
"config_hash" TEXT,
UNIQUE("config_hash"),
PRIMARY KEY("eq_id" AUTOINCREMENT)
);
CREATE TABLE IF NOT EXISTS "Measurements" (
"measurement_id" INTEGER,
"run_id" INTEGER,
"power_laser" REAL,
"power_rop" REAL,
"power_pd_in" REAL,
"power_mpi_interference" REAL,
"power_mpi_signal" REAL,
"voa_class" TEXT,
"pdfa_class" TEXT,
"laser_class" TEXT,
PRIMARY KEY("measurement_id" AUTOINCREMENT),
FOREIGN KEY("run_id") REFERENCES "Runs"("run_id")
);
CREATE TABLE IF NOT EXISTS "Results" (
"result_id" INTEGER,
"run_id" INTEGER,
"eqParam_id" INTEGER,
"date_of_processing" DATETIME DEFAULT (datetime('now', 'localtime')),
"numBits" INTEGER,
"numBitErr" INTEGER,
"BER" REAL,
"numBitErr_precoded" REAL,
"BER_precoded" REAL,
"SNR" REAL,
"SNR_level" TEXT,
"GMI" REAL,
"AIR" REAL,
"EVM" REAL,
"EVM_level" TEXT,
"Alpha" REAL,
"result_hash" TEXT UNIQUE,
"MLSE_dir" INTEGER,
PRIMARY KEY("result_id" AUTOINCREMENT),
FOREIGN KEY("eqParam_id") REFERENCES "EqualizerParameters"("eq_id"),
FOREIGN KEY("run_id") REFERENCES "Runs"("run_id")
);
CREATE TABLE IF NOT EXISTS "Runs" (
"run_id" INTEGER,
"date_of_run" DATETIME DEFAULT (datetime('now', 'localtime')),
"tx_bits_path" TEXT,
"tx_symbols_path" TEXT,
"rx_sync_path" TEXT,
"rx_raw_path" TEXT,
"filename" TEXT,
"tx_signal_path" TEXT,
PRIMARY KEY("run_id" AUTOINCREMENT)
);
CREATE VIEW "View_ResultOverview" AS
SELECT
-- Run info
Runs.run_id,
Runs.date_of_run,
Results.BER,
Results.SNR,
Results.GMI,
Results.AIR,
Results.EVM,
Results.Alpha,
-- Configurations
Configurations.symbolrate,
Configurations.pam_level,
Configurations.db_mode,
Configurations.pulsef_alpha,
Configurations.v_bias,
Configurations.v_awg,
Configurations.precomp_amp,
Configurations.is_mpi,
Configurations.signal_attenuation,
Configurations.interference_path_length,
Configurations.interference_attenuation,
-- Measurement data
Measurements.power_laser,
Measurements.power_rop,
Measurements.power_pd_in,
Measurements.power_mpi_interference,
Measurements.power_mpi_signal,
-- Equalizer parameters
EqualizerParameters.equalizer_structure,
EqualizerParameters.db_target,
EqualizerParameters.diff_precode
FROM Results
-- Join related tables
LEFT JOIN Runs ON Results.run_id = Runs.run_id
LEFT JOIN Configurations ON Configurations.run_id = Runs.run_id
LEFT JOIN Measurements ON Measurements.run_id = Runs.run_id
LEFT JOIN EqualizerParameters ON Results.eqParam_id = EqualizerParameters.eq_id;
CREATE INDEX IF NOT EXISTS "idx_run_id_on_Configurations" ON "Configurations" (
"run_id"
);
CREATE INDEX IF NOT EXISTS "idx_run_id_on_Measurements" ON "Measurements" (
"run_id"
);
COMMIT;

View File

@@ -0,0 +1,57 @@
function [output, future] = submit_dsp(run_id, basePath, database_name, savePath, options)
% runDSPInBackground Runs or submits DSP processing based on 'parallel' flag.
%
% [output, future] = runDSPInBackground(..., options)
% - output: result from dsp_run_id (only immediately available in serial mode)
% - future: FevalFuture object if run in parallel, otherwise []
arguments
run_id
basePath
database_name
savePath
options.parallel (1,1) logical = true
options.max_occurences = 1;
options.paramstruct = struct();
end
if options.parallel
% Check if a pool exists
pool = gcp('nocreate');
if isempty(pool)
parpool(4);
end
% Submit the DSP function asynchronously
future = parfeval( ...
@dsp_run_id, 1, ... % One output
run_id, ...
"database_path", basePath, ...
"database_name", database_name, ...
'storage_path', savePath, ...
'append_to_db', 1, ...
'max_occurences', options.max_occurences, ...
'parameters', options.paramstruct ...
);
output = [];
fprintf('DSP task for run_id %d submitted to the pool.\n', run_id);
else
% Run synchronously (debug mode), capture output
fprintf('Running DSP task for run_id %d in main thread (debug mode).\n', run_id);
output = dsp_run_id( ...
run_id, ...
"database_path", basePath, ...
"database_name", database_name, ...
'storage_path', savePath, ...
'append_to_db', 1, ...
'max_occurences', options.max_occurences,...
'parameters', options.paramstruct ...
);
future = []; % No future since it's synchronous
end
end

View File

@@ -0,0 +1,89 @@
% This script is used to evaluate Fig. 1b) in the paper "Adaptive Removal of Multipath Interference in Short Reach 112 GBd PAM-4 IM/DD Systems"
%% Parameters
df = 1e6; % Laser linewidth [Hz]
SIR_dB = 20; % Interference attenuation [dB]
alpha = 10^(-SIR_dB/20); % Interference attenuation [linear]
n_fiber = 1.467; % Refractive index
c = physconst('lightspeed'); % [m/s]
L = linspace(0,250,50); % Interference delay [m]
tau = n_fiber./c.*L; % Interference time (= tau) [s]
tau_c = 1/(pi*df); % laser coherence time [s]
L_c = (c/n_fiber)*tau_c; % laser coherence length [m]
var_sat = 2*alpha^2; % Analytical saturation of variance
%% MonteCarlo Simulation
fs = 100e9; % sampling rate [Hz]
Tsim = 50e-6; % sim duration [s]
N = round(Tsim*fs); % number of samples for each realization
max_delay_samples = round(max(tau)*fs); % largest delay that is evaluated (based on max. Interference delay)
phase_noise_std = sqrt(2*pi*df/fs); % standard dev. phase noise
num_realizations = 50; % number of parallel runs
monte_carlo_variance = zeros(num_realizations, length(L));
parfor r = 1:num_realizations
% generate a realization of phase noise random walk
dphi = phase_noise_std * randn(1, N + max_delay_samples); % matlab randn process has std = 1
phi = cumsum(dphi);
phi_direct = phi(max_delay_samples+1 : max_delay_samples+N);
var_k = zeros(1, length(L));
for t = 1:length(tau)
nd = round( tau(t)*fs ); % delay in samples for current interference time
phi_delayed = phi(max_delay_samples+1-nd : max_delay_samples+N-nd); %cut out interfering signal part (was earlier)
E = exp(1j*phi_direct) + alpha*exp(1j*phi_delayed); % E-fields combined
I = abs(E).^2; % photo current as magnitude square of E-field
var_k(t) = var(I);
end
monte_carlo_variance(r, :) = var_k;
end
avg_of_mc_variances = mean(monte_carlo_variance, 1);
std_of_mc_variances = std(monte_carlo_variance, 0, 1);
%% Analytic variance
L_ = linspace(0,250,500); % Interference delay [m]
tau_ = n_fiber./c.*L_;
analytic_variance = 2*alpha^2 * (1 - exp(-2*pi*df.*tau_)).^2;
%% Plot
cols = [0.3467 0.5360 0.6907
0.9153 0.2816 0.2878
0.4416 0.7490 0.4322];
coherence_length_multiples = 0.5:0.5:ceil(L(end)/L_c);
figure();
hold on;
[hl, hp] = boundedline(L, avg_of_mc_variances, std_of_mc_variances, 'alpha', 'cmap', cols(1,:));
set(hl, 'LineWidth', 2, 'DisplayName', 'Simulation');
set(hp, 'HandleVisibility', 'off', 'FaceAlpha', 0.8); % Hide patch from legend to match original behavior
plot(L_, analytic_variance, 'LineWidth',2, 'DisplayName','Analytic','Color',cols(2,:),'LineStyle','-');
xticks(coherence_length_multiples.*L_c);
xticklabels(round(coherence_length_multiples.*L_c,1));
norm_to_coherence_len = 1;
if norm_to_coherence_len
xticklabels(coherence_length_multiples);
xlabel('$n \cdot L_c$', 'FontSize',12);
else
xlabel('Interference Delay [m]', 'FontSize',12);
end
%xline(L_c.*coherence_length_multiples, 'LineWidth',1.5,'HandleVisibility','off','Color',[0.7,0.7,0.7],'LineStyle','-');
xlim([0,L(end)]);
yline(var_sat, '-.k','LineWidth',1.5, 'DisplayName','Saturation: 2$\alpha ^2$');
grid on;
ylabel('Intensity Variance', 'FontSize',12);
title(sprintf('MPI Variance; %d MHz; SIR: %d dB',df.*1e-6,SIR_dB), 'FontSize',14);
legend('Location','southeast');
% mat2tikz_improved("C:\Users\Silas\Documents\6971e0b65b380ca6d71c837f\02_IMDD_System\tikz\mpi\analytical_mpi_variance2.tikz");

View File

@@ -0,0 +1,33 @@
%% Parameters
df = linspace(1,50e6,10000); % Laser FWHM linewidth [Hz]
n_fiber = 1.467; % Fiber group index
c = 3e8; % Speed of light [m/s]
% Compute coherence length (1/e of mean-fringe decay)
tau_c = 1./(pi*df);
L_c = (c.* tau_c/n_fiber) ; % Coherence length [m]
%% Plot
figure('Color','w');
loglog(df/1e6, L_c, 'LineWidth',2,'LineStyle','-'); % linewidth in MHz
% xticks([0.1, 1, 10, 50]);
% yticks([1, 10, 100, 1000]);
% yticklabels({'1','10','100','1000'})
grid on; box on;
xlabel('Laser linewidth [MHz]','FontSize',12,'Interpreter','latex');
ylabel('Coherence length [m]','FontSize',12,'Interpreter','latex');
title('Coherence Length vs. Laser Linewidth','FontSize',14,'Interpreter','latex');
mat2tikz_improved("C:\Users\Silas\Documents\6971e0b65b380ca6d71c837f\02_IMDD_System\tikz\mpi\laser_linewidth_vs_coherence.tikz");
%% Annotate some key points
% hold on;
% freqs = [150e3, 1e6, 10e6, 50e6]; % [Hz]
% for f = freqs
% x = f/1e6;
% y = (c/n_fiber) * (1/(pi*f));
% scatter(x,y,'Marker','x','LineWidth',1,'MarkerEdgeColor','black');
%
% text(x*1.1,y, sprintf('%.2f MHz', f/1e6), ...
% 'FontSize',10,'HorizontalAlignment','left');
%
% end