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

tdl.py (26344B)


      1 #
      2 # SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
      3 # SPDX-License-Identifier: Apache-2.0
      4 #
      5 """Tapped delay line (TDL) channel model from 3GPP TR38.901 specification"""
      6 
      7 import json
      8 from importlib_resources import files
      9 import numpy as np
     10 
     11 import tensorflow as tf
     12 
     13 from sionna import PI, SPEED_OF_LIGHT
     14 from sionna import config
     15 from sionna.utils import insert_dims, expand_to_rank, matrix_sqrt, split_dim, flatten_last_dims
     16 from sionna.channel import ChannelModel
     17 
     18 from . import models # pylint: disable=relative-beyond-top-level
     19 
     20 class TDL(ChannelModel):
     21     # pylint: disable=line-too-long
     22     r"""TDL(model, delay_spread, carrier_frequency, num_sinusoids=20, los_angle_of_arrival=PI/4., min_speed=0., max_speed=None, num_rx_ant=1, num_tx_ant=1, spatial_corr_mat=None, rx_corr_mat=None, tx_corr_mat=None, dtype=tf.complex64)
     23 
     24     Tapped delay line (TDL) channel model from the 3GPP [TR38901]_ specification.
     25 
     26     The power delay profiles (PDPs) are normalized to have a total energy of one.
     27 
     28     Channel coefficients are generated using a sum-of-sinusoids model [SoS]_.
     29     Channel aging is simulated in the event of mobility.
     30 
     31     If a minimum speed and a maximum speed are specified such that the
     32     maximum speed is greater than the minimum speed, then speeds are randomly
     33     and uniformly sampled from the specified interval for each link and each
     34     batch example.
     35 
     36     The TDL model only works for systems with a single transmitter and a single
     37     receiver. The transmitter and receiver can be equipped with multiple
     38     antennas. Spatial correlation is simulated through filtering by specified
     39     correlation matrices.
     40 
     41     The ``spatial_corr_mat`` parameter can be used to specify an arbitrary
     42     spatial correlation matrix. In particular, it can be used to model
     43     correlated cross-polarized transmit and receive antennas as follows
     44     (see, e.g., Annex G.2.3.2.1 [TS38141-1]_):
     45 
     46     .. math::
     47 
     48         \mathbf{R} = \mathbf{R}_{\text{rx}} \otimes \mathbf{\Gamma} \otimes \mathbf{R}_{\text{tx}}
     49 
     50     where :math:`\mathbf{R}` is the spatial correlation matrix ``spatial_corr_mat``,
     51     :math:`\mathbf{R}_{\text{rx}}` the spatial correlation matrix at the receiver
     52     with same polarization, :math:`\mathbf{R}_{\text{tx}}` the spatial correlation
     53     matrix at the transmitter with same polarization, and :math:`\mathbf{\Gamma}`
     54     the polarization correlation matrix. :math:`\mathbf{\Gamma}` is 1x1 for single-polarized
     55     antennas, 2x2 when only the transmit or receive antennas are cross-polarized, and 4x4 when
     56     transmit and receive antennas are cross-polarized.
     57 
     58     It is also possible not to specify ``spatial_corr_mat``, but instead the correlation matrices
     59     at the receiver and transmitter, using the ``rx_corr_mat`` and ``tx_corr_mat``
     60     parameters, respectively.
     61     This can be useful when single polarized antennas are simulated, and it is also
     62     more computationally efficient.
     63     This is equivalent to setting ``spatial_corr_mat`` to :
     64 
     65     .. math::
     66         \mathbf{R} = \mathbf{R}_{\text{rx}} \otimes \mathbf{R}_{\text{tx}}
     67 
     68     where :math:`\mathbf{R}_{\text{rx}}` is the correlation matrix at the receiver
     69     ``rx_corr_mat`` and  :math:`\mathbf{R}_{\text{tx}}` the correlation matrix at
     70     the transmitter ``tx_corr_mat``.
     71 
     72     Example
     73     --------
     74 
     75     The following code snippet shows how to setup a TDL channel model assuming
     76     an OFDM waveform:
     77 
     78     >>> tdl = TDL(model = "A",
     79     ...           delay_spread = 300e-9,
     80     ...           carrier_frequency = 3.5e9,
     81     ...           min_speed = 0.0,
     82     ...           max_speed = 3.0)
     83     >>>
     84     >>> channel = OFDMChannel(channel_model = tdl,
     85     ...                       resource_grid = rg)
     86 
     87     where ``rg`` is an instance of :class:`~sionna.ofdm.ResourceGrid`.
     88 
     89     Notes
     90     ------
     91 
     92     The following tables from [TR38901]_ provide typical values for the delay
     93     spread.
     94 
     95     +--------------------------+-------------------+
     96     | Model                    | Delay spread [ns] |
     97     +==========================+===================+
     98     | Very short delay spread  | :math:`10`        |
     99     +--------------------------+-------------------+
    100     | Short short delay spread | :math:`10`        |
    101     +--------------------------+-------------------+
    102     | Nominal delay spread     | :math:`100`       |
    103     +--------------------------+-------------------+
    104     | Long delay spread        | :math:`300`       |
    105     +--------------------------+-------------------+
    106     | Very long delay spread   | :math:`1000`      |
    107     +--------------------------+-------------------+
    108 
    109     +-----------------------------------------------+------+------+----------+-----+----+-----+
    110     |              Delay spread [ns]                |             Frequency [GHz]             |
    111     +                                               +------+------+----+-----+-----+----+-----+
    112     |                                               |   2  |   6  | 15 |  28 |  39 | 60 |  70 |
    113     +========================+======================+======+======+====+=====+=====+====+=====+
    114     | Indoor office          | Short delay profile  | 20   | 16   | 16 | 16  | 16  | 16 | 16  |
    115     |                        +----------------------+------+------+----+-----+-----+----+-----+
    116     |                        | Normal delay profile | 39   | 30   | 24 | 20  | 18  | 16 | 16  |
    117     |                        +----------------------+------+------+----+-----+-----+----+-----+
    118     |                        | Long delay profile   | 59   | 53   | 47 | 43  | 41  | 38 | 37  |
    119     +------------------------+----------------------+------+------+----+-----+-----+----+-----+
    120     | UMi Street-canyon      | Short delay profile  | 65   | 45   | 37 | 32  | 30  | 27 | 26  |
    121     |                        +----------------------+------+------+----+-----+-----+----+-----+
    122     |                        | Normal delay profile | 129  | 93   | 76 | 66  | 61  | 55 | 53  |
    123     |                        +----------------------+------+------+----+-----+-----+----+-----+
    124     |                        | Long delay profile   | 634  | 316  | 307| 301 | 297 | 293| 291 |
    125     +------------------------+----------------------+------+------+----+-----+-----+----+-----+
    126     | UMa                    | Short delay profile  | 93   | 93   | 85 | 80  | 78  | 75 | 74  |
    127     |                        +----------------------+------+------+----+-----+-----+----+-----+
    128     |                        | Normal delay profile | 363  | 363  | 302| 266 | 249 |228 | 221 |
    129     |                        +----------------------+------+------+----+-----+-----+----+-----+
    130     |                        | Long delay profile   | 1148 | 1148 | 955| 841 | 786 | 720| 698 |
    131     +------------------------+----------------------+------+------+----+-----+-----+----+-----+
    132     | RMa / RMa O2I          | Short delay profile  | 32   | 32   | N/A| N/A | N/A | N/A| N/A |
    133     |                        +----------------------+------+------+----+-----+-----+----+-----+
    134     |                        | Normal delay profile | 37   | 37   | N/A| N/A | N/A | N/A| N/A |
    135     |                        +----------------------+------+------+----+-----+-----+----+-----+
    136     |                        | Long delay profile   | 153  | 153  | N/A| N/A | N/A | N/A| N/A |
    137     +------------------------+----------------------+------+------+----+-----+-----+----+-----+
    138     | UMi / UMa O2I          | Normal delay profile | 242                                     |
    139     |                        +----------------------+-----------------------------------------+
    140     |                        | Long delay profile   | 616                                     |
    141     +------------------------+----------------------+-----------------------------------------+
    142 
    143     Parameters
    144     -----------
    145 
    146     model : str
    147         TDL model to use. Must be one of "A", "B", "C", "D", "E", "A30", "B100", or "C300".
    148 
    149     delay_spread : float
    150         RMS delay spread [s].
    151         For the "A30", "B100", and "C300" models, the delay spread must be set
    152         to 30ns, 100ns, and 300ns, respectively.
    153 
    154     carrier_frequency : float
    155         Carrier frequency [Hz]
    156 
    157     num_sinusoids : int
    158         Number of sinusoids for the sum-of-sinusoids model. Defaults to 20.
    159 
    160     los_angle_of_arrival : float
    161         Angle-of-arrival for LoS path [radian]. Only used with LoS models.
    162         Defaults to :math:`\pi/4`.
    163 
    164     min_speed : float
    165         Minimum speed [m/s]. Defaults to 0.
    166 
    167     max_speed : None or float
    168         Maximum speed [m/s]. If set to `None`,
    169         then ``max_speed`` takes the same value as ``min_speed``.
    170         Defaults to `None`.
    171 
    172     num_rx_ant : int
    173         Number of receive antennas.
    174         Defaults to 1.
    175 
    176     num_tx_ant : int
    177         Number of transmit antennas.
    178         Defaults to 1.
    179 
    180     spatial_corr_mat : [num_rx_ant*num_tx_ant,num_rx_ant*num_tx_ant], tf.complex or `None`
    181         Spatial correlation matrix.
    182         If not set to `None`, then ``rx_corr_mat`` and ``tx_corr_mat`` are ignored and
    183         this matrix is used for spatial correlation.
    184         If set to `None` and ``rx_corr_mat`` and ``tx_corr_mat`` are also set to `None`,
    185         then no correlation is applied.
    186         Defaults to `None`.
    187 
    188     rx_corr_mat : [num_rx_ant,num_rx_ant], tf.complex or `None`
    189         Spatial correlation matrix for the receiver.
    190         If set to `None` and ``spatial_corr_mat`` is also set to `None`, then no receive
    191         correlation is applied.
    192         Defaults to `None`.
    193 
    194     tx_corr_mat : [num_tx_ant,num_tx_ant], tf.complex or `None`
    195         Spatial correlation matrix for the transmitter.
    196         If set to `None` and ``spatial_corr_mat`` is also set to `None`, then no transmit
    197         correlation is applied.
    198         Defaults to `None`.
    199 
    200     dtype : Complex tf.DType
    201         Defines the datatype for internal calculations and the output
    202         dtype. Defaults to `tf.complex64`.
    203 
    204     Input
    205     -----
    206 
    207     batch_size : int
    208         Batch size
    209 
    210     num_time_steps : int
    211         Number of time steps
    212 
    213     sampling_frequency : float
    214         Sampling frequency [Hz]
    215 
    216     Output
    217     -------
    218     a : [batch size, num_rx = 1, num_rx_ant = 1, num_tx = 1, num_tx_ant = 1, num_paths, num_time_steps], tf.complex
    219         Path coefficients
    220 
    221     tau : [batch size, num_rx = 1, num_tx = 1, num_paths], tf.float
    222         Path delays [s]
    223 
    224     """
    225 
    226     def __init__(   self,
    227                     model,
    228                     delay_spread,
    229                     carrier_frequency,
    230                     num_sinusoids=20,
    231                     los_angle_of_arrival=PI/4.,
    232                     min_speed=0.,
    233                     max_speed=None,
    234                     num_rx_ant=1,
    235                     num_tx_ant=1,
    236                     spatial_corr_mat=None,
    237                     rx_corr_mat=None,
    238                     tx_corr_mat=None,
    239                     dtype=tf.complex64):
    240 
    241         assert dtype.is_complex, "dtype must be a complex datatype"
    242         self._dtype = dtype
    243         real_dtype = dtype.real_dtype
    244         self._real_dtype = real_dtype
    245 
    246         # Set the file from which to load the model
    247         assert model in ('A', 'B', 'C', 'D', 'E', 'A30', 'B100', 'C300'),\
    248             "Invalid TDL model"
    249         if model == 'A':
    250             parameters_fname = "TDL-A.json"
    251         elif model == 'B':
    252             parameters_fname = "TDL-B.json"
    253         elif model == 'C':
    254             parameters_fname = "TDL-C.json"
    255         elif model == 'D':
    256             parameters_fname = "TDL-D.json"
    257         elif model == 'E':
    258             parameters_fname = "TDL-E.json"
    259         elif model == 'A30':
    260             parameters_fname = "TDL-A30.json"
    261             if delay_spread != 30e-9:
    262                 print("Warning: Delay spread is set to 30ns with this model")
    263                 delay_spread = 30e-9
    264         elif model == 'B100':
    265             parameters_fname = "TDL-B100.json"
    266             if delay_spread != 100e-9:
    267                 print("Warning: Delay spread is set to 100ns with this model")
    268                 delay_spread = 100e-9
    269         elif model == 'C300':
    270             parameters_fname = "TDL-C300.json"
    271             if delay_spread != 300e-9:
    272                 print("Warning: Delay spread is set to 300ns with this model")
    273                 delay_spread = 300e-9
    274 
    275         # Load model parameters
    276         # pylint: disable=possibly-used-before-assignment
    277         self._load_parameters(parameters_fname)
    278 
    279         self._num_rx_ant = num_rx_ant
    280         self._num_tx_ant = num_tx_ant
    281         self._carrier_frequency = tf.constant(carrier_frequency, real_dtype)
    282         self._num_sinusoids = tf.constant(num_sinusoids, tf.int32)
    283         self._los_angle_of_arrival = tf.constant(   los_angle_of_arrival,
    284                                                     real_dtype)
    285         self._delay_spread = tf.constant(delay_spread, real_dtype)
    286         self._min_speed = tf.constant(min_speed, real_dtype)
    287         if max_speed is None:
    288             self._max_speed = self._min_speed
    289         else:
    290             assert max_speed >= min_speed, \
    291                 "min_speed cannot be larger than max_speed"
    292             self._max_speed = tf.constant(max_speed, real_dtype)
    293 
    294         # Pre-compute maximum and minimum Doppler shifts
    295         self._min_doppler = self._compute_doppler(self._min_speed)
    296         self._max_doppler = self._compute_doppler(self._max_speed)
    297 
    298         # Precompute average angles of arrivals for each sinusoid
    299         alpha_const = 2.*PI/num_sinusoids * \
    300                       tf.range(1., self._num_sinusoids+1, 1., dtype=real_dtype)
    301         self._alpha_const = tf.reshape( alpha_const,
    302                                         [   1, # batch size
    303                                             1, # num rx
    304                                             1, # num rx ant
    305                                             1, # num tx
    306                                             1, # num tx ant
    307                                             1, # num clusters
    308                                             1, # num time steps
    309                                             num_sinusoids])
    310 
    311         # Precompute square root of spatial covariance matrices
    312         if spatial_corr_mat is not None:
    313             spatial_corr_mat = tf.cast(spatial_corr_mat, self._dtype)
    314             spatial_corr_mat_sqrt = matrix_sqrt(spatial_corr_mat)
    315             spatial_corr_mat_sqrt = expand_to_rank(spatial_corr_mat_sqrt, 7, 0)
    316             self._spatial_corr_mat_sqrt = spatial_corr_mat_sqrt
    317         else:
    318             self._spatial_corr_mat_sqrt = None
    319             if rx_corr_mat is not None:
    320                 rx_corr_mat = tf.cast(rx_corr_mat, self._dtype)
    321                 rx_corr_mat_sqrt = matrix_sqrt(rx_corr_mat)
    322                 rx_corr_mat_sqrt = expand_to_rank(rx_corr_mat_sqrt, 7, 0)
    323                 self._rx_corr_mat_sqrt = rx_corr_mat_sqrt
    324             else:
    325                 self._rx_corr_mat_sqrt = None
    326             if tx_corr_mat is not None:
    327                 tx_corr_mat = tf.cast(tx_corr_mat, self._dtype)
    328                 tx_corr_mat_sqrt = matrix_sqrt(tx_corr_mat)
    329                 tx_corr_mat_sqrt = expand_to_rank(tx_corr_mat_sqrt, 7, 0)
    330                 self._tx_corr_mat_sqrt = tx_corr_mat_sqrt
    331             else:
    332                 self._tx_corr_mat_sqrt = None
    333 
    334     @property
    335     def num_clusters(self):
    336         r"""Number of paths (:math:`M`)"""
    337         return self._num_clusters
    338 
    339     @property
    340     def los(self):
    341         r"""`True` if this is a LoS model. `False` otherwise."""
    342         return self._los
    343 
    344     @property
    345     def k_factor(self):
    346         r"""K-factor in linear scale. Only available with LoS models."""
    347         assert self._los, "This property is only available for LoS models"
    348         return tf.math.real(self._los_power/self._mean_powers[0])
    349 
    350     @property
    351     def delays(self):
    352         r"""Path delays [s]"""
    353         if self._scale_delays:
    354             return self._delays*self._delay_spread
    355         else:
    356             return self._delays*1e-9 # ns to s
    357 
    358     @property
    359     def mean_powers(self):
    360         r"""Path powers in linear scale"""
    361         if self._los:
    362             mean_powers = tf.concat([self._mean_powers[:1] + self._los_power,
    363                                       self._mean_powers[1:]], axis=0)
    364         else:
    365             mean_powers = self._mean_powers
    366         return tf.math.real(mean_powers)
    367 
    368     @property
    369     def mean_power_los(self):
    370         r"""LoS component power in linear scale.
    371         Only available with LoS models."""
    372         assert self._los, "This property is only available for LoS models"
    373         return tf.math.real(self._los_power)
    374 
    375     @property
    376     def delay_spread(self):
    377         r"""RMS delay spread [s]"""
    378         return self._delay_spread
    379 
    380     @delay_spread.setter
    381     def delay_spread(self, value):
    382         if self._scale_delays:
    383             self._delay_spread = value
    384         else:
    385             print("Warning: The delay spread cannot be set with this model")
    386 
    387     def __call__(self, batch_size, num_time_steps, sampling_frequency):
    388 
    389         # Time steps
    390         sample_times = tf.range(num_time_steps, dtype=self._real_dtype)\
    391             /sampling_frequency
    392         sample_times = tf.expand_dims(insert_dims(sample_times, 6, 0), -1)
    393 
    394         # Generate random maximum Doppler shifts for each sample
    395         # The Doppler shift is different for each TX-RX link, but shared by
    396         # all RX ant and TX ant couple for a given link.
    397         doppler = config.tf_rng.uniform([batch_size,
    398                                          1, # num rx
    399                                          1, # num rx ant
    400                                          1, # num tx
    401                                          1, # num tx ant
    402                                          1, # num clusters
    403                                          1, # num time steps
    404                                          1], # num sinusoids
    405                                         self._min_doppler,
    406                                         self._max_doppler,
    407                                         self._real_dtype)
    408 
    409         # Eq. (7) in the paper [TDL] (see class docstring)
    410         # The angle of arrival is different for each TX-RX link.
    411         theta = config.tf_rng.uniform([batch_size,
    412                                        1, # num rx
    413                                        1, # 1 RX antenna
    414                                        1, # num tx
    415                                        1, # 1 TX antenna
    416                                        self._num_clusters,
    417                                        1, # num time steps
    418                                        self._num_sinusoids],
    419                                       -PI/tf.cast(self._num_sinusoids,
    420                                                   self._real_dtype),
    421                                       PI/tf.cast(self._num_sinusoids,
    422                                                  self._real_dtype),
    423                                       self._real_dtype)
    424         alpha = self._alpha_const + theta
    425 
    426         # Eq. (6a)-(6c) in the paper [TDL] (see class docstring)
    427         phi = config.tf_rng.uniform([batch_size,
    428                                      1, # 1 RX
    429                                      self._num_rx_ant, # 1 RX antenna
    430                                      1, # 1 TX
    431                                      self._num_tx_ant, # 1 TX antenna
    432                                      self._num_clusters,
    433                                      1, # Phase shift shared by all time steps
    434                                      self._num_sinusoids],
    435                                     -PI,
    436                                     PI,
    437                                     self._real_dtype)
    438 
    439         argument = doppler * sample_times * tf.cos(alpha) + phi
    440 
    441         # Eq. (6a) in the paper [SoS]
    442         h = tf.complex(tf.cos(argument), tf.sin(argument))
    443         normalization_factor = 1./tf.sqrt(  tf.cast(self._num_sinusoids,
    444                                             self._real_dtype))
    445         h = tf.complex(normalization_factor, tf.constant(0., self._real_dtype))\
    446             *tf.reduce_sum(h, axis=-1)
    447 
    448         # Scaling by average power
    449         mean_powers = tf.expand_dims(insert_dims(self._mean_powers, 5, 0), -1)
    450         h = tf.sqrt(mean_powers)*h
    451 
    452         # Add specular component to first tap Eq. (11) in [SoS] if LoS
    453         if self._los:
    454             # The first tap follows a Rician
    455             # distribution
    456 
    457             # Specular component phase shift
    458             phi_0 = config.tf_rng.uniform([batch_size,
    459                                            1, # num rx
    460                                            1, # 1 RX antenna
    461                                            1, # num tx
    462                                            1, # 1 TX antenna
    463                                            1, # only the first tap is concerned
    464                                            1], # Shared by all time steps
    465                                           -PI,
    466                                           PI,
    467                                           self._real_dtype)
    468             # Remove the sinusoids dim
    469             doppler = tf.squeeze(doppler, axis=-1)
    470             sample_times = tf.squeeze(sample_times, axis=-1)
    471             arg_spec = doppler*sample_times*tf.cos(self._los_angle_of_arrival)\
    472                     + phi_0
    473             h_spec = tf.complex(tf.cos(arg_spec), tf.sin(arg_spec))
    474 
    475             # Update the first tap with the specular component
    476             h = tf.concat([ h_spec*tf.sqrt(self._los_power) + h[:,:,:,:,:,:1,:],
    477                             h[:,:,:,:,:,1:,:]],
    478                             axis=5) # Path dims
    479 
    480         # Delays
    481         if self._scale_delays:
    482             delays = self._delays*self._delay_spread
    483         else:
    484             delays = self._delays*1e-9 # ns to s
    485         delays = insert_dims(delays, 3, 0)
    486         delays = tf.tile(delays, [batch_size, 1, 1, 1])
    487 
    488         # Apply spatial correlation if required
    489         if self._spatial_corr_mat_sqrt is not None:
    490             h = tf.transpose(h, [0,1,3,5,6,2,4]) # [..., num_rx_ant, num_tx_ant]
    491             #h = flatten_dims(h, 2, tf.rank(h)-2)  # [..., num_rx_ant*num_tx_ant]
    492             h = flatten_last_dims(h, 2) # [..., num_rx_ant*num_tx_ant]
    493             h = tf.expand_dims(h, axis=-1) # [..., num_rx_ant*num_tx_ant, 1]
    494             h = tf.matmul(self._spatial_corr_mat_sqrt, h)
    495             h = tf.squeeze(h, axis=-1)
    496             h = split_dim(h, [self._num_rx_ant, self._num_tx_ant],
    497                             tf.rank(h)-1)  # [..., num_rx_ant, num_tx_ant]
    498             h = tf.transpose(h, [0,1,5,2,6,3,4])
    499         else:
    500             if ( (self._rx_corr_mat_sqrt is not None)
    501                     or (self._tx_corr_mat_sqrt is not None) ):
    502                 h = tf.transpose(h, [0,1,3,5,6,2,4])
    503                 if self._rx_corr_mat_sqrt is not None:
    504                     h = tf.matmul(self._rx_corr_mat_sqrt, h)
    505                 if self._tx_corr_mat_sqrt is not None:
    506                     h = tf.matmul(h, self._tx_corr_mat_sqrt)
    507                 h = tf.transpose(h, [0,1,5,2,6,3,4])
    508 
    509         # Stop gadients to avoid useless backpropagation
    510         h = tf.stop_gradient(h)
    511         delays = tf.stop_gradient(delays)
    512 
    513         return h, delays
    514 
    515     ###########################################
    516     # Internal utility functions
    517     ###########################################
    518 
    519     def _compute_doppler(self, speed):
    520         r"""Compute the maximum radian Doppler frequency [Hz] for a given
    521         speed [m/s].
    522 
    523         The maximum radian Doppler frequency :math:`\omega_d` is calculated
    524         as:
    525 
    526         .. math::
    527             \omega_d = 2\pi  \frac{v}{c} f_c
    528 
    529         where :math:`v` [m/s] is the speed of the receiver relative to the
    530         transmitter, :math:`c` [m/s] is the speed of light and,
    531         :math:`f_c` [Hz] the carrier frequency.
    532 
    533         Input
    534         ------
    535         speed : float
    536             Speed [m/s]
    537 
    538         Output
    539         --------
    540         doppler_shift : float
    541             Doppler shift [Hz]
    542         """
    543         return 2.*PI*speed/SPEED_OF_LIGHT*self._carrier_frequency
    544 
    545     def _load_parameters(self, fname):
    546         r"""Load parameters of a TDL model.
    547 
    548         The model parameters are stored as JSON files with the following keys:
    549         * los : boolean that indicates if the model is a LoS model
    550         * num_clusters : integer corresponding to the number of clusters (paths)
    551         * delays : List of path delays in ascending order normalized by the RMS
    552             delay spread
    553         * powers : List of path powers in dB scale
    554 
    555         For LoS models, the two first paths have zero delay, and are assumed
    556         to correspond to the specular and NLoS component, in this order.
    557 
    558         Input
    559         ------
    560         fname : str
    561             File from which to load the parameters.
    562 
    563         Output
    564         ------
    565         None
    566         """
    567 
    568         source = files(models).joinpath(fname)
    569         # pylint: disable=unspecified-encoding
    570         with open(source) as parameter_file:
    571             params = json.load(parameter_file)
    572 
    573         # LoS scenario ?
    574         self._los = bool(params['los'])
    575 
    576         # Scale the delays
    577         self._scale_delays = bool(params['scale_delays'])
    578 
    579         # Loading cluster delays and mean powers
    580         self._num_clusters = tf.constant(params['num_clusters'], tf.int32)
    581 
    582         # Retrieve power and delays
    583         delays = tf.constant(params['delays'], self._real_dtype)
    584         mean_powers = np.power(10.0, np.array(params['powers'])/10.0)
    585         mean_powers = tf.constant(mean_powers, self._dtype)
    586 
    587         if self._los:
    588             # The power of the specular component of the first path is stored
    589             # separately
    590             self._los_power = mean_powers[0]
    591             mean_powers = mean_powers[1:]
    592             # The first two paths have 0 delays as they correspond to the
    593             # specular and reflected components of the first path.
    594             # We need to keep only one.
    595             delays = delays[1:]
    596 
    597         # Normalize the PDP
    598         if self._los:
    599             norm_factor = tf.reduce_sum(mean_powers) + self._los_power
    600             self._los_power = self._los_power / norm_factor
    601             mean_powers = mean_powers / norm_factor
    602         else:
    603             norm_factor = tf.reduce_sum(mean_powers)
    604             mean_powers = mean_powers / norm_factor
    605 
    606         self._delays = delays
    607         self._mean_powers = mean_powers