We have seen the LimeSDR mini several times on this blog. So far, we have only seen how it can generate analog modulation to transmit voice and what not. Being an SDR capable to transmit practically anything, we can even transmit binary data. Speaking of binary, we will have to jump into digital modulation techniques. In this article, I will demonstrate how I used the LimeSDR mini to transmit a small CSV text file over the air. For this experiment, I will use three things. One of them is obviously the LimeSDR mini. The second one is our friend GNURadio and the third being an RTL-SDR to receive it on the other side.
The transmitter setup
Here, we start with the File source that provides an output in byte format. The 'Packet Encoder' accepts the byte and encodes the data in '1 bit' per symbol format. Further, the constellation modulator maps the bit to corresponding phase shifts known as symbols. In case of BPSK modulation, the phase shifts between two symbols are going to be \(180^o\) apart. If we were to use QPSK modulation, the packet encoder would have to provide '2 bits' per symbol. Correspondingly, the Constellation modulator would map the symbols \(90^o\) apart. Remember, the modulated bits are simply mapped to their respective phases represented as either \(1 and -1[latex].[caption id="attachment_6393" align="alignnone" width="800"] Phase vs. symbol. This is what we call a constellation diagram.[/caption]
I am sure you must have seen the QPSK constellation while downloading Meteor-M2 images with your RTL-SDR setup.
A little math
To understand this process better, imagine that you have a sine wave. Multiplying it with [latex]+1\) will result in,
\(+1 \times sin(2\pi ft) = sin(2\pi ft)\)
Whereas, multiplying it with \(-1\) will result in,
\(-1 \times sin(2\pi ft) = -sin(2\pi ft)\)
The negative sign on the sine indicates a \(180^o\) phase shift. Correspondingly, we would see the following waveform.
The phase shift or rather the symbols now need to be mounted on a carrier. To do that, we multiply the symbols with a 20kHz sine wave. It could be any frequency in the nyquist range. After doing do, we will change the sampling rate to match the sampling rate of the LimeSDR DAC. The rational resampler block interpolates the modulated signal by 20 times to achieve the LimeSDR sampling rate of 2.048MHz. I chose the sampling rate of the LimeSDR to be an integer multiple of the base band sampling rate to reduce the complications. Notice that the LimeSDR sink is set to up-conversion mode. We will be transmitting the data on 435MHz.
The receiver setup
On the receiving end, I am using an RTL-SDR with a sampling rate set to 1.024MHz. The gains are set to automatic and the RF value set to 435MHz. Again, on the receiving side, we decimate the data stream from RTL-SDR. Further, multiply it with a 20kHz source to center the constellation around DC. The received data could also contain unwanted signals which need to be filtered out. The low pass filter takes care of that. Furthermore, the polyphase clock sync block synchronizes the sampling time such that we get the maximum possible signal to noise ratio. The costas loop is meant for fine frequency correction which is necessary for correct demodulation of digital signals. An uncorrected frequency will result in the phase rotating constantly leading to no demodulation happening altogether. The costas loop basically locks onto the received signal's carrier and outputs a good demodulated constellation.
After the Costas Loop, you can see the LMS DD Equalizer block. What this does is basically tries to compensate the multipath effects. In case you don't know, whenever we transmit a signal, it will travel wherever it can and even bounce of multiple walls, objects, moving vehicles and so on before finally arriving at the receiving antenna. Correspondingly, all these paths vary in distance resulting in a shifted versions of the signal arriving at the receiver. The Equalizer block tries to minimize the effect of multipath propagation.
Furthermore, we demodulate the carrier and end up with bits. Remember, we have enabled differential encoding in the transmitter and we need to decode it to retrieve the original bit pattern. The differential decoder does that job and passes the data further to the packet decoder and then to the File sink. To elaborate, the File Sink block requires the data to be 8 bits in size. The Packet decoder block packs the 1 bit per symbol into 8 bits before sending it to the File sink.
If you wish to read more about this in detail, you can follow this link to GNURadio official guide.
While transmitting the file, the receiver often drops the initial few bytes of data. This happens because the receiver algorithms, especially the clock sync and the costas loop require time to converge. For testing the setup, I chose a small CSV file and transmitted it. Except for losing the initial couple of bytes, the transmission was flawless.
I used an online tool to compare the transmitted and received files and here is the result below. We are able to receive correct data over the air and the comparison of two files prove it.
When trying this with QPSK, the packet encoder does not work correctly every time. It took me several tries to make it work. Apparently, the initial convergence time plays a significant role in QPSK transmission especially when you are using the packet encoder block which is known to be flawed. Perhaps, I will have to find an alternative to that. Finally, this is the link to the GRC file which contains both, the TX as well as the RX blocks. Simply enable or disable them as per your need.
If you don't have a LimeSDR mini, you could try this setup on a PlutoSDR or even HackRF one. In case you have neither of those, go ahead and buy one from Aliexpress.