Source code for hermespy.channel.delay.random

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

from __future__ import annotations
from typing import Set, Tuple
from typing_extensions import override

from hermespy.core import DeserializationProcess, SerializationProcess
from ..channel import ChannelSampleHook, LinkState
from ..consistent import ConsistentGenerator, ConsistentRealization, ConsistentUniform
from .delay import DelayChannelBase, DelayChannelRealization, DelayChannelSample

__author__ = "Jan Adler"
__copyright__ = "Copyright 2024, Barkhausen Institut gGmbH"
__credits__ = ["Jan Adler"]
__license__ = "AGPLv3"
__version__ = "1.5.0"
__maintainer__ = "Jan Adler"
__email__ = "jan.adler@barkhauseninstitut.org"
__status__ = "Prototype"


[docs] class RandomDelayChannelRealization(DelayChannelRealization): """Realization of a random delay channel. Generated from :class:`RandomDelayChannel's<RandomDelayChannel>` :meth:`_realize<RandomDelayChannel._realize>` routine. """ __consistent_realization: ConsistentRealization __delay_variable: ConsistentUniform __delay: float | Tuple[float, float] def __init__( self, consistent_realization: ConsistentRealization, delay_variable: ConsistentUniform, delay: float | Tuple[float, float], model_propagation_loss: bool, sample_hooks: Set[ChannelSampleHook[DelayChannelSample]], gain: float, ) -> None: # Initialize base class DelayChannelRealization.__init__(self, model_propagation_loss, sample_hooks, gain) # Store attributes self.__consistent_realization = consistent_realization self.__delay_variable = delay_variable self.__delay = delay @property def delay(self) -> float | Tuple[float, float]: """Assumed propagation delay in seconds. If set to a scalar floating point, the delay is assumed to be constant. If set to a tuple of two floats, the tuple values indicate the mininum and maxium values of a uniform distribution, respectively. Raises: ValueError: If the delay is set to a negative value. ValueError: If the delay is set to a tuple of two values where the first value is greater than the second value. """ return self.__delay def _sample(self, state: LinkState) -> DelayChannelSample: if isinstance(self.__delay, float): delay = self.__delay else: # Sample the consistent realization consistent_sample = self.__consistent_realization.sample( state.transmitter.position, state.receiver.position ) # Realize the delay delay = float( self.__delay[0] + (self.__delay[1] - self.__delay[0]) * self.__delay_variable.sample(consistent_sample) ) # Generate a sample return DelayChannelSample(delay, self.model_propagation_loss, self.gain, state)
[docs] @override def serialize(self, process: SerializationProcess) -> None: process.serialize_object(self.__consistent_realization, "consistent_realization") process.serialize_object(self.__delay_variable, "delay_variable") process.serialize_range(self.__delay, "delay") DelayChannelRealization.serialize(self, process)
[docs] @classmethod @override def Deserialize(cls, process: DeserializationProcess) -> RandomDelayChannelRealization: return RandomDelayChannelRealization( process.deserialize_object("consistent_realization", ConsistentRealization), process.deserialize_object("delay_variable", ConsistentUniform), process.deserialize_range("delay"), sample_hooks=set(), **DelayChannelRealization._DeserializeParameters(process), # type: ignore[arg-type] )
[docs] class RandomDelayChannel(DelayChannelBase[RandomDelayChannelRealization]): """Delay channel assuming random propagation delays.""" __DEFAULT_DECORRELATION_DISTANCE = float("inf") __delay: float | Tuple[float, float] def __init__( self, delay: float | Tuple[float, float], decorrelation_distance: float = __DEFAULT_DECORRELATION_DISTANCE, model_propagation_loss: bool = True, gain: float = DelayChannelBase._DEFAULT_GAIN, seed: int | None = None, ) -> None: """ Args: delay: Assumed propagation delay in seconds. If a scalar floating point, the delay is assumed to be constant. If a tuple of two floats, the tuple values indicate the mininum and maxium values of a uniform distribution, respectively. decorrelation_distance: Distance in meters at which the channel decorrelates. By default, the channel is assumed to be static in space. model_propagation_loss: Should free space propagation loss be modeled? Enabled by default. gain: Linear power gain factor a signal experiences when being propagated over this realization. :math:`1.0` by default. seed: Seed used to initialize the pseudo-random number generator. """ # Initialize base class DelayChannelBase.__init__(self, model_propagation_loss, gain, seed) # Store attributes self.delay = delay self.decorrelation_distance = decorrelation_distance self.__consistent_generator = ConsistentGenerator(self) self.__delay_variable = self.__consistent_generator.uniform() @property def delay(self) -> float | Tuple[float, float]: """Assumed propagation delay in seconds. If set to a scalar floating point, the delay is assumed to be constant. If set to a tuple of two floats, the tuple values indicate the mininum and maxium values of a uniform distribution, respectively. Raises: ValueError: If the delay is set to a negative value. ValueError: If the delay is set to a tuple of two values where the first value is greater than the second value. """ return self.__delay @delay.setter def delay(self, value: float | Tuple[float, float]) -> None: if isinstance(value, float): if value < 0.0: raise ValueError(f"Delay must be greater or equal to zero (not {value})") elif isinstance(value, tuple): if len(value) != 2: raise ValueError("Delay limit tuple must contain two entries") if any(v < 0.0 for v in value): raise ValueError( f"Delay must be greater or equal to zero (not {value[0]} and {value[1]})" ) if value[0] > value[1]: raise ValueError("First tuple entry must be smaller or equal to second tuple entry") else: raise ValueError("Unsupported value type") self.__delay = value @property def decorrelation_distance(self) -> float: """Distance in meters at which the channel decorrelates. Raises: ValueError: If the decorrelation distance is set to a negative value. """ return self.__decorrelation_distance @decorrelation_distance.setter def decorrelation_distance(self, value: float) -> None: if value < 0.0: raise ValueError( f"Decorrelation distance must be greater or equal to zero (not {value})" ) self.__decorrelation_distance = value
[docs] def _realize(self) -> RandomDelayChannelRealization: # Realize the consistent generator consistent_realization = self.__consistent_generator.realize(self.decorrelation_distance) # Return the realization return RandomDelayChannelRealization( consistent_realization, self.__delay_variable, self.__delay, self.model_propagation_loss, self.sample_hooks, self.gain, )
[docs] @override def serialize(self, process: SerializationProcess) -> None: process.serialize_object(self.__delay_variable, "delay_variable") process.serialize_range(self.delay, "delay") process.serialize_floating(self.__decorrelation_distance, "decorrelation_distance") DelayChannelBase.serialize(self, process)
[docs] @classmethod @override def Deserialize(cls, process: DeserializationProcess) -> RandomDelayChannel: return RandomDelayChannel( process.deserialize_range("delay"), process.deserialize_floating( "decorrelation_distance", cls.__DEFAULT_DECORRELATION_DISTANCE ), **DelayChannelBase._DeserializeParameters(process), # type: ignore[arg-type] )