Receiving with 2 channels on PlutoSDR
The Analog devices PlutoSDR comes with the AD9361 chip. Meant for learning, the PlutoSDR comes at a much more affordable cost as compared to the USRP. Of course, the USRP has its own advantages while the PlutoSDR is like a stepping stone into the world of signal processing and communications. While the AD9361 is capable of 2TX and 2RX, PlutoSDR rev. A and rev. B, both had the second TX and RX channels unrouted on the PCB. Finally, revision C and D comes with the second RX and TX channels brought out on a u.FL connector. By default, the PlutoSDR factory firmware only enables single RX and TX channels. In this article, we will unlock the second TX/RX pair and further use it to receive some signals.
Unlocking the second TX/RX pair
There are multiple steps involved to achieve our intent. Firstly, let us verify that we are using the right firmware. Login to the plutoSDR over SSH and check the firmware version.
______ _ _ _________________
| ___ \ | | | / ___| _ \ ___ \
| |_/ / |_ _| |_ ___ \ `--.| | | | |_/ /
| __/| | | | | __/ _ \ `--. \ | | | /
| | | | |_| | || (_) /\__/ / |/ /| |\ \
\_| |_|\__,_|\__\___/\____/|___/ \_| \_|
sh: /usr/bin/xauth: not found
We can see that the PlutoSDR that I am using has version 0.34 firmware. Analog Devices says that you need at least version 0.33 to unlock dual rx/tx mode. In case you happen to have a lower version, follow this link and get your firmware updated.
Once you are on the latest firmware like I am, you should be able to proceed further and follow the instructions. Let us run a command and check the device mode.
# fw_printenv mode
# fw_setenv attr_name compatible
# fw_setenv attr_val ad9361
# fw_setenv compatible ad9361
# fw_setenv mode 2r2t
Here, we set the compatible devices as “AD9361” and mode as “2r2t” which is required to enable the second transmit-receive pair.
Run the following commands once the PlutoSDR reboots.
# fw_printenv mode
We can see that both the transmit receive pairs are now active.
Using both the receivers in Python
Having both the transmit-receive pairs active, it’s time to actually use them. I tried with GNURadio but wasn’t really sure if the data I am receiving is correct. So, I decided to use python and process the data with a custom-written script. Now, I won’t go over the steps involved in setting up the environment and libraries. You can very well find those with a web search. Our main focus remains to acquire both the receiver’s data.
import matplotlib.pyplot as plt
import numpy as np
# Create radio
sdr = adi.ad9361("ip:192.168.2.1")
samp_rate = 20e6
'''Configure Rx properties'''
sdr.rx_enabled_channels = [0, 1]
sdr.sample_rate = int(samp_rate)
sdr.rx_rf_bandwidth = int(2e6)
sdr.rx_lo = int(2.71e9)
sdr.rx_buffer_size = 8192
sdr.gain_control_mode_chan0 = "manual"
sdr.gain_control_mode_chan1 = "manual"
sdr.rx_hardwaregain_chan0 = int(40)
sdr.rx_hardwaregain_chan1 = int(40)
samples = sdr.rx() # receive samples off Pluto
x = sdr.rx()
a = np.real(x[:])
b = np.real(x[:])
corr = np.correlate(np.real(a), np.real(b), mode='full')
# plt.ylim([-20e6, 20e6])
The given Python snippet configures both the receiver channels to have manual gain of 40dB, sets the LO to 2.71GHz and a Rx FIFO of 8192 depth. On calling the sdr.rx() function, PlutoSDR simultaneously collects the samples from both the receivers. On the python level, the data gets stored in a list.
The statements a = x[:] brings out the channel 0 data while b = x[:] brings out the channel 1 data from the list. Throughout the entire process, we have access to the complex, raw IQ data that’s ideal for all sorts of signal processing techniques.
In my little snippet, I separate out the real part and correlate the data from both the channels. The result looks something like this.
Unlocking the second receiver opens a world of new possibilities. A simple phased array receiver or maybe a DoA estimator using an interferometer technique? If I happen to do something interesting with this, I will be sure to post it here.