#
# SPDX-FileCopyrightText: Copyright (c) 2021-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
"""Class for generating channel responses in the time domain"""
from typing import Optional
import torch
from sionna.phy.object import Object
from sionna.phy.channel.utils import cir_to_time_channel
__all__ = ["GenerateTimeChannel"]
[docs]
class GenerateTimeChannel(Object):
# pylint: disable=line-too-long
r"""
Generate channel responses in the time domain
For each batch example, ``num_time_samples`` + ``l_max`` - ``l_min`` time steps of a
channel realization are generated by this layer.
These can be used to filter a channel input of length ``num_time_samples`` using the
:class:`~sionna.phy.channel.ApplyTimeChannel` layer.
The channel taps :math:`\bar{h}_{b,\ell}` (``h_time``) returned by this layer
are computed assuming a sinc filter is used for pulse shaping and receive filtering.
Therefore, given a channel impulse response
:math:`(a_{m}(t), \tau_{m}), 0 \leq m \leq M-1`, generated by the ``channel_model``,
the channel taps are computed as follows:
.. math::
\bar{h}_{b, \ell}
= \sum_{m=0}^{M-1} a_{m}\left(\frac{b}{W}\right)
\text{sinc}\left( \ell - W\tau_{m} \right)
for :math:`\ell` ranging from ``l_min`` to ``l_max``, and where :math:`W` is
the ``bandwidth``.
:param channel_model: Channel model to be used
:param bandwidth: Bandwidth (:math:`W`) [Hz]
:param num_time_samples: Number of time samples forming the channel input (:math:`N_B`)
:param l_min: Smallest time-lag for the discrete complex baseband channel (:math:`L_{\text{min}}`)
:param l_max: Largest time-lag for the discrete complex baseband channel (:math:`L_{\text{max}}`)
:param normalize_channel: If set to `True`, the channel is normalized over the block size
to ensure unit average energy per time step. Defaults to `False`.
:param precision: Precision used for internal calculations and outputs.
If set to `None`, :attr:`~sionna.phy.config.Config.precision` is used.
:param device: Device for computation. If `None`,
:attr:`~sionna.phy.config.Config.device` is used.
:input batch_size: `None` (default) | `int`.
Batch size. Defaults to `None` for channel models that do not require this parameter.
:output h_time: [batch size, num_rx, num_rx_ant, num_tx, num_tx_ant, num_time_samples + l_max - l_min, l_max - l_min + 1], `torch.complex`.
Channel responses.
For each batch example, ``num_time_samples`` + ``l_max`` - ``l_min`` time steps of a
channel realization are generated by this layer.
These can be used to filter a channel input of length ``num_time_samples`` using the
:class:`~sionna.phy.channel.ApplyTimeChannel` layer.
.. rubric:: Examples
.. code-block:: python
import torch
from sionna.phy.channel import RayleighBlockFading, GenerateTimeChannel
channel_model = RayleighBlockFading(num_rx=1, num_rx_ant=2, num_tx=1, num_tx_ant=4)
gen_channel = GenerateTimeChannel(
channel_model,
bandwidth=1e6,
num_time_samples=100,
l_min=-6,
l_max=20
)
h_time = gen_channel(batch_size=32)
print(h_time.shape)
# torch.Size([32, 1, 2, 1, 4, 126, 27])
"""
def __init__(
self,
channel_model,
bandwidth: float,
num_time_samples: int,
l_min: int,
l_max: int,
normalize_channel: bool = False,
precision: Optional[str] = None,
device: Optional[str] = None,
**kwargs,
) -> None:
super().__init__(precision=precision, device=device, **kwargs)
self._cir_sampler = channel_model
self._l_min = l_min
self._l_max = l_max
self._l_tot = l_max - l_min + 1
self._bandwidth = bandwidth
self._num_time_steps = num_time_samples
self._normalize_channel = normalize_channel
@property
def l_min(self) -> int:
"""Smallest time-lag"""
return self._l_min
@property
def l_max(self) -> int:
"""Largest time-lag"""
return self._l_max
@property
def l_tot(self) -> int:
"""Total number of channel taps"""
return self._l_tot
@property
def bandwidth(self) -> float:
"""Bandwidth [Hz]"""
return self._bandwidth
@property
def num_time_samples(self) -> int:
"""Number of time samples"""
return self._num_time_steps
def __call__(self, batch_size: Optional[int] = None) -> torch.Tensor:
"""Generate time domain channel response.
:param batch_size: Batch size. Defaults to `None` for channel models
that do not require this parameter.
:output h_time: Channel taps coefficients
"""
# Sample channel impulse responses
h, tau = self._cir_sampler(
batch_size, self._num_time_steps + self._l_tot - 1, self._bandwidth
)
# Convert CIR to time domain channel
h_time = cir_to_time_channel(
self._bandwidth, h, tau, self._l_min, self._l_max, self._normalize_channel
)
return h_time