antenna: Add CircularApertureAntennaModel

This commit is contained in:
pagmatt
2024-05-19 17:52:27 +02:00
committed by Tom Henderson
parent 9a725b5463
commit 63b7032101
9 changed files with 780 additions and 3 deletions

View File

@@ -123,7 +123,8 @@ For MacPorts packages we show the most recent package version available as of ea
| Emulation with virtual | | |
| machines | Not available for macOS | Not available for macOS |
+-----------------------------+----------------------------------+--------------------------+
| Support for openflow | ``boost`` | ``boost`` |
| Support for openflow, | ``boost`` | ``boost`` |
| CircularApertureAntennaModel| | |
+-----------------------------+----------------------------------+--------------------------+
Caveats and troubleshooting

View File

@@ -111,6 +111,7 @@ SOURCEFIGS = \
figures/testbed.dia \
figures/emulated-channel.dia \
$(SRC)/antenna/doc/source/figures/antenna-coordinate-system.dia \
$(SRC)/antenna/doc/source/figures/circular-antenna-pattern.png \
$(SRC)/applications/doc/http-embedded-object-size.png \
$(SRC)/applications/doc/http-main-object-size.png \
$(SRC)/applications/doc/http-num-of-embedded-objects.png \

View File

@@ -1,6 +1,61 @@
# Check for dependencies and add sources accordingly
check_cxx_source_compiles(
"#include <cmath>
int main()
{
return std::cyl_bessel_j(1, 1);
}"
HAVE_STD_BESSEL_FUNC
)
set(circular_aperture_antenna_sources)
set(circular_aperture_antenna_headers)
set(circular_aperture_antenna_test_sources)
if(${HAVE_STD_BESSEL_FUNC})
set(circular_aperture_antenna_sources
model/circular-aperture-antenna-model.cc
)
set(circular_aperture_antenna_headers
model/circular-aperture-antenna-model.h
)
set(circular_aperture_antenna_test_sources
test/test-circular-aperture-antenna.cc
)
message(STATUS "Standard library Bessel function has been found")
else()
check_include_files(
"boost/math/special_functions/bessel.hpp"
HAVE_BOOST_BESSEL_FUNC
LANGUAGE
CXX
)
if(${HAVE_BOOST_BESSEL_FUNC})
set(circular_aperture_antenna_sources
model/circular-aperture-antenna-model.cc
)
set(circular_aperture_antenna_headers
model/circular-aperture-antenna-model.h
)
set(circular_aperture_antenna_test_sources
test/test-circular-aperture-antenna.cc
)
add_definitions(-DNEED_AND_HAVE_BOOST_BESSEL_FUNC)
message(STATUS "Boost Bessel function has been found.")
else()
message(
STATUS
"Boost Bessel function is required for CircularApertureAntennaModel"
" on platforms using Clang libc++ such as macOS."
" You may need to clean up the CMake cache after installing it to pass this check."
)
endif()
endif()
build_lib(
LIBNAME antenna
SOURCE_FILES
${circular_aperture_antenna_sources}
model/angles.cc
model/antenna-model.cc
model/cosine-antenna-model.cc
@@ -10,6 +65,7 @@ build_lib(
model/three-gpp-antenna-model.cc
model/uniform-planar-array.cc
HEADER_FILES
${circular_aperture_antenna_headers}
model/angles.h
model/antenna-model.h
model/cosine-antenna-model.h
@@ -20,10 +76,11 @@ build_lib(
model/uniform-planar-array.h
LIBRARIES_TO_LINK ${libcore}
TEST_SOURCES
${circular_aperture_antenna_test_sources}
test/test-angles.cc
test/test-cosine-antenna.cc
test/test-degrees-radians.cc
test/test-isotropic-antenna.cc
test/test-cosine-antenna.cc
test/test-parabolic-antenna.cc
test/test-uniform-planar-array.cc
)

View File

@@ -113,7 +113,9 @@ pattern.
ParabolicAntennaModel
+++++++++++++++++++++
This model is based on the parabolic approximation of the main lobe radiation pattern. It is often used in the context of cellular system to model the radiation pattern of a cell sector, see for instance [R4-092042a]_ and [Calcev]_. The antenna gain in dB is determined as:
This model is based on the parabolic approximation of the main lobe radiation pattern. It is often
used in the context of cellular system to model the radiation pattern of a cell sector, see for
instance [R4-092042a]_ and [Calcev]_. The antenna gain in dB is determined as:
.. math::
@@ -192,6 +194,40 @@ are configured through the attribute "PolSlantAngle"; while the antenna elements
the second polarization have the polarization slant angle minus 90 degrees,
as described in [38901]_ (i.e., :math:`{\zeta}`).
CircularApertureAntennaModel
++++++++++++++++++++++++++++
The class CircularApertureAntennaModel implements the radiation pattern described in [38811]_.
Specifically, the latter represents parabolic antennas, i.e., antennas which are typically used
for achieving long range communications such as earth-to-satellite links.
The default boresight orientation is parallel to the positive z-axis, and it can be tuned by
using the AntennaInclination and AntennaAzimuth parameters.
This implementation provides an exact characterization of the antenna field pattern, by leveraging
the standard library Bessel functions implementation introduced with C++17.
Accordingly, the antenna gain :math:`G` at an angle :math:`\theta` from the boresight main beam
is evaluated as:
.. math::
G \cdot 4\left | \frac{J_{1}\left ( k\cdot a\cdot sin\theta \right )}{k\cdot a\cdot sin\theta} \right
|^{2}\;\;\;\;\; for\; 0<\left | \theta \right |\leq 90^{\circ} \\
G \cdot 1\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\; for\; \theta=0
where :math:`J_{1}()` is the Bessel function of the first kind and first order, and :math:`a` is
the radius of the antenna's circular aperture.
The parameter :math:`k` is equal to :math:`k=\frac{2\pi f}{x}`, where :math:`f` is the carrier
frequency, and :math:`c` is the speed of light in vacuum.
The parameters :math:`G` (in logarithmic scale), :math:`a` and :math:`f` can be configured by using
the attributes "AntennaMaxGainDb", "AntennaCircularApertureRadius" and "OperatingFrequency", respectively.
This type of antennas features a symmetric radiation pattern, meaning that a single angle, measured
from the boresight direction, is sufficient to characterize the radiation strength along a given direction.
.. _fig-circular-antenna-pattern:
.. figure:: figures/circular-antenna-pattern.png
:align: center
Circular aperture antenna radiation pattern with :math:`G =` 38.5 dB and :math:`a =` 10 :math:`\frac{c}{f}.`
.. [Balanis] C.A. Balanis, "Antenna Theory - Analysis and Design", Wiley, 2nd Ed.
@@ -208,6 +244,8 @@ as described in [38901]_ (i.e., :math:`{\zeta}`).
.. [38901] 3GPP. 2018. TR 38.901, Study on channel model for frequencies from 0.5 to 100 GHz, V15.0.0. (2018-06).
.. [38811] 3GPP. 2018. TR 38.811, Study on New Radio (NR) to support non-terrestrial networks, V15.4.0. (2020-09).
.. [Mailloux] Robert J. Mailloux, "Phased Array Antenna Handbook", Artech House, 2nd Ed.
.. [3GPP_TR36897] 3GPP. 2015. TR 36.897. Study on elevation beamforming / Full-Dimension (FD)

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@@ -0,0 +1,193 @@
/*
* Copyright (c) 2022 University of Padova, Dep. of Information Engineering, SIGNET lab.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Mattia Sandri <mattia.sandri@unipd.it>
*/
#include "circular-aperture-antenna-model.h"
// The host system uses Clang libc++, which does not support the Mathematical special functions
// (P0226R1) and Boost's implementation of cyl_bessel_j has been found.
#ifdef NEED_AND_HAVE_BOOST_BESSEL_FUNC
#include <boost/math/special_functions/bessel.hpp>
#endif
#include "antenna-model.h"
#include <ns3/double.h>
#include <ns3/log.h>
#include <math.h>
/**
* \file
* \ingroup antenna
* Class CircularApertureAntennaModel implementation.
*/
namespace
{
constexpr double C = 299792458.0; ///< speed of light in vacuum, in m/s
} // namespace
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("CircularApertureAntennaModel");
NS_OBJECT_ENSURE_REGISTERED(CircularApertureAntennaModel);
TypeId
CircularApertureAntennaModel::GetTypeId()
{
static TypeId tid =
TypeId("ns3::CircularApertureAntennaModel")
.SetParent<AntennaModel>()
.SetGroupName("Antenna")
.AddConstructor<CircularApertureAntennaModel>()
.AddAttribute("AntennaCircularApertureRadius",
"The radius of the aperture of the antenna, in meters",
DoubleValue(0.5),
MakeDoubleAccessor(&CircularApertureAntennaModel::SetApertureRadius),
MakeDoubleChecker<double>(0.0))
.AddAttribute("OperatingFrequency",
"The operating frequency in Hz of the antenna",
DoubleValue(2e9),
MakeDoubleAccessor(&CircularApertureAntennaModel::SetOperatingFrequency),
MakeDoubleChecker<double>(0.0))
.AddAttribute("AntennaMinGainDb",
"The minimum gain value in dB of the antenna",
DoubleValue(-100.0),
MakeDoubleAccessor(&CircularApertureAntennaModel::SetMinGain),
MakeDoubleChecker<double>())
.AddAttribute("AntennaMaxGainDb",
"The maximum gain value in dB of the antenna",
DoubleValue(1),
MakeDoubleAccessor(&CircularApertureAntennaModel::SetMaxGain),
MakeDoubleChecker<double>(0.0));
return tid;
}
void
CircularApertureAntennaModel::SetApertureRadius(double aMeter)
{
NS_LOG_FUNCTION(this << aMeter);
NS_ASSERT_MSG(aMeter > 0, "Setting invalid aperture radius: " << aMeter);
m_apertureRadiusMeter = aMeter;
}
double
CircularApertureAntennaModel::GetApertureRadius() const
{
return m_apertureRadiusMeter;
}
void
CircularApertureAntennaModel::SetOperatingFrequency(double freqHz)
{
NS_LOG_FUNCTION(this << freqHz);
NS_ASSERT_MSG(freqHz > 0, "Setting invalid operating frequency: " << freqHz);
m_operatingFrequencyHz = freqHz;
}
double
CircularApertureAntennaModel::GetOperatingFrequency() const
{
return m_operatingFrequencyHz;
}
void
CircularApertureAntennaModel::SetMaxGain(double gainDb)
{
NS_LOG_FUNCTION(this << gainDb);
m_maxGain = gainDb;
}
double
CircularApertureAntennaModel::GetMaxGain() const
{
return m_maxGain;
}
void
CircularApertureAntennaModel::SetMinGain(double gainDb)
{
NS_LOG_FUNCTION(this << gainDb);
m_minGain = gainDb;
}
double
CircularApertureAntennaModel::GetMinGain() const
{
return m_minGain;
}
double
CircularApertureAntennaModel::GetGainDb(Angles a)
{
NS_LOG_FUNCTION(this << a);
// In 3GPP TR 38.811 v15.4.0, Section 6.4.1, the gain depends on a single angle only.
// We assume that this angle represents the angle between the vectors corresponding
// to the cartesian coordinates of the provided spherical coordinates, and the spherical
// coordinates (r = 1, azimuth = 0, elevation = PI/2)
double theta1 = a.GetInclination();
double theta2 = M_PI_2; // reference direction
// Convert to ISO range: the input azimuth angle phi is in [-pi,pi],
// while the ISO convention for spherical to cartesian coordinates
// assumes phi in [0,2*pi].
double phi1 = M_PI + a.GetAzimuth();
double phi2 = M_PI; // reference direction
// Convert the spherical coordinates of the boresight and the incoming ray
// to Cartesian coordinates
Vector p1(sin(theta1) * cos(phi1), sin(theta1) * sin(phi1), cos(theta1));
Vector p2(sin(theta2) * cos(phi2), sin(theta2) * sin(phi2), cos(theta2));
// Calculate the angle between the antenna boresight and the incoming ray
double theta = acos(p1 * p2);
double gain = 0;
if (theta == 0)
{
gain = m_maxGain;
}
// return value of std::arccos is in [0, PI] deg
else if (theta >= M_PI_2)
{
// This is an approximation. 3GPP TR 38.811 does not provide indications
// on the antenna field pattern outside its PI degrees FOV.
gain = m_minGain;
}
else // 0 < theta < |PI/2|
{
// 3GPP TR 38.811 v15.4.0, Section 6.4.1
double k = (2 * M_PI * m_operatingFrequencyHz) / C;
double kasintheta = k * m_apertureRadiusMeter * sin(theta);
// If needed, fall back to Boost cyl_bessel_j
#ifdef NEED_AND_HAVE_BOOST_BESSEL_FUNC
gain = boost::math::cyl_bessel_j(1, kasintheta) / kasintheta;
// Otherwise, use the std implementation
#else
gain = std::cyl_bessel_j(1, kasintheta) / kasintheta;
#endif
gain = 10 * log10(4 * gain * gain) + m_maxGain;
}
return gain;
}
} // namespace ns3

View File

@@ -0,0 +1,140 @@
/*
* Copyright (c) 2022 University of Padova, Dep. of Information Engineering, SIGNET lab.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Mattia Sandri <mattia.sandri@unipd.it>
*/
#ifndef CIRCULAR_APERTURE_ANTENNA_MODEL_H
#define CIRCULAR_APERTURE_ANTENNA_MODEL_H
#include "antenna-model.h"
#include <ns3/object.h>
/**
* \file
* \ingroup antenna
* Class CircularApertureAntennaModel declaration
*/
namespace ns3
{
/**
* \brief Circular Aperture Antenna Model
*
* This class implements the circular aperture antenna as described in 3GPP 38.811 6.4.1
* https://www.3gpp.org/ftp/Specs/archive/38_series/38.811 without the cosine approximation, thanks
* to the Bessel functions introduced in C++17. Spherical coordinates are used, in particular of the
* azimuth and inclination angles. All working parameters can be set, namely: operating frequency,
* aperture radius, maximum and minimum gain.
* Since Clang libc++ does not support the Mathematical special functions (P0226R1) yet, this class
* falls back to Boost's implementation of cyl_bessel_j whenever the above standard library is in
* use. If neither is available in the host system, this class is not compiled.
*/
class CircularApertureAntennaModel : public AntennaModel
{
public:
CircularApertureAntennaModel() = default;
~CircularApertureAntennaModel() override = default;
/**
* Register this type.
* \return The object TypeId.
*/
static TypeId GetTypeId();
/**
* \brief Set the antenna aperture radius
*
* Sets the antenna operating frequency, asserting that
* the provided value is within the acceptable range [0, +inf[.
*
* \param aMeter the strictly positive antenna radius in meters
*/
void SetApertureRadius(double aMeter);
/**
* \brief Return the antenna aperture radius
*
* \return the antenna radius in meters
*/
double GetApertureRadius() const;
/**
* \brief Set the antenna operating frequency.
*
* Sets the antenna operating frequency, asserting that
* the provided value is within the acceptable range [0, +inf[.
*
* \param freqHz the strictly positive antenna operating frequency, in Hz
*/
void SetOperatingFrequency(double freqHz);
/**
* \brief Return the antenna operating frequency
*
* \return the antenna operating frequency, in Hz
*/
double GetOperatingFrequency() const;
/**
* \brief Set the antenna max gain
*
* \param gainDb the antenna max gain in dB
*/
void SetMaxGain(double gainDb);
/**
* \brief Return the antenna max gain
*
* \return the antenna max gain in dB
*/
double GetMaxGain() const;
/**
* \brief Set the antenna min gain
*
* \param gainDb the antenna min gain in dB
*/
void SetMinGain(double gainDb);
/**
* \brief Return the antenna min gain
*
* \return the antenna min gain in dB
*/
double GetMinGain() const;
/**
* \brief Get the gain in dB, using Bessel equation of first kind and first order.
*
* \param a the angle at which the gain need to be calculated with respect to the antenna
* bore sight
*
* \return the antenna gain at the specified Angles a
*/
double GetGainDb(Angles a) override;
private:
double m_apertureRadiusMeter; //!< antenna aperture radius in meters
double m_operatingFrequencyHz; //!< antenna operating frequency in Hz
double m_maxGain; //!< antenna gain in dB towards the main orientation
double m_minGain; //!< antenna min gain in dB
};
} // namespace ns3
#endif // CIRCULAR_APERTURE_ANTENNA_MODEL_H

View File

@@ -0,0 +1,105 @@
%{
Compute the antenna gain pattern of the reflector antenna with circular aperture
specified in the Sec. 6.4.1, 3GPP 38.811 v.15.4.0 and generate reference gain values
for the CircularApertureAntennaModelTest.
%}
clc; clearvars;
%{
Consider two testing vectors, one with elevation fixed to 90 degrees and
varying azimuth, the other with azimuth fixed to 180 degrees and varying elevation.
The boresight direction, is (az, el) = (180 degrees, 90 degrees)
%}
az_test_fixed_el = 90:10:180;
eL_test_fixed_az = 0:9:90;
% Uncomment line below and comment line below for the fixed elevation test
[theta_vec, az_vec, el_vec] = theta_vec_from_sph_coord_vecs(az_test_fixed_el, 90);
% Uncomment line above and comment line below for the fixed azimuth test
%[theta_vec, az_vec, el_vec] = theta_vec_from_sph_coord_vecs(180, eL_test_fixed_az);
%{
The theta angle which represents the input of the pattern formula is
computed as theta = arccos(<p1, p2>), where p_i = sin(theta_i) *
cos(phi_i), sin(theta_i) * sin(phi_i), cos(theta_i). The default
boresight is (phi1 (az), theta1(el)) = (180deg, 90deg)
%}
%{
3GPP specifies the antenna gain only for |theta| < 90 degrees.
The gain outside of this region is approximated as min_gain_dB
%}
min_gain_dB = -50;
max_gain_dB = 0;
c=physconst('LightSpeed');
f=28e9; % operating frequency
k=2*pi*f/c; % wave number
a=10*c/f; % radius antenna aperture
arg_vec=k*a*sind(theta_vec);
pattern=besselj(1, arg_vec)./arg_vec;
pattern= 4*(abs(pattern).^2);
% Manually set gain for theta = 0
where_arg_zero=find(~theta_vec);
pattern(where_arg_zero)=1;
gain_dB=10*log10(pattern) + max_gain_dB;
% Manually set gain to minimum gain for |theta| >= 90 degrees
where_arg_outside_pattern_domain = find(abs(theta_vec) >= 90);
gain_dB(where_arg_outside_pattern_domain)=min_gain_dB;
%{
Plot and output the C++ code which defines the reference vector of data points.
Test points are assumed to be encoded as C++ structs
%}
scatter(theta_vec, gain_dB);
out_str = ns3_output_from_vecs(az_vec, el_vec, gain_dB, max_gain_dB, min_gain_dB, a, f);
function theta = theta_from_sph_coord(phi1, theta1)
% theta_from_sph_coord Computes theta (the input of the radiation pattern formula)
% from the azimuth and elevation angles with resepct to boresight.
% phi1 the azimuth with respect to boresight
% theta1 the elevation angle with respect to boresight
p1 = [sind(theta1)*cosd(phi1), sind(theta1)*sind(phi1), cosd(theta1)];
% p2, i.e., the boresight direction, is phi1 (az), theta1(el)) =
% (180deg, 90deg)
theta2 = 90; phi2 = 180;
p2 = [sind(theta2)*cosd(phi2), sind(theta2)*sind(phi2), cosd(theta2)];
theta = acosd(dot(p1,p2));
end
function [theta_vec, az_vec, el_vec] = theta_vec_from_sph_coord_vecs(az_vec, el_vec)
% theta_vec_from_sph_coord_vecs Computes the vectors of thetas
% (the inputs of the radiation pattern formula)
% from vectors of azimuth and elevation angles with resepct to boresight.
% phi1 the vector of azimuths with respect to boresight
% theta1 the vector of elevation angles with respect to boresight
if size(az_vec, 2) > 1
el_vec = repelem(el_vec, size(az_vec, 2));
elseif size(el_vec, 2) > 1
az_vec = repelem(az_vec, size(el_vec, 2));
end
theta_vec = arrayfun(@theta_from_sph_coord, az_vec, el_vec);
end
function out_str = ns3_output_from_vecs(az_vec, el_vec, gain_dB_vec, max_gain, min_gain, a, f)
% ns3_output_from_vecs Outputs the reference gain values
% as a vector of vectors.
% The entries of the inner vectors, representing a single test point,
% represent: (gain at boresight (dB), gain outside the 3GPP pattern region (dB),
% aperture (meters), carrier frequency (Hz), test azimuth (degrees),
% test elevation angle (degrees), reference antenna gain (dB))
out_str = '';
for i=1:size(el_vec, 2)
out_str = [out_str, '{', ...
num2str(max_gain), ',', ...
num2str(min_gain), ',', ...
num2str(a), ',', ...
num2str(f), ',', ...
num2str(az_vec(i) - 180), ',', ...
num2str(el_vec(i)), ',', ...
num2str(gain_dB_vec(i)), '},'];
end
end

View File

@@ -0,0 +1,242 @@
/*
* Copyright (c) 2023 University of Padova, Dep. of Information Engineering, SIGNET lab.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ns3/circular-aperture-antenna-model.h"
#include "ns3/double.h"
#include "ns3/log.h"
#include "ns3/pointer.h"
#include "ns3/simulator.h"
#include "ns3/test.h"
#include "ns3/uinteger.h"
#include "ns3/uniform-planar-array.h"
#include <cmath>
#include <iostream>
#include <sstream>
#include <string>
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("TestCircularApertureAntennaModel");
/**
* \ingroup antenna-tests
*
* \brief CircularApertureAntennaModel Test Case
*
* Note: Since Clang libc++ does not support the Mathematical special functions (P0226R1) yet, this
* class falls back to Boost's implementation of cyl_bessel_j whenever the above standard library is
* in use. If neither is available in the host system, this class is not compiled.
*/
class CircularApertureAntennaModelTestCase : public TestCase
{
public:
CircularApertureAntennaModelTestCase();
/**
* \brief Description of a single test point
*
* Description of a test point, which is characterized
* by the CircularApertureAntennaModel parameters,
* the directions towards which the antenna gain
* is to be tested, and the expected gain value.
*/
struct TestPoint
{
/**
* @brief Constructor
*
* @param antennaMaxGainDb the antenna maximum possible gain [dB]
* @param antennaMinGainDb the antenna minimum possible gain [dB]
* @param antennaCircularApertureRadius the radius of the parabolic aperture [m]
* @param operatingFrequency operating frequency [Hz]
* @param testAzimuth test azimuth [rad]
* @param testInclination test inclination [rad]
* @param expectedGain the expected gain value [dB]
*/
TestPoint(double antennaMaxGainDb,
double antennaMinGainDb,
double antennaCircularApertureRadius,
double operatingFrequency,
double testAzimuth,
double testInclination,
double expectedGain)
: m_antennaMaxGainDb(antennaMaxGainDb),
m_antennaMinGainDb(antennaMinGainDb),
m_antennaCircularApertureRadius(antennaCircularApertureRadius),
m_operatingFrequency(operatingFrequency),
m_testAzimuth(DegreesToRadians(testAzimuth)),
m_testInclination(DegreesToRadians(testInclination)),
m_expectedGain(expectedGain)
{
}
double m_antennaMaxGainDb; ///< the antenna maximum possible gain [dB]
double m_antennaMinGainDb; ///< the antenna minimum possible gain [dB]
double m_antennaCircularApertureRadius; ///< the radius of the parabolic aperture [m]
double m_operatingFrequency; ///< operating frequency [Hz]
double m_testAzimuth; ///< test azimuth [rad]
double m_testInclination; ///< test inclination [rad]
double m_expectedGain; ///< the expected gain value [dB]
};
/**
* Generate a string containing all relevant parameters
* \param testPoint the parameter configuration to be tested
* \return the string containing all relevant parameters
*/
static std::string BuildNameString(TestPoint testPoint);
/**
* Test the antenna gain for a specific parameter configuration,
* by comparing the antenna gain obtained using CircularApertureAntennaModel::GetGainDb
* and the one obtained using MATLAB.
*
* \param testPoint the parameter configuration to be tested
*/
void TestAntennaGain(TestPoint testPoint);
private:
/**
* Run the test
*/
void DoRun() override;
};
CircularApertureAntennaModelTestCase::CircularApertureAntennaModelTestCase()
: TestCase("Creating CircularApertureAntennaModelTestCase")
{
}
std::string
CircularApertureAntennaModelTestCase::BuildNameString(TestPoint testPoint)
{
std::ostringstream oss;
oss << " Maximum gain=" << testPoint.m_antennaMaxGainDb << "dB"
<< " minimum gain=" << testPoint.m_antennaMinGainDb << "dB"
<< ", antenna aperture radius=" << testPoint.m_antennaCircularApertureRadius << "m"
<< ", frequency" << testPoint.m_operatingFrequency << "Hz"
<< ", test inclination=" << RadiansToDegrees(testPoint.m_testInclination) << " deg"
<< ", test azimuth=" << RadiansToDegrees(testPoint.m_testAzimuth) << " deg";
return oss.str();
}
void
CircularApertureAntennaModelTestCase::TestAntennaGain(TestPoint testPoint)
{
Ptr<CircularApertureAntennaModel> antenna =
CreateObjectWithAttributes<CircularApertureAntennaModel>(
"AntennaMaxGainDb",
DoubleValue(testPoint.m_antennaMaxGainDb),
"AntennaMinGainDb",
DoubleValue(testPoint.m_antennaMinGainDb),
"AntennaCircularApertureRadius",
DoubleValue(testPoint.m_antennaCircularApertureRadius),
"OperatingFrequency",
DoubleValue(testPoint.m_operatingFrequency));
Ptr<UniformPlanarArray> upa =
CreateObjectWithAttributes<UniformPlanarArray>("AntennaElement",
PointerValue(antenna),
"NumColumns",
UintegerValue(1),
"NumRows",
UintegerValue(1));
auto [fieldPhi, fieldTheta] =
upa->GetElementFieldPattern(Angles(testPoint.m_testAzimuth, testPoint.m_testInclination),
0);
// Compute the antenna gain as the squared sum of the field pattern components
double gainDb = 10 * log10(fieldPhi * fieldPhi + fieldTheta * fieldTheta);
auto log = BuildNameString(testPoint);
NS_LOG_INFO(log);
NS_TEST_EXPECT_MSG_EQ_TOL(gainDb, testPoint.m_expectedGain, 0.1, log);
}
void
CircularApertureAntennaModelTestCase::DoRun()
{
// Vector of test points
std::vector<TestPoint> testPoints = {
// MaxGainDb MinGainDb Radius (m) Freq (Hz) Azimuth (deg) Incl (deg) ExpGain (dB)
// Test invariant: gain always equal to max gain at boresight (inclination 90, azimuth = 0)
// for different frequency
{30, -30, 0.5, 2e9, 0, 90, 30},
{30, -30, 2, 20e9, 0, 90, 30},
// Test invariant: gain always equal to max gain at boresight (inclination 90, azimuth = 0)
// for different max gain
{20, -30, 0.5, 2e9, 0, 90, 20},
{10, -30, 2, 20e9, 0, 90, 10},
// Test invariant: gain always equal to min gain outside of |theta| < 90 deg
// for different frequency
{30, -100, 0.5, 2e9, 0, 0, -100},
{30, -100, 2, 20e9, 0, 0, -100},
// Test invariant: gain always equal to min gain outside of |theta| < 90 deg
// for different orientations
{30, -100, 0.5, 2e9, 180, 90, -100},
{30, -100, 2, 20e9, -180, 90, -100},
// Fixed elevation to boresight (90deg) and azimuth varying in [-90, 0] deg with steps of 10
// degrees
{0, -50, 0.10707, 28000000000, -90, 90, -50},
{0, -50, 0.10707, 28000000000, -80, 90, -49.8022},
{0, -50, 0.10707, 28000000000, -70, 90, -49.1656},
{0, -50, 0.10707, 28000000000, -60, 90, -60.9132},
{0, -50, 0.10707, 28000000000, -50, 90, -59.2368},
{0, -50, 0.10707, 28000000000, -40, 90, -44.6437},
{0, -50, 0.10707, 28000000000, -30, 90, -43.9686},
{0, -50, 0.10707, 28000000000, -20, 90, -36.3048},
{0, -50, 0.10707, 28000000000, -10, 90, -30.5363},
{0, -50, 0.10707, 28000000000, 0, 90, 0},
// Fixed azimuth to boresight (0 deg) and azimuth varying in [0, 90] deg with steps of 9
// degrees
{0, -50, 0.10707, 28e9, 0, 0, -50},
{0, -50, 0.10707, 28e9, 0, 9, -49.7256},
{0, -50, 0.10707, 28e9, 0, 18, -52.9214},
{0, -50, 0.10707, 28e9, 0, 27, -48.6077},
{0, -50, 0.10707, 28e9, 0, 36, -60.684},
{0, -50, 0.10707, 28e9, 0, 45, -55.1468},
{0, -50, 0.10707, 28e9, 0, 54, -42.9648},
{0, -50, 0.10707, 28e9, 0, 63, -45.6472},
{0, -50, 0.10707, 28e9, 0, 72, -48.6378},
{0, -50, 0.10707, 28e9, 0, 81, -35.1613},
{0, -50, 0.10707, 28e9, 0, 90, 0}};
// Call TestAntennaGain on each test point
for (auto& point : testPoints)
{
TestAntennaGain(point);
}
}
/**
* \ingroup antenna-tests
*
* \brief UniformPlanarArray Test Suite
*/
class CircularApertureAntennaModelTestSuite : public TestSuite
{
public:
CircularApertureAntennaModelTestSuite();
};
CircularApertureAntennaModelTestSuite::CircularApertureAntennaModelTestSuite()
: TestSuite("circular-aperture-antenna-test", Type::UNIT)
{
AddTestCase(new CircularApertureAntennaModelTestCase());
}
static CircularApertureAntennaModelTestSuite staticCircularApertureAntennaModelTestSuiteInstance;