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