Files
imdd_silas/projects/WDM/plot_FEC_violin.m
silas (home) 01a3881455 few changes in WDM stuff
cleaned up the base system... but its not yet a good minimal example...
2026-01-07 08:30:14 +01:00

214 lines
6.1 KiB
Matlab

function plot_FEC_violin(res, varargin)
% plot_fec_violin_from_res Self-contained violin plot (no dependency on other routines)
%
% Usage:
% plot_fec_violin_from_res('WDM_20260106_....mat', 'tech', 'FFE', 'fec', 3.8e-3, 'eval_ptr', 2, 'ylim', [-10 -6]);
% plot_fec_violin_from_res(res, 'tech', 'FFE', 'fec', 3.8e-3, 'eval_ptr', 2);
%
% Notes:
% - Works with partially-filled res.* cell arrays (missing eval distances / missing realizations).
% - Uses only complete realizations per channel (all ROP points present).
% - Requires violinplot.m on path.
p = inputParser;
p.addParameter('tech', 'FFE', @(x)ischar(x) || isstring(x));
p.addParameter('fec', 3.8e-3, @(x)isnumeric(x)&&isscalar(x));
p.addParameter('eval_ptr', [], @(x)isnumeric(x)&&isscalar(x));
p.addParameter('ylim', [], @(x)isnumeric(x)&&numel(x)==2);
p.addParameter('title_prefix', 'ROP at FEC crossing', @(x)ischar(x)||isstring(x));
p.parse(varargin{:});
if exist('violinplot','file') ~= 2
error('violinplot.m not found on path. Add it to path first.');
end
% ---------- Metadata ----------
rop = res.settings.rop(:);
wavelengthplan = res.settings.wavelengthplan(:);
distances = res.eval_dist_km(:);
N_ch = numel(wavelengthplan);
N_rop = numel(rop);
% Determine distances dim robustly from the stored cell arrays (prefer ffe)
dims = size(res.ffe);
if numel(dims) < 4, dims(end+1:4) = 1; end
N_distances = dims(4);
if numel(distances) ~= N_distances
distances = (1:N_distances).';
end
% Choose eval distance
eval_ptr = p.Results.eval_ptr;
if isempty(eval_ptr)
eval_ptr = N_distances; % default: last available
end
if eval_ptr < 1 || eval_ptr > N_distances
error('eval_ptr=%d out of range. Available: 1..%d', eval_ptr, N_distances);
end
% Choose technique field
tech = upper(string(p.Results.tech));
switch tech
case "FFE", C4 = res.ffe;
case "DFE", C4 = res.dfe;
case "VNLE", C4 = res.vnle;
case "MLSE", C4 = res.mlse;
case "DBT", C4 = res.dbt;
otherwise
error('Unknown tech "%s". Use one of: FFE, DFE, VNLE, MLSE, DBT.', tech);
end
fec = p.Results.fec;
% ---------- Compute crossings per channel ----------
% ---------- Plot ----------
figure('Name', sprintf('%s violin @ %s', tech, distLabel(distances, eval_ptr)));
hold on;
c = linspecer(N_distances);
for eval_ptr = 1:N_distances
S_cell = cell(1, N_ch);
for ch = 1:N_ch
cells = sliceCells4D(C4, ch, eval_ptr, N_rop); % N_rop x N_realiz cell
S_cell{ch} = fecCrossings_only(rop, cells, fec); % 1 x K crossings (may be empty)
end
% Pad to matrix for violinplot: rows=realizations (max K), cols=channels
Kmax = max(cellfun(@numel, S_cell));
if isempty(Kmax) || Kmax == 0
warning('No FEC crossings found for %s at eval_ptr=%d (%s).', tech, eval_ptr, distLabel(distances, eval_ptr));
return;
end
S_mat = NaN(Kmax, N_ch);
for ch = 1:N_ch
k = numel(S_cell{ch});
if k > 0
S_mat(1:k, ch) = S_cell{ch}(:);
end
end
catLabels = arrayfun(@(nm) sprintf('%d nm', nm), round(wavelengthplan), 'UniformOutput', false);
if numel(S_mat)>numel(wavelengthplan)
S_mat(isnan(S_mat)) = 0;
v=violinplot(S_mat, catLabels, ...
'ViolinColor', c(eval_ptr,:), ...
'ViolinAlpha', 0.10, ...
'MarkerSize', 20, ...
'ShowMedian', true, ...
'EdgeColor', c(eval_ptr,:), ...
'ShowWhiskers', false, ...
'ShowData', true, ...
'ShowBox', false, ...
'Bandwidth', 0.05);
else
channs = 1:numel(wavelengthplan);
scatter( wavelengthplan ,S_mat,10,c(eval_ptr,:),'Marker','o','LineWidth',2,'DisplayName',distLabel(distances, eval_ptr));
channs = 1:numel(wavelengthplan);
scatter( round(wavelengthplan(S_mat==0)),S_mat(S_mat==0),10,c(eval_ptr,:),'Marker','o','HandleVisibility','off');
end
end
ylabel('ROP at FEC crossing');
title(sprintf('%s: %s | BER %.2e | %s', ...
p.Results.title_prefix, tech, fec, distLabel(distances, eval_ptr)));
grid on; box on;
if ~isempty(p.Results.ylim)
ylim(p.Results.ylim);
end
end
%% ================= helpers (self-contained) =================
function cells2D = sliceCells4D(C4, ch, eval_ptr, N_rop)
dims = size(C4);
if numel(dims) < 4, dims(end+1:4) = 1; end
N_realiz = dims(3);
cells2D = cell(N_rop, N_realiz);
chMax = dims(1);
ropMax = dims(2);
rMax = dims(3);
eMax = dims(4);
if ch > chMax || eval_ptr > eMax
return;
end
ropUse = min(N_rop, ropMax);
rUse = min(N_realiz, rMax);
tmp = squeeze(C4(ch, 1:ropUse, 1:rUse, eval_ptr));
tmp = reshape(tmp, ropUse, rUse);
cells2D(1:ropUse, 1:rUse) = tmp;
end
function lbl = distLabel(distances, eval_ptr)
if eval_ptr <= numel(distances)
d = distances(eval_ptr);
if isfinite(d)
lbl = sprintf('%.0f km', d);
return;
end
end
lbl = sprintf('eval\\_%d', eval_ptr);
end
function S = fecCrossings_only(rop, cellsNxR, fec)
% Returns 1xK crossing positions for all complete realizations (drops bad ones).
Y = extractCompleteBER(cellsNxR); % N_rop x K
if isempty(Y), S = []; return; end
% Optional quality gate (same as your previous scripts)
ok = mean(Y,1,'omitnan') <= 0.1;
Y = Y(:, ok);
if isempty(Y), S = []; return; end
nR = size(Y,2);
S = nan(1,nR);
rop = rop(:);
for j = 1:nR
y = Y(:,j);
above = (y > fec);
idx = find(above(1:end-1) & ~above(2:end), 1, 'first');
if ~isempty(idx)
x1 = rop(idx); y1 = y(idx);
x2 = rop(idx+1); y2 = y(idx+1);
if isfinite(y1) && isfinite(y2) && y2 ~= y1
t = (fec - y1) / (y2 - y1);
S(j) = x1 + t*(x2 - x1);
end
end
end
S = S(isfinite(S)); % keep only successful crossings
end
function Y = extractCompleteBER(cellSlice)
% Keep only those realization columns where ALL N_rop entries are non-empty
if isempty(cellSlice), Y = []; return; end
nR = size(cellSlice,2);
keep = false(1,nR);
for r = 1:nR
col = cellSlice(:,r);
keep(r) = all(cellfun(@(c) ~isempty(c), col));
end
if ~any(keep), Y = []; return; end
Y = cellfun(@(c) c.metrics.BER, cellSlice(:,keep), 'UniformOutput', true);
end