Files
imdd_silas/projects/WDM/WDM_auswertung.m
Silas Oettinghaus 719e5508e7 FSO analysis:
- minimal changes in several files
- new Timing Rec from Magnus
2026-01-20 10:49:59 +01:00

234 lines
6.2 KiB
Matlab

base = "C:\Users\Silas\Nextcloud4\Cluster";
all_files = dir(fullfile(base, "**/*.mat"));
schemes = ["co","pair","alt","seg"];
% Preallocate as table (minimal + convenient)
T = table('Size',[0 10], ...
'VariableTypes', ["string","string","string","datetime","double","double","double","double","double","double"], ...
'VariableNames', ["folder","file","scheme","date","node","jobid","L_km","Nch","df_GHz","alpha"]);
% filename parser
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$";
for i = 1:numel(all_files)
f = all_files(i);
folder = string(f.folder);
file = string(f.name);
tok = regexp(file, rx, 'names');
if isempty(tok)
continue
end
% scheme from filename is the most reliable
scheme = string(tok.scheme);
% optional: ignore unexpected schemes
if ~any(strcmpi(scheme, schemes)), continue; end
node = str2double(tok.node);
jobid = str2double(tok.jobid);
L_km = str2double(tok.L);
Nch = str2double(tok.Nch);
df_GHz = str2double(tok.df);
date = datetime(strcat(tok.date, tok.time), 'InputFormat','yyyyMMddHHmmss');
% alpha uses "_" as decimal separator in your filenames
alpha = str2double(strrep(tok.alpha, "_", "."));
T(end+1,:) = {folder, file, scheme,date, node, jobid, L_km, Nch, df_GHz, alpha};
end
%%
idx = strcmp(T.scheme,"co") & ...
T.alpha == 0.4 & ...
T.date >= datetime(2026,1,9) & ...
T.date <= datetime(2026,1,10,23,59,59);
T_sel = T(idx,:);
% ---- Load all res ----
R = cell(numel(T_sel.file),1);
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
S = plot_BER_vs_ROP(res_all, 'fec', 3.8e-3);
%% Routine B: violin plot (independent)
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