# -*- coding: utf-8 -*-
from __future__ import annotations
from typing import Set
import numpy as np
from h5py import Group
from .channel import (
Channel,
ChannelRealization,
ChannelSample,
LinkState,
ChannelSampleHook,
InterpolationMode,
)
from hermespy.core import ChannelStateInformation, ChannelStateFormat, SignalBlock
__author__ = "Jan Adler"
__copyright__ = "Copyright 2024, Barkhausen Institut gGmbH"
__credits__ = ["Andre Noll Barreto", "Tobias Kronauer", "Jan Adler"]
__license__ = "AGPLv3"
__version__ = "1.3.0"
__maintainer__ = "Jan Adler"
__email__ = "jan.adler@barkhauseninstitut.org"
__status__ = "Prototype"
[docs]
class IdealChannelSample(ChannelSample):
"""Sample of an ideal channel realization.
Generated by the :meth:`_sample<IdealChannelRealization._sample>` routine of :class:`IdealChannelRealization`.
"""
def __init__(self, gain: float, state: LinkState) -> None:
"""
Args:
gain (float):
Linear channel power factor.
state (ChannelState):
State of the channel at the time of sampling.
"""
# Initialize base class
ChannelSample.__init__(self, state)
# Initialize class attributes
self.__gain = gain
@property
def expected_energy_scale(self) -> float:
return self.__gain
[docs]
def state(
self,
num_samples: int,
max_num_taps: int,
interpolation_mode: InterpolationMode = InterpolationMode.NEAREST,
) -> ChannelStateInformation:
# MISO case
if self.num_receive_antennas == 1:
spatial_response = np.ones(
(1, self.transmitter_state.antennas.num_transmit_antennas), dtype=np.complex_
)
# SIMO case
elif self.num_transmit_antennas == 1:
spatial_response = np.ones(
(self.receiver_state.antennas.num_receive_antennas, 1), dtype=np.complex_
)
# MIMO case
else:
spatial_response = np.eye(
self.num_receive_antennas, self.num_transmit_antennas, dtype=np.complex_
)
# Scale response by channel gain
spatial_response *= np.sqrt(self.__gain)
sampled_state = np.expand_dims(
np.repeat(spatial_response[:, :, np.newaxis], num_samples, 2), axis=3
)
return ChannelStateInformation(ChannelStateFormat.IMPULSE_RESPONSE, sampled_state)
[docs]
def _propagate(self, signal: SignalBlock, interpolation: InterpolationMode) -> SignalBlock:
# Single antenna transmitter case
if self.num_transmit_antennas == 1:
propagated_samples = np.repeat(signal[[0], :], self.num_receive_antennas, axis=0)
# Single antenna receiver case
elif self.num_receive_antennas == 1:
propagated_samples = np.sum(signal, axis=0, keepdims=True)
# No receiving antenna case
elif self.num_receive_antennas == 0:
propagated_samples = np.empty((0, signal.num_samples), dtype=np.complex_)
# MIMO case
else:
propagated_samples = signal
if self.num_transmit_antennas < self.num_receive_antennas:
propagated_samples = np.append(
propagated_samples,
np.zeros(
(
self.num_receive_antennas - self.num_transmit_antennas,
signal.num_samples,
),
dtype=np.complex_,
),
axis=0,
)
# Apply channel gain
propagated_samples *= np.sqrt(self.__gain)
return SignalBlock(propagated_samples, signal._offset)
[docs]
class IdealChannelRealization(ChannelRealization[IdealChannelSample]):
"""Realization of an ideal channel.
Generated by the :meth:`_realize()<IdealChannel._realize>` routine of :class:`IdealChannels<IdealChannel>`.
"""
[docs]
def _sample(self, state: LinkState) -> IdealChannelSample:
# Since the ideal channel is deterministic, this is just a pass-through
return IdealChannelSample(self.gain, state)
def _reciprocal_sample(
self, sample: IdealChannelSample, state: LinkState
) -> IdealChannelSample:
return IdealChannelSample(self.gain, state)
[docs]
def to_HDF(self, group: Group) -> None:
group.attrs["gain"] = self.gain
[docs]
@staticmethod
def From_HDF(
group: Group, sample_hooks: Set[ChannelSampleHook[IdealChannelSample]]
) -> IdealChannelRealization:
return IdealChannelRealization(sample_hooks, group.attrs["gain"])
[docs]
class IdealChannel(Channel[IdealChannelRealization, IdealChannelSample]):
"""An ideal distortion-less channel model."""
yaml_tag: str = "Channel"
[docs]
def _realize(self) -> IdealChannelRealization:
return IdealChannelRealization(self.sample_hooks, self.gain)
[docs]
def recall_realization(self, group: Group) -> IdealChannelRealization:
return IdealChannelRealization.From_HDF(group, self.sample_hooks)