A cell tower with a spotlight shining on it.

How to Turn Pluto into a Phased Array Beamformer

Ok, we're making our way through understanding and building our own phased array beamformer.  In the last post, we got it set up and we were able to control/plot data with Python.  In this post, we’ll add some Python code to implement the phase shifting.  And then we’ll actually see the array pattern!  Finally! 

Digital Phase Shifting

To transform our setup into a proper phased array, we’ll need to phase shift the received signals.  This is going to compensate for the delay that one antenna element receives, relative to the other.  Fortunately, applying a phase shift (Δφ) to Pluto’s received data is really easy – you just multiply by the complex exponential of -jΔφ.  So the Python code is:

delayed_Rx_1 = Rx_1 * np.exp(1j*np.deg2rad(phase_delay))

                where phase_delay is in degrees

You can see the completed code at https://github.com/jonkraft/Pluto_Beamformer/blob/main/Pluto_beamformer_PlotPeaks_youtube.py

And there’s a video walkthrough of it all here:

Most of this code is very similar to the “Hello World” code that we went through in the previous blog.  But there are two new functions: “calcTheta” and “dbfs”.    The “calcTheta” function simply returns the steering angle (Ɵ), for a given phase shift.  We saw those equations in the last blog.  The “dbfs” function scales the received data relative to Pluto’s full-scale range.  It’s not the same as dBm – which is dB’s relative to 1 mW of power.  Instead, dBFS is dB’s relative to the full scale of the receiver.  In this case, it is the full scale of Pluto’s ADC – which is 12 bits. 

Finally, the big change in this new Python program is implementing that phase shift on the Rx_1 data.  But doing one phase shift and looking at a single data point isn’t very interesting.  So instead, let’s sweep all possible phase shifts from -180° to + 180°.  We could shift beyond this, but the phase will just wrap back onto itself, so it won’t give us any more unique data. 

Final Setup

But before you click that run button, let’s make sure our setup is correct.  It should look something like this:

 ADALM Pluto setup

To start with, let’s move that TX1 antenna to a Ɵ = 0° position.  This is the mechanical boresight position.  And set d to about half of a wavelength.  In my program, I use 2.3 GHz, so I will set the RX1 and RX2 antennas to d=65 mm apart.   

Plotting the Beam

And with that, we can hit run!  What you will see is a plot of the amplitude as a function of the phase shift we are applying to RX2.  It’ll look something like this:

 Beam plot diagram

The program will note where the peak is, and convert that phase shift into the corresponding steering angle.  In this case, the peak was at a phase shift of 116°, which corresponds to a direction of Ɵ = 40°.  But we positioned the transmit antenna with for a Ɵ = 0°!!  So what is going on?  The issue is phase calibration – and it’s a really big deal in beamforming systems. 


Between our two receive channels, there are all kinds of phase (and gain!) mismatches.    For example, the SMA cables that attach the antennas to the filters are not EXACTLY the same length.  And even if they were, slight differences in their construction would make their electrical length different.  So you get a different phase shift, at 2.3 GHz, from the two cables.  You can buy phase-matched cables, but they are very expensive!   And even if you did, there are other mismatches in the filter and analog front end. 

Fortunately, for a two-element array, the calibration is very simple.  We’ll just line up our transmitter at mechanical boresight, and then shift the array over until the receive pattern peaks where the transmitter is physically located (i.e. 0°).  The uncalibrated peak is now at Δφ=116°, so we just need to add that to every phase delay that we apply.  Enter whatever your value is (it probably won’t be 116°) into the “phase_cal” variable.  Doing so should now get you this:

  Beam plot diagram

Now we see a nice pattern, centered at 0°.  And if you’re doing this in your own lab (and I really hope you are – that’s the whole point of this blog!), then move that transmitter around and note how the plot changes.  You’ll see the peak (indicated by the vertical red dotted line) move to wherever the transmitter is positioned.  In every case, you can see the amplitude is largely flat around where the transmitter is, then rolls off with increasing phase shifts.  You can see a video of this in action at the youtube link listed above.  This phase shift should hold pretty well over power ups and over time.  So you probably don't need to do this every time you start up Pluto. 

Direction of Arrival

That red vertical line, on the plot above, is giving the “direction of arrival” (DOA) of the transmitted signal.   We’ll do other things with the beamformer in future blogs.  But for right now, let’s just dwell on using this as a DOA measurement tool.  And for DOA, it is a long standing tradition that we must do a compass plot.  In fact, it's already in the example code file I linked above.  Just set “Plot_Compass = True”, and you’ll see something like this:

 Compass plot

That blue line will point to exactly where the transmitter is.


We finally did it!  We built a very simple digital beamformer.  But this is just the beginning.  There’s a lot more we could do with this.  And in the next blog will cover a fun one:  monopulse tracking.