work on MPI
This commit is contained in:
237
projects/Diss/MPI_revisit/algorithms/PLOT_ber_vs_sir.m
Normal file
237
projects/Diss/MPI_revisit/algorithms/PLOT_ber_vs_sir.m
Normal file
@@ -0,0 +1,237 @@
|
||||
%% Simple BER over SIR plot from the recombined warehouses
|
||||
% This script intentionally keeps the mechanics visible:
|
||||
% 1) load the run_id warehouse and the grouped config warehouse
|
||||
% 2) loop over occupied config cells
|
||||
% 3) read the physical coordinates of the cell
|
||||
% 4) extract all BER values stored in that cell
|
||||
% 5) plot individual BER dots and one larger mean marker per SIR
|
||||
|
||||
clear; clc;
|
||||
|
||||
scriptDir = fileparts(mfilename("fullpath"));
|
||||
|
||||
runWhFile = fullfile(scriptDir, "combined_by_run_id_results.mat");
|
||||
configWhFile = fullfile(scriptDir, "combined_by_config_results.mat");
|
||||
|
||||
runWhData = load(runWhFile);
|
||||
configWhData = load(configWhFile);
|
||||
|
||||
wh_run_id_combined = runWhData.wh_run_id_combined;
|
||||
wh_config_combined = configWhData.wh_config_combined;
|
||||
algorithmStorageNames = configWhData.algorithmStorageNames;
|
||||
|
||||
fprintf("Loaded run_id warehouse:\n %s\n", runWhFile);
|
||||
wh_run_id_combined.showInfo;
|
||||
|
||||
fprintf("\nLoaded config-grouped warehouse:\n %s\n", configWhFile);
|
||||
wh_config_combined.showInfo;
|
||||
|
||||
%% Plot settings
|
||||
|
||||
pathLengthToPlot = 1; % use 300 to see duplicate run_ids per config; set 1000 for the previous path
|
||||
selectedPamLevels = 4;%wh_config_combined.parameter.pam_level.values;
|
||||
selectedAlgorithms = algorithmStorageNames([1,5]);
|
||||
|
||||
useBoundedLines = true; % switch uncertainty bands on/off here
|
||||
usePolyfit = true; % switch fitted dashed trend lines on/off here
|
||||
polyfitOrderMax = 4;
|
||||
|
||||
|
||||
% SIR vs BER: one subplot per PAM level, all algorithms compared
|
||||
|
||||
if exist("linspecer", "file")
|
||||
algColors = linspecer(numel(selectedAlgorithms));
|
||||
else
|
||||
algColors = lines(numel(selectedAlgorithms));
|
||||
end
|
||||
|
||||
figure(); clf;
|
||||
tiledlayout(numel(selectedPamLevels), 1, "TileSpacing", "compact");
|
||||
|
||||
for pamIdx = 1:numel(selectedPamLevels)
|
||||
pamLevel = selectedPamLevels(pamIdx);
|
||||
nexttile; hold on;
|
||||
|
||||
for algIdx = 1:numel(selectedAlgorithms)
|
||||
algorithmName = selectedAlgorithms{algIdx};
|
||||
algColor = algColors(algIdx, :);
|
||||
|
||||
berTable = buildBerTable(wh_config_combined, algorithmName, pathLengthToPlot);
|
||||
berTable = berTable(berTable.pam_level == pamLevel, :);
|
||||
|
||||
% berTable(berTable.sir == 23,:) = [];
|
||||
|
||||
if isempty(berTable)
|
||||
continue
|
||||
end
|
||||
|
||||
sirValues = unique(berTable.sir(:).');
|
||||
sirValues = sort(sirValues);
|
||||
|
||||
meanBer = nan(size(sirValues));
|
||||
minBer = nan(size(sirValues));
|
||||
maxBer = nan(size(sirValues));
|
||||
|
||||
for sirIdx = 1:numel(sirValues)
|
||||
sirValue = sirValues(sirIdx);
|
||||
sirRows = berTable(berTable.sir == sirValue, :);
|
||||
berValues = [sirRows.ber_values{:}];
|
||||
berValues = berValues(isfinite(berValues));
|
||||
berValues = berValues(berValues<0.1);
|
||||
berValues = rmoutliers(berValues);
|
||||
|
||||
|
||||
if isempty(berValues)
|
||||
continue
|
||||
end
|
||||
|
||||
scatter(repmat(sirValue, size(berValues)), berValues, ...
|
||||
30, ...
|
||||
"Marker", ".", ...
|
||||
"MarkerEdgeColor", algColor, ...
|
||||
"MarkerFaceColor", algColor, ...
|
||||
"HandleVisibility", "off");
|
||||
|
||||
meanBer(sirIdx) = mean(berValues, "omitnan");
|
||||
minBer(sirIdx) = min(berValues);
|
||||
maxBer(sirIdx) = max(berValues);
|
||||
end
|
||||
|
||||
valid = isfinite(sirValues) & isfinite(meanBer);
|
||||
if ~any(valid)
|
||||
continue
|
||||
end
|
||||
|
||||
if useBoundedLines && exist("boundedline", "file")
|
||||
yLower = max(meanBer - minBer, 0);
|
||||
yUpper = max(maxBer - meanBer, 0);
|
||||
yBounds = [yLower(:), yUpper(:)];
|
||||
|
||||
[hl, hp] = boundedline(sirValues(valid).', meanBer(valid).', yBounds(valid, :), ...
|
||||
'alpha', 'transparency', 0.08, ...
|
||||
'cmap', algColor, ...
|
||||
'nan', 'fill', ...
|
||||
'orientation', 'vert');
|
||||
set(hl, "LineStyle", "none", "Marker", "none", "HandleVisibility", "off");
|
||||
set(hp, "LineStyle", "none", "HandleVisibility", "off");
|
||||
end
|
||||
|
||||
scatter(sirValues(valid), meanBer(valid), ...
|
||||
20, ...
|
||||
"Marker", "o", ...
|
||||
"MarkerEdgeColor", algColor, ...
|
||||
"MarkerFaceColor", algColor, ...
|
||||
"LineWidth", 1, ...
|
||||
"DisplayName", algorithmName);
|
||||
|
||||
if usePolyfit
|
||||
fitMask = valid & meanBer > 0;
|
||||
if nnz(fitMask) >= 2
|
||||
fitOrder = min(polyfitOrderMax, nnz(fitMask) - 1);
|
||||
fitCoeff = polyfit(sirValues(fitMask), log10(meanBer(fitMask)), fitOrder);
|
||||
xFit = linspace(min(sirValues(fitMask)), max(sirValues(fitMask)), 300);
|
||||
yFit = 10 .^ polyval(fitCoeff, xFit);
|
||||
|
||||
plot(xFit, yFit, ...
|
||||
"LineStyle", "--", ...
|
||||
"LineWidth", 1.1, ...
|
||||
"Color", algColor, ...
|
||||
"HandleVisibility", "off");
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
title(sprintf("PAM %.0f, path %.0f m", pamLevel, pathLengthToPlot));
|
||||
xlabel("SIR (dB)");
|
||||
ylabel("BER");
|
||||
set(gca, "YScale", "log");
|
||||
xlim([15,45]);
|
||||
grid on;
|
||||
box on;
|
||||
legend("Location", "best", "Interpreter", "none");
|
||||
end
|
||||
|
||||
%% Local helpers
|
||||
|
||||
function berTable = buildBerTable(wh, storageName, pathLength)
|
||||
rows = struct( ...
|
||||
"pam_level", {}, ...
|
||||
"symbolrate", {}, ...
|
||||
"interference_path_length", {}, ...
|
||||
"sir", {}, ...
|
||||
"block_update", {}, ...
|
||||
"run_ids", {}, ...
|
||||
"n_run_ids", {}, ...
|
||||
"ber_values", {}, ...
|
||||
"n_ber_values", {});
|
||||
|
||||
for linIdx = 1:numel(wh.sto.(storageName))
|
||||
storedValue = wh.sto.(storageName){linIdx};
|
||||
if isempty(storedValue)
|
||||
continue
|
||||
end
|
||||
|
||||
phys = linearIndexToStruct(wh, linIdx);
|
||||
if phys.interference_path_length ~= pathLength
|
||||
continue
|
||||
end
|
||||
|
||||
berValues = extractBerValues(storedValue);
|
||||
if isempty(berValues)
|
||||
continue
|
||||
end
|
||||
|
||||
runIds = wh.sto.run_id{linIdx};
|
||||
if isempty(runIds)
|
||||
runIds = NaN;
|
||||
end
|
||||
|
||||
row = struct();
|
||||
row.pam_level = phys.pam_level;
|
||||
row.symbolrate = phys.symbolrate;
|
||||
row.interference_path_length = phys.interference_path_length;
|
||||
row.sir = phys.sir;
|
||||
row.block_update = phys.block_update;
|
||||
row.run_ids = {runIds};
|
||||
row.n_run_ids = numel(runIds);
|
||||
row.ber_values = {berValues};
|
||||
row.n_ber_values = numel(berValues);
|
||||
|
||||
rows(end+1) = row; %#ok<AGROW>
|
||||
end
|
||||
|
||||
if isempty(rows)
|
||||
berTable = struct2table(rows);
|
||||
else
|
||||
berTable = struct2table(rows);
|
||||
berTable = sortrows(berTable, ["pam_level", "sir", "symbolrate"]);
|
||||
end
|
||||
end
|
||||
|
||||
function physStruct = linearIndexToStruct(wh, linIdx)
|
||||
[physValues, physNames] = wh.getPhysIndicesByLinIndex(linIdx);
|
||||
physStruct = struct();
|
||||
|
||||
for paramIdx = 1:numel(physNames)
|
||||
physStruct.(char(physNames{paramIdx})) = physValues{paramIdx};
|
||||
end
|
||||
end
|
||||
|
||||
function berValues = extractBerValues(value)
|
||||
berValues = [];
|
||||
|
||||
if isstruct(value)
|
||||
if isfield(value, "metrics") && hasBer(value.metrics)
|
||||
berValues(end+1) = value.metrics.BER;
|
||||
end
|
||||
elseif iscell(value)
|
||||
for valueIdx = 1:numel(value)
|
||||
berValues = [berValues, extractBerValues(value{valueIdx})]; %#ok<AGROW>
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function tf = hasBer(metrics)
|
||||
tf = (isstruct(metrics) && isfield(metrics, "BER")) || ...
|
||||
(isobject(metrics) && isprop(metrics, "BER"));
|
||||
end
|
||||
539
projects/Diss/MPI_revisit/algorithms/recombine_warehouses.m
Normal file
539
projects/Diss/MPI_revisit/algorithms/recombine_warehouses.m
Normal file
@@ -0,0 +1,539 @@
|
||||
% === DSP settings ===
|
||||
dsp_options = struct();
|
||||
dsp_options.mode = "run_id";
|
||||
dsp_options.recipe = @mpi_recipe_dev;
|
||||
dsp_options.append_to_db = false;
|
||||
dsp_options.start_occurence = 1;
|
||||
dsp_options.max_occurences = 15;
|
||||
dsp_options.debug_plots = false;
|
||||
|
||||
dsp_options.database_type = "mysql";
|
||||
|
||||
dsp_options.dataBase = "labor";
|
||||
dsp_options.storage_path = "W:\labdata\ECOC Silas\ecoc_2025";
|
||||
|
||||
dsp_options.server = "192.168.178.192";
|
||||
dsp_options.port = 3306;
|
||||
dsp_options.user = "silas";
|
||||
dsp_options.password = "silas";
|
||||
|
||||
db = DBHandler("dataBase", [dsp_options.dataBase], ...
|
||||
"type", dsp_options.database_type, ...
|
||||
"server", dsp_options.server, ...
|
||||
"user", dsp_options.user, "password", dsp_options.password);
|
||||
|
||||
scriptDir = fileparts(mfilename("fullpath"));
|
||||
|
||||
whpam4_loaded = load(fullfile(scriptDir, "pam_4_results.mat"));
|
||||
whpam6_loaded = load(fullfile(scriptDir, "pam_6_results.mat"));
|
||||
whpam8_loaded = load(fullfile(scriptDir, "pam_8_results.mat"));
|
||||
|
||||
whpam4 = whpam4_loaded.obj;
|
||||
whpam6 = whpam6_loaded.obj;
|
||||
whpam8 = whpam8_loaded.obj;
|
||||
|
||||
whList = {whpam4, whpam6, whpam8};
|
||||
algorithmStorageNames = fieldnames(whpam4.sto);
|
||||
|
||||
%% Build a lossless run_id warehouse and cache run metadata
|
||||
|
||||
pamformats = [4, 6, 8];
|
||||
baudrates = [112e9, 96e9, 72e9];
|
||||
runMetaTable = queryRunMetadata(db, whList, pamformats, baudrates);
|
||||
|
||||
wh_run_id_combined = mergeDataStoragesByUnion(whList);
|
||||
wh_run_id_combined = addRunMetadataStorages(wh_run_id_combined, runMetaTable);
|
||||
|
||||
runIdSavePath = fullfile(scriptDir, "combined_by_run_id_results.mat");
|
||||
save(runIdSavePath, "wh_run_id_combined", "runMetaTable", "algorithmStorageNames", '-v7.3');
|
||||
|
||||
fprintf("Saved run_id-combined warehouse:\n %s\n", runIdSavePath);
|
||||
wh_run_id_combined.showInfo;
|
||||
|
||||
%% Build a grouped config warehouse for analysis views
|
||||
|
||||
configFields = ["pam_level", "symbolrate", "interference_path_length", "sir"];
|
||||
wh_config_combined = buildConfigWarehouse( ...
|
||||
wh_run_id_combined, runMetaTable, algorithmStorageNames, configFields);
|
||||
|
||||
configSavePath = fullfile(scriptDir, "combined_by_config_results.mat");
|
||||
save(configSavePath, "wh_config_combined", "runMetaTable", "algorithmStorageNames", '-v7.3');
|
||||
|
||||
fprintf("Saved config-grouped warehouse:\n %s\n", configSavePath);
|
||||
wh_config_combined.showInfo;
|
||||
|
||||
%% BER over SIR at fixed path length
|
||||
|
||||
plot_options = struct();
|
||||
plot_options.path_length = 1000;
|
||||
plot_options.use_boundedlines = true;
|
||||
plot_options.boundedline_alpha = 0.10;
|
||||
plot_options.polyfit_order_max = 3;
|
||||
plot_options.fec_ber_threshold = 2e-2;
|
||||
plot_options.figure_base = 7100;
|
||||
plot_options.xlim = [15 35];
|
||||
|
||||
plotBerOverSirForPath(wh_config_combined, algorithmStorageNames, plot_options);
|
||||
|
||||
%% Local functions
|
||||
|
||||
function runMetaTable = queryRunMetadata(db, whList, pamformats, baudrates)
|
||||
metaTables = {};
|
||||
metadataFields = db.getTableFieldNames('Runs');
|
||||
|
||||
for whIdx = 1:numel(whList)
|
||||
curWh = whList{whIdx};
|
||||
curRunIds = curWh.parameter.run_id.values(:);
|
||||
|
||||
fp = QueryFilter();
|
||||
fp.where('Runs', 'run_id', 'GREATER_EQUAL', 3153);
|
||||
fp.where('Runs', 'symbolrate', 'EQUALS', baudrates(whIdx));
|
||||
fp.where('Runs', 'fiber_length', 'EQUALS', 0);
|
||||
fp.where('Runs', 'db_mode', 'EQUALS', '"no_db"');
|
||||
fp.where('Runs', 'pam_level', 'EQUALS', pamformats(whIdx));
|
||||
|
||||
[dataTable, ~] = db.queryDB(fp, metadataFields);
|
||||
dataTable.run_id = numericColumn(dataTable.run_id);
|
||||
dataTable = dataTable(ismember(dataTable.run_id, curRunIds), :);
|
||||
|
||||
missingRunIds = setdiff(curRunIds, dataTable.run_id);
|
||||
if ~isempty(missingRunIds)
|
||||
warning("recombine_warehouses:MissingMetadata", ...
|
||||
"%d run_id(s) from PAM %.0f warehouse were not found in the metadata query.", ...
|
||||
numel(missingRunIds), pamformats(whIdx));
|
||||
end
|
||||
|
||||
metaTables{end+1} = dataTable; %#ok<AGROW>
|
||||
end
|
||||
|
||||
runMetaTable = vertcat(metaTables{:});
|
||||
[~, uniqueIdx] = unique(numericColumn(runMetaTable.run_id), "stable");
|
||||
runMetaTable = runMetaTable(uniqueIdx, :);
|
||||
|
||||
numericFields = ["run_id", "pam_level", "symbolrate", "interference_path_length", "sir"];
|
||||
for fieldIdx = 1:numel(numericFields)
|
||||
fieldName = numericFields(fieldIdx);
|
||||
if ismember(fieldName, string(runMetaTable.Properties.VariableNames))
|
||||
runMetaTable.(char(fieldName)) = numericColumn(runMetaTable.(char(fieldName)));
|
||||
end
|
||||
end
|
||||
|
||||
[~, sortIdx] = sort(runMetaTable.run_id);
|
||||
runMetaTable = runMetaTable(sortIdx, :);
|
||||
end
|
||||
|
||||
function x = numericColumn(x)
|
||||
if isnumeric(x)
|
||||
x = double(x);
|
||||
elseif iscell(x)
|
||||
x = str2double(string(x));
|
||||
elseif isstring(x) || ischar(x) || iscategorical(x)
|
||||
x = str2double(string(x));
|
||||
else
|
||||
x = double(x);
|
||||
end
|
||||
x = x(:);
|
||||
end
|
||||
|
||||
function whMerged = mergeDataStoragesByUnion(whList)
|
||||
templateWh = whList{1};
|
||||
paramNames = cellstr(templateWh.fn);
|
||||
|
||||
for whIdx = 2:numel(whList)
|
||||
if ~isequal(sort(cellstr(whList{whIdx}.fn)), sort(paramNames))
|
||||
error("recombine_warehouses:ParameterMismatch", ...
|
||||
"All input warehouses must use the same parameter names.");
|
||||
end
|
||||
end
|
||||
|
||||
mergedParams = struct();
|
||||
for paramIdx = 1:numel(paramNames)
|
||||
paramName = paramNames{paramIdx};
|
||||
values = [];
|
||||
|
||||
for whIdx = 1:numel(whList)
|
||||
newValues = whList{whIdx}.parameter.(paramName).values(:).';
|
||||
values = [values, newValues]; %#ok<AGROW>
|
||||
end
|
||||
|
||||
values = unique(values, "stable");
|
||||
if isnumeric(values)
|
||||
values = sort(values);
|
||||
end
|
||||
mergedParams.(paramName) = values;
|
||||
end
|
||||
|
||||
whMerged = DataStorage(mergedParams);
|
||||
|
||||
for whIdx = 1:numel(whList)
|
||||
curWh = whList{whIdx};
|
||||
curStorageNames = fieldnames(curWh.sto);
|
||||
|
||||
for storageIdx = 1:numel(curStorageNames)
|
||||
storageName = curStorageNames{storageIdx};
|
||||
if ~isfield(whMerged.sto, storageName)
|
||||
whMerged.addStorage(storageName);
|
||||
end
|
||||
|
||||
for linIdx = 1:numel(curWh.sto.(storageName))
|
||||
storedValue = curWh.sto.(storageName){linIdx};
|
||||
if isempty(storedValue)
|
||||
continue
|
||||
end
|
||||
|
||||
targetLinIdx = mapLinearIndex(curWh, whMerged, linIdx);
|
||||
existingValue = whMerged.sto.(storageName){targetLinIdx};
|
||||
whMerged.sto.(storageName){targetLinIdx} = mergeStoredValue(existingValue, storedValue);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function wh = addRunMetadataStorages(wh, runMetaTable)
|
||||
metadataStorageNames = ["meta_pam_level", "meta_symbolrate", ...
|
||||
"meta_interference_path_length", "meta_sir"];
|
||||
metadataTableFields = ["pam_level", "symbolrate", "interference_path_length", "sir"];
|
||||
|
||||
for fieldIdx = 1:numel(metadataStorageNames)
|
||||
if ~isfield(wh.sto, metadataStorageNames(fieldIdx))
|
||||
wh.addStorage(char(metadataStorageNames(fieldIdx)));
|
||||
end
|
||||
end
|
||||
|
||||
for linIdx = 1:wh.getLastLinIndice()
|
||||
physStruct = linearIndexToStruct(wh, linIdx);
|
||||
if ~isfield(physStruct, "run_id")
|
||||
continue
|
||||
end
|
||||
|
||||
metaIdx = find(runMetaTable.run_id == physStruct.run_id, 1);
|
||||
if isempty(metaIdx)
|
||||
continue
|
||||
end
|
||||
|
||||
for fieldIdx = 1:numel(metadataStorageNames)
|
||||
wh.sto.(char(metadataStorageNames(fieldIdx))){linIdx} = ...
|
||||
runMetaTable.(char(metadataTableFields(fieldIdx)))(metaIdx);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function whConfig = buildConfigWarehouse(whRun, runMetaTable, algorithmStorageNames, configFields)
|
||||
runParamNames = cellstr(whRun.fn);
|
||||
extraParamNames = setdiff(string(runParamNames), "run_id", "stable");
|
||||
|
||||
configParams = struct();
|
||||
for fieldIdx = 1:numel(configFields)
|
||||
fieldName = configFields(fieldIdx);
|
||||
values = unique(runMetaTable.(char(fieldName))(:).', "stable");
|
||||
if isnumeric(values)
|
||||
values = sort(values);
|
||||
end
|
||||
configParams.(char(fieldName)) = values;
|
||||
end
|
||||
|
||||
for paramIdx = 1:numel(extraParamNames)
|
||||
paramName = extraParamNames(paramIdx);
|
||||
configParams.(char(paramName)) = whRun.parameter.(char(paramName)).values;
|
||||
end
|
||||
|
||||
whConfig = DataStorage(configParams);
|
||||
for storageIdx = 1:numel(algorithmStorageNames)
|
||||
whConfig.addStorage(algorithmStorageNames{storageIdx});
|
||||
end
|
||||
whConfig.addStorage("run_id");
|
||||
|
||||
for storageIdx = 1:numel(algorithmStorageNames)
|
||||
storageName = algorithmStorageNames{storageIdx};
|
||||
|
||||
for linIdx = 1:numel(whRun.sto.(storageName))
|
||||
storedValue = whRun.sto.(storageName){linIdx};
|
||||
if isempty(storedValue)
|
||||
continue
|
||||
end
|
||||
|
||||
sourcePhys = linearIndexToStruct(whRun, linIdx);
|
||||
metaIdx = find(runMetaTable.run_id == sourcePhys.run_id, 1);
|
||||
if isempty(metaIdx)
|
||||
continue
|
||||
end
|
||||
|
||||
targetArgs = configTargetArgs(whConfig, sourcePhys, runMetaTable, metaIdx, configFields);
|
||||
targetLinIdx = whConfig.getIndicesByPhys(targetArgs);
|
||||
|
||||
existingValue = whConfig.sto.(storageName){targetLinIdx};
|
||||
whConfig.sto.(storageName){targetLinIdx} = mergeStoredValue(existingValue, storedValue);
|
||||
end
|
||||
end
|
||||
|
||||
for linIdx = 1:whRun.getLastLinIndice()
|
||||
sourcePhys = linearIndexToStruct(whRun, linIdx);
|
||||
metaIdx = find(runMetaTable.run_id == sourcePhys.run_id, 1);
|
||||
if isempty(metaIdx)
|
||||
continue
|
||||
end
|
||||
|
||||
targetArgs = configTargetArgs(whConfig, sourcePhys, runMetaTable, metaIdx, configFields);
|
||||
targetLinIdx = whConfig.getIndicesByPhys(targetArgs);
|
||||
whConfig.sto.run_id{targetLinIdx} = mergeUniqueNumeric( ...
|
||||
whConfig.sto.run_id{targetLinIdx}, sourcePhys.run_id);
|
||||
end
|
||||
end
|
||||
|
||||
function targetArgs = configTargetArgs(whConfig, sourcePhys, runMetaTable, metaIdx, configFields)
|
||||
targetArgs = cell(1, numel(whConfig.fn));
|
||||
|
||||
for paramIdx = 1:numel(whConfig.fn)
|
||||
paramName = string(whConfig.fn(paramIdx));
|
||||
if any(configFields == paramName)
|
||||
targetArgs{paramIdx} = runMetaTable.(char(paramName))(metaIdx);
|
||||
else
|
||||
targetArgs{paramIdx} = sourcePhys.(char(paramName));
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function targetLinIdx = mapLinearIndex(sourceWh, targetWh, sourceLinIdx)
|
||||
sourcePhys = linearIndexToStruct(sourceWh, sourceLinIdx);
|
||||
targetArgs = cell(1, numel(targetWh.fn));
|
||||
|
||||
for paramIdx = 1:numel(targetWh.fn)
|
||||
paramName = char(targetWh.fn(paramIdx));
|
||||
targetArgs{paramIdx} = sourcePhys.(paramName);
|
||||
end
|
||||
|
||||
targetLinIdx = targetWh.getIndicesByPhys(targetArgs);
|
||||
end
|
||||
|
||||
function physStruct = linearIndexToStruct(wh, linIdx)
|
||||
[physValues, physNames] = wh.getPhysIndicesByLinIndex(linIdx);
|
||||
physStruct = struct();
|
||||
|
||||
for paramIdx = 1:numel(physNames)
|
||||
physStruct.(char(physNames{paramIdx})) = physValues{paramIdx};
|
||||
end
|
||||
end
|
||||
|
||||
function out = mergeStoredValue(existingValue, newValue)
|
||||
if isempty(existingValue)
|
||||
out = newValue;
|
||||
return
|
||||
end
|
||||
|
||||
existingPackages = asPackageCell(existingValue);
|
||||
newPackages = asPackageCell(newValue);
|
||||
out = [existingPackages(:); newPackages(:)].';
|
||||
end
|
||||
|
||||
function packages = asPackageCell(value)
|
||||
if isempty(value)
|
||||
packages = {};
|
||||
elseif iscell(value)
|
||||
packages = value(:).';
|
||||
else
|
||||
packages = {value};
|
||||
end
|
||||
end
|
||||
|
||||
function out = mergeUniqueNumeric(existingValue, newValue)
|
||||
if isempty(existingValue)
|
||||
out = newValue;
|
||||
else
|
||||
out = unique([existingValue(:); newValue(:)].', "stable");
|
||||
end
|
||||
end
|
||||
|
||||
function plotBerOverSirForPath(whConfig, algorithmStorageNames, plotOptions)
|
||||
paramNames = string(whConfig.fn);
|
||||
|
||||
if ~any(paramNames == "pam_level") || ~any(paramNames == "interference_path_length") || ~any(paramNames == "sir")
|
||||
error("recombine_warehouses:MissingPlotParameters", ...
|
||||
"The config warehouse must contain pam_level, interference_path_length, and sir parameters.");
|
||||
end
|
||||
|
||||
pamLevels = whConfig.parameter.pam_level.values;
|
||||
hasBlockUpdate = any(paramNames == "block_update");
|
||||
if hasBlockUpdate
|
||||
blockUpdates = whConfig.parameter.block_update.values;
|
||||
else
|
||||
blockUpdates = NaN;
|
||||
end
|
||||
|
||||
if exist("linspecer", "file")
|
||||
curveColors = linspecer(max(numel(algorithmStorageNames), 1));
|
||||
else
|
||||
curveColors = lines(max(numel(algorithmStorageNames), 1));
|
||||
end
|
||||
|
||||
figureIdx = plotOptions.figure_base;
|
||||
for blockIdx = 1:numel(blockUpdates)
|
||||
for pamIdx = 1:numel(pamLevels)
|
||||
pamLevel = pamLevels(pamIdx);
|
||||
figureIdx = figureIdx + 1;
|
||||
figure(figureIdx); clf; hold on
|
||||
|
||||
for storageIdx = 1:numel(algorithmStorageNames)
|
||||
storageName = algorithmStorageNames{storageIdx};
|
||||
curveColor = curveColors(storageIdx, :);
|
||||
|
||||
filter = struct();
|
||||
filter.pam_level = pamLevel;
|
||||
filter.interference_path_length = plotOptions.path_length;
|
||||
if hasBlockUpdate
|
||||
filter.block_update = blockUpdates(blockIdx);
|
||||
end
|
||||
|
||||
[sirValues, berGroups] = collectBerBySir(whConfig, storageName, filter);
|
||||
if isempty(sirValues)
|
||||
continue
|
||||
end
|
||||
|
||||
[sirValues, sortIdx] = sort(sirValues);
|
||||
berGroups = berGroups(sortIdx);
|
||||
|
||||
berAvg = nan(1, numel(sirValues));
|
||||
berMin = nan(1, numel(sirValues));
|
||||
berMax = nan(1, numel(sirValues));
|
||||
|
||||
for sirIdx = 1:numel(sirValues)
|
||||
rawBer = berGroups{sirIdx};
|
||||
rawBer = rawBer(isfinite(rawBer));
|
||||
if isempty(rawBer)
|
||||
continue
|
||||
end
|
||||
|
||||
scatter(repmat(sirValues(sirIdx), size(rawBer)), rawBer, ...
|
||||
12, ...
|
||||
"Marker", ".", ...
|
||||
"MarkerEdgeColor", curveColor, ...
|
||||
"MarkerFaceColor", curveColor, ...
|
||||
"HandleVisibility", "off");
|
||||
|
||||
berAvg(sirIdx) = mean(rawBer, "omitnan");
|
||||
berMin(sirIdx) = min(rawBer);
|
||||
berMax(sirIdx) = max(rawBer);
|
||||
end
|
||||
|
||||
validAvg = isfinite(sirValues) & isfinite(berAvg);
|
||||
if ~any(validAvg)
|
||||
continue
|
||||
end
|
||||
|
||||
if plotOptions.use_boundedlines && exist("boundedline", "file")
|
||||
yLower = max(berAvg - berMin, 0);
|
||||
yUpper = max(berMax - berAvg, 0);
|
||||
yBounds = [yLower(:), yUpper(:)];
|
||||
|
||||
[hl, hp] = boundedline(sirValues(:), berAvg(:), yBounds, ...
|
||||
"alpha", "transparency", plotOptions.boundedline_alpha, ...
|
||||
"cmap", curveColor, ...
|
||||
"nan", "fill", ...
|
||||
"orientation", "vert");
|
||||
set(hl, "LineStyle", "none", "Marker", "none", "HandleVisibility", "off");
|
||||
set(hp, "HandleVisibility", "off", "LineStyle", "none");
|
||||
end
|
||||
|
||||
scatter(sirValues(validAvg), berAvg(validAvg), ...
|
||||
54, ...
|
||||
"Marker", "o", ...
|
||||
"MarkerEdgeColor", curveColor, ...
|
||||
"MarkerFaceColor", "none", ...
|
||||
"LineWidth", 1.2, ...
|
||||
"DisplayName", storageName);
|
||||
|
||||
fitMask = validAvg & berAvg > 0;
|
||||
if nnz(fitMask) >= 2
|
||||
fitOrder = min(plotOptions.polyfit_order_max, nnz(fitMask) - 1);
|
||||
fitCoeff = polyfit(sirValues(fitMask), log10(berAvg(fitMask)), fitOrder);
|
||||
xFit = linspace(min(sirValues(fitMask)), max(sirValues(fitMask)), 300);
|
||||
yFit = 10 .^ polyval(fitCoeff, xFit);
|
||||
|
||||
plot(xFit, yFit, ...
|
||||
"LineWidth", 1.2, ...
|
||||
"LineStyle", "--", ...
|
||||
"Marker", "none", ...
|
||||
"Color", curveColor, ...
|
||||
"HandleVisibility", "off");
|
||||
end
|
||||
end
|
||||
|
||||
if hasBlockUpdate
|
||||
title(sprintf("BER over SIR, PAM %.0f, path %.0f m, block update = %g", ...
|
||||
pamLevel, plotOptions.path_length, blockUpdates(blockIdx)));
|
||||
else
|
||||
title(sprintf("BER over SIR, PAM %.0f, path %.0f m", ...
|
||||
pamLevel, plotOptions.path_length));
|
||||
end
|
||||
xlabel("SIR (dB)");
|
||||
ylabel("BER");
|
||||
xlim(plotOptions.xlim);
|
||||
grid on
|
||||
box on
|
||||
|
||||
if exist("beautifyBERplot", "file")
|
||||
beautifyBERplot("logscale", true, "setcolors", false, "setmarkers", false);
|
||||
else
|
||||
set(gca, "YScale", "log");
|
||||
end
|
||||
legend("Location", "best", "Interpreter", "none");
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function [sirValues, berGroups] = collectBerBySir(whConfig, storageName, filter)
|
||||
sirValues = [];
|
||||
berGroups = {};
|
||||
|
||||
for linIdx = 1:numel(whConfig.sto.(storageName))
|
||||
storedValue = whConfig.sto.(storageName){linIdx};
|
||||
if isempty(storedValue)
|
||||
continue
|
||||
end
|
||||
|
||||
physStruct = linearIndexToStruct(whConfig, linIdx);
|
||||
if ~matchesFilter(physStruct, filter)
|
||||
continue
|
||||
end
|
||||
|
||||
berValues = extractBerValues(storedValue);
|
||||
if isempty(berValues)
|
||||
continue
|
||||
end
|
||||
|
||||
sirValue = physStruct.sir;
|
||||
sirIdx = find(sirValues == sirValue, 1);
|
||||
if isempty(sirIdx)
|
||||
sirValues(end+1) = sirValue; %#ok<AGROW>
|
||||
berGroups{end+1} = berValues; %#ok<AGROW>
|
||||
else
|
||||
berGroups{sirIdx} = [berGroups{sirIdx}, berValues]; %#ok<AGROW>
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function tf = matchesFilter(physStruct, filter)
|
||||
tf = true;
|
||||
filterFields = fieldnames(filter);
|
||||
|
||||
for fieldIdx = 1:numel(filterFields)
|
||||
fieldName = filterFields{fieldIdx};
|
||||
if ~isfield(physStruct, fieldName) || physStruct.(fieldName) ~= filter.(fieldName)
|
||||
tf = false;
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function berValues = extractBerValues(value)
|
||||
berValues = [];
|
||||
|
||||
if isstruct(value)
|
||||
if isfield(value, "metrics") && isfield(value.metrics, "BER")
|
||||
berValues(end+1) = value.metrics.BER;
|
||||
end
|
||||
elseif iscell(value)
|
||||
for valueIdx = 1:numel(value)
|
||||
berValues = [berValues, extractBerValues(value{valueIdx})]; %#ok<AGROW>
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user