If you've been around for a while, then you know that one of my favorite applications for FPGAs is any form of software defined radio (SDR). While I've enjoyed designing my own SDR hardware/software, all of those projects have started with a prebuilt development platform such as an Analog Devices FMC card with AMD FPGA development board or something from Ettus Research's universal software radio peripheral (USRP) catalog.
Previously I've posted tutorials for using the Ettus USRP B205mini with GNU Radio for SDR basics such as using matched filters and simple FM radio receivers. The B205mini is a highly flexible SDR development platform that I've used quite a bit and now it is getting a refresh with the release of the B206mini.
As I mentioned, the USRP B206mini is a part of Ettus' B200 series of cognitive software defined radios that is a refreshed version of the business card form-factored B200mini, and B205mini devices.
The core of the B206mini is still the tried and true combination of the AMD Spartan FPGA with Analog Devices AD9364 RF transceiver chip. When I've designed SDRs in past projects (especially for hand-held/batter powered applications) I always end up with some combination of lower power FPGA like an AMD Spartan or Artix with the AD9364 because of how easy the AD9364 can be configured and the wideband frequency support it offers.
The AD9364 is super flexible with its internal programmable filters that are easily configured to the desired settings for you application via a SPI interface that simply reads/writes to a set of configuration registers. This means the firmware on the FPGA controlling the configuration of the transceiver can be easily optimized for power and fabric space saving purposes.
This also makes the AD9364 an ideal chip to use with SDR development software like GNU Radio for constant reconfiguration on the fly.
The B206mini is a nice "refinement" revision of the B205mini with the micro-USB 3.0 connector replaced with a USB-C 3.0 connector and different surface-mount SMA connectors with more surface area on their posts so the soldered connection is more physically robust.
For all intents and purposes, the B206mini is a drop-in replacement for the B205mini so long as the UHD driver and GNU Radio version requirements are met. So I thought I'd take the time to document my process for switching over from the B205mini to the B206mini. See how well it dropped into my existing projects and test out some new things with it.
Install GNU Radio + UHD DriversThe B206mini can be used with any development platform that is able to use the USRP Hardware Driver™ (UHD) software API, so long as the the UHD is version 4.9.0 or later. More in-depth or complex projects push me towards Matlab but GNU Radio is my preferred starting point and it's much more beginner friendly.
I'm currently running Ubuntu 24.04 on my PC and while the main Ubuntu Noble repository includes GNU Radio, the underlying UHD version it uses is still 4.6.0 as of November 2025. Which is not compatible with talking to the B206mini via it's shiny new USB-C connector.
Therefore we need to add the GNU Radio PPA to be able to get the latest verion of GNU Radio with UHD 4.9.0:
~$ sudo add-apt-repository ppa:gnuradio/gnuradio-releasesUse the apt list command to see the different versions of GNU Radio so you know which to specify for the install (which is version 3.10.12.0-2 in this case):
~$ apt list --all-versions gnuradio
Listing... Done
gnuradio/noble,now 3.10.12.0-2~noble1 amd64
gnuradio/noble 3.10.9.2-1.1ubuntu2 amd64Running the apt-cache depends command for this GNU Radio version will confirm its use of UHD version 4.9.0:
~$ apt-cache depends gnuradio=3.10.12.0-2~noble1 | grep uhd
Depends: libgnuradio-uhd3.10.12
Depends: libuhd4.9.0
Suggests: uhd-hostAgain, since the main Ubuntu Noble repository contains an older version of the UHD driver (version 4.6.0 to be specific), use the apt list command again to see the specific version name to specify for the install:
~$ apt list --all-versions uhd-host
Listing... Done
uhd-host/noble,now 4.9.0.0+ds1-1~noble2 amd64
uhd-host/noble 4.9.0.0-0ubuntu1~noble3 amd64
uhd-host/noble-updates 4.6.0.0+ds1-5.1ubuntu0.24.04.1 amd64
uhd-host/noble 4.6.0.0+ds1-5.1build4 amd64Do the same for the libuhd-dev driver if you plan to create your own executable the uses the UHD driver version 4.9.0 (this step is not necessary to follow the rest of this tutorial):
~$ apt list --all-versions libuhd-dev
Listing... Done
libuhd-dev/noble,now 4.9.0.0+ds1-1~noble2 amd64
libuhd-dev/noble 4.9.0.0-0ubuntu1~noble3 amd64
libuhd-dev/noble-updates 4.6.0.0+ds1-5.1ubuntu0.24.04.1 amd64
libuhd-dev/noble 4.6.0.0+ds1-5.1build4 amd64Once you know the exact package version names to specify, install GNU Radio and the UHD drivers:
~$ sudo apt-get install gnuradio=3.10.12.0-2~noble1
~$ sudo apt-get install uhd-host=4.9.0.0+ds1-1~noble2
~$ sudo apt-get install libuhd-dev=4.9.0.0+ds1-1~noble2Download USRP BitstreamsOnce the UHD drivers have been installed, they need to download the latest bitstreams for the USRP devices that contains the firmware to interface with the software API. This is what the uhd_images_downloader command is used for, and is does need to be run with super user privileges since the bitstreams are downloaded to the /usr/share/ directory under /usr/share/uhd/4.9.0/images.
Run the UHD image download command.
~$ sudo uhd_images_downloaderIf you forget this step, GNU Radio and the underlying UHD commands will return an error that the images are not available.
Test USRP HardwareOnce the UHD drivers are installed, plug in the B206mini and use the uhd_find_devices command to validate the UHD drivers are able to see it:
~$ uhd_find_devicesFor testing purposes, I have both my B205mini and my B206 mini connected so both show indexed via their serial address:
That string for the USRP's 6-character serial ID is how the UHD drivers address bus series USRP devices such as the B206mini and B205mini (the "B" in the title refers to bus series).
There are a few UHD terminal commands that can be used for basic testing such at the uhd_fft command. This command calls a basic GNU radio flowgraph under the hood that takes the raw received samples from the USRP, passes them through an FFT block then dumps them into a QT GUI Sink.
This command is ideal for a quick test to see what spectral content is in a target frequency band or simply validate that the USRP hardware is able to successfully take measurements.
Since the GUI makes most of the controls for settings such as the target center frequency, sample rate, FFT parameters, etc are made configurable from the flowgraph GUI while running, the only necessary parameter to pass the the uhd_fft command from the terminal is the string from the USRP's 6-character serial ID.
~$ uhd_fft --args "serial=<USRP serial ID>" --freq 101e6I still like to also pass the initial center frequency for measurement, I chose the target FM radio station I'm going to look at with my existing FM radio receiver flowgraph in later steps:
So this confirms that the B206mini is able to see something where the 101.1 MHz FM radio station is using my trusty basic dipole antenna.
Speaking of antennas, something I feel like isn't always covered in getting started posts involving SDRs is why the given antenna is selected for the example application. While I don't want to dive too deep here and end up writing a textbooks-worth of info here, there are a couple very basic rules to follow to get started down the path of antenna selection.
The first and most basic of which is knowing how to size an antenna to the target application. The physical length of the antenna being used for an SDR application has a mathematical relationship to the target center operating frequency of the application.
The ideal length of antenna for an application is 1/4 (one-quarter) the wavelength of the target center operating frequency. The definition of "ideal" here being that it will provide the best signal fidelity in transmission and reception and is referred to as the "resonant length" of an antenna for a specific frequency.
When a sine wave starts out at the input of the antenna's feedpoint, it has one polarity. When it hits the end of the antenna 1/4 of a cycle later, it is reflected back in the opposite polarity and makes it back to the feedpoint another 1/4 cycle later. Since that reflected wave in the opposite polarity makes it back to the feedpoint 1/2 a cycle later, it now matches the polarity of the 2nd half of the cycle of the original sine wave is being driven into it.
Therefore the current at the antenna is "reinforced", giving it a low impedance. In other words, it can drive larger current at a lower voltage; and the larger the current, the stronger the EMI field is that radiates from the antenna. This also gets the antenna to that standard 50Ω mark and minimize the amount of complex impedance matching that needs to be done to avoid loss.
If you're curious, the formula to calculate wavelength of a given frequency is simply the speed of light divided by that frequency. There are plenty of online wavelength calculators like this one and this one.
The antenna kit that I've gotten the most use out of is the RTL-SDR multipurpose dipole antenna kit that includes two 23cm to 1m telescopic antenna and two 5cm to 13cm telescopic antenna, which covers the two most commonly used hobby SDR frequency bands of VHF and UHF:
Another kit I've used frequently is the Nooelec RaTLSnake antenna bundle that is also sized for UHF applications. I like this kit because the base for holding the antenna comes with magnet and longer feed cable for easy mounting.
I decided to hook them both up to my B206mini for ease of switching between VHF and UHF applications:
As I previously mentioned, the first thing I wanted to test with my B206mini was if it really was a seamless drop-in replacement for my B205mini. So I open my existing FM radio receiver flowgraph in the new version of GNU Radio installed in the previous steps.
I added couple of new variable blocks to it: one for the serial ID for the B205mini and another for the serial ID for the B206mini so I could easy switch between the two in the UHD USRP Source blocks between runs.
I've described this flowgraph in more detail in past tutorials such as this one, but the TL;DR of the functionality is the utilization of the WBFM Receive block to demodulate FM Radio station broadcasts and sink the output into the host PC's sound card to listen to the selected radio station.
As described in more detail here, the first step is to filter the input from the UHD USRP Source block through a matched filter of some type (I went with a root raised cosine filter) in order to filter out noise/interference while aligning the signals within the passband such that they all sum to zero - aka "pulse shaping".
The filtered relevant signal is them demodulated and passed through a final resampling block in order to match the final data rate to the 48kHz that the PC sound card operates at. In order to select my USRP sample rate and what ratios to interpolate/decimate the signal throughout the chain by, I like to start with the final output data rate I need and work backwards.
So in this case, I started with the 48kHz that the PC sound card operates at worked backwards to make sure the sample rate I set in the USRP was some whole integer value of 48kHz that didn't require an excessive number of resampling stages to achieve.
It's important that resampling for interpolation/decimation should be kept to only the minimum necessary for achieving a data rate in the system such that samples are not overflowing or underflowing anywhere in the signal chain. Because every time the data is resampled, a little more signal fidelity is lost.
To really see the impact of this add a Rational Resampler block in-between the UHD USRP Source block and the Raised Root Cosine Filter block, move the decimation factor of 2 to the Rational Resampler block, and set the decimation in the Raised Root Cosine Filter block back to 0.
You'll notice a significant increase in the static interference in the radio audio with the additional resampling step done ahead of the filter instead of just taking in every other sample into the RRC filter directly. If you change the Rational Resampler block to interpolate by 2 and decimate by 4 to still achieve an overall decimation factor of 2, you'll hear the static in the received radio audio get even worse.
ADS-B Receiver (Out-Of-Tree Example)The FM radio receiver flowgraph is a great basic project that can be achieved using blocks all from the internal catalog of GNU Radio. These blocks at a very basic level are just C++/C# code performing the actual DSP functions with a Python wrapper for passing data in/out from the upper flowgraph level. So one can easily create their own blocks and add them to the path the GNU Radio uses for its catalog. This is referred to as an "out-of-tree" module in GNU Radio, and you can find tons of great out-of-tree module repositories in the open source community.
Since I live in smack the middle of a cluster of airports of varying sizes, my interest has been peaked lately in Automatic Dependent Surveillance–Broadcast or ADS-B messaging. So before I started writing my own module, I did a quick google search of ADS-B encoder/decoder blocks people had already created and I found a great example Matt Hostetter created and posted in his Github repository here.
If you're not familiar, ADS-B messaging is used by various aircraft, including commercial airliners, for determining their position via satellite navigation then broadcast that position for other aircraft to be aware of as well as ground control towers. Since ADS-B are broadcast without prompt from a transmitted interrogation signal, they are perfect for a hobby SDR receiver application.
Create a directory and clone the ADS-B block repository into it:
~$ mkdir -p ads-b_sdr
~$ cd ./ads-b_sdr/
~/ads-b_sdr$ git clone -b maint-3.10 https://github.com/mhostetter/gr-adsbI had a few dependencies I still needed to install that the underlying code in the blocks needed, as well as the corresponding webserver to display the captured ADS-B messages on a map:
~$ sudo apt install python3-colorama python3-zmq python3-flask python3-flask-socketio python3-gevent python3-gevent-websocketChange directories into the cloned repository and build the source files for the blocks:
~/ads-b_sdr$ cd gr-adsb/
~/ads-b_sdr/gr-adsb$ mkdir build
~/ads-b_sdr/gr-adsb$ cd build/
~/ads-b_sdr/gr-adsb/build$ cmakeThen run make and make install to install the new out-of-tree ADS-B modules in the GNU Radio catalog:
~/ads-b_sdr/gr-adsb/build$ make
~/ads-b_sdr/gr-adsb/build$ sudo make install
~/ads-b_sdr/gr-adsb/build$ sudo ldconfigAfter running the installation, you'll notice the new ADS-B catalog tab appear above the Core catalog tab in GNU Radio:
In the ./examples directory of the repository there is a simple ADS-B receiver that shows the bare bones of what's needed for receiving and decoding messages:
Opting for the verbose print option in the ADS-B decoder and setting my B206mini as the UHD source, I ran the ADS-B receiver as-in to view the received signal quality in order to evaluate the best place to mount my antenna.
I'm using my Nooelec M6 UHF antenna since the center operating frequency for ADS-B is 1.09GHz, and after some trail and error I found the best place for reception was in my window sill downstairs.
The really cool thing about this particular ADS-B module repository is that it also includes a webserver that receives the ZeroMQ packets from the ZMQ PUB Message Sink in the flowgraph and plots them over an instance of Google Maps.
Simply change directories into the ./web directory of the cloned repository and run the webserver.py script:
~/ads-b_sdr/gr-adsb/web$ python3 ./webserver.pyAnd connect to it via a web browser using the following address for your localhost on port 5000:
localhost:5000The map will update so long as the ADS-B receiver flowgraph is running in GNU Radio and outputting the received messages via ZeroMQ HTTP publishes.
As a side note, I did have to make a slight modification default homepage, index.html, file on line 40 to call the latest version of the socket.io library as the version it previously called was too out-of-date for a web browser in my Ubuntu 24.04 PC to connect to:
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.8.1/socket.io.js"></script>Also, make sure you have the libncurses-dev dependency installed as well if you still have trouble with the webpage loading:
~$ sudo apt install libncurses-devThis is such a cool find of a repository an I will definitely be using it in my future ADS-B based SDR projects!
ConclusionWell there you have it: the B206mini dropped in perfectly with my existing flowgraph for a simple FM radio receiver, as well as worked seamlessly with the new ADS-B out-of-tree module I installed!
This only hiccup I ran into was that the USB-C connection did not want to operate via USB 3.0 so I instead had to use a hub to force it to operate via USB 2.0. But my B205mini was also acting weird via USB 3.0 as well, and since this is the first time I've used GNU Radio on my Ubuntu 24.04 virtual machine (VM). I have a feeling it is something to do with my 24.04 VM settings since everything works fine in my Ubuntu 22.04 VM following all of the same steps. I only mention here in passing just in case anyone else who likes to use VMs as I do faces the same issue.
Overall, I'm excited to have both the B206mini and B205mini to be able to use them together by running the transmit side of my SDR chain on one and the receive side of the chain on the other. So stay tuned for more tutorials!




Comments