Source code for sionna.phy.channel.flat_fading_channel

#
# SPDX-FileCopyrightText: Copyright (c) 2021-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
"""Classes for the simulation of flat-fading channels"""

from typing import Optional, Tuple, Union

import torch

from sionna.phy import Block
from sionna.phy.channel import AWGN
from sionna.phy.channel.spatial_correlation import SpatialCorrelation
from sionna.phy.config import Precision
from sionna.phy.utils import complex_normal

__all__ = [
    "GenerateFlatFadingChannel",
    "ApplyFlatFadingChannel",
    "FlatFadingChannel",
]


[docs] class GenerateFlatFadingChannel(Block): r"""Generates tensors of flat-fading channel realizations This class generates batches of random flat-fading channel matrices. A spatial correlation can be applied. :param num_tx_ant: Number of transmit antennas :param num_rx_ant: Number of receive antennas :param spatial_corr: Spatial correlation to be applied. Defaults to `None`. :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 (e.g., 'cpu', 'cuda:0'). If `None`, :attr:`~sionna.phy.config.Config.device` is used. :input batch_size: `int`. Number of channel matrices to generate. :output h: [batch_size, num_rx_ant, num_tx_ant], `torch.complex`. Batch of random flat fading channel matrices. .. rubric:: Examples .. code-block:: python import torch from sionna.phy.channel import GenerateFlatFadingChannel gen_chn = GenerateFlatFadingChannel(num_tx_ant=4, num_rx_ant=16) h = gen_chn(batch_size=32) print(h.shape) # torch.Size([32, 16, 4]) """ def __init__( self, num_tx_ant: int, num_rx_ant: int, spatial_corr: Optional[SpatialCorrelation] = None, precision: Optional[Precision] = None, device: Optional[str] = None, **kwargs, ) -> None: super().__init__(precision=precision, device=device, **kwargs) self._num_tx_ant = num_tx_ant self._num_rx_ant = num_rx_ant self.spatial_corr = spatial_corr @property def spatial_corr(self) -> Optional[SpatialCorrelation]: r"""Get/set spatial correlation to be applied""" return self._spatial_corr @spatial_corr.setter def spatial_corr(self, value: Optional[SpatialCorrelation]) -> None: self._spatial_corr = value def call(self, batch_size: int) -> torch.Tensor: """Generate batch of random flat fading channel matrices. :param batch_size: Number of channel matrices to generate :output h: Batch of random flat fading channel matrices """ # Generate standard complex Gaussian matrices with unit variance # Uses smart random that switches to global RNG in compiled mode for graph fusion shape = [batch_size, self._num_rx_ant, self._num_tx_ant] h = complex_normal( shape, precision=self.precision, device=self.device, generator=self.torch_rng, ) # Apply spatial correlation if self.spatial_corr is not None: h = self.spatial_corr(h) return h
[docs] class ApplyFlatFadingChannel(Block): r"""Applies given channel matrices to a vector input and adds AWGN This class applies a given tensor of flat-fading channel matrices to an input tensor. AWGN noise can be optionally added. Mathematically, for channel matrices :math:`\mathbf{H}\in\mathbb{C}^{M\times K}` and input :math:`\mathbf{x}\in\mathbb{C}^{K}`, the output is .. math:: \mathbf{y} = \mathbf{H}\mathbf{x} + \mathbf{n} where :math:`\mathbf{n}\in\mathbb{C}^{M}\sim\mathcal{CN}(0, N_o\mathbf{I})` is an AWGN vector that is optionally added. :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 (e.g., 'cpu', 'cuda:0'). If `None`, :attr:`~sionna.phy.config.Config.device` is used. :input x: [batch_size, num_tx_ant], `torch.complex`. Transmit vectors. :input h: [batch_size, num_rx_ant, num_tx_ant], `torch.complex`. Channel realizations. Will be broadcast to the dimensions of ``x`` if needed. :input no: `None` (default) | `torch.Tensor`, `torch.float`. (Optional) noise power ``no`` per complex dimension. Will be broadcast to the shape of ``y``. For more details, see :class:`~sionna.phy.channel.AWGN`. :output y: [batch_size, num_rx_ant], `torch.complex`. Channel output. .. rubric:: Examples .. code-block:: python import torch from sionna.phy.channel import ApplyFlatFadingChannel app_chn = ApplyFlatFadingChannel() x = torch.randn(32, 4, dtype=torch.complex64) h = torch.randn(32, 16, 4, dtype=torch.complex64) y = app_chn(x, h) print(y.shape) # torch.Size([32, 16]) """ def __init__( self, precision: Optional[Precision] = None, device: Optional[str] = None, **kwargs, ) -> None: super().__init__(precision=precision, device=device, **kwargs) self._awgn = AWGN(precision=self.precision, device=self.device) def call( self, x: torch.Tensor, h: torch.Tensor, no: Optional[Union[float, torch.Tensor]] = None, ) -> torch.Tensor: """Apply flat fading channel to input. :param x: Transmit vectors :param h: Channel realizations :param no: Optional noise power per complex dimension :output y: Channel output """ y = (h @ x.unsqueeze(-1)).squeeze(-1) if no is not None: y = self._awgn(y, no) return y
[docs] class FlatFadingChannel(Block): r"""Applies random channel matrices to a vector input and adds AWGN This class combines :class:`~sionna.phy.channel.GenerateFlatFadingChannel` and :class:`~sionna.phy.channel.ApplyFlatFadingChannel` and computes the output of a flat-fading channel with AWGN. For a given batch of input vectors :math:`\mathbf{x}\in\mathbb{C}^{K}`, the output is .. math:: \mathbf{y} = \mathbf{H}\mathbf{x} + \mathbf{n} where :math:`\mathbf{H}\in\mathbb{C}^{M\times K}` are randomly generated flat-fading channel matrices and :math:`\mathbf{n}\in\mathbb{C}^{M}\sim\mathcal{CN}(0, N_o\mathbf{I})` is an AWGN vector that is optionally added. A :class:`~sionna.phy.channel.SpatialCorrelation` can be configured and the channel realizations optionally returned. This is useful to simulate receiver algorithms with perfect channel knowledge. :param num_tx_ant: Number of transmit antennas :param num_rx_ant: Number of receive antennas :param spatial_corr: Spatial correlation to be applied. Defaults to `None`. :param return_channel: Indicates if the channel realizations should be returned. 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 (e.g., 'cpu', 'cuda:0'). If `None`, :attr:`~sionna.phy.config.Config.device` is used. :input x: [batch_size, num_tx_ant], `torch.complex`. Tensor of transmit vectors. :input no: `None` (default) | `torch.Tensor`, `torch.float`. (Optional) noise power ``no`` per complex dimension. Will be broadcast to the shape of ``y``. For more details, see :class:`~sionna.phy.channel.AWGN`. :output y: [batch_size, num_rx_ant], `torch.complex`. Channel output. :output h: [batch_size, num_rx_ant, num_tx_ant], `torch.complex`. Channel realizations. Will only be returned if ``return_channel==True``. .. rubric:: Examples .. code-block:: python import torch from sionna.phy.channel import FlatFadingChannel chn = FlatFadingChannel(num_tx_ant=4, num_rx_ant=16, return_channel=True) x = torch.randn(32, 4, dtype=torch.complex64) y, h = chn(x) print(y.shape) # torch.Size([32, 16]) print(h.shape) # torch.Size([32, 16, 4]) """ def __init__( self, num_tx_ant: int, num_rx_ant: int, spatial_corr: Optional[SpatialCorrelation] = None, return_channel: bool = False, precision: Optional[Precision] = None, device: Optional[str] = None, **kwargs, ) -> None: super().__init__(precision=precision, device=device, **kwargs) self._num_tx_ant = num_tx_ant self._num_rx_ant = num_rx_ant self._return_channel = return_channel self._gen_chn = GenerateFlatFadingChannel( self._num_tx_ant, self._num_rx_ant, spatial_corr, precision=precision, device=device, ) self._app_chn = ApplyFlatFadingChannel(precision=precision, device=device) @property def spatial_corr(self) -> Optional[SpatialCorrelation]: r"""Get/set spatial correlation to be applied""" return self._gen_chn.spatial_corr @spatial_corr.setter def spatial_corr(self, value: Optional[SpatialCorrelation]) -> None: self._gen_chn.spatial_corr = value @property def generate(self) -> GenerateFlatFadingChannel: r"""Access the internal :class:`GenerateFlatFadingChannel`""" return self._gen_chn @property def apply(self) -> ApplyFlatFadingChannel: r"""Access the internal :class:`ApplyFlatFadingChannel`""" return self._app_chn def call( self, x: torch.Tensor, no: Optional[Union[float, torch.Tensor]] = None, ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]: """Apply flat fading channel to input. :param x: Transmit vectors :param no: Optional noise power per complex dimension :output y: Channel output. :output h: (Optional) Channel realizations. Returned only if ``return_channel`` is `True`. """ # Generate a batch of channel realizations batch_size = x.shape[0] h = self._gen_chn(batch_size) # Apply the channel to the input y = self._app_chn(x, h, no) if self._return_channel: return y, h else: return y