Skip to content

Sync SoapyBladeRF with libbladeRF v2.6.0 #65

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,18 @@

## Licensing information

* LGPLv2.1: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
*LGPLv2.1: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt

## Adding OVERSAMPLE Support

1. Install libbladeRF [link](https://github.com/Nuand/bladeRF)
2. Install Soapy [link](https://github.com/pothosware/SoapySDR/wiki/BuildGuide#unix-instructions) at tag 0.8.0
3. Install SoapyBladeRF plugin [link](https://github.com/pothosware/SoapyBladeRF/wiki) at master
4. Build example and run

```bash
gcc -std=c99 ../example.c -L/usr/local/lib -lSoapySDR
./a.out
```

5. gnuradio-companion 3.10.9.2 will prioritize the local v0.8.0 Soapy lib after install
35 changes: 35 additions & 0 deletions bladeRF_Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,13 @@ SoapySDR::ArgInfoList bladeRF_SoapySDR::getSettingInfo(void) const

if (_isBladeRF1) setArgs.push_back(xb200SettingArg);

SoapySDR::ArgInfo oversampleArg;
oversampleArg.key = "oversample";
oversampleArg.value = "false";
oversampleArg.name = "Oversample";
oversampleArg.description = "Enable oversampling in FPGA";
oversampleArg.type = SoapySDR::ArgInfo::BOOL;

// Sampling mode
SoapySDR::ArgInfo samplingModeArg;
samplingModeArg.key = "sampling_mode";
Expand Down Expand Up @@ -1199,6 +1206,15 @@ SoapySDR::ArgInfoList bladeRF_SoapySDR::getSettingInfo(void) const

setArgs.push_back(biasTeeRx);

// Oversampling
SoapySDR::ArgInfo oversample;
oversample.key = "oversample";
oversample.value = "false";
oversample.name = "Enable oversampling";
oversample.description = "Enables oversampling in FPGA";
oversample.type = SoapySDR::ArgInfo::BOOL;
setArgs.push_back(oversample);

return setArgs;
}

Expand Down Expand Up @@ -1234,6 +1250,14 @@ std::string bladeRF_SoapySDR::readSetting(const std::string &key) const
return "false";
} else if (key == "biastee_rx") {
return "false";
} else if (key == "oversample") {
bladerf_feature feature;
int ret = bladerf_get_feature(_dev, &feature);
if (ret != 0) {
SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_get_feature(BLADERF_FEATURE_OVERSAMPLE) returned %s", _err2str(ret).c_str());
throw std::runtime_error("readSetting() " + _err2str(ret));
}
return feature == BLADERF_FEATURE_OVERSAMPLE ? "true" : "false";
}

SoapySDR_logf(SOAPY_SDR_WARNING, "Unknown setting '%s'", key.c_str());
Expand Down Expand Up @@ -1552,6 +1576,17 @@ void bladeRF_SoapySDR::writeSetting(const std::string &key, const std::string &v
}
}
}
else if (key == "oversample") {
bool enable = (value == "true");
int ret = bladerf_enable_feature(_dev, BLADERF_FEATURE_OVERSAMPLE, enable);
if (ret != 0)
{
SoapySDR::logf(SOAPY_SDR_ERROR, "bladerf_enable_feature(OVERSAMPLE, %s) returned %s",
value.c_str(), _err2str(ret).c_str());
throw std::runtime_error("writeSetting() " + _err2str(ret));
}
SoapySDR::logf(SOAPY_SDR_INFO, "bladerf_enable_feature(OVERSAMPLE, %s)", value.c_str());
}
else
{
throw std::runtime_error("writeSetting(" + key + ") unknown setting");
Expand Down
1 change: 1 addition & 0 deletions bladeRF_SoapySDR.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ class bladeRF_SoapySDR : public SoapySDR::Device
std::string _xb200Mode;
std::string _samplingMode;
std::string _loopbackMode;
bladerf_format _sample_format;

bladerf *_dev;

Expand Down
80 changes: 54 additions & 26 deletions bladeRF_Streaming.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,15 @@ SoapySDR::ArgInfoList bladeRF_SoapySDR::getStreamArgsInfo(const int, const size_
xfersArg.range = SoapySDR::Range(0, 32);
streamArgs.push_back(xfersArg);

SoapySDR::ArgInfo metaArg;
xfersArg.key = "meta";
xfersArg.value = "auto";
xfersArg.name = "Meta mode";
xfersArg.description = "Timestamp and burst streaming mode.\n"
"Automatic: meta in single channel mode, meta off in dual channel mode";
xfersArg.type = SoapySDR::ArgInfo::STRING;
xfersArg.options = {"auto", "meta", "normal"};
xfersArg.optionNames = {"Automatic", "Metadata Streams", "Normal Streams"};
streamArgs.push_back(metaArg);
SoapySDR::ArgInfo formatArg;
formatArg.key = "format";
formatArg.value = "sc16_meta";
formatArg.name = "Sample Format";
formatArg.description = "Sample format (sc16, sc16_meta, sc8, sc8_meta, sc16_packed)";
formatArg.type = SoapySDR::ArgInfo::STRING;
formatArg.options = {"sc16", "sc16_meta", "sc8", "sc8_meta", "sc16_packed"};
formatArg.optionNames = {"16-bit", "16-bit with Metadata", "8-bit", "8-bit with Metadata", "Packed 16-bit"};
streamArgs.push_back(formatArg);

return streamArgs;
}
Expand All @@ -97,29 +96,42 @@ SoapySDR::Stream *bladeRF_SoapySDR::setupStream(
auto channels = channels_;
if (channels.empty()) channels.push_back(0);

//meta mode, automatically on in single channel mode
auto metaMode = (args.count("meta") == 0)? "auto" : args.at("meta");
bladerf_format sync_format = BLADERF_FORMAT_SC16_Q11;
if (metaMode == "meta") sync_format = BLADERF_FORMAT_SC16_Q11_META;
if (metaMode == "normal") sync_format = BLADERF_FORMAT_SC16_Q11;
auto sampleFormat = (args.count("format") == 0)? "sc16_meta" : args.at("format");

if (sampleFormat == "sc16") {
_sample_format = BLADERF_FORMAT_SC16_Q11;
} else if (sampleFormat == "sc16_meta") {
_sample_format = BLADERF_FORMAT_SC16_Q11_META;
} else if (sampleFormat == "sc8") {
_sample_format = BLADERF_FORMAT_SC8_Q7;
} else if (sampleFormat == "sc8_meta") {
_sample_format = BLADERF_FORMAT_SC8_Q7_META;
} else if (sampleFormat == "sc16_packed") {
_sample_format = BLADERF_FORMAT_SC16_Q11_PACKED;
} else {
std::stringstream err;
err << "Invalid sample format: '" << sampleFormat << "'\n"
<< "Valid formats: [sc16, sc16_meta, sc8, sc8_meta, sc16_packed]";
throw std::runtime_error(err.str());
}

//check the channel configuration
bladerf_channel_layout layout;
if (channels.size() == 1 and (channels.at(0) == 0 or channels.at(0) == 1))
{
layout = (direction == SOAPY_SDR_RX)?BLADERF_RX_X1:BLADERF_TX_X1;
if (metaMode == "auto") sync_format = BLADERF_FORMAT_SC16_Q11_META;
}
else if (channels.size() == 2 and channels.at(0) == 0 and channels.at(1) == 1)
{
layout = (direction == SOAPY_SDR_RX)?BLADERF_RX_X2:BLADERF_TX_X2;
if (metaMode == "auto") sync_format = BLADERF_FORMAT_SC16_Q11;
}
else
{
throw std::runtime_error("setupStream invalid channel selection");
}

SoapySDR::logf(SOAPY_SDR_INFO, "Sample format: %s", bladerf_format_to_string(_sample_format));

//check the format
if (format == SOAPY_SDR_CF32) {}
else if (format == SOAPY_SDR_CS16) {}
Expand All @@ -145,7 +157,7 @@ SoapySDR::Stream *bladeRF_SoapySDR::setupStream(
int ret = bladerf_sync_config(
_dev,
layout,
sync_format,
_sample_format,
numBuffs,
bufSize,
numXfers,
Expand Down Expand Up @@ -353,6 +365,8 @@ int bladeRF_SoapySDR::readStream(
for (size_t i = 0; i < 2 * numElems; i++)
{
output[i] = float(_rxConvBuff[i])/2048;
if (_sample_format == BLADERF_FORMAT_SC8_Q7 || _sample_format == BLADERF_FORMAT_SC8_Q7_META)
output[i] = float(((int8_t*)(_rxConvBuff))[i])/128;
}
}
else if (not _rxFloats and _rxChans.size() == 2)
Expand All @@ -361,10 +375,17 @@ int bladeRF_SoapySDR::readStream(
int16_t *output1 = (int16_t *)buffs[1];
for (size_t i = 0; i < 4 * numElems;)
{
*(output0++) = _rxConvBuff[i++];
*(output0++) = _rxConvBuff[i++];
*(output1++) = _rxConvBuff[i++];
*(output1++) = _rxConvBuff[i++];
if (_sample_format == BLADERF_FORMAT_SC8_Q7 || _sample_format == BLADERF_FORMAT_SC8_Q7_META) {
*(output0++) = ((int8_t*)(_rxConvBuff))[i++];
*(output0++) = ((int8_t*)(_rxConvBuff))[i++];
*(output1++) = ((int8_t*)(_rxConvBuff))[i++];
*(output1++) = ((int8_t*)(_rxConvBuff))[i++];
} else {
*(output0++) = _rxConvBuff[i++];
*(output0++) = _rxConvBuff[i++];
*(output1++) = _rxConvBuff[i++];
*(output1++) = _rxConvBuff[i++];
}
}
}
else if (_rxFloats and _rxChans.size() == 2)
Expand All @@ -373,10 +394,17 @@ int bladeRF_SoapySDR::readStream(
float *output1 = (float *)buffs[1];
for (size_t i = 0; i < 4 * numElems;)
{
*(output0++) = float(_rxConvBuff[i++])/2048;
*(output0++) = float(_rxConvBuff[i++])/2048;
*(output1++) = float(_rxConvBuff[i++])/2048;
*(output1++) = float(_rxConvBuff[i++])/2048;
if (_sample_format == BLADERF_FORMAT_SC8_Q7 || _sample_format == BLADERF_FORMAT_SC8_Q7_META) {
*(output0++) = float(((int8_t*)(_rxConvBuff))[i++])/128;
*(output0++) = float(((int8_t*)(_rxConvBuff))[i++])/128;
*(output1++) = float(((int8_t*)(_rxConvBuff))[i++])/128;
*(output1++) = float(((int8_t*)(_rxConvBuff))[i++])/128;
} else {
*(output0++) = float(_rxConvBuff[i++])/2048;
*(output0++) = float(_rxConvBuff[i++])/2048;
*(output1++) = float(_rxConvBuff[i++])/2048;
*(output1++) = float(_rxConvBuff[i++])/2048;
}
}
}

Expand Down
116 changes: 116 additions & 0 deletions example.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include <SoapySDR/Device.h>
#include <SoapySDR/Formats.h>
#include <stdio.h> //printf
#include <stdlib.h> //free
#include <complex.h>

int main(void)
{
size_t length;

//enumerate devices
SoapySDRKwargs *results = SoapySDRDevice_enumerate(NULL, &length);
for (size_t i = 0; i < length; i++)
{
printf("Found device #%d: ", (int)i);
for (size_t j = 0; j < results[i].size; j++)
{
printf("%s=%s, ", results[i].keys[j], results[i].vals[j]);
}
printf("\n");
}
SoapySDRKwargsList_clear(results, length);

//create device instance
//args can be user defined or from the enumeration result
SoapySDRKwargs args = {};
SoapySDRKwargs_set(&args, "driver", "bladerf");
SoapySDRDevice *sdr = SoapySDRDevice_make(&args);
SoapySDRKwargs_clear(&args);

if (sdr == NULL)
{
printf("SoapySDRDevice_make fail: %s\n", SoapySDRDevice_lastError());
return EXIT_FAILURE;
}

//query device info
char** names = SoapySDRDevice_listAntennas(sdr, SOAPY_SDR_RX, 0, &length);
printf("Rx antennas: ");
for (size_t i = 0; i < length; i++) printf("%s, ", names[i]);
printf("\n");
SoapySDRStrings_clear(&names, length);

names = SoapySDRDevice_listGains(sdr, SOAPY_SDR_RX, 0, &length);
printf("Rx gains: ");
for (size_t i = 0; i < length; i++) printf("%s, ", names[i]);
printf("\n");
SoapySDRStrings_clear(&names, length);

SoapySDRRange *ranges = SoapySDRDevice_getFrequencyRange(sdr, SOAPY_SDR_RX, 0, &length);
printf("Rx freq ranges: ");
for (size_t i = 0; i < length; i++) printf("[%g Hz -> %g Hz], ", ranges[i].minimum, ranges[i].maximum);
printf("\n");
free(ranges);

//apply settings
if (SoapySDRDevice_writeSetting(sdr, "oversample", "true") != 0)
{
printf("Failed to enable oversample: %s\n", SoapySDRDevice_lastError());
}
if (SoapySDRDevice_setSampleRate(sdr, SOAPY_SDR_RX, 0, 122e6) != 0)
{
printf("setSampleRate fail: %s\n", SoapySDRDevice_lastError());
}
if (SoapySDRDevice_setFrequency(sdr, SOAPY_SDR_RX, 0, 912.3e6, NULL) != 0)
{
printf("setFrequency fail: %s\n", SoapySDRDevice_lastError());
}

// Setup stream arguments
SoapySDRKwargs streamArgs = {};
char *key = "format";
char *val = "sc8";
streamArgs.size = 1;
streamArgs.keys = (char**)malloc(sizeof(char*));
streamArgs.vals = (char**)malloc(sizeof(char*));
streamArgs.keys[0] = key;
streamArgs.vals[0] = val;

SoapySDRStream *rxStream = SoapySDRDevice_setupStream(sdr, SOAPY_SDR_RX, SOAPY_SDR_CF32, NULL, 0, &streamArgs);

// Free the allocated memory
free(streamArgs.keys);
free(streamArgs.vals);

if (rxStream == NULL)
{
printf("setupStream fail: %s\n", SoapySDRDevice_lastError());
SoapySDRDevice_unmake(sdr);
return EXIT_FAILURE;
}
SoapySDRDevice_activateStream(sdr, rxStream, 0, 0, 0); //start streaming

//create a re-usable buffer for rx samples
complex float buff[1024];

//receive some samples
for (size_t i = 0; i < 10; i++)
{
void *buffs[] = {buff}; //array of buffers
int flags; //flags set by receive operation
long long timeNs; //timestamp for receive buffer
int ret = SoapySDRDevice_readStream(sdr, rxStream, buffs, 1024, &flags, &timeNs, 100000);
printf("ret=%d, flags=%d, timeNs=%lld\n", ret, flags, timeNs);
}

//shutdown the stream
SoapySDRDevice_deactivateStream(sdr, rxStream, 0, 0); //stop streaming
SoapySDRDevice_closeStream(sdr, rxStream);

//cleanup device handle
SoapySDRDevice_unmake(sdr);

printf("Done\n");
return EXIT_SUCCESS;
}
Loading