satdump/plugins/sdr_sources/bladerf_sdr_support/bladerf_dev.cpp

383 lines
16 KiB
C++
Raw Permalink Normal View History

2025-04-21 23:17:21 +02:00
#include "bladerf_dev.h"
#include "common/dsp/complex.h"
2025-04-22 19:56:59 +02:00
#include "dsp/block.h"
#include "nlohmann/json.hpp"
2025-04-21 23:17:21 +02:00
#include <bladeRF2.h>
#include <libbladeRF.h>
#include <logger.h>
#include <string>
namespace satdump
{
namespace ndsp
{
BladeRFDevBlock::BladeRFDevBlock() : DeviceBlock("bladerf_dev_cc", {}, {{"RX", DSP_SAMPLE_TYPE_CF32}})
{
2025-08-29 15:04:58 +02:00
outputs[0].fifo = std::make_shared<DSPStream>(16); // TODOREWORK
2025-04-21 23:17:21 +02:00
}
BladeRFDevBlock::~BladeRFDevBlock()
{
stop(); // TODOREWORK (make sure to close device)
}
void BladeRFDevBlock::set_frequency()
{
if (is_open)
{
if (rx_ch_number > 0)
{
2025-04-22 19:56:59 +02:00
bladerf_frequency actual;
2025-04-21 23:17:21 +02:00
bladerf_set_frequency(bladerf_dev_obj, BLADERF_CHANNEL_RX(0), rx_frequency);
2025-04-22 19:56:59 +02:00
bladerf_get_frequency(bladerf_dev_obj, BLADERF_CHANNEL_RX(0), &actual);
logger->debug("Set BladeRF RX frequency to %f, actual %f", rx_frequency, (double)actual);
2025-04-21 23:17:21 +02:00
}
if (tx_ch_number > 0)
{
2025-04-22 19:56:59 +02:00
bladerf_frequency actual;
2025-04-21 23:17:21 +02:00
bladerf_set_frequency(bladerf_dev_obj, BLADERF_CHANNEL_TX(0), tx_frequency);
2025-04-22 19:56:59 +02:00
bladerf_get_frequency(bladerf_dev_obj, BLADERF_CHANNEL_TX(0), &actual);
logger->debug("Set BladeRF TX frequency to %f, actual %f", tx_frequency, (double)actual);
2025-04-21 23:17:21 +02:00
}
}
}
void BladeRFDevBlock::set_gains()
{
if (is_open)
{
2025-04-22 19:56:59 +02:00
for (int i = 0; i < 2; i++)
2025-04-21 23:17:21 +02:00
{
2025-04-22 19:56:59 +02:00
if (rx_ch_number > 0)
2025-04-21 23:17:21 +02:00
{
if (rx_ch_number == 2 || rx_ch_id == i)
{
bladerf_set_gain_mode(bladerf_dev_obj, BLADERF_CHANNEL_RX(i), BLADERF_GAIN_MANUAL);
bladerf_set_gain(bladerf_dev_obj, BLADERF_CHANNEL_RX(i), rx_gain[i]);
logger->debug("Set BladeRF RX%d gain to %d", i + 1, rx_gain[i]);
}
2025-04-22 19:56:59 +02:00
}
2025-04-21 23:17:21 +02:00
2025-04-22 19:56:59 +02:00
if (tx_ch_number > 0)
{
2025-04-21 23:17:21 +02:00
if (tx_ch_number == 2 || tx_ch_id == i)
{
bladerf_set_gain(bladerf_dev_obj, BLADERF_CHANNEL_TX(i), tx_gain[i]);
logger->debug("Set BladeRF TX%d gain to %d", i + 1, tx_gain[i]);
}
}
}
}
}
void BladeRFDevBlock::set_others()
{
if (is_open)
{
// if (bladerf_model == 2) // TODOREWORK?
{
bladerf_set_pll_enable(bladerf_dev_obj, extclock);
logger->debug("Set BladeRF External Clock to %d", (int)extclock);
}
if (rx_ch_number > 0)
{
bladerf_set_bias_tee(bladerf_dev_obj, BLADERF_CHANNEL_RX(0), rx_bias);
logger->debug("Set BladeRF RX bias to %d", rx_bias);
}
if (tx_ch_number > 0)
{
bladerf_set_bias_tee(bladerf_dev_obj, BLADERF_CHANNEL_TX(0), tx_bias);
logger->debug("Set BladeRF TX bias to %d", rx_bias);
}
}
}
void BladeRFDevBlock::init()
{
set_frequency();
set_gains();
set_others();
}
void BladeRFDevBlock::start()
{
struct bladerf_devinfo info;
bladerf_init_devinfo(&info);
2025-04-22 19:56:59 +02:00
if (dev_serial.size() >= BLADERF_SERIAL_LENGTH - 1)
2025-04-21 23:17:21 +02:00
{
strncpy(info.serial, dev_serial.c_str(), BLADERF_SERIAL_LENGTH - 1);
2025-04-22 19:56:59 +02:00
info.serial[BLADERF_SERIAL_LENGTH - 1] = '\0';
2025-04-21 23:17:21 +02:00
}
info.serial[BLADERF_SERIAL_LENGTH - 1] = '\0';
2025-04-22 19:56:59 +02:00
int r = 0;
if (r = bladerf_open_with_devinfo(&bladerf_dev_obj, &info))
throw satdump_exception("Could not open BladeRF '" + std::string(info.serial) + "' device! " + std::string(bladerf_strerror(r)));
2025-04-21 23:17:21 +02:00
is_open = true;
2025-08-09 14:56:51 +02:00
// Get the board version. TODOREWORK MAKE THIS COMMON!!!!
int bladerf_model;
const char *bladerf_name = bladerf_get_board_name(bladerf_dev_obj);
if (std::string(bladerf_name) == "bladerf1")
bladerf_model = 1;
else if (std::string(bladerf_name) == "bladerf2")
bladerf_model = 2;
else
bladerf_model = 0;
#ifdef BLADERF_HAS_WIDEBAND
if (samplerate > 61.44e6)
{
is_8bit = true;
if (bladerf_enable_feature(bladerf_dev_obj, BLADERF_FEATURE_OVERSAMPLE, true) != 0)
logger->error("Could not set Oversample mode");
logger->debug("Using BladeRF Wideband mode");
}
else
{
is_8bit = false;
if (bladerf_enable_feature(bladerf_dev_obj, BLADERF_FEATURE_DEFAULT, true) != 0)
logger->error("Could not set Default mode");
logger->debug("Using BladeRF Default mode");
}
#endif
#ifdef BLADERF_HAS_WIDEBAND
if (bladerf_model == 2 && samplerate <= 61.44e6)
{
#endif
bladerf_set_sample_rate(bladerf_dev_obj, BLADERF_CHANNEL_RX(0), samplerate, NULL);
logger->info("Actual samplerate %f (precise)", samplerate);
#ifdef BLADERF_HAS_WIDEBAND
}
else
{
struct bladerf_rational_rate rational_rate, actual;
rational_rate.integer = static_cast<uint32_t>(samplerate);
rational_rate.den = 10000;
rational_rate.num = (samplerate - rational_rate.integer) * rational_rate.den;
bladerf_set_rational_sample_rate(bladerf_dev_obj, BLADERF_CHANNEL_RX(0), &rational_rate, &actual);
uint64_t actuals = actual.integer + (actual.num / static_cast<double>(actual.den));
logger->info("Actual samplerate %d (rational)", actuals);
}
#endif
2025-04-21 23:17:21 +02:00
bladerf_set_bandwidth(bladerf_dev_obj, BLADERF_CHANNEL_RX(0), samplerate, NULL);
2025-08-09 14:56:51 +02:00
sample_buffer_size = std::min<int>(samplerate / 250, 1e6);
if (is_8bit && bladerf_model == 2)
sample_buffer_size = (sample_buffer_size / 2048) * 2048;
else
sample_buffer_size = (sample_buffer_size / 1024) * 1024;
2025-04-21 23:17:21 +02:00
if (sample_buffer_size < 1024)
sample_buffer_size = 1024;
2025-08-09 14:56:51 +02:00
if (sample_buffer_size > 1048576)
sample_buffer_size = 1048576;
2025-04-21 23:17:21 +02:00
logger->trace("BladeRF Buffer size %d", sample_buffer_size);
init();
if (rx_ch_number > 0)
{
2025-08-09 14:56:51 +02:00
#ifdef BLADERF_HAS_WIDEBAND
bladerf_sync_config(bladerf_dev_obj, rx_ch_number == 2 ? BLADERF_RX_X2 : BLADERF_RX_X1, is_8bit ? BLADERF_FORMAT_SC8_Q7 : BLADERF_FORMAT_SC16_Q11, 16, sample_buffer_size, 8, 4000);
#else
2025-04-21 23:17:21 +02:00
bladerf_sync_config(bladerf_dev_obj, rx_ch_number == 2 ? BLADERF_RX_X2 : BLADERF_RX_X1, BLADERF_FORMAT_SC16_Q11, 16, sample_buffer_size, 8, 4000);
2025-08-09 14:56:51 +02:00
#endif
2025-04-21 23:17:21 +02:00
if (rx_ch_number == 2 || rx_ch_id == 0)
bladerf_enable_module(bladerf_dev_obj, BLADERF_CHANNEL_RX(0), true);
if (rx_ch_number == 2 || rx_ch_id == 1)
bladerf_enable_module(bladerf_dev_obj, BLADERF_CHANNEL_RX(1), true);
}
if (tx_ch_number > 0)
{
2025-08-09 14:56:51 +02:00
#ifdef BLADERF_HAS_WIDEBAND
bladerf_sync_config(bladerf_dev_obj, tx_ch_number == 2 ? BLADERF_TX_X2 : BLADERF_TX_X1, is_8bit ? BLADERF_FORMAT_SC8_Q7 : BLADERF_FORMAT_SC16_Q11, 16, sample_buffer_size, 8, 4000);
#else
2025-04-21 23:17:21 +02:00
bladerf_sync_config(bladerf_dev_obj, tx_ch_number == 2 ? BLADERF_TX_X2 : BLADERF_TX_X1, BLADERF_FORMAT_SC16_Q11, 16, sample_buffer_size, 8, 4000);
2025-08-09 14:56:51 +02:00
#endif
2025-04-21 23:17:21 +02:00
if (tx_ch_number == 2 || tx_ch_id == 0)
bladerf_enable_module(bladerf_dev_obj, BLADERF_CHANNEL_TX(0), true);
if (tx_ch_number == 2 || tx_ch_id == 1)
bladerf_enable_module(bladerf_dev_obj, BLADERF_CHANNEL_TX(1), true);
}
if (rx_ch_number > 0)
{
thread_should_run_rx = true;
work_thread_rx = std::thread(&BladeRFDevBlock::mainThread_rx, this);
}
if (tx_ch_number > 0)
{
work_thread_tx = std::thread(&BladeRFDevBlock::mainThread_tx, this);
}
is_started = true;
}
void BladeRFDevBlock::stop(bool stop_now)
{
2025-04-22 19:56:59 +02:00
if (is_started) // TODOREWORK Split wait & stop?
2025-04-21 23:17:21 +02:00
{
if (rx_ch_number > 0)
{
2025-04-22 19:56:59 +02:00
if (stop_now)
thread_should_run_rx = false;
2025-04-21 23:17:21 +02:00
logger->info("Waiting for the RX thread...");
if (work_thread_rx.joinable())
work_thread_rx.join();
logger->info("RX Thread stopped");
for (int i = 0; i < rx_ch_number; i++)
2025-08-29 15:04:58 +02:00
outputs[i].fifo->wait_enqueue(outputs[i].fifo->newBufferTerminator());
2025-04-21 23:17:21 +02:00
}
if (tx_ch_number > 0)
{
logger->info("Waiting for the TX thread...");
if (work_thread_tx.joinable())
work_thread_tx.join();
logger->info("TX Thread stopped");
}
bladerf_set_bias_tee(bladerf_dev_obj, BLADERF_CHANNEL_RX(0), false);
2025-04-22 19:56:59 +02:00
bladerf_set_bias_tee(bladerf_dev_obj, BLADERF_CHANNEL_TX(0), false);
if (rx_ch_number > 0)
{
if (rx_ch_number == 2 || rx_ch_id == 0)
bladerf_enable_module(bladerf_dev_obj, BLADERF_CHANNEL_RX(0), false);
if (rx_ch_number == 2 || rx_ch_id == 1)
bladerf_enable_module(bladerf_dev_obj, BLADERF_CHANNEL_RX(1), false);
}
if (tx_ch_number > 0)
{
if (tx_ch_number == 2 || tx_ch_id == 0)
bladerf_enable_module(bladerf_dev_obj, BLADERF_CHANNEL_TX(0), false);
if (tx_ch_number == 2 || tx_ch_id == 1)
bladerf_enable_module(bladerf_dev_obj, BLADERF_CHANNEL_TX(1), false);
}
2025-04-21 23:17:21 +02:00
bladerf_close(bladerf_dev_obj);
is_started = false;
is_open = false;
}
}
std::vector<DeviceInfo> BladeRFDevBlock::listDevs()
{
std::vector<DeviceInfo> r;
bladerf_devinfo *devs_list = nullptr;
int devs_cnt = bladerf_get_device_list(&devs_list);
bladerf *bladerf_dev_obj;
for (int i = 0; i < devs_cnt; i++)
{
2025-04-22 19:56:59 +02:00
nlohmann::ordered_json c;
2025-04-21 23:17:21 +02:00
// Open device
if (bladerf_open_with_devinfo(&bladerf_dev_obj, &devs_list[i]) != 0)
{
logger->warn("Could not open BladeRF for probing!");
continue;
}
// Get channel count
int rx_channel_cnt = bladerf_get_channel_count(bladerf_dev_obj, BLADERF_RX);
int tx_channel_cnt = bladerf_get_channel_count(bladerf_dev_obj, BLADERF_TX);
// Get the board version
int bladerf_model;
const char *bladerf_name = bladerf_get_board_name(bladerf_dev_obj);
if (std::string(bladerf_name) == "bladerf1")
bladerf_model = 1;
else if (std::string(bladerf_name) == "bladerf2")
bladerf_model = 2;
else
bladerf_model = 0;
// Get all ranges, per channel
for (int tx_rx = 0; tx_rx < 2; tx_rx++)
{
2025-04-22 19:56:59 +02:00
const bladerf_range *bladerf_range_samplerate;
const bladerf_range *bladerf_range_bandwidth;
const bladerf_range *bladerf_range_gain_rx;
const bladerf_range *bladerf_range_gain_tx;
bladerf_get_sample_rate_range(bladerf_dev_obj, BLADERF_CHANNEL_RX(0), &bladerf_range_samplerate);
bladerf_get_bandwidth_range(bladerf_dev_obj, BLADERF_CHANNEL_RX(0), &bladerf_range_bandwidth);
bladerf_get_gain_range(bladerf_dev_obj, BLADERF_CHANNEL_RX(0), &bladerf_range_gain_rx);
bladerf_get_gain_range(bladerf_dev_obj, BLADERF_CHANNEL_TX(0), &bladerf_range_gain_tx);
// Get available samplerates
std::vector<double> available_samplerates;
available_samplerates.push_back(bladerf_range_samplerate->min);
for (int i = 1e6; i < bladerf_range_samplerate->max; i += 1e6)
available_samplerates.push_back(i);
2025-08-09 14:56:51 +02:00
auto old_max = bladerf_range_samplerate->max;
2025-04-22 19:56:59 +02:00
available_samplerates.push_back(bladerf_range_samplerate->max);
2025-08-09 14:56:51 +02:00
#ifdef BLADERF_HAS_WIDEBAND
if (bladerf_model == 2)
{
if (bladerf_enable_feature(bladerf_dev_obj, BLADERF_FEATURE_OVERSAMPLE, true) != 0)
logger->warn("Couldn't set oversample mode!");
// Get all ranges
bladerf_get_sample_rate_range(bladerf_dev_obj, BLADERF_CHANNEL_RX(0), &bladerf_range_samplerate);
for (int i = 1e6; i < bladerf_range_samplerate->max; i += 1e6)
{
if (i > old_max)
available_samplerates.push_back(i);
}
available_samplerates.push_back(bladerf_range_samplerate->max);
2025-08-09 18:18:17 +02:00
if (bladerf_enable_feature(bladerf_dev_obj, BLADERF_FEATURE_DEFAULT, true) != 0)
2025-08-09 14:56:51 +02:00
logger->warn("Couldn't unset oversample mode!");
}
#endif
2025-04-22 19:56:59 +02:00
// add_param_list(c, "samplerate", "uint", available_samplerates, "Samplerate");
// add_param_range(c, "samplerate", "uint", bladerf_range_samplerate->min, bladerf_range_samplerate->max, bladerf_range_samplerate->step, "Samplerate");
// c["samplerate"]["range_noslider"] = true;
c["samplerate"]["type"] = "samplerate";
c["samplerate"]["name"] = "Samplerate";
c["samplerate"]["list"] = available_samplerates;
c["samplerate"]["allow_manual"] = true;
add_param_range(c, "rx1_gain", "int", bladerf_range_gain_rx->min, bladerf_range_gain_rx->max, bladerf_range_gain_rx->step, "RX1 Gain");
if (bladerf_model == 2)
add_param_range(c, "rx2_gain", "int", bladerf_range_gain_rx->min, bladerf_range_gain_rx->max, bladerf_range_gain_rx->step, "RX2 Gain");
add_param_range(c, "tx1_gain", "float", bladerf_range_gain_tx->min * bladerf_range_gain_tx->scale, bladerf_range_gain_tx->max * bladerf_range_gain_tx->scale,
bladerf_range_gain_tx->step * bladerf_range_gain_tx->scale, "TX1 Gain");
if (bladerf_model == 2)
add_param_range(c, "tx2_gain", "float", bladerf_range_gain_tx->min * bladerf_range_gain_tx->scale, bladerf_range_gain_tx->max * bladerf_range_gain_tx->scale,
bladerf_range_gain_tx->step * bladerf_range_gain_tx->scale, "TX2 Gain");
2025-04-21 23:17:21 +02:00
}
std::string name = "BladeRF";
if (bladerf_model == 1)
name += "1";
else if (bladerf_model == 2)
name += "2";
name += " " + std::string(devs_list[i].serial);
nlohmann::json p;
p["serial"] = std::string(devs_list[i].serial);
r.push_back({"bladerf", name, p, c});
2025-04-22 19:56:59 +02:00
bladerf_close(bladerf_dev_obj);
2025-04-21 23:17:21 +02:00
}
if (devs_list != nullptr && devs_cnt > 0)
bladerf_free_device_list(devs_list);
return r;
}
} // namespace ndsp
} // namespace satdump