Simulation¶
The simulation module provides the basis for all HermesPy simulations, including noise and hardware modeling.
It is design around the Ray framework for parallel and distributed computing, enabling the efficient parallelization of physical layer Monte Carlo simulations scaling to multicore-CPUs, computing clusters and cloud computing environments. Every Monte-Carlo physical layer simulation campaign is composed of a set of parallel numeric physical layer simulations, each of which is individually parameterized.
During execution, a central simulation controller will distribute parameter combinations selected from the simulation parameter grid to the individual physical layer simulation instances and collect the resulting performance evaluations. Each of the physical layer simulations is executed in a separate Python thread and distributed to the available CPU / GPU and memory resources by Ray.
The physical layer in turn, at its core, is composed of simulated devices and reciprocal channels interconnecting the physical devices.
Simulated devices represent any physical entity capabable of transmitting or receiving electromagnetic waveforms. This can be on the one hand communication devices such base-stations, smartphones, wifi routers, laptops or other Internet-of-things devices. On the other hand, they may be sensing devices such as automotive FMCW radars and bistatic radar transmitters and receivers. In general, devices can be aribtrarily positioned and oriented in space and transmit and/or receive arbitrary electromagnetic waveforms at arbitrary carrier frequencies.
The physical properties of devices’ front-end hardware can be specified in detail to allow for a more realistic simulation of the hardware effects on the transmitted and received signals. This includes modeling analog to digital conversion (ADC) and digital to analog conversion (DAC) introducing quantization noise and non-linearities, oscillator effects such as in-phase/quadrature imbalance (I/Q) and phase noise (PN), amplification non-linearities in the power amplifier (PA) and low-noise amplifier (LNA), mutual coupling between individual antennas in antenna arrays (MC), polarimetric radiation patterns of individual antennas in antenna arrays (ANT), and transmit-receive isolation between transmitting and receiving antennas in antenna arrays of duplex devices (ISO).
Considering a single simulation iteration, within each simulated device, a digital base-band signal model to be transmitted is generated and sequentially passed through the radio-frequency transmission chain (RF Tx) models. The emerging analog signal is then duplicated, with one copy being passed through the mutual coupling and antenna models and the other copy being passed throug the transmit-receive isolation model. The signal copy considering mutual coupling and antenna effects is then propagated over all channel models linking the specific device to all other devices in the simulation. The received signals, after propgation over the channel models, are then once again passed through the antenna and mutual coupling models. Before considering the effects of the radio-frequency reception chain (RF Rx) models, the received signals are superimposed with the signal copy considering transmit-receive isolation. Finally, the received signal is passed through the RF Rx models and the resulting digital base-band signal is processed by the configured receive digital signal processing.
Note
The order of hardware-impairment models is currently fixed and cannot be changed. This system is under active development and will be expanded in the future to enable arbitrary ordering of hardware-impairment models and thus more accurate representations of custom front-ends.
Configuring the simulation exactly as the above graphs show is as simple as:
1# Alias the ideal channel as Channel
2Channel = IdealChannel
3
4# Initialize a new simulation
5simulation = Simulation()
6
7# Limit the number of actors, i.e. parallel physical layers, to 3
8simulation.num_actors = 3
9
10# Add two devices to the simulation
11device_alpha = simulation.new_device()
12device_beta = simulation.new_device()
13
14# Configure the three unique channels
15alpha_alpha_channel = Channel()
16beta_beta_channel = Channel()
17alpha_beta_channel = Channel()
18
19simulation.set_channel(device_alpha, device_alpha, alpha_alpha_channel)
20simulation.set_channel(device_beta, device_beta, beta_beta_channel)
21simulation.set_channel(device_alpha, device_beta, alpha_beta_channel)
In practice, limiting the number of parallel simulations by setting the num_actors
property
is usually not required, as Ray will automatically scale the number of parallel simulations.
However, sometimes memory constraints may require setting a lower value.
Typical parameters to be varied in a Monte-Carlo simulation are the receive signal-to-noise ratio and the carrier frequency of the exchanged signals. For the specific values of an SNR between 0 and 20 dB and carrier frequencies of 1, 10 and 100 GHz, the overall simulation parameter grid may look as follows:
Carrier-Frequency |
|||
---|---|---|---|
SNR |
\(1~\mathrm{GHz}\) |
\(10~\mathrm{GHz}\) |
\(100~\mathrm{GHz}\) |
\(0~\mathrm{dB}\) |
\((\ 0~\mathrm{dB}, 1~\mathrm{GHz})\) |
\((\ 0~\mathrm{dB}, 10~\mathrm{GHz})\) |
\((\ 0~\mathrm{dB}, 100~\mathrm{GHz})\) |
\(10~\mathrm{dB}\) |
\((10~\mathrm{dB}, 1~\mathrm{GHz})\) |
\((10~\mathrm{dB}, 10~\mathrm{GHz})\) |
\((10~\mathrm{dB}, 100~\mathrm{GHz})\) |
\(20~\mathrm{dB}\) |
\((20~\mathrm{dB}, 1~\mathrm{GHz})\) |
\((20~\mathrm{dB}, 10~\mathrm{GHz})\) |
\((20~\mathrm{dB}, 100~\mathrm{GHz})\) |
The simulation class provides a convenient interface to sweep over virtually any class property of any class contained within the simulation.
In the above example, the carrier frequency is configured by setting the carrier_frequency
property
of the simulated devices.
Since the SNR is a frequently used parameter, it has a shorthand and can be globally set for all devices within the simulation without the need to
specify the device class:
1
2# Configure the three unique channels
3alpha_alpha_channel = Channel()
4beta_beta_channel = Channel()
5alpha_beta_channel = Channel()
6
7simulation.set_channel(device_alpha, device_alpha, alpha_alpha_channel)
8simulation.set_channel(device_beta, device_beta, beta_beta_channel)
9simulation.set_channel(device_alpha, device_beta, alpha_beta_channel)
10
11# Sweep over the SNR from 0 to 20 dB in steps of 10 dB
12simulation.new_dimension("noise_level", dB(0, 10, 20), device_beta)
13
14# Sweep over the carrier frequency from 1 GHz to 100 GHz in steps of 10 GHz
15simulation.new_dimension("carrier_frequency", (1e9, 1e10, 1e11), device_alpha, device_beta)
Now, by default, devices won’t transmit or receive any signals. For the sake of simplicity in this example, we will configure the devices to transmit and receive \(100\) samples of a random complex signal with an amplitude of \(1\) and a bandwith of \(100~\mathrm{MHz}\).
1# Make both devices transmit 100 samples at 100 MHz
2ns, fs = 100, 1e8
3transmitted_signal = Signal.Create(np.exp(2j * np.random.uniform(0, np.pi, (1, ns))), fs)
4alpha_transmitter = SignalTransmitter(transmitted_signal)
5beta_transmitter = SignalTransmitter(transmitted_signal)
6alpha_receiver = SignalReceiver(ns, fs, 1.0)
7beta_receiver = SignalReceiver(ns, fs, 1.0)
8
9device_alpha.transmitters.add(alpha_transmitter)
10device_beta.transmitters.add(beta_transmitter)
11device_alpha.receivers.add(alpha_receiver)
12device_beta.receivers.add(beta_receiver)
Even though the simulation is now fully configured in terms of its basic physical layer description and the parameter grid, no performance evaluations will be generated when executing the simulation. Instead, each performance indicator to be evaluated must be explicitly configured. Multiple modules of HermesPy provide implementations of performance indicator evaluators for performance indicators relevant to the respective module’s topic. In this example, one of the most basic performance indicators is the signal power received by each device:
1# Add an evaluator estimating the received power to the simulation
2simulation.add_evaluator(ReceivedPowerEvaluator(alpha_receiver))
3simulation.add_evaluator(ReceivedPowerEvaluator(beta_receiver))
The simulation can be executed by calling the run
method.
All configured performance indicators will be evaluated for each parameter combination and the results
returned in a MonteCarloResult
.
From there, the result can be printed to the console, plotted, or saved to the drive.
1# Run the simulation
2result = simulation.run()
3
4# Print the result and plot graphs
5result.print()
6result.plot()
7
8# Save the result to a file
9result.save_to_matlab("simulation.mat")