libbladerf-rs 0.1.0

Fully Rust native BladeRF driver
Documentation

A reimplementation of basic libbladeRF functions in Rust, based on nusb usb backend. Currently supporting BladeRF1 on Windows, macOS and Linux only.

Use libbladerf-rs to control your bladeRF1 from your Rust application. This software shall currently not be considered as a replacement for the official libbladeRF due to several features not being available.

Usage overview

After a BladeRF1 is connected via USB (High or SuperSpeed USB port required) and fully booted, an instance to a BladeRF can be opened using [bladerf1::BladeRf1::from_first]. A handle to a specific BladeRF1 can also be obtained by its [bladerf1::BladeRf1::from_bus_addr] or [bladerf1::BladeRf1::from_serial].

After obtaining an instance of a [bladerf1::BladeRf1], you can set basic parameters like Gain, Frequency and Sample Rate or Bandwidth.

Examples

An example exists to demonstrate the current functionality of libbladerf-rs:

use anyhow::Result;
use libbladerf_rs::bladerf1::xb::ExpansionBoard;
use libbladerf_rs::bladerf1::{BladeRf1, GainDb, SampleFormat};
use libbladerf_rs::{BLADERF_MODULE_RX, BLADERF_MODULE_TX, Direction};

fn main() -> Result<()> {
    env_logger::builder()
        .filter_level(log::LevelFilter::Debug)
        .filter_module("nusb", log::LevelFilter::Info)
        .init();

    let bladerf = BladeRf1::from_first()?;

    log::debug!("Speed: {:?}", bladerf.speed());
    log::debug!("Serial: {}", bladerf.serial()?);
    log::debug!("Manufacturer: {}", bladerf.manufacturer()?);
    log::debug!("FX3 Firmware: {}", bladerf.fx3_firmware()?);
    log::debug!("Product: {}", bladerf.product()?);

    let languages = bladerf.get_supported_languages()?;
    log::debug!("Languages: {:x?}", languages);

    bladerf.initialize()?;

    log::debug!("FPGA: {}", bladerf.fpga_version()?);

    let xb = bladerf.expansion_get_attached();
    log::debug!("XB: {xb:?}");

    bladerf.expansion_attach(ExpansionBoard::Xb200)?;

    let xb = bladerf.expansion_get_attached();
    log::debug!("XB: {xb:?}");

    let frequency_range = bladerf.get_frequency_range()?;
    log::debug!("Frequency Range: {frequency_range:?}");

    // Set Frequency to minimum frequency
    bladerf.set_frequency(BLADERF_MODULE_RX, frequency_range.min().unwrap() as u64)?;
    bladerf.set_frequency(BLADERF_MODULE_TX, frequency_range.min().unwrap() as u64)?;

    let frequency_rx = bladerf.get_frequency(BLADERF_MODULE_RX)?;
    let frequency_tx = bladerf.get_frequency(BLADERF_MODULE_TX)?;
    log::debug!("Frequency RX: {}", frequency_rx);
    log::debug!("Frequency TX: {}", frequency_tx);

    let sample_rate_range = BladeRf1::get_sample_rate_range();
    log::debug!("Sample Rate: {sample_rate_range:?}");

    // Set Sample Rate to minimum Sample Rate
    bladerf.set_sample_rate(BLADERF_MODULE_RX, sample_rate_range.min().unwrap() as u32)?;
    bladerf.set_sample_rate(BLADERF_MODULE_TX, sample_rate_range.min().unwrap() as u32)?;

    let sample_rate_rx = bladerf.get_sample_rate(BLADERF_MODULE_RX)?;
    let sample_rate_tx = bladerf.get_sample_rate(BLADERF_MODULE_TX)?;
    log::debug!("Sample Rate RX: {}", sample_rate_rx);
    log::debug!("Sample Rate TX: {}", sample_rate_tx);

    let bandwidth_range = BladeRf1::get_bandwidth_range();
    log::debug!("Bandwidth: {bandwidth_range:?}");

    // Set Sample Rate to minimum Sample Rate
    bladerf.set_bandwidth(BLADERF_MODULE_RX, bandwidth_range.min().unwrap() as u32)?;
    bladerf.set_bandwidth(BLADERF_MODULE_TX, bandwidth_range.min().unwrap() as u32)?;

    let bandwidth_rx = bladerf.get_bandwidth(BLADERF_MODULE_RX)?;
    let bandwidth_tx = bladerf.get_bandwidth(BLADERF_MODULE_TX)?;
    log::debug!("Bandwidth RX: {}", bandwidth_rx);
    log::debug!("Bandwidth TX: {}", bandwidth_tx);

    let gain_stages_rx = BladeRf1::get_gain_stages(BLADERF_MODULE_RX);
    let gain_stages_tx = BladeRf1::get_gain_stages(BLADERF_MODULE_TX);
    log::debug!("Gain Stages RX: {gain_stages_rx:?}");
    log::debug!("Gain Stages TX: {gain_stages_tx:?}");

    let gain_range_rx = BladeRf1::get_gain_range(BLADERF_MODULE_RX);
    let gain_range_tx = BladeRf1::get_gain_range(BLADERF_MODULE_TX);
    log::debug!("Gain Range RX: {gain_range_rx:?}");
    log::debug!("Gain Range TX: {gain_range_tx:?}");

    // Set Sample Rate to minimum Sample Rate
    bladerf.set_gain(
        BLADERF_MODULE_RX,
        GainDb {
            db: gain_range_rx.min().unwrap() as i8,
        },
    )?;
    bladerf.set_gain(
        BLADERF_MODULE_TX,
        GainDb {
            db: gain_range_tx.min().unwrap() as i8,
        },
    )?;

    let gain_rx = bladerf.get_gain(BLADERF_MODULE_RX)?;
    let gain_tx = bladerf.get_gain(BLADERF_MODULE_TX)?;
    log::debug!("Gain RX: {}", gain_rx.db);
    log::debug!("Gain TX: {}", gain_tx.db);

    bladerf.perform_format_config(Direction::Rx, SampleFormat::Sc16Q11)?;

    bladerf.enable_module(BLADERF_MODULE_RX, true)?;

    bladerf.experimental_control_urb()?;

    // bladerf.run_stream()?;

    bladerf.perform_format_deconfig(Direction::Rx)?;

    bladerf.enable_module(BLADERF_MODULE_RX, false)?;

    Ok(())
}

Build this example by executing the following command in your shell:

cargo run --package info

Limitations

libbladerf-rs currently only supports the BladeRF1. Support for BladeRF2 is currently not possible, as I am not in the possession of named SDR.

Implemented Features

  • Getting/Setting gain levels of individual stages like rxvga1, rxvga2, lna, txvga1 and txvga2.
  • Getting/Setting RX/TX frequency
  • Getting/Setting Bandwidth
  • Getting/Setting Sample Rate
  • Support for BladeRF1 Expansion boards (XB100, XB200, XB300)
  • Interface for sending and receiving I/Q samples

Missing Features

  • Support for BladeRF2
  • Support for Firmware and FPGA flashing/validation
  • Support for different I/Q sample formats and timestamps
  • DC calibration table support
  • Usage from both async and blocking contexts (currently sync only)
  • Extensive documentation
  • AGC enablement

Developers

Contributions of any kind are welcome!

If possible, method names should adhere to the documented methods in libbladeRF-doc

For debugging purposes, it is useful to compare the communication between the SDR and the original libbladeRF with the communication of libbladerf-rs. Hand tooling for this purpose is Wireshark. Allow wireshark to monitor USB traffic:

sudo usermod -a -G wireshark <your_user>
sudo modprobe usbmon
sudo setfacl -m u:<your_user>:r /dev/usbmon*

Filter out unwanted traffic by using a Wireshark filter like e.g.

usb.bus_id == 1 and usb.device_address == 2

Datasheets for the BladeRF1 hardware are available at the following resources:

SI5338

SI5338 Datasheet

SI5338 Reference Manual

LMS6002D

LMS6002D Datasheet

LMS6002D Programming and Calibration Guide

DAC161S055

DAC Datasheet

libbladerf-rs is a pure rust implementation for interacting with a Nuand BladeRF1.

To view the documentation, build it with:

cargo doc --open

Examples on how to use libbladerf-rs can be found in the examples/ directory

Testing

Run tests using the following command

cargo test -- --test-threads=1

Run tests and display output

cargo test -- --nocapture --test-threads=1

Run a specific test and display output

cargo test --test bladerf1_tuning -- --nocapture --test-threads=1