GNU Radio (GR) is a popular and remarkably effective framework for developing software-defined radio (SDR) applications. Nevertheless, while the framework offers a rich set of features and resources, its initial installation and setup can be overwhelming. This tutorial aims at alleviating this problem by leveraging a reproducible Docker-based environment.
The main goal of the setup explained in the sequel is to separate the roles of the host and the Docker container. It is usually preferable to edit the GR sources directly from the host on an editor of choice. Hence, the adopted setup ensures the host controls the GR sources. On the other hand, one usually wants to avoid installing dependencies and GR itself on the host filesystem. This is where the container environment comes in. In the setup that follows, you will use the container environment to compile GR, install it, and install all software dependencies. Ultimately, this arrangement preserves the host in a clean state.
Before proceeding, note this tutorial was developed and tested on a macOS Big Sur environment, using Docker Desktop (M1 build) v3.3.0. However, it can easily be adapted to other platforms.
To start, clone the project as follows:
git clone https://github.com/igorauad/gnuradio-docker-env
Next, clone GR inside the
cd gnuradio-docker-env/ git clone https://github.com/gnuradio/gnuradio.git
This GR directory is meant to be your working directory for GR development.
Next, prepare to run GUI applications inside the container (e.g., to run
gnuradio-companion). Note there will be no X Server running in the
container. Hence, the container will need to use the host’s X Server.
For example, on macOS, you can use the XQuartz application on the host to
display GUI applications running inside the container. To do so, first, you need
to define the
DISPLAY env var on the running container such that it points to
the host’s X server. You can test whether this works by running a GUI-based
image such as
xeyes. For example, run the following:
docker run -e DISPLAY=host.docker.internal:0 gns3/xeyes
At this point, you will likely see
Error: Can't open display. That’s because
you still need to authorize the container to access the host’s X server. To do
so, check the source IP address of the X11 packets coming from the
container. Open a terminal window and run:
sudo tcpdump -i any port 6000
Then, on another window, run the
xeyes container and observe the packets on
tcpdump. You should see packets coming from an IP address in the same subnet of
the Docker bridge network.
For example, let’s say the source IP address is
192.168.64.2. Then, you can
authorize this IP address to access the X server by running:
xhost + 192.168.64.2
xhostprovides a simple way to grant access to your host’s X server. However, it is not the safest approach. If you are worried about security, you can implement the X server access using xauth instead.
xeyes. It should open the GUI successfully.
If the GUI still fails, make sure that:
XQuartz (on macOS) is configured to allow connections from network clients (at
Preferences > Security).
The container can ping the host. On Docker Mac, you can test with
Next, build and launch the container in detached mode:
docker-compose up --build -d
NOTE: change the
docker-compose.ymlif you are not running on Docker Mac.
This compose stack creates three volumes:
gr_prefix: a named volume where GR will be installed.
gr_build: a named volume containing the GR build directory.
- A bind mount of the
gnuradiodirectory cloned earlier.
The latter (the bind mount volume) allows for editing the GR sources directly
from the host. Any changes made to the
gnuradio/ directory are reflected
inside the container. Likewise, any changes made to the sources inside the
container (e.g., output products from
gr_modtool) are synchronized back to
In contrast, the first two volumes (the named volumes) are not tied to a host
directory. Instead, they are isolated on purpose. For example, the
volume will hold the GR build directory such that only the container can access
it, while the host does not see the build directory. This approach is meant to
preserve the state of the host’s
gnuradio directory as clean as possible.
Next, launch an interactive bash session on the running container:
docker exec -it gnuradio-docker-env_gnuradio_1 bash
Finally, compile GR inside the container:
cd src/gnuradio/build/ cmake \ -DCMAKE_INSTALL_PREFIX=/root/gr_prefix/ \ -DCMAKE_BUILD_TYPE=Debug \ -DENABLE_GR_AUDIO=OFF \ -DENABLE_GR_TRELLIS=OFF \ -DENABLE_GR_VIDEO_SDL=OFF \ -DENABLE_GR_VOCODER=OFF \ -DENABLE_GR_WAVELET=OFF \ -DENABLE_GR_ZEROMQ=OFF \ ../ make -j`nproc` make test make install ldconfig
Note: you may need to adjust the Docker resources. On macOS, access
Docker Desktop > Preferences > Resources.
The given steps will install GR on the
gr_prefix named volume mentioned
earlier. Hence, the installation will persist across container sessions. That
is, you can stop the container any time and resume later.
Furthermore, the build directory will be preserved on the
volume. Thus, after the first compilation, you can make incremental changes to
the sources without recompiling the whole project tree.
At this point, you should be ready to run and develop with GR. For example, try
If you would like to develop an out-of-tree (OOT) GR module using the same environment, you can do so by proceeding with the following steps:
- Clone your OOT module in the root directory of this project, just like how
- Modify the
Dockerfilecontainer image recipe such that it installs the dependencies required for your OOT module.
- Add the appropriate volumes on
docker-compose.yml. For example, if you are developing an OOT module named
gr-oot, add the following to
services: gnuradio: ... volumes: ... - ./gr-oot/:/root/gr_prefix/src/gr-oot/ - gr_oot_build:/root/gr_prefix/src/gr-oot/build/ volumes: ... gr_oot_build
- Build and install the OOT module from the build directory at
src/gr-oot/build/inside the container.