plot radio metrics as a function of distance
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
function [f, R] = plotRadioLogs(resultsPath)
|
||||
function [f, fDist, R] = plotRadioLogs(resultsPath, G, tLim)
|
||||
arguments (Input)
|
||||
resultsPath (1, 1) string;
|
||||
G cell = {};
|
||||
tLim (1, 2) datetime = [datetime(-Inf, 'ConvertFrom', 'datenum'), datetime(Inf, 'ConvertFrom', 'datenum')];
|
||||
end
|
||||
arguments (Output)
|
||||
f (1, 1) matlab.ui.Figure;
|
||||
fDist (1, 1) matlab.ui.Figure;
|
||||
R cell;
|
||||
end
|
||||
|
||||
@@ -41,6 +44,7 @@ function [f, R] = plotRadioLogs(resultsPath)
|
||||
metricNames = ["SNR", "Power", "Quality", "PathLoss", "NoiseFloor", "FreqOffset"];
|
||||
yLabels = ["SNR (dB)", "Power (dB)", "Quality", "Path Loss (dB)", "Noise Floor (dB)", "Freq Offset (MHz)"];
|
||||
|
||||
% --- Time-based figure ---
|
||||
f = figure;
|
||||
tl = tiledlayout(numel(metricNames), 1, 'TileSpacing', 'compact', 'Padding', 'compact');
|
||||
|
||||
@@ -57,10 +61,10 @@ function [f, R] = plotRadioLogs(resultsPath)
|
||||
for ti = 1:numel(txIDs)
|
||||
txID = txIDs(ti);
|
||||
rows = tbl(tbl.TxUAVID == txID, :);
|
||||
rows = rows(rows.Timestamp >= tLim(1) & rows.Timestamp <= tLim(2), :);
|
||||
vals = rows.(metricNames(mi));
|
||||
|
||||
% Skip if all NaN for this metric
|
||||
if all(isnan(vals))
|
||||
if isempty(rows) || all(isnan(vals))
|
||||
continue;
|
||||
end
|
||||
|
||||
@@ -81,4 +85,100 @@ function [f, R] = plotRadioLogs(resultsPath)
|
||||
end
|
||||
|
||||
title(tl, 'Radio Channel Metrics');
|
||||
end
|
||||
|
||||
% --- Distance-based figure ---
|
||||
fDist = figure;
|
||||
|
||||
if isempty(G)
|
||||
return;
|
||||
end
|
||||
|
||||
tl2 = tiledlayout(numel(metricNames), 1, 'TileSpacing', 'compact', 'Padding', 'compact');
|
||||
|
||||
for mi = 1:numel(metricNames)
|
||||
ax = nexttile(tl2);
|
||||
hold(ax, 'on');
|
||||
grid(ax, 'on');
|
||||
|
||||
legendEntries = string.empty;
|
||||
ci = 1;
|
||||
for rxIdx = 1:nUAV
|
||||
tbl = R{rxIdx};
|
||||
txIDs = unique(tbl.TxUAVID);
|
||||
for ti = 1:numel(txIDs)
|
||||
txID = txIDs(ti);
|
||||
rows = tbl(tbl.TxUAVID == txID, :);
|
||||
|
||||
if isempty(rows)
|
||||
continue;
|
||||
end
|
||||
|
||||
rows = rows(rows.Timestamp >= tLim(1) & rows.Timestamp <= tLim(2), :);
|
||||
if isempty(rows)
|
||||
continue;
|
||||
end
|
||||
|
||||
vals = rows.(metricNames(mi));
|
||||
if all(isnan(vals))
|
||||
continue;
|
||||
end
|
||||
|
||||
% Map 0-based UAV IDs to 1-based GPS cell indices
|
||||
txGpsIdx = double(txID) + 1;
|
||||
rxGpsIdx = double(rows.RxUAVID(1)) + 1;
|
||||
|
||||
if txGpsIdx > numel(G) || rxGpsIdx > numel(G)
|
||||
continue;
|
||||
end
|
||||
|
||||
Gtx = G{txGpsIdx};
|
||||
Grx = G{rxGpsIdx};
|
||||
|
||||
if ~ismember('East', Gtx.Properties.VariableNames) || ...
|
||||
~ismember('East', Grx.Properties.VariableNames)
|
||||
continue;
|
||||
end
|
||||
|
||||
% Strip timezone before posixtime so radio and GPS timestamps
|
||||
% are treated on the same scale (both are AERPAW wall-clock time)
|
||||
txTs = Gtx.Timestamp; txTs.TimeZone = '';
|
||||
rxTs = Grx.Timestamp; rxTs.TimeZone = '';
|
||||
txPt = posixtime(txTs);
|
||||
rxPt = posixtime(rxTs);
|
||||
radioPt = posixtime(rows.Timestamp);
|
||||
|
||||
% Interpolate GPS positions at radio measurement times.
|
||||
% Exclude NaN ENU entries (outside algorithm flight range).
|
||||
validTx = ~isnan(Gtx.East);
|
||||
validRx = ~isnan(Grx.East);
|
||||
|
||||
txE = interp1(txPt(validTx), Gtx.East(validTx), radioPt, 'linear', NaN);
|
||||
txN = interp1(txPt(validTx), Gtx.North(validTx), radioPt, 'linear', NaN);
|
||||
txU = interp1(txPt(validTx), Gtx.Up(validTx), radioPt, 'linear', NaN);
|
||||
rxE = interp1(rxPt(validRx), Grx.East(validRx), radioPt, 'linear', NaN);
|
||||
rxN = interp1(rxPt(validRx), Grx.North(validRx), radioPt, 'linear', NaN);
|
||||
rxU = interp1(rxPt(validRx), Grx.Up(validRx), radioPt, 'linear', NaN);
|
||||
|
||||
dist = vecnorm([txE - rxE, txN - rxN, txU - rxU], 2, 2);
|
||||
|
||||
if all(isnan(dist))
|
||||
continue;
|
||||
end
|
||||
|
||||
si = mod(ci - 1, numel(styles)) + 1;
|
||||
scatter(ax, dist, vals, 9, colors(ci, :), strrep(styles(si), "-", ""), 'filled');
|
||||
legendEntries(end+1) = sprintf("TX %d → RX %d", txID, rows.RxUAVID(1)); %#ok<AGROW>
|
||||
ci = ci + 1;
|
||||
end
|
||||
end
|
||||
|
||||
ylabel(ax, yLabels(mi));
|
||||
if mi == numel(metricNames)
|
||||
xlabel(ax, 'Distance (m)');
|
||||
end
|
||||
legend(ax, legendEntries, 'Location', 'best');
|
||||
hold(ax, 'off');
|
||||
end
|
||||
|
||||
title(tl2, 'Radio Channel Metrics vs Distance');
|
||||
end
|
||||
|
||||
@@ -26,9 +26,8 @@ seaToGroundLevel = 110; % measured approximately from USGS national map viewer
|
||||
plotWholeFlight = true; % do not attempt to automatically trim initial and final positioning and landing from flight plot (buggy)
|
||||
[fGlobe, G] = plotGpsLogs(resultsPath, seaToGroundLevel, true);
|
||||
|
||||
% Plot radio statistics
|
||||
[fRadio, R] = plotRadioLogs(resultsPath);
|
||||
set(findobj(fRadio, 'Type', 'axes'), 'XLim', controller.timestamp([1, end]));
|
||||
% Plot radio statistics (time-based and distance-based)
|
||||
[fRadio, fRadioDist, R] = plotRadioLogs(resultsPath, G, controller.timestamp([1, end]));
|
||||
|
||||
%% Run simulation
|
||||
% Run miSim using same AERPAW scenario definition CSV
|
||||
|
||||
Reference in New Issue
Block a user