full simulation with RF sensors

This commit is contained in:
2026-05-08 13:07:03 -07:00
parent 030dd30c7d
commit 78f9dcd579
12 changed files with 312 additions and 87 deletions
+18 -9
View File
@@ -1,15 +1,24 @@
function value = RSS(obj, d, t, a)
function value = RSS(obj, d, dx, dy, dz)
arguments (Input)
obj (1, 1) {mustBeA(obj, "rfSensor")};
d (:, 1) double; % distance from agent to target
t (:, 1) double; % LOS tilt angle
a (:, 1) double; % LOS azimuth angle
d (:, 1) double;
dx (:, 1) double;
dy (:, 1) double;
dz (:, 1) double;
end
arguments (Output)
value (:, 1) double
end
assert(size(d, 1) == size(t, 1), "Mismatch in number of distances (%d) and tilts (%d) provided", size(d, 1), size(t, 1));
% RSS (dBm) = TX Power (dBm) + TX Antenna Gain (dBi) + RX Antenna Gain (dBi) - Path Loss (dB)
value = obj.P_TX_dBm + obj.transmitterGain(t, a) + obj.G_RX_dBi - obj.pathLoss(d);
end
% Boresight unit vector: [st*sa, st*ca, -ct]
% Target direction unit vector: [dx, dy, dz] / d
% cos_theta = dot product of the two, computed without per-point trig.
st = sind(obj.tilt);
ct = cosd(obj.tilt);
sa = sind(obj.azimuth);
ca = cosd(obj.azimuth);
cos_theta = (st .* (dx .* sa + dy .* ca) - ct .* dz) ./ max(d, eps);
cos_theta = max(-1, min(1, cos_theta));
theta = acosd(cos_theta);
gain = 10 .* obj.beamwidthExponent .* log10((1 + cosd(theta)) ./ 2);
value = obj.P_TX_dBm + gain + obj.G_RX_dBi - obj.pathLoss(d);
end
+6 -24
View File
@@ -1,24 +1,6 @@
function [d, t, a] = computePointToPoints(obj, agentPos, targetPos)
arguments (Input)
obj (1, 1) {mustBeA(obj, "rfSensor")};
agentPos (1, 3) double;
targetPos (:, 3) double;
end
arguments (Output)
d (:, 1) double;
t (:, 1) double;
a (:, 1) double;
end
% distance from sensor to target
d = vecnorm(agentPos - targetPos, 2, 2);
% distance from sensor nadir to target nadir (i.e. distance ignoring altitude)
x = vecnorm(agentPos(1:2) - targetPos(:, 1:2), 2, 2);
% tilt angle (degrees) (0 (nadir), 180 (zenith))
t = (180 - atan2d(x, targetPos(:, 3) - agentPos(3)));
% azimuth angle (degrees) (0 (+y) clockwise to 360)
a = mod(atan2d(targetPos(:,1) - agentPos(1), targetPos(:,2) - agentPos(2)), 360);
end
function [d, dx, dy, dz] = computePointToPoints(~, agentPos, targetPos)
dx = targetPos(:,1) - agentPos(1);
dy = targetPos(:,2) - agentPos(2);
dz = targetPos(:,3) - agentPos(3);
d = sqrt(dx.^2 + dy.^2 + dz.^2);
end
+3 -3
View File
@@ -15,17 +15,17 @@ classdef rfSensor
P_TX_dBm = NaN; % Transmit power (dBm)
N = NaN; % Thermal noise
% Cached state (per timestep)
rssCache (:,1) double = double.empty(0,1); % linear-scale RSS to last ground targets grid
end
properties (Access = public)
tilt = NaN; % Antenna boresight tilt (deg): 0=nadir, 90=horizon
azimuth = NaN; % Antenna boresight azimuth (deg): 0=+y, 90=+x, 180=-y, 270=-x
rssCache (:,1) double = double.empty(0,1); % linear-scale RSS to last ground targets grid
end
methods (Access = public)
[obj] = initialize(obj, txPower, bandwidth, centerFreq, rxGain, beamwidthExponent, tilt, azimuth); % 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
[d, t, a] = computePointToPoints(obj, agentPos, targetPos);
[d, dx, dy, dz] = 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] = plotPerformance(obj, altitude, otherSensorsPos, otherSensors); % debug, plot SNR or SINR ground heatmap for a given geometry
@@ -33,7 +33,7 @@ classdef rfSensor
obj = clearRssCache(obj);
end
methods (Access = private)
x = RSS(obj, d, t, a); % Received signal strength (function of distance and tilt angle)
x = RSS(obj, d, dx, dy, dz); % Received signal strength (function of distance and tilt angle)
G_TX_dB = transmitterGain(obj, t, a); % Antenna gain for a given TX/RX pair
L_FSPL_dB = pathLoss(obj, d); % Free space path loss for a given TX/RX pair
end
+6 -7
View File
@@ -14,22 +14,21 @@ function [SINR, SNR, obj, otherSensors] = sensorPerformance(obj, agentPos, targe
end
assert(size(otherSensorsPos, 1) == size(otherSensors, 1), "Mismatch in number of other sensor positions (%d) and number of other sensors (%d) provided", size(otherSensorsPos, 1), size(otherSensors, 1));
[d, t, a] = obj.computePointToPoints(agentPos, targetPos);
if isempty(obj.rssCache)
obj.rssCache = 1e-3 .* 10 .^ (0.1 .* obj.RSS(d, t, a)); % dBm W
[d, dx, dy, dz] = obj.computePointToPoints(agentPos, targetPos);
obj.rssCache = 1e-3 .* 10 .^ (0.1 .* obj.RSS(d, dx, dy, dz)); % dBm W
end
S = obj.rssCache;
I = zeros(size(d));
I = zeros(size(S));
for ii = 1:size(otherSensors, 1)
if isempty(otherSensors{ii}.rssCache)
[d_other, t_other, a_other] = otherSensors{ii}.computePointToPoints(otherSensorsPos(ii, 1:3), targetPos);
otherSensors{ii}.rssCache = 1e-3 .* 10 .^ (0.1 .* otherSensors{ii}.RSS(d_other, t_other, a_other)); % dBm W
[d_o, dx_o, dy_o, dz_o] = otherSensors{ii}.computePointToPoints(otherSensorsPos(ii, 1:3), targetPos);
otherSensors{ii}.rssCache = 1e-3 .* 10 .^ (0.1 .* otherSensors{ii}.RSS(d_o, dx_o, dy_o, dz_o)); % dBm W
end
I = I + otherSensors{ii}.rssCache;
end
SINR = 10*log10(S ./ (I + obj.N));
SNR = 10*log10(S ./ obj.N);
SNR = 10*log10(S ./ obj.N);
end