anomaly-detection-material-parameters-calibration

Sionna param calibration (research proj)
git clone https://git.ea.contact/anomaly-detection-material-parameters-calibration
Log | Files | Refs | README

rma_scenario.py (10367B)


      1 #
      2 # SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
      3 # SPDX-License-Identifier: Apache-2.0
      4 #
      5 """3GPP TR39.801 rural macrocell (RMa) channel scenario"""
      6 
      7 import tensorflow as tf
      8 
      9 from sionna import SPEED_OF_LIGHT, PI
     10 from sionna.utils import log10
     11 from . import SystemLevelScenario
     12 
     13 class RMaScenario(SystemLevelScenario):
     14     r"""
     15     3GPP TR 38.901 rural macrocell (RMa) channel model scenario.
     16 
     17     Parameters
     18     -----------
     19 
     20     carrier_frequency : float
     21         Carrier frequency [Hz]
     22 
     23     rx_array : PanelArray
     24         Panel array used by the receivers. All receivers share the same
     25         antenna array configuration.
     26 
     27     tx_array : PanelArray
     28         Panel array used by the transmitters. All transmitters share the
     29         same antenna array configuration.
     30 
     31     direction : str
     32         Link direction. Either "uplink" or "downlink".
     33 
     34     enable_pathloss : bool
     35         If `True`, apply pathloss. Otherwise doesn't. Defaults to `True`.
     36 
     37     enable_shadow_fading : bool
     38         If `True`, apply shadow fading. Otherwise doesn't.
     39         Defaults to `True`.
     40 
     41     average_street_width : float
     42         Average street width [m]. Defaults to 5m.
     43 
     44     average_street_width : float
     45         Average building height [m]. Defaults to 20m.
     46 
     47     always_generate_lsp : bool
     48         If `True`, new large scale parameters (LSPs) are generated for every
     49         new generation of channel impulse responses. Otherwise, always reuse
     50         the same LSPs, except if the topology is changed. Defaults to
     51         `False`.
     52 
     53     dtype : Complex tf.DType
     54         Defines the datatype for internal calculations and the output
     55         dtype. Defaults to `tf.complex64`.
     56     """
     57 
     58     def __init__(self, carrier_frequency, ut_array, bs_array,
     59         direction, enable_pathloss=True, enable_shadow_fading=True,
     60         average_street_width=20.0, average_building_height=5.0,
     61         dtype=tf.complex64):
     62 
     63         # Only the low-loss O2I model if available for RMa.
     64         super().__init__(carrier_frequency, 'low', ut_array, bs_array,
     65             direction, enable_pathloss, enable_shadow_fading, dtype)
     66 
     67         # Average street width [m]
     68         self._average_street_width = tf.constant(average_street_width,
     69             self._dtype.real_dtype)
     70 
     71         # Average building height [m]
     72         self._average_building_height = tf.constant(average_building_height,
     73             self._dtype.real_dtype)
     74 
     75     #########################################
     76     # Public methods and properties
     77     #########################################
     78 
     79     def clip_carrier_frequency_lsp(self, fc):
     80         r"""Clip the carrier frequency ``fc`` in GHz for LSP calculation
     81 
     82         Input
     83         -----
     84         fc : float
     85             Carrier frequency [GHz]
     86 
     87         Output
     88         -------
     89         : float
     90             Clipped carrier frequency, that should be used for LSp computation
     91         """
     92         return fc
     93 
     94     @property
     95     def min_2d_in(self):
     96         r"""Minimum indoor 2D distance for indoor UTs [m]"""
     97         return tf.constant(0.0, self._dtype.real_dtype)
     98 
     99     @property
    100     def max_2d_in(self):
    101         r"""Maximum indoor 2D distance for indoor UTs [m]"""
    102         return tf.constant(10.0, self._dtype.real_dtype)
    103 
    104     @property
    105     def average_street_width(self):
    106         r"""Average street width [m]"""
    107         return self._average_street_width
    108 
    109     @property
    110     def average_building_height(self):
    111         r"""Average building height [m]"""
    112         return self._average_building_height
    113 
    114     @property
    115     def los_probability(self):
    116         r"""Probability of each UT to be LoS. Used to randomly generate LoS
    117         status of outdoor UTs.
    118 
    119         Computed following section 7.4.2 of TR 38.901.
    120 
    121         [batch size, num_ut]"""
    122         distance_2d_out = self._distance_2d_out
    123         los_probability = tf.exp(-(distance_2d_out-10.0)/1000.0)
    124         los_probability = tf.where(tf.math.less(distance_2d_out, 10.0),
    125             tf.constant(1.0, self._dtype.real_dtype), los_probability)
    126         return los_probability
    127 
    128     @property
    129     def rays_per_cluster(self):
    130         r"""Number of rays per cluster"""
    131         return tf.constant(20, tf.int32)
    132 
    133     @property
    134     def los_parameter_filepath(self):
    135         r""" Path of the configuration file for LoS scenario"""
    136         return 'RMa_LoS.json'
    137 
    138     @property
    139     def nlos_parameter_filepath(self):
    140         r""" Path of the configuration file for NLoS scenario"""
    141         return'RMa_NLoS.json'
    142 
    143     @property
    144     def o2i_parameter_filepath(self):
    145         r""" Path of the configuration file for indoor scenario"""
    146         return 'RMa_O2I.json'
    147 
    148     #########################
    149     # Utility methods
    150     #########################
    151 
    152     def _compute_lsp_log_mean_std(self):
    153         r"""Computes the mean and standard deviations of LSPs in log-domain"""
    154 
    155         batch_size = self.batch_size
    156         num_bs = self.num_bs
    157         num_ut = self.num_ut
    158         distance_2d = self.distance_2d
    159         h_bs = self.h_bs
    160         h_bs = tf.expand_dims(h_bs, axis=2) # For broadcasting
    161         h_ut = self.h_ut
    162         h_ut = tf.expand_dims(h_ut, axis=1) # For broadcasting
    163 
    164         ## Mean
    165         # DS
    166         log_mean_ds = self.get_param("muDS")
    167         # ASD
    168         log_mean_asd = self.get_param("muASD")
    169         # ASA
    170         log_mean_asa = self.get_param("muASA")
    171         # SF.  Has zero-mean.
    172         log_mean_sf = tf.zeros([batch_size, num_bs, num_ut],
    173                                 self._dtype.real_dtype)
    174         # K.  Given in dB in the 3GPP tables, hence the division by 10
    175         log_mean_k = self.get_param("muK")/10.0
    176         # ZSA
    177         log_mean_zsa = self.get_param("muZSA")
    178         # ZSD mean is of the form max(-1, A*d2D/1000 - 0.01*(hUT-1.5) + B)
    179         log_mean_zsd = (self.get_param("muZSDa")*(distance_2d/1000.)
    180             - 0.01*(h_ut-1.5) + self.get_param("muZSDb"))
    181         log_mean_zsd = tf.math.maximum(tf.constant(-1.0,
    182                                         self._dtype.real_dtype), log_mean_zsd)
    183 
    184         lsp_log_mean = tf.stack([log_mean_ds,
    185                                 log_mean_asd,
    186                                 log_mean_asa,
    187                                 log_mean_sf,
    188                                 log_mean_k,
    189                                 log_mean_zsa,
    190                                 log_mean_zsd], axis=3)
    191 
    192         ## STD
    193         # DS
    194         log_std_ds = self.get_param("sigmaDS")
    195         # ASD
    196         log_std_asd = self.get_param("sigmaASD")
    197         # ASA
    198         log_std_asa = self.get_param("sigmaASA")
    199         # SF. Given in dB in the 3GPP tables, hence the division by 10
    200         # O2I and NLoS cases just require the use of a predefined value
    201         log_std_sf_o2i_nlos = self.get_param("sigmaSF")/10.0
    202         # For LoS, two possible scenarion depending on the 2D location of the
    203         # user
    204         distance_breakpoint = (2.*PI*h_bs*h_ut*self.carrier_frequency/
    205             SPEED_OF_LIGHT)
    206         log_std_sf_los=tf.where(tf.math.less(distance_2d, distance_breakpoint),
    207             self.get_param("sigmaSF1")/10.0, self.get_param("sigmaSF2")/10.0)
    208         # Use the correct SF STD according to the user scenario: NLoS/O2I, or
    209         # LoS
    210         log_std_sf = tf.where(self.los, log_std_sf_los, log_std_sf_o2i_nlos)
    211         # K. Given in dB in the 3GPP tables, hence the division by 10.
    212         log_std_k = self.get_param("sigmaK")/10.0
    213         # ZSA
    214         log_std_zsa = self.get_param("sigmaZSA")
    215         # ZSD
    216         log_std_zsd = self.get_param("sigmaZSD")
    217 
    218         lsp_log_std = tf.stack([log_std_ds,
    219                                log_std_asd,
    220                                log_std_asa,
    221                                log_std_sf,
    222                                log_std_k,
    223                                log_std_zsa,
    224                                log_std_zsd], axis=3)
    225 
    226         self._lsp_log_mean = lsp_log_mean
    227         self._lsp_log_std = lsp_log_std
    228 
    229         # ZOD offset
    230         zod_offset = (tf.atan((35.-3.5)/distance_2d)
    231           - tf.atan((35.-1.5)/distance_2d))
    232         zod_offset = tf.where(self.los, tf.constant(0.0,self._dtype.real_dtype),
    233             zod_offset)
    234         self._zod_offset = zod_offset
    235 
    236     def _compute_pathloss_basic(self):
    237         r"""Computes the basic component of the pathloss [dB]"""
    238 
    239         distance_2d = self.distance_2d
    240         distance_3d = self.distance_3d
    241         fc = self.carrier_frequency/1e9 # Carrier frequency (GHz)
    242         h_bs = self.h_bs
    243         h_bs = tf.expand_dims(h_bs, axis=2) # For broadcasting
    244         h_ut = self.h_ut
    245         h_ut = tf.expand_dims(h_ut, axis=1) # For broadcasting
    246         average_building_height = self.average_building_height
    247 
    248         # Beak point distance
    249         # For this computation, the carrifer frequency needs to be in Hz
    250         distance_breakpoint = (2.*PI*h_bs*h_ut*self.carrier_frequency
    251             /SPEED_OF_LIGHT)
    252 
    253         ## Basic path loss for LoS
    254 
    255         pl_1 = (20.0*log10(40.0*PI*distance_3d*fc/3.)
    256             + tf.math.minimum(0.03*tf.math.pow(average_building_height,1.72),
    257                 10.0)*log10(distance_3d)
    258             - tf.math.minimum(0.044*tf.math.pow(average_building_height,1.72),
    259                 14.77)
    260             + 0.002*log10(average_building_height)*distance_3d)
    261         pl_2 = (20.0*log10(40.0*PI*distance_breakpoint*fc/3.)
    262             + tf.math.minimum(0.03*tf.math.pow(average_building_height,1.72),
    263                 10.0)*log10(distance_breakpoint)
    264             - tf.math.minimum(0.044*tf.math.pow(average_building_height,1.72),
    265                 14.77)
    266             + 0.002*log10(average_building_height)*distance_breakpoint
    267             + 40.0*log10(distance_3d/distance_breakpoint))
    268         pl_los = tf.where(tf.math.less(distance_2d, distance_breakpoint),
    269             pl_1, pl_2)
    270 
    271         ## Basic pathloss for NLoS and O2I
    272 
    273         pl_3 = (161.04 - 7.1*log10(self.average_street_width)
    274                 + 7.5*log10(average_building_height)
    275                 - (24.37 - 3.7*tf.square(average_building_height/h_bs))
    276                 *log10(h_bs)
    277                 + (43.42 - 3.1*log10(h_bs))*(log10(distance_3d)-3.0)
    278                 + 20.0*log10(fc) - (3.2*tf.square(log10(11.75*h_ut))
    279                 - 4.97))
    280         pl_nlos = tf.math.maximum(pl_los, pl_3)
    281 
    282         ## Set the basic pathloss according to UT state
    283 
    284         # LoS
    285         pl_b = tf.where(self.los, pl_los, pl_nlos)
    286 
    287         self._pl_b = pl_b