234 lines
6.2 KiB
Matlab
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
|
|
|