added sensor tilting and rf sensor sim test cases
This commit is contained in:
+1
-1
@@ -35,5 +35,5 @@ function obj = initialize(obj, pos, collisionGeometry, sensorModel, comRange, ma
|
|||||||
|
|
||||||
% Initialize FOV cone
|
% Initialize FOV cone
|
||||||
obj.fovGeometry = cone;
|
obj.fovGeometry = cone;
|
||||||
obj.fovGeometry = obj.fovGeometry.initialize([obj.pos(1:3)], tand(obj.sensorModel.alphaTilt) * obj.pos(3), obj.pos(3), REGION_TYPE.FOV, sprintf("%s FOV", obj.label));
|
obj.fovGeometry = obj.fovGeometry.initialize([obj.pos(1:3)], tand(obj.sensorModel.halfAngle()) * obj.pos(3), obj.pos(3), REGION_TYPE.FOV, sprintf("%s FOV", obj.label), obj.sensorModel.tilt, obj.sensorModel.azimuth);
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
function value = halfAngle(obj)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, "rfSensor")};
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
value (1, 1) double;
|
||||||
|
end
|
||||||
|
% Sweep angular offset from boresight by evaluating transmitterGain at
|
||||||
|
% (obj.tilt + dtheta, obj.azimuth). The cosine difference identity guarantees
|
||||||
|
% the resulting angular offset from boresight equals dtheta exactly,
|
||||||
|
% independent of the actual pointing direction.
|
||||||
|
dtheta = (0:0.1:179.9)';
|
||||||
|
gain = obj.transmitterGain(obj.tilt + dtheta, obj.azimuth * ones(size(dtheta)));
|
||||||
|
target = gain(1) - 3;
|
||||||
|
idx = find(gain <= target, 1);
|
||||||
|
if isempty(idx) || idx == 1
|
||||||
|
value = dtheta(end);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
% Linear interpolation between bracketing samples
|
||||||
|
value = dtheta(idx-1) + (target - gain(idx-1)) * ...
|
||||||
|
(dtheta(idx) - dtheta(idx-1)) / (gain(idx) - gain(idx-1));
|
||||||
|
end
|
||||||
@@ -22,6 +22,7 @@ classdef rfSensor
|
|||||||
[obj] = initialize(obj, txPower, bandwidth, centerFreq, rxGain); % initialize sensor, define parameters
|
[obj] = initialize(obj, txPower, bandwidth, centerFreq, rxGain); % initialize sensor, define parameters
|
||||||
[SINR, SNR, obj, otherSensors] = sensorPerformance(obj, agentPos, targetPos, otherSensorsPos, otherSensors); % determine sensor performance for a given single sensor and target geometry
|
[SINR, SNR, obj, otherSensors] = sensorPerformance(obj, agentPos, targetPos, otherSensorsPos, otherSensors); % determine sensor performance for a given single sensor and target geometry
|
||||||
[d, t, a] = computePointToPoints(obj, agentPos, targetPos);
|
[d, t, a] = computePointToPoints(obj, agentPos, targetPos);
|
||||||
|
[value] = halfAngle(obj); % tilt angle (deg) at which sensor performance is halved
|
||||||
[f] = plotParameters(obj); % debug, plot sensor response as a function of distance and tilt angle
|
[f] = plotParameters(obj); % debug, plot sensor response as a function of distance and tilt angle
|
||||||
[f] = plotPerformance(obj, altitude, otherSensorsPos, otherSensors); % debug, plot SNR or SINR ground heatmap for a given geometry
|
[f] = plotPerformance(obj, altitude, otherSensorsPos, otherSensors); % debug, plot SNR or SINR ground heatmap for a given geometry
|
||||||
obj = clearRssCache(obj);
|
obj = clearRssCache(obj);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ function [SINR, SNR, obj, otherSensors] = sensorPerformance(obj, agentPos, targe
|
|||||||
[d, t, a] = obj.computePointToPoints(agentPos, targetPos);
|
[d, t, a] = obj.computePointToPoints(agentPos, targetPos);
|
||||||
|
|
||||||
if isempty(obj.rssCache)
|
if isempty(obj.rssCache)
|
||||||
obj.rssCache = 10 .^ (0.1 .* obj.RSS(d, t, a));
|
obj.rssCache = 1e-3 .* 10 .^ (0.1 .* obj.RSS(d, t, a)); % dBm → W
|
||||||
end
|
end
|
||||||
S = obj.rssCache;
|
S = obj.rssCache;
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ function [SINR, SNR, obj, otherSensors] = sensorPerformance(obj, agentPos, targe
|
|||||||
for ii = 1:size(otherSensors, 1)
|
for ii = 1:size(otherSensors, 1)
|
||||||
if isempty(otherSensors{ii}.rssCache)
|
if isempty(otherSensors{ii}.rssCache)
|
||||||
[d_other, t_other, a_other] = otherSensors{ii}.computePointToPoints(otherSensorsPos(ii, 1:3), targetPos);
|
[d_other, t_other, a_other] = otherSensors{ii}.computePointToPoints(otherSensorsPos(ii, 1:3), targetPos);
|
||||||
otherSensors{ii}.rssCache = 10 .^ (0.1 .* otherSensors{ii}.RSS(d_other, t_other, a_other));
|
otherSensors{ii}.rssCache = 1e-3 .* 10 .^ (0.1 .* otherSensors{ii}.RSS(d_other, t_other, a_other)); % dBm → W
|
||||||
end
|
end
|
||||||
I = I + otherSensors{ii}.rssCache;
|
I = I + otherSensors{ii}.rssCache;
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ function value = transmitterGain(obj, t, a)
|
|||||||
error("t and a must be the same size");
|
error("t and a must be the same size");
|
||||||
end
|
end
|
||||||
|
|
||||||
n = 4; % beamwidth exponent (higher = narrower beam)
|
n = 6; % beamwidth exponent (higher = narrower beam)
|
||||||
|
|
||||||
% Angular offset from boresight via spherical law of cosines
|
% Angular offset from boresight via spherical law of cosines
|
||||||
% Convention: t=0° nadir, t=90° horizon; a=0° +y, a=90° +x
|
% Convention: t=0° nadir, t=90° horizon; a=0° +y, a=90° +x
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
function value = halfAngle(obj)
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, "sigmoidSensor")};
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
value (1, 1) double;
|
||||||
|
end
|
||||||
|
value = obj.alphaTilt;
|
||||||
|
end
|
||||||
@@ -1,17 +1,24 @@
|
|||||||
function obj = initialize(obj, alphaDist, betaDist, alphaTilt, betaTilt)
|
function obj = initialize(obj, alphaDist, betaDist, alphaTilt, betaTilt, tilt, azimuth)
|
||||||
arguments (Input)
|
arguments (Input)
|
||||||
obj (1, 1) {mustBeA(obj, "sigmoidSensor")}
|
obj (1, 1) {mustBeA(obj, "sigmoidSensor")}
|
||||||
alphaDist (1, 1) double;
|
alphaDist (1, 1) double;
|
||||||
betaDist (1, 1) double;
|
betaDist (1, 1) double;
|
||||||
alphaTilt (1, 1) double;
|
alphaTilt (1, 1) double;
|
||||||
betaTilt (1, 1) double;
|
betaTilt (1, 1) double;
|
||||||
|
tilt (1, 1) double = 0;
|
||||||
|
azimuth (1, 1) double = 0;
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
obj (1, 1) {mustBeA(obj, "sigmoidSensor")}
|
obj (1, 1) {mustBeA(obj, "sigmoidSensor")}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
% Sensor performance parameters
|
||||||
obj.alphaDist = alphaDist;
|
obj.alphaDist = alphaDist;
|
||||||
obj.betaDist = betaDist;
|
obj.betaDist = betaDist;
|
||||||
obj.alphaTilt = alphaTilt;
|
obj.alphaTilt = alphaTilt;
|
||||||
obj.betaTilt = betaTilt;
|
obj.betaTilt = betaTilt;
|
||||||
|
|
||||||
|
% Sensor pointing parameters
|
||||||
|
obj.tilt = tilt;
|
||||||
|
obj.azimuth = azimuth;
|
||||||
end
|
end
|
||||||
@@ -8,16 +8,20 @@ function value = sensorPerformance(obj, agentPos, targetPos)
|
|||||||
value (:, 1) double;
|
value (:, 1) double;
|
||||||
end
|
end
|
||||||
|
|
||||||
% compute direct distance and distance projected onto the ground
|
% Unit vectors from agent to each target
|
||||||
d = vecnorm(agentPos - targetPos, 2, 2); % distance from sensor to target
|
diffs = targetPos - agentPos;
|
||||||
x = vecnorm(agentPos(1:2) - targetPos(:, 1:2), 2, 2); % distance from sensor nadir to target nadir (i.e. distance ignoring height difference)
|
d = vecnorm(diffs, 2, 2);
|
||||||
|
dirs = diffs ./ d;
|
||||||
|
|
||||||
% compute tilt angle
|
% Boresight unit vector: tilt=0 → nadir [0,0,-1]; azimuth 0=+Y, 90=+X clockwise
|
||||||
tiltAngle = (180 - atan2d(x, targetPos(:, 3) - agentPos(3))); % degrees
|
boresight = [sind(obj.tilt)*sind(obj.azimuth), sind(obj.tilt)*cosd(obj.azimuth), -cosd(obj.tilt)];
|
||||||
|
|
||||||
|
% Angular offset from boresight to each target direction
|
||||||
|
angularOffset = acosd(dirs * boresight');
|
||||||
|
|
||||||
% Membership functions
|
% Membership functions
|
||||||
mu_d = obj.distanceMembership(d);
|
mu_d = obj.distanceMembership(d);
|
||||||
mu_t = obj.tiltMembership(tiltAngle);
|
mu_t = obj.tiltMembership(angularOffset);
|
||||||
|
|
||||||
value = mu_d .* mu_t; % assume pan membership is always 1
|
value = mu_d .* mu_t; % assume pan membership is always 1
|
||||||
end
|
end
|
||||||
@@ -12,8 +12,9 @@ classdef sigmoidSensor
|
|||||||
end
|
end
|
||||||
|
|
||||||
methods (Access = public)
|
methods (Access = public)
|
||||||
[obj] = initialize(obj, alphaDist, betaDist, alphaTilt, betaTilt); % initialize sensor, define parameters
|
[obj] = initialize(obj, alphaDist, betaDist, alphaTilt, betaTilt, tilt, azimuth); % initialize sensor, define parameters
|
||||||
[value] = sensorPerformance(obj, agentPos, targetPos); % determine sensor performance for a given single sensor and target geometry
|
[value] = sensorPerformance(obj, agentPos, targetPos); % determine sensor performance for a given single sensor and target geometry
|
||||||
|
[value] = halfAngle(obj); % tilt angle (deg) at which sensor performance is halved
|
||||||
[f] = plotParameters(obj); % debug, plot sensor response as a function of distance and tilt angle
|
[f] = plotParameters(obj); % debug, plot sensor response as a function of distance and tilt angle
|
||||||
end
|
end
|
||||||
methods (Access = private)
|
methods (Access = private)
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ classdef cone
|
|||||||
label = "";
|
label = "";
|
||||||
|
|
||||||
% Spatial
|
% Spatial
|
||||||
center = NaN;
|
center = NaN;
|
||||||
radius = NaN;
|
radius = NaN;
|
||||||
height = NaN;
|
height = NaN;
|
||||||
|
tilt = 0; % degrees, 0=nadir 90=horizon
|
||||||
|
azimuth = 0; % degrees, 0=+Y 90=+X clockwise
|
||||||
|
|
||||||
% Plotting
|
% Plotting
|
||||||
surface;
|
surface;
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
function obj = initialize(obj, center, radius, height, tag, label)
|
function obj = initialize(obj, center, radius, height, tag, label, tilt, azimuth)
|
||||||
arguments (Input)
|
arguments (Input)
|
||||||
obj (1, 1) {mustBeA(obj, "cone")};
|
obj (1, 1) {mustBeA(obj, "cone")};
|
||||||
center (1, 3) double;
|
center (1, 3) double;
|
||||||
radius (1, 1) double;
|
radius (1, 1) double;
|
||||||
height (1, 1) double;
|
height (1, 1) double;
|
||||||
tag (1, 1) REGION_TYPE = REGION_TYPE.INVALID;
|
tag (1, 1) REGION_TYPE = REGION_TYPE.INVALID;
|
||||||
label (1, 1) string = "";
|
label (1, 1) string = "";
|
||||||
|
tilt (1, 1) double = 0;
|
||||||
|
azimuth (1, 1) double = 0;
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
obj (1, 1) {mustBeA(obj, "cone")};
|
obj (1, 1) {mustBeA(obj, "cone")};
|
||||||
end
|
end
|
||||||
|
|
||||||
obj.center = center;
|
obj.center = center;
|
||||||
obj.radius = radius;
|
obj.radius = radius;
|
||||||
obj.height = height;
|
obj.height = height;
|
||||||
obj.tag = tag;
|
obj.tag = tag;
|
||||||
obj.label = label;
|
obj.label = label;
|
||||||
|
obj.tilt = tilt;
|
||||||
|
obj.azimuth = azimuth;
|
||||||
end
|
end
|
||||||
@@ -21,6 +21,17 @@ function [obj, f] = plot(obj, ind, f, maxAlt)
|
|||||||
% Scale to match height
|
% Scale to match height
|
||||||
Z = Z * maxAlt;
|
Z = Z * maxAlt;
|
||||||
|
|
||||||
|
% Rotate mesh around apex to match boresight tilt and azimuth.
|
||||||
|
% Apex sits at [0, 0, maxAlt] before center translation.
|
||||||
|
% Convention: tilt 0=nadir, 90=horizon; azimuth 0=+Y, 90=+X, clockwise.
|
||||||
|
Ry = [cosd(obj.tilt), 0, -sind(obj.tilt); 0, 1, 0; sind(obj.tilt), 0, cosd(obj.tilt)];
|
||||||
|
Rz = [sind(obj.azimuth), -cosd(obj.azimuth), 0; cosd(obj.azimuth), sind(obj.azimuth), 0; 0, 0, 1];
|
||||||
|
R = Rz * Ry;
|
||||||
|
pts = R * [X(:)'; Y(:)'; Z(:)' - maxAlt];
|
||||||
|
X = reshape(pts(1, :), size(X));
|
||||||
|
Y = reshape(pts(2, :), size(Y));
|
||||||
|
Z = reshape(pts(3, :) + maxAlt, size(Z));
|
||||||
|
|
||||||
% Move to center location
|
% Move to center location
|
||||||
X = X + obj.center(1);
|
X = X + obj.center(1);
|
||||||
Y = Y + obj.center(2);
|
Y = Y + obj.center(2);
|
||||||
|
|||||||
+64
-1
@@ -456,7 +456,70 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makePlots, tc.makeVideo, tc.useDoubleIntegrator, tc.dampingCoeff, tc.useFixedTopology);
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makePlots, tc.makeVideo, tc.useDoubleIntegrator, tc.dampingCoeff, tc.useFixedTopology);
|
||||||
|
|
||||||
% Run the simulation
|
% Run the simulation
|
||||||
tc.testClass = tc.testClass.run();end
|
tc.testClass = tc.testClass.run();
|
||||||
|
end
|
||||||
|
function test_single_agent_gradient_ascent_tilted(tc)
|
||||||
|
% make basic domain
|
||||||
|
tc.minDimension = 10; % domain size
|
||||||
|
tc.domain = tc.domain.initialize([zeros(1, 3);tc.minDimension* ones(1, 3)], REGION_TYPE.DOMAIN, "Domain");
|
||||||
|
|
||||||
|
% make basic sensing objective
|
||||||
|
tc.domain.objective = tc.domain.objective.initialize(objectiveFunctionWrapper([7, 6]), tc.domain, tc.discretizationStep, tc.protectedRange, 1e-6, [7, 6]);
|
||||||
|
|
||||||
|
% Initialize agent collision geometry
|
||||||
|
tc.agents = {agent};
|
||||||
|
geometry1 = spherical;
|
||||||
|
geometry1 = geometry1.initialize([tc.domain.center(1:2)-tc.domain.dimensions(1)/4, 3], tc.collisionRanges(1), REGION_TYPE.COLLISION);
|
||||||
|
|
||||||
|
% Initialize agent sensor model with fixed parameters
|
||||||
|
tc.sensor = tc.sensor.initialize(tc.minDimension / 2, 3, 20, 3, 25, 155);
|
||||||
|
|
||||||
|
% Initialize agents
|
||||||
|
tc.maxIter = 75;
|
||||||
|
tc.agents{1} = tc.agents{1}.initialize([tc.domain.center(1:2)-tc.domain.dimensions(1)/4, 3], geometry1, tc.sensor, tc.commsRanges(1), tc.maxIter, tc.initialStepSize);
|
||||||
|
|
||||||
|
% Initialize the simulation
|
||||||
|
tc.obstacles = cell(0, 1);
|
||||||
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makePlots, tc.makeVideo, tc.useDoubleIntegrator, tc.dampingCoeff, tc.useFixedTopology);
|
||||||
|
|
||||||
|
% Run the simulation
|
||||||
|
tc.testClass = tc.testClass.run();
|
||||||
|
end
|
||||||
|
function test_single_agent_gradient_ascent_tilted_RF_sensor(tc)
|
||||||
|
% make basic domain
|
||||||
|
tc.minDimension = 10; % domain size
|
||||||
|
tc.domain = tc.domain.initialize([zeros(1, 3);tc.minDimension* ones(1, 3)], REGION_TYPE.DOMAIN, "Domain");
|
||||||
|
|
||||||
|
% make basic sensing objective
|
||||||
|
minimumSINR = 50; % (dB)
|
||||||
|
tc.domain.objective = tc.domain.objective.initialize(objectiveFunctionWrapper([7, 6]), tc.domain, tc.discretizationStep, tc.protectedRange, minimumSINR, [7, 6]);
|
||||||
|
|
||||||
|
% Initialize agent collision geometry
|
||||||
|
tc.agents = {agent};
|
||||||
|
geometry1 = spherical;
|
||||||
|
geometry1 = geometry1.initialize([tc.domain.center(1:2)-tc.domain.dimensions(1)/4, 3], tc.collisionRanges(1), REGION_TYPE.COLLISION);
|
||||||
|
|
||||||
|
% Initialize agent sensor model with fixed parameters
|
||||||
|
P_TX = 1e-3; % Transmit power (Watts)
|
||||||
|
BW = 20e6; % Bandwidth (Hz)
|
||||||
|
f_c = 2e9; % Center frequency (Hz)
|
||||||
|
G_RX_dBi = 3; % Receiving Antenna Gain (dBi)
|
||||||
|
|
||||||
|
tc.sensor = rfSensor;
|
||||||
|
tc.sensor = tc.sensor.initialize(P_TX, BW, f_c, G_RX_dBi, 45, 45);
|
||||||
|
|
||||||
|
% Initialize agents
|
||||||
|
tc.maxIter = 75;
|
||||||
|
tc.agents{1} = tc.agents{1}.initialize([tc.domain.center(1:2)-tc.domain.dimensions(1)/4, 3], geometry1, tc.sensor, tc.commsRanges(1), tc.maxIter, tc.initialStepSize);
|
||||||
|
|
||||||
|
% Initialize the simulation
|
||||||
|
tc.obstacles = cell(0, 1);
|
||||||
|
tc.minAlt = 0.5;
|
||||||
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makePlots, tc.makeVideo, tc.useDoubleIntegrator, tc.dampingCoeff, tc.useFixedTopology);
|
||||||
|
|
||||||
|
% Run the simulation
|
||||||
|
tc.testClass = tc.testClass.run();
|
||||||
|
end
|
||||||
function test_collision_avoidance(tc)
|
function test_collision_avoidance(tc)
|
||||||
% No obstacles
|
% No obstacles
|
||||||
% Fixed agent initial conditions
|
% Fixed agent initial conditions
|
||||||
|
|||||||
Reference in New Issue
Block a user