This static document was automatically created from the output of a jupyter notebook.

Execute and modify the notebook online here.

Implementing Channels#

Wireless propagation channels are a core concept in the physical layer modeling of communication and sensing systems. In essence, they describe the behaviour of electromagnetic waves during their propagation between devices capable of transmitting electromagnetic radiation, receiving electromagnetic radation, or both. Within Hermes’ API, channels are addressed by the Channel Module, with each implemented channel model inheriting from a common Channel base class.

Adding a new channel model to the set of provided implementations is rather straightfoward. On the most fundamental level, each channel model class is expected to provide only a realize method generating a realization of the channel’s random variables and impulse response. To demonstrate the API workflow, we will implement a basic channel only introducing a random phase shift to the propagated waveform, no time of flight delays or multiple antenna scenarios are considered:

import numpy as np

from import Channel, ChannelRealization

class PhaseShiftChannel(Channel[ChannelRealization]):

    def realize(self, num_samples: int, sampling_rate: float) -> ChannelRealization:

        if self.receiver.num_antennas != 1 or self.transmitter.num_antennas != 1:
            raise RuntimeError("Phase shift channel only supports SISO links")

        phase_shift = np.exp(2j * self._rng.normal(0, np.pi))
        impulse_response = phase_shift * np.ones((1, 1, num_samples, 1), dtype=complex)

        return ChannelRealization(self, impulse_response)

During simulation runtime, the impulse response routine will be called for each channel propagation over a single link configured to the newly created PhaseShiftChannel. It is expected to return a four-dimensional numpy tensor modeling a channel impulse sampled num_samples times at frequency sampling_rate. The first tensor dimension denotes the number of time-domain impulse response samples, the second and third dimension the number of transmit and receive antennas, and the fourth dimension the impulse response of each sample instance, respectively.

We can now plug the newly generated channel model into a simulation scenario evaluating an OFDM waveform with access to ideal channel state information, equalizing the channel by zero forcing:

import matplotlib.pyplot as plt

from hermespy.core import dB, ConsoleMode
from hermespy.simulation import Simulation
from hermespy.modem import BitErrorEvaluator, DuplexModem, ElementType, FrameElement, FrameResource, FrameSymbolSection, OFDMIdealChannelEstimation, OFDMWaveform, OFDMZeroForcingChannelEqualization

# Create a new Monte Carlo simulation
simulation = Simulation(console_mode=ConsoleMode.SILENT)

# Add a single device, operated by a communication modem
operator = DuplexModem()
operator.device = simulation.new_device()
operator.reference = operator.device

# Configure an OFDM waveform with a frame consisting of a single symbol section
operator.waveform_generator = OFDMWaveform(resources=[FrameResource(elements=[FrameElement(ElementType.DATA, 1024)])],

# Add channel estimation and equalization routines
operator.waveform_generator.channel_estimation = OFDMIdealChannelEstimation()
operator.waveform_generator.channel_equalization = OFDMZeroForcingChannelEqualization()

# Configure our newly implemented channel model
simulation.scenario.set_channel(operator.device, operator.device, PhaseShiftChannel())

# Configure a parameter sweep over the receiver SNR, effectively simulating an AWGN channel
simulation.new_dimension('snr', dB(0, 2, 4, 8, 16))

# Evaluate the BER
simulation.add_evaluator(BitErrorEvaluator(operator, operator))

# Run the simulation and plot the results
result =

We can highlight the channel effect by disabling the zero-forcing channel equalization routine for the configured OFDM waveform.

In this case, the communication bit error rate should roughly approximate \(\tfrac{1}{2}\), indicating that no information is exchanged and the bits are esentially random at the receiver.

from hermespy.modem import OFDMChannelEqualization

# Disable channel equalization by replacing the ZF routine with the default stub
operator.waveform_generator.channel_equalization = OFDMChannelEqualization()

# Run the simulation and plot the results
result =
The Kernel crashed while executing code in the the current cell or a previous cell. Please review the code in the cell(s) to identify a possible cause of the failure. Click <a href=''>here</a> for more info. View Jupyter <a href='command:jupyter.viewOutput'>log</a> for further details.