antenna: Add CircularApertureAntennaModel
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
BIN
src/antenna/doc/source/figures/circular-antenna-pattern.png
Normal file
BIN
src/antenna/doc/source/figures/circular-antenna-pattern.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
193
src/antenna/model/circular-aperture-antenna-model.cc
Normal file
193
src/antenna/model/circular-aperture-antenna-model.cc
Normal 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
|
||||
140
src/antenna/model/circular-aperture-antenna-model.h
Normal file
140
src/antenna/model/circular-aperture-antenna-model.h
Normal 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
|
||||
@@ -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
|
||||
242
src/antenna/test/test-circular-aperture-antenna.cc
Normal file
242
src/antenna/test/test-circular-aperture-antenna.cc
Normal 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;
|
||||
Reference in New Issue
Block a user