added radio plotting tools
This commit is contained in:
@@ -1,86 +0,0 @@
|
||||
% Plot setup
|
||||
f = uifigure;
|
||||
gf = geoglobe(f);
|
||||
hold(gf, "on");
|
||||
c = ["g", "b", "m", "c"]; % plotting colors
|
||||
|
||||
% paths
|
||||
scenarioCsv = fullfile("aerpaw", "config", "scenario.csv");
|
||||
|
||||
% configured data
|
||||
params = readScenarioCsv(scenarioCsv);
|
||||
|
||||
% coordinate system constants
|
||||
seaToGroundLevel = 110; % meters, measured approximately from USGS national map viewer
|
||||
|
||||
fID = fopen(fullfile("aerpaw", "config", "client1.yaml"), 'r');
|
||||
yaml = fscanf(fID, '%s');
|
||||
fclose(fID);
|
||||
% origin (LLA)
|
||||
lla0 = [str2double(yaml((strfind(yaml, 'lat:') + 4):(strfind(yaml, 'lon:') - 1))), str2double(yaml((strfind(yaml, 'lon:') + 4):(strfind(yaml, 'alt:') - 1))), seaToGroundLevel];
|
||||
|
||||
% Paths to logs
|
||||
logDirs = dir(fullfile("sandbox", "t1"));
|
||||
logDirs = logDirs(3:end);
|
||||
logDirs = logDirs([logDirs(:).isdir] == 1);
|
||||
|
||||
G = cell(size(logDirs));
|
||||
for ii = 1:size(logDirs, 1)
|
||||
% Find GPS log CSV
|
||||
gpsCsv = dir(fullfile(logDirs(ii).folder, logDirs(ii).name));
|
||||
gpsCsv = gpsCsv(endsWith({gpsCsv(:).name}, "_gps_log.csv"));
|
||||
gpsCsv = fullfile(gpsCsv.folder, gpsCsv.name);
|
||||
|
||||
% Read GPS log CSV
|
||||
G{ii} = readGpsCsv(gpsCsv);
|
||||
|
||||
% Find when algorithm begins/ends (using ENU altitude rate change)
|
||||
enuTraj = lla2enu([G{ii}.Latitude, G{ii}.Longitude, G{ii}.Altitude], lla0, 'flat');
|
||||
|
||||
verticalSpeed = movmean(abs(diff(G{ii}.Altitude)), [10, 0]);
|
||||
|
||||
% Automatically detect start/stop of algorithm flight (ignore takeoff, setup, return to liftoff, landing segments of flight)
|
||||
pctThreshold = 60; % pctThreshold may need adjusting depending on your flight
|
||||
startIdx = find(verticalSpeed <= prctile(verticalSpeed, pctThreshold), 1, 'first');
|
||||
stopIdx = find(verticalSpeed <= prctile(verticalSpeed, pctThreshold), 1, 'last');
|
||||
|
||||
% % Plot whole flight, including setup/cleanup
|
||||
% startIdx = 1;
|
||||
% stopIdx = length(verticalSpeed);
|
||||
|
||||
% Plot recorded trajectory over specified range of indices
|
||||
geoplot3(gf, G{ii}.Latitude(startIdx:stopIdx), G{ii}.Longitude(startIdx:stopIdx), G{ii}.Altitude(startIdx:stopIdx) + seaToGroundLevel, c(mod(ii, length(c))), 'LineWidth', 2, "MarkerSize", 5);
|
||||
end
|
||||
|
||||
% Plot domain
|
||||
altOffset = 1; % to avoid clipping into the ground when displayed
|
||||
domain = [lla0; enu2lla(params.domainMax, lla0, 'flat')];
|
||||
geoplot3(gf, [domain(1, 1), domain(2, 1), domain(2, 1), domain(1, 1), domain(1, 1)], [domain(1, 2), domain(1, 2), domain(2, 2), domain(2, 2), domain(1, 2)], repmat(domain(1, 3) + altOffset, 1, 5), 'LineWidth', 3, 'Color', 'k');
|
||||
geoplot3(gf, [domain(1, 1), domain(2, 1), domain(2, 1), domain(1, 1), domain(1, 1)], [domain(1, 2), domain(1, 2), domain(2, 2), domain(2, 2), domain(1, 2)], repmat(domain(2, 3) + altOffset, 1, 5), 'LineWidth', 3, 'Color', 'k');
|
||||
geoplot3(gf, [domain(1, 1), domain(1, 1)], [domain(1, 2), domain(1, 2)], domain(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'k');
|
||||
geoplot3(gf, [domain(2, 1), domain(2, 1)], [domain(1, 2), domain(1, 2)], domain(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'k');
|
||||
geoplot3(gf, [domain(1, 1), domain(1, 1)], [domain(2, 2), domain(2, 2)], domain(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'k');
|
||||
geoplot3(gf, [domain(2, 1), domain(2, 1)], [domain(2, 2), domain(2, 2)], domain(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'k');
|
||||
|
||||
% Plot floor (minimum altitude constraint)
|
||||
floorAlt = params.minAlt;
|
||||
geoplot3(gf, [domain(1, 1), domain(2, 1), domain(2, 1), domain(1, 1), domain(1, 1)], [domain(1, 2), domain(1, 2), domain(2, 2), domain(2, 2), domain(1, 2)], repmat(domain(1, 3) + altOffset + floorAlt, 1, 5), 'LineWidth', 3, 'Color', 'r');
|
||||
|
||||
% Plot objective
|
||||
objectivePos = [params.objectivePos, 0];
|
||||
llaObj = enu2lla(objectivePos, lla0, 'flat');
|
||||
geoplot3(gf, [llaObj(1), llaObj(1)], [llaObj(2), llaObj(2)], [llaObj(3), domain(2, 3)], 'LineWidth', 3, "Color", 'y');
|
||||
|
||||
% Plot obstacles
|
||||
for ii = 1:params.numObstacles
|
||||
obstacle = enu2lla([params.obstacleMin((1 + (ii - 1) * 3):(ii * 3)); params.obstacleMax((1 + (ii - 1) * 3):(ii * 3))], lla0, 'flat');
|
||||
geoplot3(gf, [obstacle(1, 1), obstacle(2, 1), obstacle(2, 1), obstacle(1, 1), obstacle(1, 1)], [obstacle(1, 2), obstacle(1, 2), obstacle(2, 2), obstacle(2, 2), obstacle(1, 2)], repmat(obstacle(1, 3) + altOffset, 1, 5), 'LineWidth', 3, 'Color', 'r');
|
||||
geoplot3(gf, [obstacle(1, 1), obstacle(2, 1), obstacle(2, 1), obstacle(1, 1), obstacle(1, 1)], [obstacle(1, 2), obstacle(1, 2), obstacle(2, 2), obstacle(2, 2), obstacle(1, 2)], repmat(obstacle(2, 3) + altOffset, 1, 5), 'LineWidth', 3, 'Color', 'r');
|
||||
geoplot3(gf, [obstacle(1, 1), obstacle(1, 1)], [obstacle(1, 2), obstacle(1, 2)], obstacle(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'r');
|
||||
geoplot3(gf, [obstacle(2, 1), obstacle(2, 1)], [obstacle(1, 2), obstacle(1, 2)], obstacle(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'r');
|
||||
geoplot3(gf, [obstacle(1, 1), obstacle(1, 1)], [obstacle(2, 2), obstacle(2, 2)], obstacle(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'r');
|
||||
geoplot3(gf, [obstacle(2, 1), obstacle(2, 1)], [obstacle(2, 2), obstacle(2, 2)], obstacle(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'r');
|
||||
end
|
||||
|
||||
% finish
|
||||
hold(gf, "off");
|
||||
91
aerpaw/results/plotGpsLogs.m
Normal file
91
aerpaw/results/plotGpsLogs.m
Normal file
@@ -0,0 +1,91 @@
|
||||
function f = plotGpsLogs(logDirs)
|
||||
arguments (Input)
|
||||
logDirs (1, 1) string;
|
||||
end
|
||||
arguments (Output)
|
||||
f (1, 1) uifigure;
|
||||
end
|
||||
% Plot setup
|
||||
f = uifigure;
|
||||
gf = geoglobe(f);
|
||||
hold(gf, "on");
|
||||
c = ["g", "b", "m", "c"]; % plotting colors
|
||||
|
||||
% paths
|
||||
scenarioCsv = fullfile(matlab.project.rootProject().RootFolder, "aerpaw", "config", "scenario.csv");
|
||||
|
||||
% configured data
|
||||
params = readScenarioCsv(scenarioCsv);
|
||||
|
||||
% coordinate system constants
|
||||
seaToGroundLevel = 110; % meters, measured approximately from USGS national map viewer
|
||||
|
||||
fID = fopen(fullfile(matlab.project.rootProject().RootFolder, "aerpaw", "config", "client1.yaml"), 'r');
|
||||
yaml = fscanf(fID, '%s');
|
||||
fclose(fID);
|
||||
% origin (LLA)
|
||||
lla0 = [str2double(yaml((strfind(yaml, 'lat:') + 4):(strfind(yaml, 'lon:') - 1))), str2double(yaml((strfind(yaml, 'lon:') + 4):(strfind(yaml, 'alt:') - 1))), seaToGroundLevel];
|
||||
|
||||
logDirs = dir(logDirs);
|
||||
logDirs = logDirs(3:end);
|
||||
logDirs = logDirs([logDirs.isdir] == 1);
|
||||
|
||||
G = cell(size(logDirs));
|
||||
for ii = 1:size(logDirs, 1)
|
||||
% Find GPS log CSV
|
||||
gpsCsv = dir(fullfile(logDirs(ii).folder, logDirs(ii).name));
|
||||
gpsCsv = gpsCsv(endsWith({gpsCsv(:).name}, "_gps_log.csv"));
|
||||
gpsCsv = fullfile(gpsCsv.folder, gpsCsv.name);
|
||||
|
||||
% Read GPS log CSV
|
||||
G{ii} = readGpsLogs(gpsCsv);
|
||||
|
||||
% Find when algorithm begins/ends (using ENU altitude rate change)
|
||||
verticalSpeed = movmean(abs(diff(G{ii}.Altitude)), [10, 0]);
|
||||
|
||||
% Automatically detect start/stop of algorithm flight (ignore takeoff, setup, return to liftoff, landing segments of flight)
|
||||
pctThreshold = 60; % pctThreshold may need adjusting depending on your flight
|
||||
startIdx = find(verticalSpeed <= prctile(verticalSpeed, pctThreshold), 1, 'first');
|
||||
stopIdx = find(verticalSpeed <= prctile(verticalSpeed, pctThreshold), 1, 'last');
|
||||
|
||||
% % Plot whole flight, including setup/cleanup
|
||||
% startIdx = 1;
|
||||
% stopIdx = length(verticalSpeed);
|
||||
|
||||
% Plot recorded trajectory over specified range of indices
|
||||
geoplot3(gf, G{ii}.Latitude(startIdx:stopIdx), G{ii}.Longitude(startIdx:stopIdx), G{ii}.Altitude(startIdx:stopIdx) + seaToGroundLevel, c(mod(ii, length(c))), 'LineWidth', 2, "MarkerSize", 5);
|
||||
end
|
||||
|
||||
% Plot domain
|
||||
altOffset = 1; % to avoid clipping into the ground when displayed
|
||||
domain = [lla0; enu2lla(params.domainMax, lla0, 'flat')];
|
||||
geoplot3(gf, [domain(1, 1), domain(2, 1), domain(2, 1), domain(1, 1), domain(1, 1)], [domain(1, 2), domain(1, 2), domain(2, 2), domain(2, 2), domain(1, 2)], repmat(domain(1, 3) + altOffset, 1, 5), 'LineWidth', 3, 'Color', 'k');
|
||||
geoplot3(gf, [domain(1, 1), domain(2, 1), domain(2, 1), domain(1, 1), domain(1, 1)], [domain(1, 2), domain(1, 2), domain(2, 2), domain(2, 2), domain(1, 2)], repmat(domain(2, 3) + altOffset, 1, 5), 'LineWidth', 3, 'Color', 'k');
|
||||
geoplot3(gf, [domain(1, 1), domain(1, 1)], [domain(1, 2), domain(1, 2)], domain(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'k');
|
||||
geoplot3(gf, [domain(2, 1), domain(2, 1)], [domain(1, 2), domain(1, 2)], domain(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'k');
|
||||
geoplot3(gf, [domain(1, 1), domain(1, 1)], [domain(2, 2), domain(2, 2)], domain(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'k');
|
||||
geoplot3(gf, [domain(2, 1), domain(2, 1)], [domain(2, 2), domain(2, 2)], domain(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'k');
|
||||
|
||||
% Plot floor (minimum altitude constraint)
|
||||
floorAlt = params.minAlt;
|
||||
geoplot3(gf, [domain(1, 1), domain(2, 1), domain(2, 1), domain(1, 1), domain(1, 1)], [domain(1, 2), domain(1, 2), domain(2, 2), domain(2, 2), domain(1, 2)], repmat(domain(1, 3) + altOffset + floorAlt, 1, 5), 'LineWidth', 3, 'Color', 'r');
|
||||
|
||||
% Plot objective
|
||||
objectivePos = [params.objectivePos, 0];
|
||||
llaObj = enu2lla(objectivePos, lla0, 'flat');
|
||||
geoplot3(gf, [llaObj(1), llaObj(1)], [llaObj(2), llaObj(2)], [llaObj(3), domain(2, 3)], 'LineWidth', 3, "Color", 'y');
|
||||
|
||||
% Plot obstacles
|
||||
for ii = 1:params.numObstacles
|
||||
obstacle = enu2lla([params.obstacleMin((1 + (ii - 1) * 3):(ii * 3)); params.obstacleMax((1 + (ii - 1) * 3):(ii * 3))], lla0, 'flat');
|
||||
geoplot3(gf, [obstacle(1, 1), obstacle(2, 1), obstacle(2, 1), obstacle(1, 1), obstacle(1, 1)], [obstacle(1, 2), obstacle(1, 2), obstacle(2, 2), obstacle(2, 2), obstacle(1, 2)], repmat(obstacle(1, 3) + altOffset, 1, 5), 'LineWidth', 3, 'Color', 'r');
|
||||
geoplot3(gf, [obstacle(1, 1), obstacle(2, 1), obstacle(2, 1), obstacle(1, 1), obstacle(1, 1)], [obstacle(1, 2), obstacle(1, 2), obstacle(2, 2), obstacle(2, 2), obstacle(1, 2)], repmat(obstacle(2, 3) + altOffset, 1, 5), 'LineWidth', 3, 'Color', 'r');
|
||||
geoplot3(gf, [obstacle(1, 1), obstacle(1, 1)], [obstacle(1, 2), obstacle(1, 2)], obstacle(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'r');
|
||||
geoplot3(gf, [obstacle(2, 1), obstacle(2, 1)], [obstacle(1, 2), obstacle(1, 2)], obstacle(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'r');
|
||||
geoplot3(gf, [obstacle(1, 1), obstacle(1, 1)], [obstacle(2, 2), obstacle(2, 2)], obstacle(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'r');
|
||||
geoplot3(gf, [obstacle(2, 1), obstacle(2, 1)], [obstacle(2, 2), obstacle(2, 2)], obstacle(:, 3) + altOffset, 'LineWidth', 3, 'Color', 'r');
|
||||
end
|
||||
|
||||
% finish
|
||||
hold(gf, "off");
|
||||
end
|
||||
75
aerpaw/results/plotRadioLogs.m
Normal file
75
aerpaw/results/plotRadioLogs.m
Normal file
@@ -0,0 +1,75 @@
|
||||
function f = plotRadioLogs(resultsPath)
|
||||
arguments (Input)
|
||||
resultsPath (1, 1) string;
|
||||
end
|
||||
|
||||
arguments (Output)
|
||||
f (1, 1) matlab.ui.Figure;
|
||||
end
|
||||
|
||||
logDirs = dir(resultsPath);
|
||||
logDirs = logDirs(3:end);
|
||||
logDirs = logDirs([logDirs.isdir] == 1);
|
||||
|
||||
R = cell(size(logDirs));
|
||||
for ii = 1:size(logDirs, 1)
|
||||
R{ii} = readRadioLogs(fullfile(logDirs(ii).folder, logDirs(ii).name));
|
||||
end
|
||||
|
||||
% Discard rows where any non-NaN dB metric is below -200 (sentinel values)
|
||||
for ii = 1:numel(R)
|
||||
snr = R{ii}.SNR;
|
||||
pwr = R{ii}.Power;
|
||||
bad = (snr < -200 & ~isnan(snr)) | (pwr < -200 & ~isnan(pwr));
|
||||
R{ii}(bad, :) = [];
|
||||
end
|
||||
|
||||
% Build legend labels and color map for up to 4 UAVs
|
||||
nUAV = numel(R);
|
||||
colors = lines(nUAV * nUAV);
|
||||
styles = ["-o", "-s", "-^", "-d", "-v", "-p", "-h", "-<", "->", "-+", "-x", "-*"];
|
||||
|
||||
metricNames = ["SNR", "Power", "Quality"];
|
||||
yLabels = ["SNR (dB)", "Power (dB)", "Quality"];
|
||||
|
||||
f = figure;
|
||||
tl = tiledlayout(3, 1, 'TileSpacing', 'compact', 'Padding', 'compact');
|
||||
|
||||
for mi = 1:numel(metricNames)
|
||||
ax = nexttile(tl);
|
||||
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, :);
|
||||
vals = rows.(metricNames(mi));
|
||||
|
||||
% Skip if all NaN for this metric
|
||||
if all(isnan(vals))
|
||||
continue;
|
||||
end
|
||||
|
||||
si = mod(ci - 1, numel(styles)) + 1;
|
||||
plot(ax, rows.Timestamp, vals, styles(si), ...
|
||||
'Color', colors(ci, :), 'MarkerSize', 3, 'LineWidth', 1);
|
||||
legendEntries(end+1) = sprintf("TX %d → RX %d", txID, tbl.RxUAVID(1)); %#ok<AGROW>
|
||||
ci = ci + 1;
|
||||
end
|
||||
end
|
||||
|
||||
ylabel(ax, yLabels(mi));
|
||||
if mi == numel(metricNames)
|
||||
xlabel(ax, 'Time');
|
||||
end
|
||||
legend(ax, legendEntries, 'Location', 'best');
|
||||
hold(ax, 'off');
|
||||
end
|
||||
|
||||
title(tl, 'Radio Channel Metrics');
|
||||
end
|
||||
8
aerpaw/results/plotResults.m
Normal file
8
aerpaw/results/plotResults.m
Normal file
@@ -0,0 +1,8 @@
|
||||
% Define path to run results copied from AERPAW platform
|
||||
resultsPath = fullfile(matlab.project.rootProject().RootFolder, "sandbox", "t1");
|
||||
|
||||
% Plot GPS logged data and scenario information (domain, objective, obstacles)
|
||||
uif = plotGpsLogs(resultsPath);
|
||||
|
||||
% Plot radio statistics
|
||||
f = plotRadioLogs(resultsPath);
|
||||
@@ -1,33 +0,0 @@
|
||||
function [G] = readGpsCsv(csvPath)
|
||||
arguments (Input)
|
||||
csvPath (1, 1) string {isfile(csvPath)};
|
||||
end
|
||||
|
||||
arguments (Output)
|
||||
G (:, 10) table;
|
||||
end
|
||||
|
||||
G = readtable(csvPath, "ReadVariableNames", false);
|
||||
|
||||
% first column is just index, meaningless, toss it
|
||||
G = G(:, 2:end);
|
||||
|
||||
% switch to the correct LLA convention (lat, lon, alt)
|
||||
tmp = G(:, 2);
|
||||
G(:, 2) = G(:, 1);
|
||||
G(:, 1) = tmp;
|
||||
|
||||
% Split pitch, yaw, roll data read in as one string per timestep into separate columns
|
||||
PYR = cell2mat(cellfun(@(x) str2num(strip(strip(x, "left", "("), "right", ")")), table2cell(G(:, 5)), "UniformOutput", false)); %#ok<ST2NM>
|
||||
% Reinsert to original table
|
||||
G = [G(:, 1:3), table(PYR(:, 1), VariableNames="Pitch"), table(PYR(:, 2), VariableNames="Yaw"), table(PYR(:, 3), VariableNames="Roll"), G(:, 6:end)];
|
||||
|
||||
% Clean up datetime entry
|
||||
G = [table(datetime(G{:,8}, "InputFormat","yyyy-MM-dd HH:mm:ss.SSS", "TimeZone","America/New_York")), G(:, [1:7, 9:10])];
|
||||
|
||||
% Fix variable names
|
||||
G.Properties.VariableNames = ["Timestamp", "Latitude", "Longitude", "Altitude", "Pitch", "Yaw", "Roll", "Voltage", "GPS Status", "Satellites"];
|
||||
G.Properties.VariableTypes = ["datetime", "double", "double", "double", "double", "double", "double", "double", "double", "double"];
|
||||
G.Properties.VariableUnits = ["yyyy-MM-dd HH:mm:ss.SSS (UTC+5)", "deg", "deg", "m", "deg", "deg", "deg", "Volts", "", ""];
|
||||
|
||||
end
|
||||
32
aerpaw/results/readGpsLogs.m
Normal file
32
aerpaw/results/readGpsLogs.m
Normal file
@@ -0,0 +1,32 @@
|
||||
function [G] = readGpsLogs(logPath)
|
||||
arguments (Input)
|
||||
logPath (1, 1) string {isfile(logPath)};
|
||||
end
|
||||
|
||||
arguments (Output)
|
||||
G (:, 10) table;
|
||||
end
|
||||
|
||||
G = readtable(logPath, "ReadVariableNames", false);
|
||||
|
||||
% first column is just index, meaningless, toss it
|
||||
G = G(:, 2:end);
|
||||
|
||||
% switch to the correct LLA convention (lat, lon, alt)
|
||||
tmp = G(:, 2);
|
||||
G(:, 2) = G(:, 1);
|
||||
G(:, 1) = tmp;
|
||||
|
||||
% Split pitch, yaw, roll data read in as one string per timestep into separate columns
|
||||
PYR = cell2mat(cellfun(@(x) str2num(strip(strip(x, "left", "("), "right", ")")), table2cell(G(:, 5)), "UniformOutput", false)); %#ok<ST2NM>
|
||||
% Reinsert to original table
|
||||
G = [G(:, 1:3), table(PYR(:, 1), VariableNames="Pitch"), table(PYR(:, 2), VariableNames="Yaw"), table(PYR(:, 3), VariableNames="Roll"), G(:, 6:end)];
|
||||
|
||||
% Clean up datetime entry
|
||||
G = [table(datetime(G{:,8}, "InputFormat","yyyy-MM-dd HH:mm:ss.SSS", "TimeZone","America/New_York")), G(:, [1:7, 9:10])];
|
||||
|
||||
% Fix variable names
|
||||
G.Properties.VariableNames = ["Timestamp", "Latitude", "Longitude", "Altitude", "Pitch", "Yaw", "Roll", "Voltage", "GPS Status", "Satellites"];
|
||||
G.Properties.VariableTypes = ["datetime", "double", "double", "double", "double", "double", "double", "double", "double", "double"];
|
||||
G.Properties.VariableUnits = ["yyyy-MM-dd HH:mm:ss.SSS (UTC+5)", "deg", "deg", "m", "deg", "deg", "deg", "Volts", "", ""];
|
||||
end
|
||||
65
aerpaw/results/readRadioLogs.m
Normal file
65
aerpaw/results/readRadioLogs.m
Normal file
@@ -0,0 +1,65 @@
|
||||
function R = readRadioLogs(logPath)
|
||||
arguments (Input)
|
||||
logPath (1, 1) string {isfolder(logPath)};
|
||||
end
|
||||
|
||||
arguments (Output)
|
||||
R (:, 6) table;
|
||||
end
|
||||
|
||||
% Extract receiving UAV ID from directory name (e.g. "uav0_..." → 0)
|
||||
[~, dirName] = fileparts(logPath);
|
||||
rxID = int32(sscanf(dirName, 'uav%d'));
|
||||
|
||||
metrics = ["quality", "snr", "power"];
|
||||
logs = dir(logPath);
|
||||
logs = logs(endsWith({logs(:).name}, metrics + "_log.txt"));
|
||||
|
||||
R = table(datetime.empty(0,1), zeros(0,1,'int32'), zeros(0,1,'int32'), zeros(0,1), zeros(0,1), zeros(0,1), ...
|
||||
'VariableNames', ["Timestamp", "TxUAVID", "RxUAVID", "SNR", "Power", "Quality"]);
|
||||
|
||||
for ii = 1:numel(logs)
|
||||
filepath = fullfile(logs(ii).folder, logs(ii).name);
|
||||
|
||||
% Determine which metric this file contains
|
||||
metric = "";
|
||||
for m = 1:numel(metrics)
|
||||
if endsWith(logs(ii).name, metrics(m) + "_log.txt")
|
||||
metric = metrics(m);
|
||||
break;
|
||||
end
|
||||
end
|
||||
|
||||
fid = fopen(filepath, 'r');
|
||||
% Skip 3 lines: 2 junk (tail errors) + 1 header (tx_uav_id,value)
|
||||
for k = 1:3
|
||||
fgetl(fid);
|
||||
end
|
||||
data = textscan(fid, '[%26c] %d,%f');
|
||||
fclose(fid);
|
||||
|
||||
ts = datetime(data{1}, 'InputFormat', 'yyyy-MM-dd HH:mm:ss.SSSSSS');
|
||||
txId = int32(data{2});
|
||||
val = data{3};
|
||||
|
||||
n = numel(ts);
|
||||
t = table(ts, txId, repmat(rxID, n, 1), NaN(n,1), NaN(n,1), NaN(n,1), ...
|
||||
'VariableNames', ["Timestamp", "TxUAVID", "RxUAVID", "SNR", "Power", "Quality"]);
|
||||
|
||||
switch metric
|
||||
case "snr", t.SNR = val;
|
||||
case "power", t.Power = val;
|
||||
case "quality", t.Quality = val;
|
||||
end
|
||||
|
||||
R = [R; t]; %#ok<AGROW>
|
||||
end
|
||||
|
||||
R = sortrows(R, "Timestamp");
|
||||
|
||||
% Remove rows during defined guard period between TDM shifts
|
||||
R(R.TxUAVID == -1, :) = [];
|
||||
|
||||
% Remove self-reception rows (TX == RX)
|
||||
R(R.TxUAVID == R.RxUAVID, :) = [];
|
||||
end
|
||||
Reference in New Issue
Block a user