Hands-on Digital transmission with your soundcard¶

09 - Transmitting custom data¶

In the previous notebook, we eventually accomplished to transmit real digital data over our audio channel. However, the payload was fixed and could not be adapted. In this notebook, we will extend the chain to read textual data from a UDP stream and according generate packets. At the receiver side, we will print out the received textual data.

In [1]:
# Source code has been redacted in online version
# Omitting 9 lines of source code

In [2]:
try:
except ModuleNotFoundError:
print ("Did not find tikzmagic. You will not be able to compile the tex code!")

In [3]:
%matplotlib notebook

In [4]:
from audioComms import Environment, TX
from audioComms.utils import get_filter, StatefulFilter, get_filter
from audioComms.buffers import RingBuffer
from audioComms.channels import AudioChannel, SimulatedChannel, IdentityChannelEffect, HighpassChannelEffect
from audioComms.plotting import PlotEyeDiagram
from audioComms.components import Component, Upconversion, Downconversion, TimingRecovery, DetectFrames
from audioComms.packeting import Packeting, int2bin, bin2int


At the beginning, let's write a component that reads from a UDP connection and transforms the received textual data into payload bits. There, each transmitted character corresponds to 8 payload bits. The component sends the payload bits for each frame via its output signal.

In [5]:
# Source code has been redacted in online version
# Omitting 46 lines of source code


Afterwards, the TransmitSignal component receives the bits and puts them into our designed frame structure. Moreover, when no new payload data is available, the component just creates a dummy packet from a default payload array.

In [6]:
# Source code has been redacted in online version
# Omitting 49 lines of source code


These are the new components at the transmitter side. At the receiver, we only need a component to print out the data in text format. In order to do that, it groups the payload into groups of 8 bits and converts each group to a decimal number, which is in turn translated to the corresponding textual symbol. In addition, the component filters out the dummy frames that just contain a single 'X' (we will define later on that a "null"-frame just contains 'X' as the payload); instead it prints a simple '.' to stdout to see show that the transmission is still ongoing:

In [7]:
class TextDataSink(Component):
def __init__(self, environment):
super().__init__(environment, None)

rxBytes = [bin2int(p) for p in np.split(packet, len(packet) // 8)]
text = ''.join(chr(x) for x in rxBytes)

if text != 'X':
print ("Frame content:", text)
else:
sys.stdout.write('.')


We are now ready to setup the overall transmission chain. Pictorially, it consists of the following components, where the new components are marked in red:

In [6]:
# Source code has been redacted in online version
# Omitting 31 lines of source code

In [15]:
# Source code has been redacted in online version
# Omitting 32 lines of source code


Let's run it over the simulated channel:

In [16]:
runTransmission(15000, lambda env: SimulatedChannel(env, channelEffect=HighpassChannelEffect(gain=0.2)))

..........Stop received from external


Hm, the eye diagram looks nice, but no data is actually showing up, only the status dots from the TextDataSink. That's clear, as we did not yet provide any input data via UDP stream. In order to do that, we can use different tools:

• On Linux, simply open a terminal and run nc -u localhost 50009. Then type something and press return. The typed data will be printed out to this jupyter notebook.
• On Windows (or Linux) use the provided tool udpsender.py. Simply run it in a terminal, type something and press return. The typed data will appear as the output of this jupyter notebook. Let's try again with some data actually being transmitted:
In [13]:
runTransmission(15000, lambda env: SimulatedChannel(env, channelEffect=HighpassChannelEffect(gain=0.2)))

......Frame content: Hell
Frame content: o

.....Frame content: This
Frame content:  is
Frame content: just
Frame content:  a t
Frame content: est

..

Ah, great, now we get the actual data received. I typed Hello and This is just a test. at the transmitter side, which was successfully received.

Now, let's try out the same thing with the audio channel:

In [20]:
runTransmission(15000, AudioChannel)

.............Frame content: 1234
Frame content: 5678
Frame content: 9ABC
Frame content: HI

............

Yes, also with the audio channel it works (Though, here I used the cabled shortcut connection). Let's look at the received data: 1234 5678 9ABC HI. At the transmitter, I sent 1234 5678 9ABC DEFG HI (I artificially inserted the spaces for easier reading). Apparently, one frame was lost during the transmission. We believe the frame loss was due to a bit error in the PHY header, such that the PHY frame was not detected. We will address this problem in the next notebook.

Outlook¶

In this notebook, we extended our transmission chain to use variable payload, which can be supplied via a UDP network connection. As a problem we identified that some frames were lost during the transmission. In the following notebook we will address this problem by adding another control layer and repeating each frame several times.