diff --git a/@rfSensor/initialize.m b/@rfSensor/initialize.m index 9dfb966..28c584b 100644 --- a/@rfSensor/initialize.m +++ b/@rfSensor/initialize.m @@ -1,10 +1,12 @@ -function obj = initialize(obj, txPower, bandwidth, centerFreq, rxGain_dBi) +function obj = initialize(obj, txPower, bandwidth, centerFreq, rxGain_dBi, boresightTilt, boresightAzimuth) arguments (Input) obj (1, 1) {mustBeA(obj, "rfSensor")} txPower (1, 1) double; bandwidth (1, 1) double; centerFreq (1, 1) double; rxGain_dBi (1, 1) double; + boresightTilt (1, 1) double = 0; + boresightAzimuth (1, 1) double = 0; end arguments (Output) obj (1, 1) {mustBeA(obj, "rfSensor")} @@ -15,6 +17,8 @@ function obj = initialize(obj, txPower, bandwidth, centerFreq, rxGain_dBi) obj.BW = bandwidth; % Bandwidth (Hz) obj.f_c = centerFreq; % Center frequency (Hz) obj.G_RX_dBi = rxGain_dBi; % Receiving Antenna Gain (dBi) + obj.boresightTilt = boresightTilt; + obj.boresightAzimuth = boresightAzimuth; % Computed values obj.P_TX_dBm = 10*log10(obj.P_TX/1e-3); % Transmit power in dBm diff --git a/@rfSensor/plotParameters.m b/@rfSensor/plotParameters.m index f428f3f..002aab9 100644 --- a/@rfSensor/plotParameters.m +++ b/@rfSensor/plotParameters.m @@ -5,52 +5,48 @@ function f = plotParameters(obj) arguments (Output) f (1, 1) {mustBeA(f, "matlab.ui.Figure")}; end - - % Distance and tilt sample points - d_values = 10.^[1, 2, 3, 4, 5, 6]; - t_values = 0:2.5:180; % 0=nadir (center), 180=zenith (edge) + + % Agent altitude layers and angle sample points + alt_values = 10.^[1, 2, 3, 4]; + t_values = 0:2.5:87.5; % 0=nadir (center), <90=near horizon (edge) a_values = 0:2.5:360; - % Make grid of values of distances and tilts - [d_mg, t_mg, a_mg] = meshgrid(d_values, t_values, a_values); - d = d_mg(:); t = t_mg(:); a = a_mg(:); % flatten - - % Sample received signal strength (no interference or noise) - s_x = obj.RSS(d, t, a); - s_x = reshape(s_x, size(d_mg)); - [T, A] = meshgrid(t_values, a_values); % Naz x Nel Ar = deg2rad(A); f = figure; hold("on"); - for ii = 1:numel(d_values) - % Linear radial mapping: t=0 (nadir) -> center, t=180 (zenith) -> edge - % Radius in log10 units to match Z axis scale - r = log10(d_values(ii)) .* T ./ 180; + for ii = 1:numel(alt_values) + alt = alt_values(ii); + + % For agent at altitude alt, ground target at tilt T has slant distance: + D = alt ./ cosd(T); + + % Compute RSS for each (d, t, a) triple + rss = obj.RSS(D(:), T(:), A(:)); + Fslice = reshape(rss, size(D)); + + % Disc geometry: t=0 (nadir) -> center, t~90 (horizon) -> edge + r = log10(alt) .* T ./ 90; X = r .* cos(Ar); Y = r .* sin(Ar); - Z = log10(d_values(ii)) * ones(size(X)); - - % evaluate or extract this slice - Fslice = squeeze(s_x(:, ii, :))'; - - % plot as its own surface - h = surf(X, Y, Z, Fslice); - h.EdgeColor = 'none'; - h.FaceColor = 'interp'; - h.FaceAlpha = 0.25; + Z = log10(alt) * ones(size(X)); + + hs = surf(X, Y, Z, Fslice); + hs.EdgeColor = 'none'; + hs.FaceColor = 'interp'; + hs.FaceAlpha = 0.25; end colormap(turbo); colorbar; - daspect([1 1 0.2]) % Separate Z further for more distinct layers - xlabel('X (log_{10} units)'); ylabel('Y (log_{10} units)'); zlabel('log_{10} Distance (m)'); - set(gca,'ZDir','reverse'); - view(3); + daspect([1 1 0.2]); + xlabel('X (log_{10} units)'); ylabel('Y (log_{10} units)'); zlabel('log_{10} Altitude (m)'); + set(gca, 'ZDir', 'reverse'); + view(3); axis("vis3d"); grid("on"); scatter3(0, 0, 0, 'rx'); hold("off"); -end \ No newline at end of file +end diff --git a/@rfSensor/rfSensor.m b/@rfSensor/rfSensor.m index 20ac328..47ccb93 100644 --- a/@rfSensor/rfSensor.m +++ b/@rfSensor/rfSensor.m @@ -9,6 +9,8 @@ classdef rfSensor BW = NaN; % Bandwidth (Hz) f_c = NaN; % Center frequency (Hz) G_RX_dBi = NaN; % Receiver antenna gain + boresightTilt = 0; % Antenna boresight tilt (deg): 0=nadir, 90=horizon + boresightAzimuth = 0; % Antenna boresight azimuth (deg): 0=+y, 90=+x % Values computed at initialization P_TX_dBm = NaN; % Transmit power (dBm) N = NaN; % Thermal noise diff --git a/@rfSensor/transmitterGain.m b/@rfSensor/transmitterGain.m index b260380..5fbecc4 100644 --- a/@rfSensor/transmitterGain.m +++ b/@rfSensor/transmitterGain.m @@ -11,11 +11,15 @@ function value = transmitterGain(obj, t, a) error("t and a must be the same size"); end - n_t = 4; % tilt beamwidth (higher = narrower beam) - n_a = 4; % azimuth rolloff sharpness (higher = more directional) + n = 4; % beamwidth exponent (higher = narrower beam) - % Elevation: cardioid family, null at zenith (t=180°), peak at nadir (t=0°) - % Azimuth: cardioid family, peak at a=0° (+y), null at a=180° (-y) - value = 10 .* n_t .* log10((1 + cosd(t)) ./ 2) + ... - 10 .* n_a .* log10((0.5 + 0.5 .* cosd(a))); + % Angular offset from boresight via spherical law of cosines + % Convention: t=0° nadir, t=90° horizon; a=0° +y, a=90° +x + cos_theta = sind(obj.boresightTilt) .* sind(t) .* cosd(a - obj.boresightAzimuth) + ... + cosd(obj.boresightTilt) .* cosd(t); + cos_theta = max(-1, min(1, cos_theta)); % clamp for numerical safety + theta = acosd(cos_theta); + + % Cardioid family: peak at boresight (theta=0), null opposite (theta=180°) + value = 10 .* n .* log10((1 + cosd(theta)) ./ 2); end diff --git a/test/test_rfSensor.m b/test/test_rfSensor.m index 86cd3dc..2b777b6 100644 --- a/test/test_rfSensor.m +++ b/test/test_rfSensor.m @@ -19,7 +19,7 @@ classdef test_rfSensor < matlab.unittest.TestCase f_c = 2e9; % Center frequency (Hz) G_RX_dBi = 3; % Receiving Antenna Gain (dBi) - tc.testClass = tc.testClass.initialize(P_TX, BW, f_c, G_RX_dBi); + tc.testClass = tc.testClass.initialize(P_TX, BW, f_c, G_RX_dBi, 0, 0); tc.testClass.plotParameters(); end @@ -30,7 +30,7 @@ classdef test_rfSensor < matlab.unittest.TestCase f_c = 2e9; % Center frequency (Hz) G_RX_dBi = 3; % Receiving Antenna Gain (dBi) - tc.testClass = tc.testClass.initialize(P_TX, BW, f_c, G_RX_dBi); + tc.testClass = tc.testClass.initialize(P_TX, BW, f_c, G_RX_dBi, 0, 0); altitude = 30; @@ -43,7 +43,7 @@ classdef test_rfSensor < matlab.unittest.TestCase f_c = 2e9; % Center frequency (Hz) G_RX_dBi = 3; % Receiving Antenna Gain (dBi) - tc.testClass = tc.testClass.initialize(P_TX, BW, f_c, G_RX_dBi); + tc.testClass = tc.testClass.initialize(P_TX, BW, f_c, G_RX_dBi, 0, 0); altitude = 30; otherSensorsPos = [6, -4, -1]; % relative to main sensor @@ -60,7 +60,7 @@ classdef test_rfSensor < matlab.unittest.TestCase f_c = 2e9; % Center frequency (Hz) G_RX_dBi = 3; % Receiving Antenna Gain (dBi) - tc.testClass = tc.testClass.initialize(P_TX, BW, f_c, G_RX_dBi); + tc.testClass = tc.testClass.initialize(P_TX, BW, f_c, G_RX_dBi, 0, 0); altitude = 30; otherSensorsPos = [6, -4, -1; -2, 6, 0]; % relative to main sensor @@ -69,8 +69,8 @@ classdef test_rfSensor < matlab.unittest.TestCase otherSensors{2} = rfSensor; % Must use same center frequency and bandwidth for interference sources - otherSensors{1} = otherSensors{1}.initialize(10 * P_TX, BW, f_c, G_RX_dBi); - otherSensors{2} = otherSensors{2}.initialize(100 * P_TX, BW, f_c, G_RX_dBi); + otherSensors{1} = otherSensors{1}.initialize(10 * P_TX, BW, f_c, G_RX_dBi, 0, 0); + otherSensors{2} = otherSensors{2}.initialize(10 * P_TX, BW, f_c, G_RX_dBi, 0, 0); tc.testClass.plotPerformance(altitude, otherSensorsPos, otherSensors); end