Source code for pyphysim.channels.antennagain

#!/usr/bin/env python

from typing import Optional, TypeVar

import numpy as np

from ..util.conversion import dB2Linear

# See http://www.qtc.jp/3GPP/Specs/25996-a00.pdf

NumberOrArray = TypeVar("NumberOrArray", np.ndarray, float)


[docs]class AntGainBase: # pragma: no cover """Base class for antenna models. """
[docs] def get_antenna_gain(self, angle: NumberOrArray) -> NumberOrArray: """ Get the antenna gain for the given angle. Parameters ---------- angle : float | np.ndarray Angle between the direction of interest and the boresight of the antenna. This can also be a numpy array with angles. Returns ------- float | np.ndarray The gain (in linear scale) for the provided angle. """ raise NotImplementedError("Implement in a subclass")
[docs]class AntGainOmni(AntGainBase): """ Class for Omnidirectional antenna gain model. Parameters ---------- ant_gain : float, optional The antenna gain (in dBi). If not provided then 0dBi will be assumed. """ def __init__(self, ant_gain: Optional[float] = None): super().__init__() self.ant_gain: float if ant_gain is None: self.ant_gain = 1.0 else: self.ant_gain = dB2Linear(ant_gain)
[docs] def get_antenna_gain(self, angle: NumberOrArray) -> NumberOrArray: """ Get the antenna gain for the given angle. Parameters ---------- angle : float | np.ndarray Angle between the direction of interest and the boresight of the antenna. This can also be a numpy array with angles. Returns ------- float | np.ndarray The gain (in linear scale) for the provided angle. """ if isinstance(angle, np.ndarray): return self.ant_gain * np.ones(angle.shape) # type: ignore return self.ant_gain
[docs]class AntGainBS3GPP25996(AntGainBase): """ Class for antenna model defined by 3GPP in the 25996 norm for sectorized Base Stations. The antenna gain (in dBi) will depend on the number of sectors of the Base Station. NOTE: The antenna pattern here is targeted for diversity-oriented implementations (i.e. large inter-element spacings). For beamforming applications that require small spacings, alternative antenna designs may have to be considered leading to a different antenna pattern. Parameters ---------- number_of_sectors : int The number of sectors of the base station. It can be either 3 or 6. """ def __init__(self, number_of_sectors: int = 3): super().__init__() self.ant_gain: float if number_of_sectors == 3: self.theta_3db = 70. # Defined in the norm self.Am = 20. # Maximum attenuation in dB self.ant_gain = dB2Linear(14.) # Antenna gain (in dBi) elif number_of_sectors == 6: self.theta_3db = 35. # Defined in the norm self.Am = 23. # Maximum attenuation in dB self.ant_gain = dB2Linear(17.) # Antenna gain (in dBi) else: raise ValueError( "Invalid number of sectors: {0}".format(number_of_sectors)) # noinspection PyPep8
[docs] def get_antenna_gain(self, angle: NumberOrArray) -> NumberOrArray: """ Get the antenna gain for the given angle. Parameters ---------- angle : float | np.ndarray Angle between the direction of interest and the boresight of the antenna. This can also be a numpy array with angles. Returns ------- float | np.ndarray The gain (in linear scale) for the provided angle. """ # For the antenna gain model defined in 3GPP 25996 the gain (in dB) # is given by #\(-\min\left[ 12\left( \frac{\theta}{\theta_{3dB}} \right)^2, A_m \right]\), where \(-180 \geq \theta \geq 180\) ant_pattern_gain = dB2Linear( -np.minimum(12 * (angle / self.theta_3db)**2, self.Am)) return self.ant_gain * ant_pattern_gain # type: ignore