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:

[9]:
from hermespy.hardware_loop.audio import AudioDevice
from hermespy.modem import DuplexModem, RootRaisedCosineWaveform, SingleCarrierCorrelationSynchronization, SingleCarrierZeroForcingChannelEqualization, SingleCarrierLeastSquaresChannelEstimation

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.

[10]:
device = AudioDevice(6, 4, [1], [1],  max_receive_delay=1)

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.

[11]:
modem = DuplexModem(seed=42)
modem.device = device

waveform = RootRaisedCosineWaveform(symbol_rate=1e4, pilot_rate=64, num_preamble_symbols=128,
                                    num_data_symbols=2048, oversampling_factor=8, 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:

[12]:
transmission = modem.transmit()
_ = transmission.symbols.plot_constellation(title='Transmitted Symbols')
_ = transmission.signal.plot(title='Transmitted Waveform')
../_images/notebooks_audio_8_0.png
../_images/notebooks_audio_8_1.png

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!

[13]:
# Trigger device transmission
device.transmit()
device.trigger()
device.receive()

reception = modem.receive()
_ = reception.signal.plot(title='Received Waveform')
../_images/notebooks_audio_10_0.png

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:

[14]:
_ = reception.frames[0].signal.plot(title='Received Synchronized Waveform')
../_images/notebooks_audio_12_0.png

Proper demodulation and equlization can be assessed by plotting the received symbol constellation and evaluating the bit error rate of the transmitted frame:

[15]:
from hermespy.modem import BitErrorEvaluator

_ = reception.frames[0].equalized_symbols.plot_constellation(title='Received Symbol Constellation')

ber = BitErrorEvaluator(modem, modem)
_ = ber.evaluate().plot()
../_images/notebooks_audio_14_0.png
../_images/notebooks_audio_14_1.png