Source code for hermespy.channel.channel

# -*- coding: utf-8 -*-

from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Any, Dict, Generic, Optional, Tuple, Type, TypeVar, TYPE_CHECKING
from h5py import Group

from hermespy.core import (
    Device,
    DeviceOutput,
    RandomNode,
    SerializableEnum,
    Signal,
    ChannelStateInformation,
    Serializable,
)

if TYPE_CHECKING:
    from hermespy.simulation import SimulatedDevice, SimulationScenario  # pragma: no cover

__author__ = "Andre Noll Barreto"
__copyright__ = "Copyright 2023, Barkhausen Institut gGmbH"
__credits__ = ["Andre Noll Barreto", "Tobias Kronauer", "Jan Adler"]
__license__ = "AGPLv3"
__version__ = "1.2.0"
__maintainer__ = "Jan Adler"
__email__ = "jan.adler@barkhauseninstitut.org"
__status__ = "Prototype"


[docs] class InterpolationMode(SerializableEnum): """Interpolation behaviour for sampling and resampling routines. Considering a complex time series .. math:: \\mathbf{s} = \\left[s_{0}, s_{1},\\,\\dotsc, \\, s_{M-1} \\right]^{\\mathsf{T}} \\in \\mathbb{C}^{M} \\quad \\text{with} \\quad s_{m} = s(\\frac{m}{f_{\\mathrm{s}}}) sampled at rate :math:`f_{\\mathrm{s}}`, so that each sample represents a discrete sample of a time-continuous underlying function :math:`s(t)`. Given only the time-discrete sample vector :math:`\\mathbf{s}`, resampling refers to .. math:: \\hat{s}(\\tau) = \\mathscr{F} \\left\\lbrace \\mathbf{s}, \\tau \\right\\rbrace estimating a sample of the original time-continuous function at time :math:`\\tau` given only the discrete-time sample vector :math:`\\mathbf{s}`. """ NEAREST = 0 """Interpolate to the nearest sampling instance. .. math:: \\hat{s}(\\tau) = s_{\\lfloor \\tau f_{\\mathrm{s}} \\rfloor} Very fast, but not very accurate. """ SINC = 1 """Interpolate using sinc kernels. Also known as the Whittaker-Kotel'nikov-Shannon interpolation formula :footcite:p:`2002:meijering`. .. math:: \\hat{s}(\\tau) = \\sum_{m=0}^{M-1} s_{m} \\operatorname{sinc} \\left( \\tau f_{\\mathrm{s}} - m \\right) Perfect for bandlimited signals, not very fast. """
CRT = TypeVar("CRT", bound="ChannelRealization") """Type of channel realization""" CT = TypeVar("CT", bound="Channel") """Type of channel"""
[docs] class ChannelRealization(object): """Realization of a wireless channel channel model. Channel realizations represent the channel state during signal propagation. They are generated by the :meth:`realize()<Channel.realize>` method of :class:`.Channel` instances. """ __alpha_device: Device __beta_device: Device __interpolation_mode: InterpolationMode def __init__( self, alpha_device: Device, beta_device: Device, gain: float, interpolation_mode: InterpolationMode = InterpolationMode.NEAREST, ) -> None: """ Args: alpha_device (Device): First device linked by the :class:`.Channel` instance that generated this realization. beta_device (Device): Second device linked by the :class:`.Channel` instance that generated this realization. gain (float): Linear power gain factor a signal experiences when being propagated over this realization. interpolation_mode (InterpolationMode, optional): Interpolation behaviour of the channel realization's delay components with respect to the proagated signal's sampling rate. """ # Initialize class attributes self.__alpha_device = alpha_device self.__beta_device = beta_device self.__gain = gain self.__interpolation_mode = interpolation_mode @property def alpha_device(self) -> Device: """First device linked by the :class:`.Channel` instance that generated this realization.""" return self.__alpha_device @property def beta_device(self) -> Device: """Second device linked by the :class:`.Channel` instance that generated this realization.""" return self.__beta_device @property def gain(self) -> float: """Linear power gain factor a signal experiences when being propagated over this realization.""" return self.__gain @property def interpolation_mode(self) -> InterpolationMode: """Default interpolation mode. Referred to by the :meth:`propagate<.propagate>` and :meth:`state<.state>` routines if no interpolation mode is specified. """ return self.__interpolation_mode
[docs] @abstractmethod def _propagate( self, signal: Signal, transmitter: Device, receiver: Device, interpolation: InterpolationMode, ) -> Signal: """Propagate radio-frequency band signals over a channel instance. Abstract subroutine of :meth:`propagate()<ChannelRealization.propagate>`. Args: signal (Signal): The signal to be propagated. transmitter (Device): Device transmitting the `signal` to be propagated over this realization. receiver (Device): Device receiving the propagated `signal` after propagation. interpolation (InterpolationMode): Interpolation behaviour of the channel realization's delay components with respect to the proagated signal's sampling rate. Returns: The propagated signal. """ ... # pragma: no cover
[docs] def propagate( self: CRT, signal: DeviceOutput | Signal, transmitter: Device | None = None, receiver: Device | None = None, interpolation_mode: InterpolationMode | None = None, ) -> ChannelPropagation[CRT]: """Propagate a signal model over this realization. Let .. math:: \\mathbf{X} = \\left[ \\mathbf{x}^{(0)}, \\mathbf{x}^{(1)},\\, \\dots,\\, \\mathbf{x}^{(M_\\mathrm{Tx} - 1)} \\right] \\in \\mathbb{C}^{N_\\mathrm{Tx} \\times M_\\mathrm{Tx}} be the `signal` transmitted by `transmitter` and .. math:: \\mathbf{Y} = \\left[ \\mathbf{y}^{(0)}, \\mathbf{y}^{(1)},\\, \\dots,\\, \\mathbf{x}^{(M_\\mathrm{Rx} - 1)} \\right] \\in \\mathbb{C}^{N_\\mathrm{Rx} \\times M_\\mathrm{Rx}} the reception of `receiver`, this method implements the channel propagation equation .. math:: \\mathbf{y}^{(m)} = \\sum_{\\tau = 0}^{m} \\mathbf{H}^{(m, \\tau)} \mathbf{x}^{(m-\\tau)} \\ \\text{.} It wraps :meth:`._propagate`, applies the channel :attr:`.gain` and returns a :class:`ChannelPropagation<hermespy.channel.channel.ChannelPropagation>` instance. If not specified, the transmitter and receiver are assumed to be the devices linked by the channel instance that generated this realization, meaning the transmitter is :attr:`alpha_device<.alpha_device>` and receiver is :attr:`beta_device<.beta_device>`. Args: signal (DeviceOutput | Signal): Signal model to be propagated. transmitter (Device, optional): Device transmitting the `signal` to be propagated over this realization. If not specified :attr:`alpha_device<.alpha_device>` will be assumed. receiver (Device, optional): Device receiving the propagated `signal` after propagation. If not specified :attr:`beta_device<.beta_device>` will be assumed. interpolation_mode (InterpolationMode, optional): Interpolation behaviour of the channel realization's delay components with respect to the proagated signal's sampling rate. If not specified, the realization's default :attr:`.interpolation_mode` will be assumed. Returns: All information generated by the propagation. """ # Infer parameters _transmitter = self.alpha_device if transmitter is None else transmitter _receiver = self.beta_device if receiver is None else receiver _interpolation_mode = ( self.__interpolation_mode if interpolation_mode is None else interpolation_mode ) # Convert signal argument to signal model if isinstance(signal, DeviceOutput): _signal = signal.mixed_signal elif isinstance(signal, Signal): _signal = signal else: raise ValueError("Signal is of unsupported type") # Assert that the signal's number of streams matches the number of antennas of the transmitter if _signal.num_streams != _transmitter.antennas.num_transmit_antennas: raise ValueError( f"Number of signal streams to be propagated does not match the number of transmitter antennas ({_signal.num_streams} != {_transmitter.antennas.num_transmit_antennas}))" ) # Propagate signal propagated_signal = self._propagate(_signal, _transmitter, _receiver, _interpolation_mode) # Apply channel gain propagated_signal.samples *= self.gain**0.5 # Return resulting channel propagation propagation = ChannelPropagation[CRT]( self, propagated_signal, _transmitter, _receiver, _interpolation_mode ) return propagation
[docs] @abstractmethod def state( self, transmitter: Device, receiver: Device, delay: float, sampling_rate: float, num_samples: int, max_num_taps: int, ) -> ChannelStateInformation: """Generate the discrete channel state information from this channel realization. Denoted by .. math:: \\mathbf{H}^{(m, \\tau)} \\in \\mathbb{C}^{N_{\\mathrm{Rx}} \\times N_{\\mathrm{Tx}}} within the respective equations. Args: transmitter (Device): Device transmitting the signal for which the channel state information is generated. receiver (Device): Device receiving the signal for which the channel state information is generated. delay (float): Delay in seconds at which the state information should be generated. sampling_rate (float): Sampling rate of the state information's discrete samples in :math:`\\mathrm{Hz}`. num_samples (int): Number of discrete time-domain samples of the chanel state information. max_num_taps (int): Maximum number of delay taps considered per discrete time-domain sample. Returns: The channel state information representing this channel realization. """ ... # pragma: no cover
[docs] def to_HDF(self, group: Group) -> None: """Serialize the object state to HDF5. Dumps the object's state and additional information to a HDF5 group. Args: group (h5py.Group): The HDF5 group to which the object is serialized. """ group.attrs["gain"] = self.gain group.attrs["interpolation_mode"] = self.interpolation_mode.value
@classmethod def _parameters_from_HDF(cls: Type[ChannelRealization], group: Group) -> Dict[str, Any]: """Deserialize the object's parameters from HDF5. Intended to be used as a subroutine of :meth:`From_HDF`. Returns: The object's parmeters as a keyword argument dictionary. """ return { "gain": group.attrs.get("gain", 1.0), "interpolation_mode": InterpolationMode(group.attrs.get("interpolation_mode", 0)), }
[docs] @classmethod def From_HDF(cls: Type[CRT], group: Group, alpha_device: Device, beta_device: Device) -> CRT: """De-Serialized the object state from HDF5. Recalls the object's state from a HDF5 group. Args: group (h5py.Group): The HDF5 group from which the object state is recalled. alpha_device (Device): First device linked by the :class:`.Channel` instance that generated this realization. beta_device (Device): Second device linked by the :class:`.Channel` instance that generated this realization. Returns: The object initialized from the HDF5 group state. """ return cls(alpha_device, beta_device, **cls._parameters_from_HDF(group))
[docs] class DirectiveChannelRealization(Generic[CRT]): """Channel realization with specified directivity. Generated by :attr:`realization<ChannelPropagation.realization>` property of :class:`ChannelPropagations<ChannelPropagation>`. """ __transmitter: Device __receiver: Device __realization: CRT def __init__(self, transmitter: Device, receiver: Device, realization: CRT) -> None: """ Args: transmitter (Device): Device transmitting into the realized channel model. Initializes the :attr:`.transmitter` attribute. receiver (Device): Device receiving from the realized channel model. Initializes the :attr:`.receiver` attribute. realization (CRT): Realization of the channel linked by `transmitter` and `receiver`. Initializes the :attr:`.realization` attribute. """ # Initialize class attributes self.__transmitter = transmitter self.__receiver = receiver self.__realization = realization @property def transmitter(self) -> Device: """Device transmitting into the realized channel model.""" return self.__transmitter @property def receiver(self) -> Device: """Device receiving from the realized channel model.""" return self.__receiver @property def realization(self) -> CRT: """Wrapped non-directive channel realization.""" return self.__realization
[docs] def propagate( self, signal: DeviceOutput | Signal, interpolation_mode: InterpolationMode | None = None ) -> ChannelPropagation[CRT]: """Propagate a signal model over this realization. Let .. math:: \\mathbf{X} = \\left[ \\mathbf{x}^{(0)}, \\mathbf{x}^{(1)},\\, \\dots,\\, \\mathbf{x}^{(M_\\mathrm{Tx} - 1)} \\right] \\in \\mathbb{C}^{N_\\mathrm{Tx} \\times M_\\mathrm{Tx}} be the `signal` transmitted by :attr:`.transmitter` and .. math:: \\mathbf{Y} = \\left[ \\mathbf{y}^{(0)}, \\mathbf{y}^{(1)},\\, \\dots,\\, \\mathbf{x}^{(M_\\mathrm{Rx} - 1)} \\right] \\in \\mathbb{C}^{N_\\mathrm{Rx} \\times M_\\mathrm{Rx}} the reception of :attr:`.receiver`, this method implements the channel propagation equation .. math:: \\mathbf{y}^{(m)} = \\sum_{\\tau = 0}^{M_\\mathrm{Rx} - M_\\mathrm{Tx}} \\mathbf{H}^{(m, \\tau)} \mathbf{x}^{(m-\\tau)} \\ \\text{.} Args: signal (DeviceOutput | Signal): Signal model to be propagated. interpolation_mode (InterpolationMode, optional): Interpolation behaviour of the channel realization's delay components with respect to the proagated signal's sampling rate. If not specified, the realization's initialization value will be assumed. Returns: All information generated by the propagation. """ return self.__realization.propagate( signal, self.__transmitter, self.__receiver, interpolation_mode )
[docs] def state( self, delay: float, sampling_rate: float, num_samples: int, max_num_taps: int ) -> ChannelStateInformation: """Generate the discrete channel state information from this channel realization. Denoted by .. math:: \\mathbf{H}^{(m, \\tau)} \\in \\mathbb{C}^{N_{\\mathrm{Rx}} \\times N_{\\mathrm{Tx}}} within the respective equations. Args: delay (float): Delay in seconds at which the state information should be generated. sampling_rate (float): Sampling rate of the state information's discrete samples in :math:`\\mathrm{Hz}`. num_samples (int): Number of discrete time-domain samples of the chanel state information. max_num_taps (int): Maximum number of delay taps considered per discrete time-domain sample. Returns: The channel state information representing this channel realization. """ return self.__realization.state( self.__transmitter, self.__receiver, delay, sampling_rate, num_samples, max_num_taps )
[docs] class ChannelPropagation(Generic[CRT]): """Propagation over a channel realization. Generated by :meth:`ChannelRealization.propagate` or :meth:`Channel.propagate`. """ __realization: CRT __signal: Signal __transmitter: Device __receiver: Device __interpolation_mode: InterpolationMode def __init__( self, realization: CRT, signal: Signal, transmitter: Device, receiver: Device, interpolation_mode: InterpolationMode, ) -> None: """ Args: realization (CRT): Realization instance that generated this propagation. signal (Signal): Signale emerging after channel propagation. transmitter (Device): Device the signal was transmitted from. receiver (Device): Device the signal was received at. interpolation_mode (InterpolationMode): Interpolation mode used for channel propagation. """ # Initialize class attributes self.__realization = realization self.__signal = signal self.__transmitter = transmitter self.__receiver = receiver self.__interpolation_mode = interpolation_mode @property def realization(self) -> DirectiveChannelRealization[CRT]: """The channel realization used for propagation.""" return DirectiveChannelRealization[CRT](self.transmitter, self.receiver, self.__realization) @property def signal(self) -> Signal: """The emerging signal after channel propagation.""" return self.__signal @property def transmitter(self) -> Device: """The device the signal was transmitted from.""" return self.__transmitter @property def receiver(self) -> Device: """The device the signal was received at.""" return self.__receiver @property def interpolation_mode(self) -> InterpolationMode: """The interpolation mode used for channel propagation.""" return self.__interpolation_mode
[docs] def state( self, delay: float, sampling_rate: float, num_samples: int, max_num_taps: int ) -> ChannelStateInformation: """Generate the discrete channel state information from this channel realization. Resolves to :meth:`state()<ChannelRealization.state>` method of the :attr:`realization` attribute. Denoted by .. math:: \\mathbf{H}^{(m, \\tau)} \\in \\mathbb{C}^{N_{\\mathrm{Rx}} \\times N_{\\mathrm{Tx}}} within the respective equations. Args: delay (float): Delay in seconds at which the state information should be generated. sampling_rate (float): Sampling rate of the state information's discrete samples in :math:`\\mathrm{Hz}`. num_samples (int): Number of discrete time-domain samples of the chanel state information. max_num_taps (int): Maximum number of delay taps considered per discrete time-domain sample. Returns: The channel state information representing this channel realization. """ # Query the realization's channel state state = self.__realization.state( self.transmitter, self.receiver, delay, sampling_rate, num_samples, max_num_taps ) # Return the channel state return state
[docs] class Channel(ABC, RandomNode, Serializable, Generic[CRT]): """Abstract base class of all channel models. The channel model represents the basic configuration of two linked :doc:`SimulatedDevices<simulation.simulated_device.SimulatedDevice>` :meth:`alpha_device<.alpha_device>` and :meth:`beta_device<.beta_device>` exchanging electromagnetic :doc:`Signals<core.signal_model.Signal>`. Each invokation of :meth:`.propagate` and :meth:`.realize` will generate a new :doc:`channel.channel.ChannelRealization` instance by internally calling :meth:`._realize`. In the case of a :meth:`propagate` call the generated :doc:`channel.channel.ChannelRealization` will additionally be wrapped in a :doc:`channel.channel.ChannelPropagation`. The channel model represents the matrix function of time :math:`t` and delay :math:`\\tau` .. math:: \\mathbf{H}(t, \\tau; \\mathbf{\\zeta}) \\in \\mathbb{C}^{N_{\\mathrm{Rx}} \\times N_{\\mathrm{Tx}}} \\ \\text{,} the dimensionality of which depends on the number of transmitting antennas :math:`N_{\\mathrm{Tx}}` and number of receiving antennas :math:`N_{\\mathrm{Rx}}`. The vector :math:`\\mathbf{\\zeta}` represents the channel model's paramteres as random variables. Realizing the channel model is synonymous with realizing and "fixing" these random parameters by drawing a sample from their respective distributions, so that a :doc:`channel.channel.ChannelRealization` represents the deterministic function .. math:: \\mathbf{H}(t, \\tau) \\in \\mathbb{C}^{N_{\\mathrm{Rx}} \\times N_{\\mathrm{Tx}}} \\ \\text{.} """ __alpha_device: SimulatedDevice | None __beta_device: SimulatedDevice | None __scenario: SimulationScenario __gain: float __interpolation_mode: InterpolationMode __last_realization: CRT | None def __init__( self, alpha_device: SimulatedDevice | None = None, beta_device: SimulatedDevice | None = None, gain: float = 1.0, interpolation_mode: InterpolationMode = InterpolationMode.NEAREST, devices: Tuple[SimulatedDevice, SimulatedDevice] | None = None, seed: Optional[int] = None, ) -> None: """ Args: alpha_device (SimulatedDevice, optional): First device linked by this channel. Initializes the :meth:`alpha_device<alpha_device>` property. If not specified the channel is considered floating, meaning a call to :meth:`realize` will raise an exception. beta_device (SimulatedDevice, optional): Second device linked by this channel. Initializes the :meth:`beta_device<beta_device>` property. If not specified the channel is considered floating, meaning a call to :meth:`realize` will raise an exception. gain (float, optional): Linear channel power gain factor. Initializes the :meth:`gain<gain>` property. :math:`1.0` by default. interpolation_mode (InterpolationMode, optional): Interpolation behaviour of the channel realization's delay components with respect to the proagated signal's sampling rate. :attr:`NEAREST<InterpolationMode.NEAREST>` by default, meaning no resampling is required. devices (Tuple[SimulatedDevice, SimulatedDevice], optional): Tuple of devices connected by this channel model. seed (int, optional): Seed used to initialize the pseudo-random number generator. """ # Initialize base classes # Must be first in order for correct diamond resolve Serializable.__init__(self) RandomNode.__init__(self, seed=seed) # Default parameters self.__alpha_device = None self.__beta_device = None self.gain = gain self.interpolation_mode = interpolation_mode self.__scenario = None self.__last_realization = None if alpha_device is not None: self.alpha_device = alpha_device if beta_device is not None: self.beta_device = beta_device if devices is not None: if self.alpha_device is not None or self.beta_device is not None: raise ValueError( "Can't use 'devices' initialization argument in combination with specifying a alpha / beta devices" ) self.alpha_device = devices[0] self.beta_device = devices[1] @property def alpha_device(self) -> SimulatedDevice | None: """First device linked by this channel. Referred to as :math:`\\alpha` in the respective equations. If not specified, i.e. :py:obj:`None`, the channel is considered floating, meaning a call to :meth:`realize` will raise an exception. """ return self.__alpha_device @alpha_device.setter def alpha_device(self, value: SimulatedDevice) -> None: self.__alpha_device = value @property def beta_device(self) -> SimulatedDevice | None: """Second device linked by this channel. Referred to as :math:`\\beta` in the respective equations. If not specified, i.e. :py:obj:`None`, the channel is considered floating, meaning a call to :meth:`realize` will raise an exception. """ return self.__beta_device @beta_device.setter def beta_device(self, value: SimulatedDevice) -> None: self.__beta_device = value @property def scenario(self) -> SimulationScenario | None: """Simulation scenario the channel belongs to. Handle to the :class:`Scenario <hermespy.simulation.simulation.SimulationScenario>` this channel is asigned to. :py:obj:`None` if the channel is not part of any specific :class:`Scenario <hermespy.simulation.simulation.SimulationScenario>`. The recommended way to set the scenario is by calling the :meth:`set_channel<hermespy.simulation.simulation. SimulationScenario.set_channel>` method: .. code-block:: python from hermespy.simulation import SimulationScenario scenario = SimulationScenario() alpha_device = scenario.new_device() beta_device = scenario.new_device() channel = Channel() scenario.set_channel(alpha_device, beta_device, channel) """ return self.__scenario @scenario.setter def scenario(self, value: SimulationScenario) -> None: self.__scenario = value self.random_mother = value @property def gain(self) -> float: """Linear channel power gain factor. The default channel gain is 1. Realistic physical channels should have a gain less than one. For configuring logarithmic gains, set the attribute using the dB shorthand: .. code-block:: python from hermespy.core import dB # Configure a 10 dB gain channel.gain = dB(10) Raises: ValueError: For gains smaller than zero. """ return self.__gain @gain.setter def gain(self, value: float) -> None: if value < 0.0: raise ValueError("Channel gain must be greater or equal to zero") self.__gain = value @property def interpolation_mode(self) -> InterpolationMode: """Interpolation behaviour of the channel realization's delay components with respect to the proagated signal's sampling rate.""" return self.__interpolation_mode @interpolation_mode.setter def interpolation_mode(self, value: InterpolationMode) -> None: self.__interpolation_mode = value
[docs] @abstractmethod def _realize(self) -> CRT: """Generate a new channel realzation. Abstract subroutine of :meth:`.realize`. Each :class:`Channel` is required to implement their own :meth:`._realize` method. Returns: A new channel realization. """ ... # pragma: no cover
[docs] def realize(self, cache: bool = True) -> CRT: """Generate a new channel realization. If `cache` is enabled, :attr:`.realization` will be updated to the newly generated :class:`ChannelRealization<hermespy.channel.channel.ChannelRealization>`. Args: cache (bool, optional): Cache the realization. Enabled by default. Returns: A new channel realization. """ # Generate a new realization realization = self._realize() # Cache if the respective flag is enabled if cache: self.__last_realization = realization return realization
@property def realization(self) -> CRT | None: """The last realization used for channel propagation. Updated every time :meth:`.propagate` or :meth:`.realize` are called and `cache` is enabled. :py:obj:`None` if :meth:`.realize` has not been called yet. """ return self.__last_realization
[docs] def propagate( self, signal: DeviceOutput | Signal, transmitter: Device | None = None, receiver: Device | None = None, interpolation_mode: InterpolationMode = InterpolationMode.NEAREST, cache: bool = True, ) -> ChannelPropagation[CRT]: """Propagate radio-frequency band signals over this channel. Generates a new channel realization by calling :meth:`realize<.realize>` and propagates the provided signal over it. Let .. math:: \\mathbf{X} = \\left[ \\mathbf{x}^{(0)}, \\mathbf{x}^{(1)},\\, \\dots,\\, \\mathbf{x}^{(M_\\mathrm{Tx} - 1)} \\right] \\in \\mathbb{C}^{N_\\mathrm{Tx} \\times M_\\mathrm{Tx}} be the `signal` transmitted by `transmitter` and .. math:: \\mathbf{Y} = \\left[ \\mathbf{y}^{(0)}, \\mathbf{y}^{(1)},\\, \\dots,\\, \\mathbf{x}^{(M_\\mathrm{Rx} - 1)} \\right] \\in \\mathbb{C}^{N_\\mathrm{Rx} \\times M_\\mathrm{Rx}} the reception of `receiver`, this method implements the channel propagation equation .. math:: \\mathbf{y}^{(m)} = \\sum_{\\tau = 0}^{m} \\mathbf{H}^{(m, \\tau)} \mathbf{x}^{(m-\\tau)} \\ \\text{.} Args: signal (DeviceOutput | Signal): Signal models emitted by `transmitter` associated with this wireless channel model. transmitter (Device, optional): Device transmitting the `signal` to be propagated over this realization. If not specified :meth:`alpha_device<.alpha_device>` will be assumed. receiver (Device, optional): Device receiving the propagated `signal` after propagation. If not specified :meth:`beta_device<.beta_device>` will be assumed. interpolation_mode (InterpolationMode, optional): Interpolation behaviour of the channel realization's delay components with respect to the proagated signal's sampling rate. cache (bool, optional): Should the generated realization be cached at this channel instance? Enabled by default. Returns: The channel propagation resulting from the signal propagation. """ # Infer parameters _transmitter = self.alpha_device if transmitter is None else transmitter _receiver = self.beta_device if receiver is None else receiver # Generate a new realization realization = self.realize(cache=cache) # Propagate the provided signal propagation = realization.propagate(signal, _transmitter, _receiver, interpolation_mode) # Return resulting propagation return propagation
[docs] @abstractmethod def recall_realization(self, group: Group) -> CRT: """Recall a realization of this channel type from its HDF serialization. Args: group (h5py.Group): HDF group to which the channel realization was serialized. Returns: The recalled realization instance. """ ... # pragma: no cover