In my last blog post, we assembled the hardware to make a simple phased array beamformer. And before that, we walked through the basic equations of how to electrically steer that antenna array. Now we need to know how to control the radio and plot the data. It’s a very important topic, so I’m actually going to spend two blog posts on it. This first blog post will cover the basic controls and setup. Then the next blog post will about be the specific functions to turn it into an electronically steerable antenna.
Use PyADI-IIO for Interacting with Most Analog Devices Products
Pluto has very helpful Matlab and Python interfaces for programming and gathering data. To keep this example freely accessible, I’ll be using Python in this blog series. And there is an easy-to-use Python library to control Pluto called “PyADI-IIO”:
PyADI-IIO, initially developed by Dr. Travis Collins, gives human readable Python commands for many of Analog Device’s programmable products. A nice benefit to this library is that it standardizes these commands even amongst disparate products. So whether you’re using Pluto, data converters, accelerometers, synthesizers, or whatever, the syntax will be very consistent.
To install, simply follow the quick start procedures on the PyADI-IIO website. Any Python IDE could be used, I like to use Anaconda/Spyder because I can easily install the python libraries into separate “environments.” But use whatever you’re comfortable with.
“Hello World!” for the Dual Channel Pluto
To get started using Pluto in Python, let’s take advantage of an existing “Hello World” type of script. This can be found at the link below, along with other examples for a range of Analog Devices products:
Download the file called “ad9361_example.py” and open that in your Python editor. Since we’re using the two-channel version of Pluto, this example (and not the pluto.py example) will be the closest to what we want to do. Let’s walk through it to understand what it does.
The first few lines of ad9361_example.py are just importing the Python libraries we need:
The “adi” library contains all the PyADI-IIO objects. That’s the key library. To list all the devices it can control, just type help(adi) in a python console (after the import adi statement, of course).
The next section, “Create radio”, is where we construct a radio object (which is our Pluto) and then assign attributes to that object.
To create the object, you simply tell it what object you want and what its address is. We’re using Pluto—but since we exposed the second channels, we don’t call it Pluto anymore. We call it the AD9361. They are both in the adi library, so for the AD9361 use adi.ad9361. Then we give it the address. The example script calls an address of uri=’ip:analog.local’. Which searches your local computer for any radios connected that are named “AD9361.” This may work for you, but I’ve found greater consistency if I use Pluto’s default IP address of 192.168.2.1 (FYI, you can change this IP address in the config.txt file on Pluto). So I’ve updated the example to look like this:
sdr = adi.ad9361(uri='ip:192.168.2.1')
Now, we have an object, called “sdr”, that we can interact with. There are a lot of attributes we can set in this object—please refer to the PyADI-IIO website or type help(adi.ad9361) into the Python console. The code in this example sets up the receive and transmit LO frequency, the gain control mode, and a few other attributes.
The attribute sdr.rx_enabled_channels = , enables Rx0. Similarly, sdr.tx_enabled_channels = , enables Tx0.
We only need one transmit, so tx_enabled_channels can be left alone. But we want both Rx0 and Rx1 enabled, so we change that line to:
sdr.rx_enabled_channels = [0,1]
Next, the example creates a simple IQ data array, called “iq”. And sends it to Pluto with the sdr.tx(iq) command. Since sdr.tx_cyclic_buffer was set to True, this data will just keep repeating. There’s no need to send it again.
Finally, we collect a buffer of data from Pluto with the sdr.rx() command. But since we enabled both channels, we need to separate out the returned data so that we know which channel is for which receiver. This is easily done as follows:
data = sdr.rx()
f, Pxx_den = signal.periodogram(Rx_0, fs)
Now all that’s left is to plot the data—generally, we use matplotlib for this. If you make the modifications above, and the hardware connections from the previous blog post, then you should see a plot like this:
It’s not very exciting. And it doesn’t have much to do with phased arrays yet. But at least it's working! Hello World!
If you are following along with your own setup, and run into any problems, leave a comment below and I can try to help or guide you to the best resource to resolve the issue. In the next blog post, we’ll add the Python code for the phase shifting and then plot the array pattern for our simple two-element antenna. Then things will really start coming together!