Note
This static document was automatically created from the output of a jupyter notebook.
Execute and modify the notebook online here.
Using Audio Equipment¶
This notebook will outline how to setup hardaware validation loops transmitting communication waveforms over sound cards.
More precisely, we will initially set up a SISO link transmitting over the first output channel and receiving over the first input channel of a sound card, respectively. For generating this notebook’s content, the channels have been diretly linked by a patch cable.
Initially, we will import all required modules:
[2]:
import matplotlib.pyplot as plt
from hermespy.hardware_loop import AudioDevice, PhysicalDeviceDummy
from hermespy.modem import DuplexModem, RootRaisedCosineWaveform, SingleCarrierCorrelationSynchronization, SingleCarrierZeroForcingChannelEqualization, SingleCarrierLeastSquaresChannelEstimation
from hermespy.simulation import SimulatedDevice
/home/stealth/coding/python/envs/hermes-312/lib/python3.12/site-packages/uhd_wrapper/versioning.py:50: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
import pkg_resources # type: ignore
The link can be configured by assigning the proper audio device and channel indices. The device indices are native to the host system, so when executing this notebook lokally, the indices for transmitting \((6)\) and receiving \((4)\) device will differ. They may be queried by calling the function query_devices of the underyling sounddevice package.
[3]:
device = AudioDevice(6, 4, [1], [1], max_receive_delay=1)
# Use a simulated device for testing without audio hardware
# Remove the following line to use the audio hardware
device = PhysicalDeviceDummy(bandwidth=device.sampling_rate/device.oversampling_factor, oversampling_factor=device.oversampling_factor)
The device will be operated by a communication modem transmitting a single-carrier waveform at at symbol rate of \(10~\mathrm{kHz}\) filtered by a root-raised-cosine pulse impulse response.
[4]:
modem = DuplexModem(seed=42)
device.add_dsp(modem)
waveform = RootRaisedCosineWaveform(pilot_rate=64, num_preamble_symbols=128,
num_data_symbols=2048, modulation_order=4)
waveform.synchronization = SingleCarrierCorrelationSynchronization()
waveform.channel_estimation = SingleCarrierLeastSquaresChannelEstimation()
waveform.channel_equalization = SingleCarrierZeroForcingChannelEqualization()
modem.waveform = waveform
Note that the audio device will internally apply a Hilbert transformation to convert the complex-valued communication waveforms to real-valued envelopes to be transmitted over the audio channels. This requires at least an oversampling factor of two, ideally higher.
We may now generate and visualize the information of a single communication frame to be transmitted over the device output channel:
[5]:
transmission = modem.transmit(device.state())
_ = transmission.symbols.plot_constellation(title='Transmitted Symbols')
_ = transmission.signal.plot(title='Transmitted Waveform')
plt.show()
This waveform can be transmitted and received over the device by calling the hardware-loop specific configure, trigger and fetch routines.
Warning: If you are running this section for the first time, especially over speakers, turn down the volume!
[6]:
# Trigger device transmission
device.transmit()
device.trigger()
reception = device.receive()
_ = reception.impinging_signals[0].plot(title='Received Waveform')
plt.show()
As expected, we received a signal frame clearly visible in the time-domain. We can now inspect the proper synchronization by plotting only the signal portion of the detected frame:
[7]:
_ = reception.operator_receptions[0].frames[0].signal.plot(title='Received Synchronized Waveform')
plt.show()
Proper demodulation and equlization can be assessed by plotting the received symbol constellation and evaluating the bit error rate of the transmitted frame:
[8]:
from hermespy.modem import BitErrorEvaluator
_ = reception.operator_receptions[0].frames[0].equalized_symbols.plot_constellation(title='Received Symbol Constellation')
ber = BitErrorEvaluator(modem, modem)
device.transmit()
device.trigger()
device.receive()
_ = ber.evaluate().visualize()
plt.show()