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:
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:
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:
Communication Modems for information exchange in form of bits
Radars for wireless sensing
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
Bit Sources as the source of data bits to be transmitted
Channel Codings as the channel coding configuration
Waveform Generators as the transmit waveform configuration
Channel Precodings as the channel precoding configuration
while the Radar operator only considers
Radar Waveforms as the transmit waveform configuration
making it much easier to configure.
Library
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.
Transmissions
The following code generates the samples of a single communication frame transmitted by a PSK/QAM modem:
1import matplotlib.pyplot as plt
2
3from hermespy.simulation import SimulatedDevice
4from hermespy.modem import Modem, WaveformGeneratorPskQam
5
6operator = Modem()
7operator.waveform_generator = WaveformGeneratorPskQam(oversampling_factor=8)
8operator.device = SimulatedDevice()
9
10signal, _, _ = operator.transmit()
11
12signal.plot()
13plt.show()
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

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

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
2
3# Import required HermesPy modules
4from hermespy.channel import Channel
5from hermespy.simulation import SimulatedDevice
6from hermespy.modem import Modem, WaveformGeneratorPskQam, BitErrorEvaluator
7
8# Create two simulated devices acting as source and sink
9tx_device = SimulatedDevice()
10rx_device = SimulatedDevice()
11
12# Define a transmit operation on the first device
13tx_operator = Modem()
14tx_operator.waveform_generator = WaveformGeneratorPskQam(oversampling_factor=8)
15tx_operator.device = tx_device
16
17# Define a receive operation on the second device
18rx_operator = Modem()
19rx_operator.waveform_generator = WaveformGeneratorPskQam(oversampling_factor=8)
20rx_operator.device = rx_device
21
22# Simulate a channel between the two devices
23channel = Channel(tx_operator.device, rx_operator.device)
24
25# Simulate the signal transmission over the channel
26tx_signal, _, tx_bits = tx_operator.transmit()
27rx_signal, _, channel_state = channel.propagate(tx_signal)
28rx_device.receive(rx_signal)
29_, rx_symbols, rx_bits = rx_operator.receive()
30
31# Evaluate bit errors during transmission and visualize the received symbol constellation
32evaluator = BitErrorEvaluator(tx_operator, rx_operator)
33evaluator.evaluate().plot()
34rx_symbols.plot_constellation()
35plt.show()
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.
Simulations
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
2
3# Import required HermesPy modules
4from hermespy.channel import Channel
5from hermespy.simulation import Simulation
6from hermespy.modem import Modem, WaveformGeneratorPskQam, BitErrorEvaluator
7
8# Create a new HermesPy simulation scenario
9simulation = Simulation()
10
11# Create device
12device = simulation.scenario.new_device()
13
14# Configure device operator
15operator = Modem()
16operator.waveform_generator = WaveformGeneratorPskQam(oversampling_factor=8)
17operator.device = device
18
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
23
24# Launch simulation campaign
25result = simulation.run()
26
27# Visualize results
28result.plot()
29plt.show()
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 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
2
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
8
9# Create a new HermesPy simulation scenario
10simulation = Simulation()
11
12# Create a new simulated device
13device = simulation.scenario.new_device()
14
15# Add a modem at the simulated device
16modem = Modem()
17modem.waveform_generator = WaveformGeneratorPskQam()
18modem.device = device
19modem.encoder_manager.add_encoder(RepetitionEncoder(repetitions=3))
20
21# Configure simulation evaluators
22simulation.add_evaluator(BitErrorEvaluator(modem, modem))
23simulation.add_evaluator(ThroughputEvaluator(modem, modem))
24
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'
30
31# Run the simulation
32simulation.num_samples = 1000
33result = simulation.run()
34
35# Plot simulation results
36result.plot()
37plt.show()
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:
A single device featuring a single omnidirectional antenna
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
An evaluation routine for the bit error rate
A parameter sweep over the SNR between 0 and 20 dB
This scenario is represented by the following simulation.yml file:
1!<Simulation>
2
3# Physical device models within the simulated scenario
4Devices:
5
6 - &device_alpha !<SimulatedDevice>
7
8 num_antennas: 1 # Device features a single physical antenna
9
10# Operators transmitting or receiving signals over the devices
11Operators:
12
13 # A single modem operating the device #0
14 - &modem_alpha !<Modem>
15
16 device: *device_alpha # Device the modem is operating on
17
18 # Bit encoding configuration before mapping to modulation symbols
19 Encoding:
20
21 # Repetition encoding, repeating bit sections for redundancy
22 - !<Repetition>
23
24 bit_block_size: 1000 # Block size
25 repetitions: 3 # Number of times the bits within the block get repeated
26
27 # Waveform configuration
28 Waveform: !<PskQam>
29
30 # Symbol settings
31 symbol_rate: 100e6
32 modulation_order: 16
33 oversampling_factor: 4
34
35 # Filter settings
36 filter:
37 filter_type: ROOT_RAISED_COSINE
38
39 # Frame settings
40 num_preamble_symbols: 10
41 num_data_symbols: 1000
42 pilot_rate: 1e6
43 guard_interval: 1e-6
44
45# Performance indication evaluation configuration
46Evaluators:
47
48 # Evaluate the bit errors of `modem_alpha` communicating over `device_alpha`
49 - !<BitErrorEvaluator>
50
51 transmitting_modem: *modem_alpha
52 receiving_modem: *modem_alpha
53 confidence: .9
54 tolerance: .01
55 plot_scale: log
56
57# Simulation parameters
58num_samples: 50 # Number of samples per simulation grid section
59min_num_samples: 10 # Minimum number of samples per simulation grid section before premature stopping
60snr_type: EBN0 # SNR is defined as the ratio between bit energy and noise power
61plot_results: True # Visualize the evaluations after the simulation has finished
62
63# Scenario parameters over which the Monte-Carlo simulation sweeps
64Dimensions:
65
66 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.