Getting Started

Assuming HermesPy is properly installed within the currently selected Python environment, users may define custom wireless communication scenarios to be investigated within the context of Simulations or Hardware Loops. The whole HermesPy suite can either be directly integrated into custom software projects and operated as a plug-in library via a detailed object-oriented programming interface or configured by YAML-style configuration files and launched from any system command line.

This section provides a rough description of the HermesPy software architecture and gives an introduction into both the library and command line interface in order to get new users quickly accustomed.

HermesPy Architecture

In its core, the HermesPy API aims to abstract the process of wireless communication and sensing signal processing within a strictly object-oriented class structure. Each processing step is represented by a dedicated class and can be adapted and customized by the software user.

Consider a single link between a receiving and transmitting wireless Device. HermesPy does not natively distinguish between Up- Down- and Side-Link, instead every link between two spatially separated wireless entities is characterized by two Device instances and a Channel, as visualized in the following flowchart:

%%{init: {'theme': 'dark'}}%% flowchart LR channel{Channel Model} subgraph devicea[SimulatedDevice] direction TB deviceatx>Tx Slot] devicearx>Rx Slot] end subgraph deviceb[SimulatedDevice] direction TB devicebtx>Tx Slot] devicebrx>Rx Slot] end deviceatx --> channel --> devicearx devicebtx --> channel --> devicebrx

Currently two types of devices are supported, namely Simulated Devices and Physical Devices, used within simulation and hardware verification contexts, respectively. For the scope of this introduction we will focus on simulated devices, since they, as the name suggests, do not require any additional hardware from the user. Complex wireless Scenarios can theoretically be configured to feature an unlimited amount of devices. Within Simulations, the devices and the channels linking them form a symmetric matrix of channel instances:

Channel Matrix

Device #1

Device #2

Device #N

Device #1

Channel Model (1, 1)

Channel Model (1, 2)

Channel Model (1, N)

Device #2

Channel Model (1, 2)

Channel Model (2, 2)

Channel Model (2, N)

Device #N

Channel Model (1, N)

Channel Model (2, N)

Channel Model (N, N)

Each link channel model may be configured according to the scenario assumptions. Note that the diagonal of this channel matrix approach patches the devices transmission back as receptions, enabling, for example, self-interference or sensing investigations. Currently available channel models are provided by the Channel package.

Each device may transmit and arbitrary Signal Model over its transmit slot and receive an arbitrary signal over its receive slot after propagation. Signal Models contain base-band samples of the signals transmitted / received by each device antenna as well as meta-information about the assumed radio-frequency band center frequency and sampling rate. In general, an unlimited amount of Operators may be configured to operate on any device’s slots. Transmit operators may submit individual Signal Models to its configured device slot. The signal transmitted by the device will then be formed by a superposition of all submitted operator signals. Inversely, receive operators are provided with the signal received by its configured device after propagation. Currently two types of Duplex Operators, operating both the transmit and receive slot of their configured device, are implemented:

These operators each model the sequential signal processing steps for the transmission and reception of their respective waveforms in a modular fashion. Each processing step is represented by a customizable or interchangeable class slot. The Communication Modem operator class currently considers

while the Radar operator only considers

making it much easier to configure.


This chapter provides several examples outlining the utilization of HermesPy as a library within custom Python projects. A full description of the application programming interface can be found in the section HermesPy API.


The following code generates the samples of a single communication frame transmitted by a PSK/QAM modem:

 1import matplotlib.pyplot as plt
 3from hermespy.simulation import SimulatedDevice
 4from hermespy.modem import Modem, WaveformGeneratorPskQam
 6operator = Modem()
 7operator.waveform_generator = WaveformGeneratorPskQam(oversampling_factor=8)
 8operator.device = SimulatedDevice()
10signal, _, _ = operator.transmit()

Within this snippet, multiple statements lead to the generation and simulation of a single communication frame signal.

  • Initially, the required Python modules are imported (lines 1-4).

  • A new modem operator instance is created (line 6).

  • The waveform to be generated by the modem is configured by assigning a specific Waveform Generator instance to the modem’s waveform_generator property (line 7).
    In our case, this is an instance of a PKS/QAM waveform.

  • The device on which the modem operates is defined (line 9).

  • An signal model, encoding a single communication frame, emitted by the modem operator is generated and plotted (lines 10-13)

Executing the snippet will result in a plot similar to

PSK/QAM default waveform plot

Signal Model Plot

which visualizes the generated samples in time-domain (left sub-plot) and its respective discrete fourier transform (right sub-plot).

While this is only a minimal example, it highlights the philosophy behind the HermesPy API, namely that each signal processing step is represented by a class modeling its functionality. Instances of those classes are assigned to property slots, where they will be executed sequentially during signal generation. Changing the waveform generated by the modem operator defined in the previous snippet is therefore as simple as assigning a different type of Waveform Generator to its hermespy.modem.modem.Modem.waveform_generator() property slot.

Of course, a multitude of parameters can be configured to customize the behaviour of each processing step. For instance, the frame generated by a PKS/QAM waveform generator features no preamble by default. A preamble is defined as a static set of known reference symbols at the beginning of the communication frame. By modifying the property

operator.waveform_generator.num_preamble_symbols = 20

the user may freely chose the number of preamble symbols. In this case, requesting \(20\) symbols results in a generated frame

PSK/QAM waveform plot with preamble

Signal Model Plot with Preamble

featuring the added preamble. Describing all configurable parameters is beyond the scope of this introduction, the API documentation of each processing step should be consulted for detailed descriptions. In general, each settable property may be freely configured by the user.

While the previous code snippet highlighted how to generate basic waveform models, link-level simulations usually consider the signal exchange between two dedicated devices. A full communication link over an ideal channel model between two dedicated simulated devices is implemented in the next example:

 1import matplotlib.pyplot as plt
 3# Import required HermesPy modules
 4from import Channel
 5from hermespy.simulation import SimulatedDevice
 6from hermespy.modem import Modem, WaveformGeneratorPskQam, BitErrorEvaluator
 7from hermespy.simulation.analog_digital_converter import AnalogDigitalConverter, GainControlType
 9# Create two simulated devices acting as source and sink
10tx_device = SimulatedDevice()
11rx_device = SimulatedDevice()
12rx_device.analog_digital_converter = AnalogDigitalConverter(num_quantization_bits=10)
14# Define a transmit operation on the first device
15tx_operator = Modem()
16tx_operator.waveform_generator = WaveformGeneratorPskQam(oversampling_factor=8)
17tx_operator.device = tx_device
19# Define a receive operation on the second device
20rx_operator = Modem()
21rx_operator.waveform_generator = WaveformGeneratorPskQam(oversampling_factor=8)
22rx_operator.device = rx_device
24# Simulate a channel between the two devices
25channel = Channel(tx_operator.device, rx_operator.device)
27# Simulate the signal transmission over the channel
28tx_signal, _, tx_bits = tx_operator.transmit()
29rx_signal, _, channel_state = channel.propagate(tx_signal)
31_, rx_symbols, rx_bits = rx_operator.receive()
33# Evaluate bit errors during transmission and visualize the received symbol constellation
34evaluator = BitErrorEvaluator(tx_operator, rx_operator)

While this code may seem somewhat complex at first glance, it expands the previous example by some important concepts, namely Channels and Evaluators. Channels are the key simulation entity modeling waveform propagation between devices. Depending on the simulation assumptions, users may select from a multitude of different classes providing specific model implementations. Refer to the Channel Module for a detailed overview. Evaluators HermesPy’s abstraction for the extraction of specific performance indicators from simulation objects. In theory, almost any object and its respective properties can be used to implement custom evaluation routines. For communication evaluations, several default evaluation routines are already shipped within the Communication Evaluators module.

Executing the snippet above results in two visualizations being rendered after propagation simulation,


Symbol Constellation, Noiseless


Bit Errors, Noiseless

namely a symbol constellation diagram at the receiver side and a bit error evaluation stem graph. Since the channel we model is actually an ideal channel and no noise is added at the receiver, no bit errors occur during data transmission. Adapting line 28 of the snippet according to

rx_device.receive(rx_signal, snr=4.)

will result in additive white gaussian being added at the receiver side with a signal to noise ratio of \(4\). As a consequence, the constellation gets distorted, leading to false decisions during demodulation and therefore to a number of bit errors during data transmission. Executing the snippet with noise consideration results in a visualization similar to


Symbol Constellation, Noisy


Bit Errors, Noisy

where said effects are clearly visible.

Propagating signal models over a channel linking two devices is an example of one of the fundamental routines commonly executed in link-level simulations. However, complex investigations usually consider multiple devices and channel models, as well as perform Monte Carlo style simulations over a grid of model parametrizations, which can lead to computationally complex routines, even for seemingly simple scenarios. In order to streamline simulation definition and execution, HermesPy provides the Simulation helper class, which automizes the process of distributing the simulation workload in multicore systems and parameter grid evaluations. Its usage is introduced in the next section.


Consider the simulation scenario of a single device transmitting its waveforms and receiving them back after reflections from surroundings, assuming ideal isolation between transmit and receive chain. One of the most frequently conducted investigations in communication signal processing is the estimation of the bit error rate (BER) in relation to the noise power at the receiver side of the communication link. Within HermesPy, Simulations can be configured to estimate performance indicators such as bit error rate over arbitrary parameter dimensions. For example, the following snippet

 1import matplotlib.pyplot as plt
 3# Import required HermesPy modules
 4from import Channel
 5from hermespy.simulation import Simulation
 6from hermespy.modem import Modem, WaveformGeneratorPskQam, BitErrorEvaluator
 8# Create a new HermesPy simulation scenario
 9simulation = Simulation()
11# Create device
12device = simulation.scenario.new_device()
14# Configure device operator
15operator = Modem()
16operator.waveform_generator = WaveformGeneratorPskQam(oversampling_factor=8)
17operator.device = device
19# Configure Monte Carlo simulation
20simulation.add_evaluator(BitErrorEvaluator(operator, operator))
21simulation.new_dimension('snr', [10, 4, 2, 1, 0.5])
22simulation.num_samples = 1000
24# Launch simulation campaign
25result =
27# Visualize results

defines the described scenario, adds a bit error rate evaluation and, most importantly, defines a sweep over the (linear) signal to noise ratio from \(10\) to \(5\), collecting \(1000\) samples for each sweep point, respectively. Executing the script will launch a full simulation run and a rendered result

Bit Error Rate Plot

Bit Error Rate Evaluation

of the bit error rate graph.

Now, a typical approach to reduce the bit errors is the introduction of Channel Coding schemes for error correction. They introduce redundancy within the transmitted bit stream during transmission and exploit said redundancy at the receiver to correct errors. One of the most basic error-correcting channel codes is the Repetition Encoder, which simply repeats bits to be transmitted and decodes by majority voting after reception. In theory, the more repetitions per transmitted data frame, the higher the error correction capabilites. But the more redundancy is introduced, the lower the actual information throughput becomes. Therefore, there is a sweet-spot within the tradeoff between data throughput and repetitions for a given signal to noise ratio.

The following snippet configures HermesPy to conduct a simulation visualizing the data rate relative to number of repetitions and noise ratio:

 1import matplotlib.pyplot as plt
 3from hermespy.simulation.simulation import Simulation
 4from hermespy.modem.modem import Modem
 5from hermespy.modem.evaluators import BitErrorEvaluator, ThroughputEvaluator
 6from hermespy.modem.waveform_generator_psk_qam import WaveformGeneratorPskQam
 7from hermespy.coding import RepetitionEncoder
 9# Create a new HermesPy simulation scenario
10simulation = Simulation()
12# Create a new simulated device
13device = simulation.scenario.new_device()
15# Add a modem at the simulated device
16modem = Modem()
17modem.waveform_generator = WaveformGeneratorPskQam()
18modem.device = device
21# Configure simulation evaluators
22simulation.add_evaluator(BitErrorEvaluator(modem, modem))
23simulation.add_evaluator(ThroughputEvaluator(modem, modem))
25# Configure simulation sweep dimensions
26snr_dimension = simulation.new_dimension('snr', [10, 8, 6, 4, 2, 1, 0.5, 0.25, .125, .0625])
27rep_dimension = simulation.new_dimension('repetitions', [1, 3, 5, 7, 9], modem.encoder_manager[0])
28snr_dimension.title = 'SNR'
29rep_dimension.title = 'Code Repetitions'
31# Run the simulation
32simulation.num_samples = 1000
33result =
35# Plot simulation results

Executing it leads to the rendering of a surface plot visualization, from which engineers can infer the selection of a proper repetition rate in order to achieve a required data rate for a given noise ratio:


Data Throughput

Command Line Tool

This section outlines how to use HermesPy as a command line tool and provides some reference examples to get new users accustomed with the process of configuring scenarios.

Once HermesPy is installed within any Python environment, users may call the command line interface by executing the command hermes in both Linux and Windows command line terminals. Consult Hermes Command Line Interface for a detailed description of all available command line options.

In short, entering

hermes -p /path/to/settings -o /path/to/output

is the most common use-case of the command line interface. All configuration files located under /path/to/settings are parsed and interpreted as an executable scenario configuration. The configuration is subsequently being executed. All data resulting from this execution will be stored within /path/to/output.

If the command-line parameter -p is left out, then the default path /_settings will be considered. If the -o is left out, then the results will be stored in a unique sub-folder of /results/.

First Steps

Let’s begin by configuring a basic simulation scenario. It should consist of:

  1. A single device featuring a single omnidirectional antenna

  2. A modem operator with

    • \(R = \frac{1}{3}\) repetition coding

    • 100GHz symbol rate

    • Root-Raised-Cosine waveforms

    • a modulation order of 16

    • a frame consisting of 10 preamble- and 1000 data symbols

  3. An evaluation routine for the bit error rate

  4. A parameter sweep over the SNR between 0 and 20 dB

This scenario is represented by the following simulation.yml file:

 3# Physical device models within the simulated scenario
 6  - &device_alpha !<SimulatedDevice>
 8    # RF-Chain configuration
 9    rf_chain: !<RfChain>
11      # Power amplifier model
12      power_amplifier: !<Rapp>
14        smoothness_factor: 1.     # Smoothness factor of Rapp's model
17# Operators transmitting or receiving signals over the devices
20  # A single modem operating the device #0
21  - &modem_alpha !<Modem>
23    device: *device_alpha          # Device the modem is operating on
25    # Bit encoding configuration before mapping to modulation symbols
26    Encoding:
28      # Repetition encoding, repeating bit sections for redundancy
29      - !<Repetition>
31        bit_block_size: 1000       # Block size
32        repetitions: 3             # Number of times the bits within the block get repeated
34    # Waveform configuration
35    Waveform: !<PskQam>
37      # Symbol settings
38      symbol_rate: 100e6
39      modulation_order: 16
40      oversampling_factor: 4
42      # Filter settings
43      filter:
44        filter_type: FMCW
46      # Frame settings
47      chirp_bandwidth: 100e6
48      num_preamble_symbols: 10
49      num_data_symbols: 1000
50      pilot_rate: 1e6
51      guard_interval: 1e-6
53      # Post-Processing
54      channel_equalization: !<PskQamMMSE>
56# Performance indication evaluation configuration
59  # Evaluate the bit errors of `modem_alpha` communicating over `device_alpha`
60  - !<BitErrorEvaluator>
62    transmitting_modem: *modem_alpha
63    receiving_modem: *modem_alpha
64    confidence: .9
65    tolerance: .01
66    plot_scale: log
68# Simulation parameters
69num_samples: 50                    # Number of samples per simulation grid section
70min_num_samples: 10                # Minimum number of samples per simulation grid section before premature stopping
71snr_type: EBN0                     # SNR is defined as the ratio between bit energy and noise power
72plot_results: True                 # Visualize the evaluations after the simulation has finished
74# Scenario parameters over which the Monte-Carlo simulation sweeps
77  snr: [0, 1, ..., 20] dB

Assuming simulation.yml is located within /path/to/settings, calling

hermes -p /path/to/settings

will result in the rendering of four plots displaying the respective information. The resulting plots and a matlab dump of the evaluation data will be saved in your current working directory. For different types of configurations, please refer to the Examples folder within the HermesPy Github repository.