chirped WDM analsyis
minor stuff in FSO link with Magnus
This commit is contained in:
@@ -1,29 +1,38 @@
|
||||
function plot_FEC_violin(res, varargin)
|
||||
% plot_fec_violin_from_res Self-contained violin plot (no dependency on other routines)
|
||||
% plot_FEC_violin Violin plot of ROP-at-FEC crossings per wavelength/channel
|
||||
% Uses the Bechtold community violinplot (MATLAB Central 45134).
|
||||
%
|
||||
% Requirements:
|
||||
% - community violinplot must be on path, either as:
|
||||
% (a) violinplot.m (shadows MATLAB official), OR
|
||||
% (b) violinplot_community.m (renamed + function name adjusted)
|
||||
%
|
||||
% 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.
|
||||
% plot_FEC_violin(res, 'tech','VNLE', 'fec',3.8e-3, 'eval_ptr',[], 'ylim',[-10 -6], 'bandwidth',0.15);
|
||||
|
||||
% -------- args --------
|
||||
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.addParameter('tech', 'VNLE', @(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('bandwidth', [], @(x)isnumeric(x)&&isscalar(x));
|
||||
p.parse(varargin{:});
|
||||
opt = p.Results;
|
||||
|
||||
if exist('violinplot','file') ~= 2
|
||||
error('violinplot.m not found on path. Add it to path first.');
|
||||
tech = upper(string(opt.tech));
|
||||
fec = opt.fec;
|
||||
|
||||
% -------- pick result field --------
|
||||
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 FFE/DFE/VNLE/MLSE/DBT.', tech);
|
||||
end
|
||||
|
||||
|
||||
% ---------- Metadata ----------
|
||||
rop = res.settings.rop(:);
|
||||
wavelengthplan = res.settings.wavelengthplan(:);
|
||||
distances = res.eval_dist_km(:);
|
||||
@@ -31,103 +40,97 @@ 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);
|
||||
dims = size(C4);
|
||||
if numel(dims) < 4, dims(end+1:4) = 1; end
|
||||
N_distances = dims(4);
|
||||
if numel(distances) ~= N_distances
|
||||
distances = (1:N_distances).';
|
||||
N_dist = dims(4);
|
||||
|
||||
eval_ptr = opt.eval_ptr;
|
||||
if isempty(eval_ptr), eval_ptr = N_dist; end
|
||||
eval_ptr = max(1, min(N_dist, eval_ptr));
|
||||
|
||||
% -------- compute crossings per channel + "completeness" --------
|
||||
S_cell = cell(1, N_ch); % crossings values (finite only)
|
||||
K_total = zeros(1, N_ch); % number of complete realizations for that channel
|
||||
K_cross = zeros(1, N_ch); % number of realizations that crossed
|
||||
|
||||
for ch = 1:N_ch
|
||||
cells = sliceCells4D(C4, ch, eval_ptr, N_rop); % N_rop x N_realiz (may contain [])
|
||||
[S_cell{ch}, K_total(ch), K_cross(ch)] = crossings_and_counts(rop, cells, fec);
|
||||
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);
|
||||
% -------- build vector+category representation (NO NaNs in xAll) --------
|
||||
catLabels = arrayfun(@(nm)sprintf('%d nm', round(nm)), wavelengthplan, 'UniformOutput', false);
|
||||
catsAll = categorical(strings(0,1), catLabels, 'Ordinal', false);
|
||||
xAll = zeros(0,1);
|
||||
|
||||
for ch = 1:N_ch
|
||||
v = S_cell{ch}(:);
|
||||
if ~isempty(v)
|
||||
xAll = [xAll; v];
|
||||
catsAll = [catsAll; repmat( ...
|
||||
categorical(string(catLabels{ch}), catLabels, 'Ordinal', false), ...
|
||||
numel(v), 1)];
|
||||
end
|
||||
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)));
|
||||
% -------- plot --------
|
||||
figure('Name', sprintf('%s FEC 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
|
||||
|
||||
% Violin only if we have any data at all
|
||||
if ~isempty(xAll)
|
||||
violinplot_community(xAll, catsAll,'Bandwidth', 0.15, ... % KDE bandwidth (critical!)
|
||||
'ViolinColor', [0.2 0.4 0.8], ... % or Nx3 for per-channel colors
|
||||
'ViolinAlpha', 0.15, ...
|
||||
'EdgeColor', [0.3 0.3 0.3], ...
|
||||
'MarkerSize', 14, ...
|
||||
'ShowData', true, ...
|
||||
'ShowMean', false, ...
|
||||
'ShowMedian', true, ...
|
||||
'ShowBox', false, ...
|
||||
'ShowWhiskers', false);
|
||||
else
|
||||
warning('No FEC crossings found at eval_ptr=%d (%s). Plotting only markers.', eval_ptr, distLabel(distances, eval_ptr));
|
||||
set(gca,'XTick',1:N_ch,'XTickLabel',catLabels);
|
||||
end
|
||||
|
||||
% Marker X positions (violin groups are at 1..N_ch)
|
||||
xpos = 1:N_ch;
|
||||
|
||||
% -------- overlay mean markers (black/red/blue) --------
|
||||
yBlue = -9;
|
||||
|
||||
for ch = 1:N_ch
|
||||
if K_total(ch) == 0 || K_cross(ch) == 0
|
||||
% no complete realizations OR none crossed -> blue X at -9
|
||||
scatter(xpos(ch), yBlue, 80, 'x', 'LineWidth', 2, 'MarkerEdgeColor', [0 0 1], 'HandleVisibility','off');
|
||||
continue;
|
||||
end
|
||||
|
||||
mu = mean(S_cell{ch}, 'omitnan');
|
||||
|
||||
if K_cross(ch) < K_total(ch)
|
||||
% some complete realizations did not cross -> red X
|
||||
scatter(xpos(ch), mu, 80, 'x', 'LineWidth', 2, 'MarkerEdgeColor', [1 0 0], 'HandleVisibility','off');
|
||||
else
|
||||
% all complete realizations crossed -> black X
|
||||
scatter(xpos(ch), mu, 80, 'x', 'LineWidth', 2, 'MarkerEdgeColor', [0 0 0], 'HandleVisibility','off');
|
||||
end
|
||||
end
|
||||
|
||||
title(sprintf('%s | BER %.2e | %s', tech, fec, distLabel(distances, eval_ptr)));
|
||||
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);
|
||||
if ~isempty(opt.ylim)
|
||||
ylim(opt.ylim);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
%% ================= helpers (self-contained) =================
|
||||
% ================= helpers =================
|
||||
|
||||
function cells2D = sliceCells4D(C4, ch, eval_ptr, N_rop)
|
||||
dims = size(C4);
|
||||
@@ -136,68 +139,64 @@ 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
|
||||
if ch > dims(1) || eval_ptr > dims(4)
|
||||
return;
|
||||
end
|
||||
|
||||
ropUse = min(N_rop, ropMax);
|
||||
rUse = min(N_realiz, rMax);
|
||||
ropUse = min(N_rop, dims(2));
|
||||
rUse = min(N_realiz, dims(3));
|
||||
|
||||
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);
|
||||
function [S, K_total, K_cross] = crossings_and_counts(rop, cellsNxR, fec)
|
||||
% counts "complete" realizations and how many of those crossed
|
||||
% S returns only finite crossings (no NaN).
|
||||
|
||||
Y = extractCompleteBER(cellsNxR); % N_rop x K_total
|
||||
K_total = size(Y,2);
|
||||
|
||||
if K_total == 0
|
||||
S = [];
|
||||
K_cross = 0;
|
||||
return;
|
||||
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)
|
||||
% optional quality gate (keep your style)
|
||||
ok = mean(Y,1,'omitnan') <= 0.1;
|
||||
Y = Y(:, ok);
|
||||
if isempty(Y), S = []; return; end
|
||||
Y = Y(:,ok);
|
||||
K_total = size(Y,2);
|
||||
|
||||
nR = size(Y,2);
|
||||
S = nan(1,nR);
|
||||
if K_total == 0
|
||||
S = [];
|
||||
K_cross = 0;
|
||||
return;
|
||||
end
|
||||
|
||||
rop = rop(:);
|
||||
for j = 1:nR
|
||||
S = nan(1, K_total);
|
||||
|
||||
for j = 1:K_total
|
||||
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);
|
||||
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
|
||||
K_cross = sum(isfinite(S));
|
||||
S = S(isfinite(S));
|
||||
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);
|
||||
@@ -208,6 +207,13 @@ for r = 1:nR
|
||||
end
|
||||
|
||||
if ~any(keep), Y = []; return; end
|
||||
|
||||
Y = cellfun(@(c) c.metrics.BER, cellSlice(:,keep), 'UniformOutput', true);
|
||||
end
|
||||
|
||||
function lbl = distLabel(distances, eval_ptr)
|
||||
if eval_ptr <= numel(distances) && isfinite(distances(eval_ptr))
|
||||
lbl = sprintf('%.0f km', distances(eval_ptr));
|
||||
else
|
||||
lbl = sprintf('eval_%d', eval_ptr);
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user