SoapySDR
Overview
SoapySDR, a C++ library designed for properly formatting and running a SDR. I personally have used it in my SIGINT project and it is a great way to seamlessly and easily format devices and get it ready for RF scanning.
Just like everything I talk about, I am nowhere near an expert, these articles are a way for me to get better and learn these topics, while also spreading what I've learned to other people.
Installation
In order to use SoapySDR you must download the zip file from the Pothosware website, due to their download files not being updated, I recommend downloading an older version such as the 2019 Pothosware version. However, you can do this through CMake and skip all the additional setup with ease.
If you are not using CMake to build this project you must setup the C++ and Linker directories in order for SoapySDR to work which goes as the following:
C++ Additional Include Directories:
C:\Program Files\PothosSDR\include
Linker Additional Library Directories:
C:\Program Files\PothosSDR\lib
Linker Additional Dependencies
SoapySDR.lib
Once all of this is setup you can now use SoapySDR! Which also paves our way into some examples of specific SoapySDR functions and their tasks.
Usage
Now before we get into it, this does require a little bit of C++ knowledge, especially knowing what vectors, kwargs, strings and for loops are. If you do not know these, I recommend checking W3Schools introduction to C++ guide in getting familiar with C++
We will also be creating our own header file which consist of the necessary library imports as well as some C macros for easy debugging statements.
#pragma once
#ifndef UTILS_H
#define UTILS_H
#include <SoapySDR/Version.hpp>
#include <SoapySDR/Modules.hpp>
#include <SoapySDR/Registry.hpp>
#include <SoapySDR/Device.hpp>
#include <SoapySDR/ConverterRegistry.hpp>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
#define okay(msg, ...) printf("[+] " msg "\n", ##__VA_ARGS__)
#define warn(msg, ...) printf("[-] " msg "\n", ##__VA_ARGS__)
#define info(msg, ...) printf("[i] " msg "\n", ##__VA_ARGS__)
#endif
Now! For our very first task we will be checking if an SDR is connecting to our device and then printing out some basic metadata of the device.
Just some cautionary advice, the code I will be showing may be disturbing to some due to its awful nature, as time progresses I hope to disinfect my coding skills and make them more optimal
#include "enumeration.h"
#include "UTILS.h"
std::vector<std::string> Formatting()
{
/*---[Grab Metadata from available SDRs]---*/
SoapySDR::KwargsList enumerate = SoapySDR::Device::enumerate();
if (enumerate.empty()) {
warn("No devices found! Please check connections...");
}
else {
okay("Devices found! Creating formatted drivers....");
vector<string> SDR_MakeDevices;
printf("---/*Checking Device Compatability...*/---\n");
for (auto SDR = enumerate.begin(); SDR != enumerate.end(); ++SDR) {
string SDR_driver;
string SDR_serial;
string SDR_MakeDevice;
try {
SDR_serial = SDR->at("serial");
SDR_driver = SDR->at("driver");
SDR_MakeDevice = "driver=" + SDR_driver + ",serial=" + SDR_serial;
SDR_MakeDevices.push_back(SDR_MakeDevice);
printf("[*] Total formatted device strings: %zu\n", SDR_MakeDevices.size());
string SDR_label = SDR->at("label");
string SDR_part_id = SDR->at("part_id");
string SDR_version = SDR->at("version");
string SDR_complete = SDR_driver + " " + SDR_serial + " " + SDR_label + " " + SDR_part_id + " " + SDR_version;
okay("Complete information for all devices: %s", SDR_complete.c_str());
}
catch (const std::out_of_range& e) {
warn("Exception caught: %s", e.what());
continue;
}
}
return SDR_MakeDevices;
}
}
Looking at the C++ code above we see a few things being created...
Firstly, we are creating a SoapySDR::Kwargslist
named enumerate
which is equal to the function SoapySDR::Device::enumerate()
. This is a very straightforward function, once ran, it will first check to make sure an SDR if connecting using if (enumerate.empty())
, If it passes the first if statement, it will then move on to printing some basic metadata. This consists of the SDR: Serial number, Driver information, label, ID part, and version. This all will then get saved into an std::vector<string>
called SDR_MakeDevices
which will store the information needed to properly format a SDR (its serial and driver information). The rest of the information is then saved onto SDR_Complete
which is then called on alongside SDR_MakeDevices.
Example Output:
[+] Complete information for all devices: hackrf 0000000000000000XXXXXXXXXXXXXXX HackRF One #0 XXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXX 2018.01.1
[+] SDR Device Creation: driver=hackrf,serial=0000000000000000XXXXXXXXXXXXX
Continuing with this trend we also need a function to properly 'make' the device in order to fully get it ready for use in GUI or CLI applications. This however, isn't difficult, all we have to do is take the SDR_MakeDevices
that we saved from our Formatting function and loop through it for every available SDR connected.
#include "UTILS.h"
#include "enumeration.h"
SoapySDR::Device* MakeDev()
{
try {
std::vector<std::string> MakeDev = Formatting();
for (const auto& SDR_MakeDevice : MakeDev)
{
okay("SDR Device Creation: %s", SDR_MakeDevice.c_str());
SoapySDR::Device* device = SoapySDR::Device::make(SDR_MakeDevice);
if (!device)
{
warn("Could not create a valid SDR Device! %s", SDR_MakeDevice.c_str());
continue;
}
return device;
}
}
catch (std::exception& e)
{
warn("Exception caught: %s", e.what());
return nullptr;
}
}
If you want to just create a SDR device and only care about the necessary components, you can significantly simplify the process by just grabbing the driver and hardware from device
#include "enumeration.h"
int enumeration(SoapySDR::Device* device) {
try {
/*---[Create device for SDR probing]---*/
info("Driver= %s", device->getDriverKey().c_str());
info("Hardware= %s", device->getHardwareKey().c_str());
for (const auto& it : device->getHardwareInfo()) {
std::cout << " " << it.first << "=" << it.second << std::endl;
}
}
catch (std::exception& e) {
warn("Exception caught: %s", e.what());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
You may also see the #include "enumeration.h"
this is just a header that holds the function and any parameters in it, so that it's easy to call in Main.cpp.
And that's it! You just created your very first SDR probing device, with this you can easily start up and application whether it be CubicSDR, RTL-SDR, Airspy, etc. This greatly simplifies the process and ensures that your SDR is ready for data capturing!
(And if you're feeling lazy, the cpp files will be linked below...)
If you're interested in the rest of this project, or want to further advance your skills with SoapySDR, be sure to check out my Github Repository focusing on Signal Intelligence!
Last updated