├── .gitignore ├── Cargo.toml ├── .idea ├── .gitignore ├── vcs.xml ├── misc.xml └── modules.xml ├── .vscode └── settings.json ├── uhd ├── src │ ├── transmitter │ │ ├── mod.rs │ │ ├── info.rs │ │ ├── metadata.rs │ │ └── streamer.rs │ ├── receiver │ │ ├── mod.rs │ │ ├── error.rs │ │ ├── info.rs │ │ ├── streamer.rs │ │ └── metadata.rs │ ├── lib.rs │ ├── motherboard_eeprom.rs │ ├── tune_result.rs │ ├── tune_request.rs │ ├── daughter_board_eeprom.rs │ ├── error.rs │ ├── utils.rs │ ├── range.rs │ ├── stream │ │ └── mod.rs │ ├── string_vector.rs │ └── usrp.rs ├── uhd.iml ├── Cargo.toml ├── CHANGELOG.md └── examples │ ├── transmit.rs │ ├── receive.rs │ └── probe.rs ├── uhd-sys ├── CHANGELOG.md ├── src │ └── lib.rs ├── uhd-sys.iml ├── README.md ├── Cargo.toml └── build.rs └── uhd-rust.iml /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = ['uhd-sys', 'uhd'] 4 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.inlayHints.enable": false 3 | } -------------------------------------------------------------------------------- /uhd/src/transmitter/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod info; 2 | pub mod metadata; 3 | pub mod streamer; 4 | -------------------------------------------------------------------------------- /uhd/src/receiver/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod info; 3 | pub mod metadata; 4 | pub mod streamer; 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /uhd-sys/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.1.3 - 2024-08-13 2 | 3 | * Updated build script to make uhd-sys compile on Apple Silicon macOS devices 4 | 5 | # 0.1.2 - 2021-03-30 6 | 7 | * Updated build script to make uhd-sys compile on Raspberry Pi devices 8 | -------------------------------------------------------------------------------- /uhd-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | 3 | //! 4 | //! Bindings to UHD 5 | //! 6 | //! UHD repository: [https://github.com/ettusresearch/uhd](https://github.com/ettusresearch/uhd) 7 | //! 8 | //! UHD documentation: [https://files.ettus.com/manual/](https://files.ettus.com/manual/) 9 | //! 10 | 11 | include!(concat!(env!("OUT_DIR"), "/bindgen.rs")); 12 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /uhd-rust.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /uhd-sys/uhd-sys.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /uhd/uhd.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /uhd/src/receiver/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub struct ReceiveError { 3 | pub kind: ReceiveErrorKind, 4 | pub message: Option, 5 | } 6 | 7 | impl ReceiveError { 8 | pub fn kind(&self) -> ReceiveErrorKind { 9 | self.kind.clone() 10 | } 11 | pub fn message(&self) -> Option<&str> { 12 | self.message.as_deref() 13 | } 14 | } 15 | 16 | impl std::error::Error for ReceiveError {} 17 | 18 | #[non_exhaustive] 19 | #[derive(Debug, Clone)] 20 | pub enum ReceiveErrorKind { 21 | Timeout, 22 | LateCommand, 23 | BrokenChain, 24 | Overflow, 25 | OutOfSequence, 26 | Alignment, 27 | BadPacket, 28 | Other, 29 | } 30 | -------------------------------------------------------------------------------- /uhd-sys/README.md: -------------------------------------------------------------------------------- 1 | # Rust UHD bindings 2 | 3 | This crate provides low-level bindings to the UHD (USRP Hardware Driver) library, which provides support for Ettus Research / National Instruments Universal Software Radio Peripheral devices 4 | 5 | ## Requirements 6 | 7 | The UHD library must be installed, and pkg-config must be able to find it. 8 | 9 | ## License 10 | 11 | This crate is released under the MIT and Apache 2.0 licenses. You may redistribute it according to either license. 12 | 13 | The UHD library itself is mostly licensed under GPLv3 (see [https://github.com/EttusResearch/uhd/blob/master/LICENSE.md](https://github.com/EttusResearch/uhd/blob/master/LICENSE.md)). Depending on how you link the UHD library, this may have licensing implications. 14 | -------------------------------------------------------------------------------- /uhd/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uhd" 3 | version = "0.3.0" 4 | authors = ["Sam Crow "] 5 | edition = "2018" 6 | description = "Bindings to the UHD (USRP Hardware Driver) library, which provides support for Ettus Research / National Instruments Universal Software Radio Peripheral devices" 7 | repository = "https://github.com/samcrow/uhd-rust" 8 | license = "MIT OR Apache-2.0" 9 | keywords = ["sdr", "usrp", "radio"] 10 | categories = ["hardware-support"] 11 | 12 | 13 | [dependencies] 14 | num-complex = "0.4.0" 15 | libc = "0.2" 16 | thiserror = "1.0.24" 17 | anyhow = "1.0.39" 18 | 19 | [dependencies.uhd-sys] 20 | version = "0.1.3" 21 | path = "../uhd-sys" 22 | 23 | [dev-dependencies] 24 | tap = "1.0.1" 25 | log = "0.4.13" 26 | env_logger = "0.11.0" 27 | -------------------------------------------------------------------------------- /uhd-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uhd-sys" 3 | version = "0.1.3" 4 | authors = ["Sam Crow "] 5 | repository = "https://github.com/samcrow/uhd-rust" 6 | edition = "2018" 7 | license = "MIT OR Apache-2.0" 8 | description = "Low-level bindings to the UHD (USRP Hardware Driver) library, which provides support for Ettus Research / National Instruments Universal Software Radio Peripheral devices" 9 | keywords = ["sdr"] 10 | categories = ["external-ffi-bindings", "hardware-support"] 11 | links = "uhd" 12 | build = "build.rs" 13 | 14 | [lib] 15 | # output of UHD C++ documentation from bindgen sometimes has 4 spaces, which rustdoc 16 | # interprets as markdown-style code, which then triggers failures during doctest 17 | doctest = false 18 | 19 | [dependencies] 20 | 21 | [build-dependencies] 22 | metadeps = "1.1.2" 23 | bindgen = "0.55.1" 24 | 25 | [package.metadata.pkg-config] 26 | uhd = "*" 27 | -------------------------------------------------------------------------------- /uhd/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Unreleased 2 | 3 | ## Added 4 | 5 | * Time-related methods [#14](https://github.com/samcrow/uhd-rust/pull/14) 6 | 7 | # [0.3.0](https://github.com/samcrow/uhd-rust/tree/uhd-v0.3.0) - 2024-05-17 8 | 9 | ## Changed 10 | 11 | * Remove `enumerate_registers` from `Usrp` since the low level register apis have been removed from libuhd 4.0 . 12 | * Update `env-logger` dev-dependency to 0.11 13 | 14 | ## Added 15 | 16 | * Add `Usrp::set_clock_source` [#10](https://github.com/samcrow/uhd-rust/pull/10) 17 | 18 | # [0.2.0](https://github.com/samcrow/uhd-rust/tree/v0.2.0) - 2023-02-09 19 | 20 | * Added support for transmitting with TransmitStreamer 21 | * Remove kind() and message() methods on Error. Error is now implemented with the ThisError 22 | crate internally. Error message strings from UHD can instead be retrieved using 23 | `uhd::last_error_message()`. 24 | * Bump num-complex crate to v0.4 25 | * The following methods on `Ursp` now require a mutable reference: 26 | `clear_command_time` 27 | `get_rx_stream` 28 | `set_rx_agc_enabled` 29 | `set_rx_antenna` 30 | `set_rx_bandwidth` 31 | `set_rx_dc_offset_enabled` 32 | `set_rx_frequency` 33 | `set_rx_gain` 34 | `set_rx_sample_rate` 35 | `set_tx_antenna` 36 | * Use `sc16` over-the-wire format in transmit and receive examples 37 | 38 | # 0.1.1 - 2021-03-30 39 | 40 | * Fixes to compile with the version of UHD in the Raspberry Pi repositories (no public API changes, except panics in some situations) 41 | 42 | -------------------------------------------------------------------------------- /uhd/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! # `uhd`: Bindings to the USRP Hardware Driver library 3 | //! 4 | //! ## Status 5 | //! 6 | //! Basic functionality for configuring some USRP settings and receiving samples is working. 7 | //! 8 | //! Some things are not yet implemented: 9 | //! 10 | //! * Various configuration options related to transmitting 11 | //! * Some configuration options related to receiving and time synchronization 12 | //! * Sending samples to transmit 13 | //! 14 | 15 | extern crate libc; 16 | extern crate num_complex; 17 | extern crate uhd_sys; 18 | 19 | mod daughter_board_eeprom; 20 | mod error; 21 | mod motherboard_eeprom; 22 | pub mod range; 23 | mod receiver; 24 | mod stream; 25 | mod string_vector; 26 | mod transmitter; 27 | mod tune_request; 28 | mod tune_result; 29 | mod usrp; 30 | mod utils; 31 | 32 | // Re-export many public items at the root 33 | pub use daughter_board_eeprom::DaughterBoardEeprom; 34 | pub use error::*; 35 | pub use motherboard_eeprom::MotherboardEeprom; 36 | pub use receiver::{info::ReceiveInfo, metadata::*, streamer::ReceiveStreamer}; 37 | pub use stream::*; 38 | pub use transmitter::{info::TransmitInfo, metadata::*, streamer::TransmitStreamer}; 39 | pub use tune_request::*; 40 | pub use tune_result::TuneResult; 41 | pub use usrp::Usrp; 42 | pub use utils::alloc_boxed_slice; 43 | // Common definitions 44 | 45 | /// A time value, represented as an integer number of seconds and a floating-point fraction of 46 | /// a second 47 | #[derive(Debug, Clone, Default, PartialOrd, PartialEq)] 48 | pub struct TimeSpec { 49 | // In some versions of UHD, the corresponding field of uhd::time_spec_t is a time_t. 50 | // In other versions, it's a int64_t. The Rust code does conversion to keep this 51 | // an i64. 52 | pub seconds: i64, 53 | pub fraction: f64, 54 | } 55 | -------------------------------------------------------------------------------- /uhd/examples/transmit.rs: -------------------------------------------------------------------------------- 1 | use core::f32::consts; 2 | use std::env::set_var; 3 | 4 | use anyhow::{Context, Result}; 5 | use num_complex::{Complex, Complex32}; 6 | use tap::Pipe; 7 | use uhd::{self, TuneRequest, Usrp}; 8 | 9 | const CHANNEL: usize = 0; 10 | const NUM_SAMPLES: usize = 1_000_000; 11 | 12 | pub fn main() -> Result<()> { 13 | set_var("RUST_LOG", "DEBUG"); 14 | env_logger::init(); 15 | 16 | log::info!("Starting transmit test"); 17 | 18 | let mut usrp = Usrp::find("") 19 | .context("Failed to open device list")? 20 | .drain(..) 21 | .next() 22 | .context("Failed to find a valid USRP to attach to")? 23 | .pipe(|addr| Usrp::open(&addr)) 24 | .context("Failed to find properly open the USRP")?; 25 | 26 | // Set properties 27 | usrp.set_tx_sample_rate(1e6, CHANNEL)?; 28 | usrp.set_tx_gain(77.5, CHANNEL, "PGA")?; // -10dB gain 29 | usrp.set_tx_frequency(&TuneRequest::with_frequency(2.404e9), CHANNEL)?; 30 | 31 | // Check properties 32 | log::info!("Tx gain {}", usrp.get_tx_gain(CHANNEL, "PGA")?); 33 | log::info!("Tx freq {}", usrp.get_tx_frequency(CHANNEL)?); 34 | 35 | // Get TransmitStreamer 36 | let mut transmitter = usrp 37 | .get_tx_stream(&uhd::StreamArgs::>::new("sc16")) 38 | .unwrap(); 39 | 40 | // Generate a sine wave at Fs/4 41 | let mut single_chan = uhd::alloc_boxed_slice::, NUM_SAMPLES>(); 42 | for i in 0..NUM_SAMPLES { 43 | let t = i as f32 / 4.; 44 | // z = e^j*2π*theta 45 | let z = (Complex32::i() * 2. * consts::PI * t).expf(consts::E); 46 | single_chan[i] = Complex::new((8192. * z.re) as i16, (8192. * z.im) as i16); 47 | } 48 | 49 | // Transmit 50 | log::info!("Transmitting.."); 51 | let stat = transmitter.transmit_simple(single_chan.as_mut())?; 52 | log::info!("{:?}", stat); 53 | 54 | Ok(()) 55 | } 56 | -------------------------------------------------------------------------------- /uhd-sys/build.rs: -------------------------------------------------------------------------------- 1 | extern crate bindgen; 2 | extern crate metadeps; 3 | 4 | use std::env; 5 | use std::path::{Path, PathBuf}; 6 | 7 | fn main() { 8 | // This reads the metadata in Cargo.toml and sends Cargo the appropriate output to link the 9 | // libraries 10 | let libraries = metadeps::probe().unwrap(); 11 | 12 | let uhd_include_path = libraries 13 | .get("uhd") 14 | .expect("uhd library not in map") 15 | .include_paths 16 | .first() 17 | .expect("no include path for UHD headers"); 18 | generate_bindings(uhd_include_path); 19 | } 20 | 21 | fn generate_bindings(include_path: &Path) { 22 | let usrp_header = include_path.join("uhd.h"); 23 | 24 | let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); 25 | let out_path = out_dir.join("bindgen.rs"); 26 | 27 | let mut builder = bindgen::builder() 28 | .whitelist_function("^uhd.+") 29 | .default_enum_style(bindgen::EnumVariation::ModuleConsts) 30 | .header(usrp_header.to_string_lossy().clone()) 31 | // Add the include directory to ensure that #includes in the header work correctly 32 | .clang_arg(format!("-I{}", include_path.to_string_lossy().clone())); 33 | 34 | // On Raspberry Pi devices, the include directories require some adjustment. 35 | let target = env::var("TARGET").expect("No TARGET environment variable"); 36 | if target == "armv7-unknown-linux-gnueabihf" { 37 | builder = builder.clang_arg("-I/usr/lib/gcc/arm-linux-gnueabihf/8/include"); 38 | } else if target == "aarch64-apple-darwin" { 39 | // On macOS Apple Silicon, boost libs from `brew install boost` are at this path 40 | println!("cargo:rustc-link-search=/opt/homebrew/lib/"); 41 | } 42 | 43 | let bindings = builder.generate().expect("Failed to generate bindings"); 44 | bindings 45 | .write_to_file(out_path) 46 | .expect("Failed to write bindings to file"); 47 | } 48 | -------------------------------------------------------------------------------- /uhd/examples/receive.rs: -------------------------------------------------------------------------------- 1 | use std::env::set_var; 2 | 3 | use anyhow::{Context, Result}; 4 | use num_complex::Complex; 5 | use tap::Pipe; 6 | use uhd::{self, StreamCommand, StreamCommandType, StreamTime, TuneRequest, Usrp}; 7 | 8 | const CHANNEL: usize = 0; 9 | const NUM_SAMPLES: usize = 1000; 10 | 11 | pub fn main() -> Result<()> { 12 | set_var("RUST_LOG", "DEBUG"); 13 | env_logger::init(); 14 | 15 | log::info!("Starting receive test"); 16 | 17 | let mut usrp = Usrp::find("") 18 | .context("Failed to open device list")? 19 | .drain(..) 20 | .next() 21 | .context("Failed to find a valid USRP to attach to")? 22 | .pipe(|addr| Usrp::open(&addr)) 23 | .context("Failed to find properly open the USRP")?; 24 | 25 | let _ = usrp.set_clock_source("external", 0); 26 | let clock_source = usrp.get_clock_source(0).unwrap(); 27 | println!("Clock source: {:?}", clock_source); 28 | assert_eq!(clock_source, "external"); 29 | let _ = usrp.set_clock_source("internal", 0); 30 | let clock_source = usrp.get_clock_source(0).unwrap(); 31 | println!("Clock source: {:?}", clock_source); 32 | assert_eq!(clock_source, "internal"); 33 | 34 | usrp.set_rx_sample_rate(1e6, CHANNEL)?; 35 | usrp.set_rx_antenna("TX/RX", CHANNEL)?; 36 | usrp.set_rx_frequency(&TuneRequest::with_frequency(2.4e9), CHANNEL)?; 37 | 38 | let mut receiver = usrp 39 | .get_rx_stream(&uhd::StreamArgs::>::new("sc16")) 40 | .unwrap(); 41 | 42 | let mut buffer = uhd::alloc_boxed_slice::, NUM_SAMPLES>(); 43 | 44 | receiver.send_command(&StreamCommand { 45 | command_type: StreamCommandType::CountAndDone(buffer.len() as u64), 46 | time: StreamTime::Now, 47 | })?; 48 | let status = receiver.receive_simple(buffer.as_mut())?; 49 | 50 | log::info!("{:?}", status); 51 | log::info!("{:?}", &buffer[..16]); 52 | 53 | Ok(()) 54 | } 55 | -------------------------------------------------------------------------------- /uhd/src/receiver/info.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::str::Utf8Error; 3 | 4 | /// Information about a receive channel 5 | #[derive(Debug, Clone)] 6 | pub struct ReceiveInfo { 7 | motherboard_id: String, 8 | motherboard_name: String, 9 | motherboard_serial: String, 10 | daughterboard_id: String, 11 | daughterboard_serial: String, 12 | subdev_name: String, 13 | subdev_spec: String, 14 | antenna: String, 15 | } 16 | 17 | impl ReceiveInfo { 18 | pub fn motherboard_id(&self) -> &str { 19 | &self.motherboard_id 20 | } 21 | pub fn motherboard_name(&self) -> &str { 22 | &self.motherboard_name 23 | } 24 | pub fn motherboard_serial(&self) -> &str { 25 | &self.motherboard_serial 26 | } 27 | pub fn daughterboard_id(&self) -> &str { 28 | &self.daughterboard_id 29 | } 30 | pub fn daughterboard_serial(&self) -> &str { 31 | &self.daughterboard_serial 32 | } 33 | pub fn subdev_name(&self) -> &str { 34 | &self.subdev_name 35 | } 36 | pub fn subdev_spec(&self) -> &str { 37 | &self.subdev_spec 38 | } 39 | pub fn antenna(&self) -> &str { 40 | &self.antenna 41 | } 42 | 43 | pub(crate) unsafe fn from_c(info_c: &uhd_sys::uhd_usrp_rx_info_t) -> Result { 44 | Ok(ReceiveInfo { 45 | motherboard_id: CStr::from_ptr(info_c.mboard_id).to_str()?.into(), 46 | motherboard_name: CStr::from_ptr(info_c.mboard_name).to_str()?.into(), 47 | motherboard_serial: CStr::from_ptr(info_c.mboard_serial).to_str()?.into(), 48 | daughterboard_id: CStr::from_ptr(info_c.rx_id).to_str()?.into(), 49 | daughterboard_serial: CStr::from_ptr(info_c.rx_serial).to_str()?.into(), 50 | subdev_name: CStr::from_ptr(info_c.rx_subdev_name).to_str()?.into(), 51 | subdev_spec: CStr::from_ptr(info_c.rx_subdev_spec).to_str()?.into(), 52 | antenna: CStr::from_ptr(info_c.rx_antenna).to_str()?.into(), 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /uhd/src/transmitter/info.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::str::Utf8Error; 3 | 4 | /// Information about a transmit channel 5 | #[derive(Debug, Clone)] 6 | pub struct TransmitInfo { 7 | motherboard_id: String, 8 | motherboard_name: String, 9 | motherboard_serial: String, 10 | daughterboard_id: String, 11 | daughterboard_serial: String, 12 | subdev_name: String, 13 | subdev_spec: String, 14 | antenna: String, 15 | } 16 | 17 | impl TransmitInfo { 18 | pub fn motherboard_id(&self) -> &str { 19 | &self.motherboard_id 20 | } 21 | pub fn motherboard_name(&self) -> &str { 22 | &self.motherboard_name 23 | } 24 | pub fn motherboard_serial(&self) -> &str { 25 | &self.motherboard_serial 26 | } 27 | pub fn daughterboard_id(&self) -> &str { 28 | &self.daughterboard_id 29 | } 30 | pub fn daughterboard_serial(&self) -> &str { 31 | &self.daughterboard_serial 32 | } 33 | pub fn subdev_name(&self) -> &str { 34 | &self.subdev_name 35 | } 36 | pub fn subdev_spec(&self) -> &str { 37 | &self.subdev_spec 38 | } 39 | pub fn antenna(&self) -> &str { 40 | &self.antenna 41 | } 42 | 43 | pub(crate) unsafe fn from_c(info_c: &uhd_sys::uhd_usrp_tx_info_t) -> Result { 44 | Ok(TransmitInfo { 45 | motherboard_id: CStr::from_ptr(info_c.mboard_id).to_str()?.into(), 46 | motherboard_name: CStr::from_ptr(info_c.mboard_name).to_str()?.into(), 47 | motherboard_serial: CStr::from_ptr(info_c.mboard_serial).to_str()?.into(), 48 | daughterboard_id: CStr::from_ptr(info_c.tx_id).to_str()?.into(), 49 | daughterboard_serial: CStr::from_ptr(info_c.tx_serial).to_str()?.into(), 50 | subdev_name: CStr::from_ptr(info_c.tx_subdev_name).to_str()?.into(), 51 | subdev_spec: CStr::from_ptr(info_c.tx_subdev_spec).to_str()?.into(), 52 | antenna: CStr::from_ptr(info_c.tx_antenna).to_str()?.into(), 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /uhd/src/motherboard_eeprom.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{check_status, Error}; 2 | use crate::utils::copy_string; 3 | use std::ffi::CString; 4 | use std::ptr; 5 | 6 | /// Information stored in the USRP motherboard EEPROM 7 | pub struct MotherboardEeprom(uhd_sys::uhd_mboard_eeprom_handle); 8 | 9 | impl MotherboardEeprom { 10 | pub fn get(&self, key: &str) -> Result, Error> { 11 | let key = CString::new(key)?; 12 | let status = copy_string(|buffer, length| unsafe { 13 | uhd_sys::uhd_mboard_eeprom_get_value(self.0, key.as_ptr(), buffer, length as _) 14 | }); 15 | // An error with kind Key indicates that the value was not found 16 | match status { 17 | Ok(value) => Ok(Some(value)), 18 | Err(e) => match e { 19 | Error::Key => Ok(None), 20 | _ => Err(e), 21 | }, 22 | } 23 | } 24 | 25 | pub fn put(&mut self, key: String, value: String) -> Result<(), Error> { 26 | let key = CString::new(key)?; 27 | let value = CString::new(value)?; 28 | check_status(unsafe { 29 | uhd_sys::uhd_mboard_eeprom_set_value(self.0, key.as_ptr(), value.as_ptr()) 30 | }) 31 | } 32 | 33 | pub(crate) fn handle(&mut self) -> uhd_sys::uhd_mboard_eeprom_handle { 34 | self.0 35 | } 36 | } 37 | 38 | impl Default for MotherboardEeprom { 39 | fn default() -> Self { 40 | let mut handle = ptr::null_mut(); 41 | check_status(unsafe { uhd_sys::uhd_mboard_eeprom_make(&mut handle) }).unwrap(); 42 | MotherboardEeprom(handle) 43 | } 44 | } 45 | 46 | impl Drop for MotherboardEeprom { 47 | fn drop(&mut self) { 48 | let _ = unsafe { uhd_sys::uhd_mboard_eeprom_free(&mut self.0) }; 49 | } 50 | } 51 | 52 | #[cfg(test)] 53 | mod test { 54 | use super::MotherboardEeprom; 55 | 56 | #[test] 57 | fn empty_eeprom() { 58 | let eeprom = MotherboardEeprom::default(); 59 | let res = eeprom.get("jabberwock".into()); 60 | assert!(res.is_ok() && res.unwrap().is_none()) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /uhd/src/tune_result.rs: -------------------------------------------------------------------------------- 1 | /// The result of a tuning operation 2 | #[derive(Clone)] 3 | pub struct TuneResult(uhd_sys::uhd_tune_result_t); 4 | 5 | impl TuneResult { 6 | /// Returns the target RF frequency 7 | pub fn target_rf_freq(&self) -> f64 { 8 | self.0.target_rf_freq 9 | } 10 | 11 | /// Returns the target RF frequency constrained to the device's supported frequency range 12 | pub fn clipped_rf_freq(&self) -> f64 { 13 | self.0.clipped_rf_freq 14 | } 15 | 16 | /// Returns the actual RF frequency 17 | pub fn actual_rf_freq(&self) -> f64 { 18 | self.0.actual_rf_freq 19 | } 20 | 21 | /// Returns the target DSP frequency adjustment 22 | pub fn target_dsp_freq(&self) -> f64 { 23 | self.0.target_dsp_freq 24 | } 25 | 26 | /// Returns the actual DSP frequency adjustment 27 | pub fn actual_dsp_freq(&self) -> f64 { 28 | self.0.actual_dsp_freq 29 | } 30 | 31 | pub(crate) fn inner_mut(&mut self) -> &mut uhd_sys::uhd_tune_result_t { 32 | &mut self.0 33 | } 34 | } 35 | 36 | impl Default for TuneResult { 37 | fn default() -> Self { 38 | TuneResult(uhd_sys::uhd_tune_result_t { 39 | clipped_rf_freq: 0.0, 40 | target_rf_freq: 0.0, 41 | actual_rf_freq: 0.0, 42 | target_dsp_freq: 0.0, 43 | actual_dsp_freq: 0.0, 44 | }) 45 | } 46 | } 47 | 48 | unsafe impl Send for TuneResult {} 49 | unsafe impl Sync for TuneResult {} 50 | 51 | mod fmt { 52 | use super::TuneResult; 53 | use std::fmt::{Debug, Formatter, Result}; 54 | 55 | impl Debug for TuneResult { 56 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 57 | f.debug_struct("TuneResult") 58 | .field("target_rf_freq", &self.0.target_rf_freq) 59 | .field("clipped_rf_freq", &self.0.clipped_rf_freq) 60 | .field("actual_rf_freq", &self.0.actual_rf_freq) 61 | .field("target_dsp_freq", &self.0.target_dsp_freq) 62 | .field("actual_dsp_freq", &self.0.actual_dsp_freq) 63 | .finish() 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /uhd/src/tune_request.rs: -------------------------------------------------------------------------------- 1 | /// A request to tune a frontend 2 | #[derive(Debug, Clone)] 3 | pub struct TuneRequest { 4 | pub(crate) target_frequency: f64, 5 | pub(crate) rf: TuneRequestPolicy, 6 | pub(crate) dsp: TuneRequestPolicy, 7 | /// Extra arguments 8 | pub(crate) args: String, 9 | } 10 | 11 | impl TuneRequest { 12 | /// Creates a tune request that automatically configures the hardware to tune to the desired 13 | /// frequency 14 | pub fn with_frequency(frequency: f64) -> Self { 15 | TuneRequest { 16 | target_frequency: frequency, 17 | rf: TuneRequestPolicy::Auto, 18 | dsp: TuneRequestPolicy::Auto, 19 | args: String::new(), 20 | } 21 | } 22 | /// Creates a tune request that automatically configures the hardware to tune to the desired 23 | /// frequency, with an offset between the RF center frequency and the 24 | pub fn with_frequency_lo(frequency: f64, local_offset: f64) -> Self { 25 | TuneRequest { 26 | target_frequency: frequency, 27 | rf: TuneRequestPolicy::Manual(frequency + local_offset), 28 | dsp: TuneRequestPolicy::Auto, 29 | args: String::new(), 30 | } 31 | } 32 | 33 | /// Sets the policy for tuning the RF frontend 34 | pub fn set_rf_policy(&mut self, policy: TuneRequestPolicy) { 35 | self.rf = policy 36 | } 37 | /// Sets the policy for tuning the DSP 38 | pub fn set_dsp_policy(&mut self, policy: TuneRequestPolicy) { 39 | self.dsp = policy 40 | } 41 | /// Sets additional device-specific arguments 42 | pub fn set_args(&mut self, args: String) { 43 | self.args = args 44 | } 45 | } 46 | 47 | /// Policies for how tuning should be accomplished 48 | #[derive(Debug, Clone)] 49 | pub enum TuneRequestPolicy { 50 | /// Keep the current value 51 | None, 52 | /// Automatically determine a new value 53 | Auto, 54 | /// Manually set a specific value 55 | /// 56 | /// The enclosed value is the desired frequency in hertz. 57 | Manual(f64), 58 | } 59 | 60 | impl TuneRequestPolicy { 61 | pub(crate) fn c_policy(&self) -> uhd_sys::uhd_tune_request_policy_t::Type { 62 | use uhd_sys::uhd_tune_request_policy_t::*; 63 | match self { 64 | TuneRequestPolicy::None => UHD_TUNE_REQUEST_POLICY_NONE, 65 | TuneRequestPolicy::Auto => UHD_TUNE_REQUEST_POLICY_AUTO, 66 | TuneRequestPolicy::Manual(_) => UHD_TUNE_REQUEST_POLICY_MANUAL, 67 | } 68 | } 69 | pub(crate) fn frequency(&self) -> f64 { 70 | match self { 71 | TuneRequestPolicy::None | TuneRequestPolicy::Auto => 0.0, 72 | TuneRequestPolicy::Manual(frequency) => *frequency, 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /uhd/src/daughter_board_eeprom.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::copy_string; 2 | use crate::{check_status, Error}; 3 | use std::ffi::CString; 4 | use std::os::raw::c_int; 5 | use std::ptr; 6 | 7 | /// Information from the EEPROM of a daughter board 8 | pub struct DaughterBoardEeprom(uhd_sys::uhd_dboard_eeprom_handle); 9 | 10 | impl DaughterBoardEeprom { 11 | pub fn id(&self) -> Result { 12 | copy_string(|buffer, length| unsafe { 13 | uhd_sys::uhd_dboard_eeprom_get_id(self.0, buffer, length as _) 14 | }) 15 | } 16 | 17 | pub fn set_id(&mut self, id: &str) -> Result<(), Error> { 18 | let id_c = CString::new(id)?; 19 | check_status(unsafe { uhd_sys::uhd_dboard_eeprom_set_id(self.0, id_c.as_ptr()) }) 20 | } 21 | 22 | pub fn serial(&self) -> Result { 23 | copy_string(|buffer, length| unsafe { 24 | uhd_sys::uhd_dboard_eeprom_get_serial(self.0, buffer, length as _) 25 | }) 26 | } 27 | 28 | pub fn set_serial(&mut self, serial: &str) -> Result<(), Error> { 29 | let serial_c = CString::new(serial)?; 30 | check_status(unsafe { uhd_sys::uhd_dboard_eeprom_set_serial(self.0, serial_c.as_ptr()) }) 31 | } 32 | 33 | pub fn revision(&self) -> Result { 34 | let mut revision = 0; 35 | check_status(unsafe { uhd_sys::uhd_dboard_eeprom_get_revision(self.0, &mut revision) })?; 36 | Ok(revision) 37 | } 38 | 39 | pub fn set_revision(&mut self, revision: c_int) -> Result<(), Error> { 40 | check_status(unsafe { uhd_sys::uhd_dboard_eeprom_set_revision(self.0, revision) }) 41 | } 42 | 43 | pub(crate) fn handle(&mut self) -> uhd_sys::uhd_dboard_eeprom_handle { 44 | self.0 45 | } 46 | } 47 | 48 | unsafe impl Send for DaughterBoardEeprom {} 49 | unsafe impl Sync for DaughterBoardEeprom {} 50 | 51 | impl Default for DaughterBoardEeprom { 52 | fn default() -> Self { 53 | let mut handle: uhd_sys::uhd_dboard_eeprom_handle = ptr::null_mut(); 54 | check_status(unsafe { uhd_sys::uhd_dboard_eeprom_make(&mut handle) }).unwrap(); 55 | DaughterBoardEeprom(handle) 56 | } 57 | } 58 | 59 | impl Drop for DaughterBoardEeprom { 60 | fn drop(&mut self) { 61 | let _ = unsafe { uhd_sys::uhd_dboard_eeprom_free(&mut self.0) }; 62 | } 63 | } 64 | 65 | mod fmt { 66 | use super::DaughterBoardEeprom; 67 | use std::fmt::{Debug, Formatter, Result}; 68 | 69 | impl Debug for DaughterBoardEeprom { 70 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 71 | let revision = self.revision(); 72 | let revision: &dyn Debug = revision 73 | .as_ref() 74 | .map(|rev| rev as &dyn Debug) 75 | .unwrap_or(&""); 76 | 77 | f.debug_struct("DaughterBoardEeprom") 78 | .field("id", &self.id().as_deref().unwrap_or("")) 79 | .field("serial", &self.serial().as_deref().unwrap_or("")) 80 | .field("revision", revision) 81 | .finish() 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /uhd/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::copy_string; 2 | use std::ffi::NulError; 3 | use std::str::Utf8Error; 4 | 5 | use thiserror::Error as ThisError; 6 | 7 | pub type Result = std::result::Result; 8 | 9 | #[derive(ThisError, Debug)] 10 | pub enum Error { 11 | /// Used when errors need to propogate but are too unique to be typed 12 | #[error("{0}")] 13 | Unique(String), 14 | 15 | #[error("I/O Error: {0}")] 16 | IO(#[from] std::io::Error), 17 | 18 | #[error("Invalid device arguments")] 19 | InvalidDevice, 20 | 21 | #[error("uhd::index_error - A sequence index is out of range")] 22 | Index, 23 | 24 | #[error("uhd::key_error - Invalid key")] 25 | Key, 26 | 27 | #[error("uhd::not_implemented_error - Not implemented")] 28 | NotImplemented, 29 | 30 | #[error("uhd::usb_error - USB communication problem")] 31 | Usb, 32 | 33 | #[error("uhd::io_error - Input/output error")] 34 | Io, 35 | 36 | #[error("uhd::os_error - System-related error")] 37 | Os, 38 | 39 | #[error("uhd::assertion_error - Assertion failed")] 40 | Assertion, 41 | 42 | #[error("uhd::lookup_error - Invalid index or key")] 43 | Lookup, 44 | 45 | #[error("uhd::type_error - Value has incorrect type")] 46 | Type, 47 | 48 | #[error("uhd::value_error - Invalid value")] 49 | Value, 50 | 51 | #[error("uhd::runtime_error - Other runtime error")] 52 | Runtime, 53 | 54 | #[error("uhd::environment_error - Environment error")] 55 | Environment, 56 | 57 | #[error("uhd::system_error - System-related error")] 58 | System, 59 | 60 | #[error("uhd::exception - Other UHD exception")] 61 | Except, 62 | 63 | #[error("A boost::exception was thrown")] 64 | BoostExcept, 65 | 66 | #[error("A std::exception was thrown")] 67 | StdExcept, 68 | 69 | /// A string containing a null byte was provided 70 | #[error("Null byte in input string")] 71 | NullByte, 72 | 73 | /// A string was too long to be copied out through C FFI 74 | #[error("String from FFI is too long")] 75 | StringLength, 76 | /// A string from C FFI contained invalid UTF-8 77 | #[error("String from FFI contains invalid UTF-8")] 78 | Utf8, 79 | 80 | #[error("Unknown error")] 81 | Unknown, 82 | 83 | #[error(transparent)] 84 | Other(#[from] anyhow::Error), 85 | } 86 | 87 | /// Returns a string copied using uhd_get_last_error() 88 | pub fn last_error_message() -> Option { 89 | copy_string(|buffer, length| unsafe { uhd_sys::uhd_get_last_error(buffer, length as _) }).ok() 90 | } 91 | 92 | /// Converts a status code into a result 93 | pub(crate) fn check_status(status: uhd_sys::uhd_error::Type) -> Result<()> { 94 | use uhd_sys::uhd_error; 95 | use Error::*; 96 | let iserr = match status { 97 | uhd_error::UHD_ERROR_NONE => None, 98 | uhd_error::UHD_ERROR_INVALID_DEVICE => Some(InvalidDevice), 99 | uhd_error::UHD_ERROR_INDEX => Some(Index), 100 | uhd_error::UHD_ERROR_KEY => Some(Key), 101 | uhd_error::UHD_ERROR_NOT_IMPLEMENTED => Some(NotImplemented), 102 | uhd_error::UHD_ERROR_USB => Some(Usb), 103 | uhd_error::UHD_ERROR_IO => Some(Io), 104 | uhd_error::UHD_ERROR_OS => Some(Os), 105 | uhd_error::UHD_ERROR_ASSERTION => Some(Assertion), 106 | uhd_error::UHD_ERROR_LOOKUP => Some(Lookup), 107 | uhd_error::UHD_ERROR_TYPE => Some(Type), 108 | uhd_error::UHD_ERROR_VALUE => Some(Value), 109 | uhd_error::UHD_ERROR_RUNTIME => Some(Runtime), 110 | uhd_error::UHD_ERROR_ENVIRONMENT => Some(Environment), 111 | uhd_error::UHD_ERROR_SYSTEM => Some(System), 112 | uhd_error::UHD_ERROR_EXCEPT => Some(Except), 113 | uhd_error::UHD_ERROR_BOOSTEXCEPT => Some(BoostExcept), 114 | uhd_error::UHD_ERROR_STDEXCEPT => Some(StdExcept), 115 | uhd_error::UHD_ERROR_UNKNOWN | _ => Some(Unknown), 116 | }; 117 | match iserr { 118 | std::option::Option::Some(e) => Err(e), 119 | std::option::Option::None => Ok(()), 120 | } 121 | } 122 | 123 | impl From for Error { 124 | fn from(_: NulError) -> Self { 125 | Error::NullByte 126 | } 127 | } 128 | 129 | impl From for Error { 130 | fn from(_: Utf8Error) -> Self { 131 | Error::Utf8 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /uhd/examples/probe.rs: -------------------------------------------------------------------------------- 1 | extern crate uhd; 2 | 3 | use std::error::Error; 4 | 5 | use uhd::Usrp; 6 | 7 | fn main() -> Result<(), Box> { 8 | let found_usrps = Usrp::find("")?; 9 | 10 | for address in found_usrps { 11 | println!("Opening {}", address); 12 | match probe_one_usrp(&address) { 13 | Ok(_) => {} 14 | Err(e) => eprintln!("{}", e), 15 | } 16 | } 17 | 18 | Ok(()) 19 | } 20 | 21 | fn probe_one_usrp(address: &str) -> Result<(), Box> { 22 | let usrp = Usrp::open(address)?; 23 | let num_mboards = usrp.get_num_motherboards()?; 24 | for board in 0..num_mboards { 25 | println!( 26 | "Motherboard {}, name: {}", 27 | board, 28 | usrp.get_motherboard_name(board)? 29 | ); 30 | if let Ok(rate) = usrp.get_master_clock_rate(board) { 31 | println!("Master clock rate {}", rate); 32 | } 33 | if let Ok(eeprom) = usrp.get_motherboard_eeprom(board) { 34 | let keys = [ 35 | "hardware", 36 | "revision", 37 | "revision_compat", 38 | "product", 39 | "serial", 40 | "name", 41 | "mac-addr", 42 | "mac-addr0", 43 | "mac-addr1", 44 | "ip-addr", 45 | "ip-addr0", 46 | "ip-addr1", 47 | "ip-addr2", 48 | "ip-addr3", 49 | "subnet", 50 | "subnet0", 51 | "subnet1", 52 | "subnet2", 53 | "subnet3", 54 | "gateway", 55 | ]; 56 | for key in &keys { 57 | if let Ok(Some(value)) = eeprom.get(key) { 58 | println!("Motherboard EEPROM[{}] = {}", key, value); 59 | } 60 | } 61 | } 62 | if let Ok(eeprom) = usrp.get_daughter_board_eeprom("rx", "A", board) { 63 | println!("Daughter RX {:?}", eeprom); 64 | } 65 | if let Ok(eeprom) = usrp.get_daughter_board_eeprom("tx", "A", board) { 66 | println!("Daughter TX {:?}", eeprom); 67 | } 68 | if let Ok(eeprom) = usrp.get_daughter_board_eeprom("gdb", "A", board) { 69 | println!("Daughter GDB {:?}", eeprom); 70 | } 71 | if let Ok(banks) = usrp.get_gpio_banks(board) { 72 | println!("GPIO banks {:?}", banks); 73 | } 74 | } 75 | 76 | for channel in 0..usrp.get_num_tx_channels()? { 77 | println!("===="); 78 | println!("Transmit channel {}:", channel); 79 | if let Ok(antennas) = usrp.get_tx_antennas(channel) { 80 | println!("TX antennas {:?}", antennas); 81 | } 82 | if let Ok(range) = usrp.get_fe_tx_freq_range(channel) { 83 | println!("Front-end TX frequency ranges: {:?}", range); 84 | } 85 | if let Ok(gain) = usrp.get_normalized_tx_gain(channel) { 86 | println!("Normalized TX gain {}", gain); 87 | } 88 | if let Ok(names) = usrp.get_tx_gain_names(channel) { 89 | for name in names { 90 | let range = usrp.get_tx_gain_range(channel, &name)?; 91 | let current = usrp.get_tx_gain(channel, &name)?; 92 | println!( 93 | "Gain element {}: range {:?}, current {}", 94 | name, range, current 95 | ); 96 | } 97 | } 98 | println!("Local oscillators: {:?}", usrp.get_tx_lo_names(channel)?); 99 | println!("===="); 100 | } 101 | 102 | for channel in 0..usrp.get_num_rx_channels()? { 103 | println!("===="); 104 | println!("Receive channel {}:", channel); 105 | if let Ok(antennas) = usrp.get_rx_antennas(channel) { 106 | println!("RX antennas {:?}", antennas); 107 | } 108 | if let Ok(range) = usrp.get_fe_rx_freq_range(channel) { 109 | println!("Front-end RX frequency ranges: {:?}", range); 110 | } 111 | if let Ok(gain) = usrp.get_normalized_rx_gain(channel) { 112 | println!("Normalized RX gain {}", gain); 113 | } 114 | if let Ok(names) = usrp.get_rx_gain_names(channel) { 115 | for name in names { 116 | let range = usrp.get_rx_gain_range(channel, &name)?; 117 | let current = usrp.get_rx_gain(channel, &name)?; 118 | println!( 119 | "Gain element {}: range {:?}, current {}", 120 | name, range, current 121 | ); 122 | } 123 | } 124 | println!("Local oscillators: {:?}", usrp.get_rx_lo_names(channel)?); 125 | println!("===="); 126 | } 127 | 128 | Ok(()) 129 | } 130 | -------------------------------------------------------------------------------- /uhd/src/transmitter/metadata.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use crate::error::check_status; 4 | 5 | use crate::TimeSpec; 6 | 7 | /// Data about a transmit operation 8 | pub struct TransmitMetadata { 9 | /// Handle to C++ object 10 | handle: uhd_sys::uhd_tx_metadata_handle, 11 | /// Number of samples transmitted 12 | samples: usize, 13 | } 14 | 15 | impl TransmitMetadata { 16 | pub fn new() -> Self { 17 | Default::default() 18 | } 19 | 20 | /// Returns the timestamp of (the first?) of the transmitted samples, according to the USRP's 21 | /// internal clock 22 | pub fn time_spec(&self) -> Option { 23 | if self.has_time_spec() { 24 | let mut time = TimeSpec::default(); 25 | let mut seconds_time_t: libc::time_t = Default::default(); 26 | 27 | check_status(unsafe { 28 | uhd_sys::uhd_tx_metadata_time_spec( 29 | self.handle, 30 | &mut seconds_time_t, 31 | &mut time.fraction, 32 | ) 33 | }) 34 | .unwrap(); 35 | // Convert seconds from time_t to i64 36 | time.seconds = seconds_time_t.into(); 37 | Some(time) 38 | } else { 39 | None 40 | } 41 | } 42 | 43 | /// Returns true if this metadata object has a time 44 | fn has_time_spec(&self) -> bool { 45 | let mut has = false; 46 | check_status(unsafe { uhd_sys::uhd_tx_metadata_has_time_spec(self.handle, &mut has) }) 47 | .unwrap(); 48 | has 49 | } 50 | 51 | /// Returns true if the transmitted samples are at the beginning of a burst 52 | pub fn start_of_burst(&self) -> bool { 53 | let mut value = false; 54 | check_status(unsafe { uhd_sys::uhd_tx_metadata_start_of_burst(self.handle, &mut value) }) 55 | .unwrap(); 56 | value 57 | } 58 | 59 | /// Returns true if the transmitted samples are at the end of a burst 60 | pub fn end_of_burst(&self) -> bool { 61 | let mut value = false; 62 | check_status(unsafe { uhd_sys::uhd_tx_metadata_end_of_burst(self.handle, &mut value) }) 63 | .unwrap(); 64 | value 65 | } 66 | 67 | /// Returns the number of samples transmitted 68 | pub fn samples(&self) -> usize { 69 | self.samples 70 | } 71 | 72 | /// Sets the number of samples transmitted 73 | pub(crate) fn set_samples(&mut self, samples: usize) { 74 | self.samples = samples 75 | } 76 | 77 | pub(crate) fn handle_mut(&mut self) -> &mut uhd_sys::uhd_tx_metadata_handle { 78 | &mut self.handle 79 | } 80 | } 81 | 82 | // Thread safety: The uhd_tx_metadata struct just stores data. All exposed functions read fields. 83 | unsafe impl Send for TransmitMetadata {} 84 | unsafe impl Sync for TransmitMetadata {} 85 | 86 | impl Default for TransmitMetadata { 87 | fn default() -> Self { 88 | let mut handle: uhd_sys::uhd_tx_metadata_handle = ptr::null_mut(); 89 | 90 | // not sure what to do here, need to look at docs 91 | let has_time_spec = Default::default(); 92 | let full_secs = Default::default(); 93 | let frac_secs = Default::default(); 94 | let start_of_burst = Default::default(); 95 | let end_of_burst = Default::default(); 96 | 97 | check_status(unsafe { 98 | uhd_sys::uhd_tx_metadata_make( 99 | &mut handle, 100 | has_time_spec, 101 | full_secs, 102 | frac_secs, 103 | start_of_burst, 104 | end_of_burst, 105 | ) 106 | }) 107 | .unwrap(); 108 | TransmitMetadata { handle, samples: 0 } 109 | } 110 | } 111 | 112 | impl Drop for TransmitMetadata { 113 | fn drop(&mut self) { 114 | let _ = unsafe { uhd_sys::uhd_tx_metadata_free(&mut self.handle) }; 115 | } 116 | } 117 | 118 | mod fmt { 119 | use super::TransmitMetadata; 120 | use std::fmt::{Debug, Formatter, Result}; 121 | 122 | impl Debug for TransmitMetadata { 123 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 124 | f.debug_struct("TransmitMetadata") 125 | .field("time_spec", &self.time_spec()) 126 | .field("start_of_burst", &self.start_of_burst()) 127 | .field("end_of_burst", &self.end_of_burst()) 128 | .field("received_samples", &self.samples()) 129 | .finish() 130 | } 131 | } 132 | } 133 | 134 | #[cfg(test)] 135 | mod test { 136 | use super::TransmitMetadata; 137 | 138 | #[test] 139 | fn default_tx_metadata() { 140 | let metadata = TransmitMetadata::default(); 141 | assert_eq!(None, metadata.time_spec()); 142 | assert_eq!(false, metadata.start_of_burst()); 143 | assert_eq!(false, metadata.end_of_burst()); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /uhd/src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::c_char; 2 | 3 | use crate::error::{check_status, Error}; 4 | 5 | /// Initial number of bytes to allocate when copying a string out of a string vector 6 | const INITIAL_SIZE: usize = 128; 7 | /// Maximum number of bytes to allocate when copying a string out of a string vector 8 | const MAX_SIZE: usize = 1024 * 1024; 9 | 10 | /// A helper for copying a string from a C API 11 | /// 12 | /// operation should be a function that takes a pointer to a buffer of characters and the length 13 | /// of the buffer. It calls a C function that fills the buffer and returns an error code. 14 | /// 15 | /// If the error code indicates an error, the error is returned. Otherwise, this function attempts 16 | /// to convert the copied characters into a String. 17 | /// 18 | /// This function returns errors in the following cases: 19 | /// * `operation` returned an error code that indicates an error 20 | /// * The string to be copied is longer than the maximum allowed length 21 | /// * The string copied is not valid UTF-8 22 | pub(crate) fn copy_string(mut operation: F) -> Result 23 | where 24 | F: FnMut(*mut c_char, usize) -> uhd_sys::uhd_error::Type, 25 | { 26 | let mut buffer: Vec = Vec::new(); 27 | for size in BufferSizes::new() { 28 | buffer.resize(size, b'\0'); 29 | 30 | // Call into the C code to copy the string 31 | let status = operation(buffer.as_mut_ptr() as *mut c_char, buffer.len()); 32 | check_status(status)?; 33 | 34 | // Get the part of the buffer before the first null 35 | if let Some(null_index) = buffer.iter().position(|b| *b == b'\0') { 36 | buffer.truncate(null_index); 37 | buffer.shrink_to_fit(); 38 | // Try to convert to UTF-8 39 | return String::from_utf8(buffer).map_err(|_| Error::Utf8); 40 | } else { 41 | // If there is no null, the error message was longer than BUFFER_LENGTH. 42 | // Try again with the next size. 43 | continue; 44 | } 45 | } 46 | // String is too large to fully copy 47 | Err(Error::StringLength) 48 | } 49 | 50 | /// Checks that all provided buffers have the same length. Returns the length of the buffers, 51 | /// or 0 if there are no buffers. Panics if the buffer lengths are not equal. 52 | pub(crate) fn check_equal_buffer_lengths(buffers: &mut [T]) -> usize 53 | where 54 | T: core::borrow::Borrow<[I]>, 55 | { 56 | buffers 57 | .iter() 58 | .fold(None, |prev_size, buffer| { 59 | let buffer: &[I] = buffer.borrow(); 60 | match prev_size { 61 | // Store the size of the first buffer 62 | None => Some(buffer.len()), 63 | 64 | Some(prev_size) => { 65 | assert_eq!(prev_size, buffer.len(), "Unequal buffer sizes"); 66 | Some(prev_size) 67 | } 68 | } 69 | }) 70 | .unwrap_or(0) 71 | } 72 | 73 | /// An iterator over buffer sizes that yields INITIAL_SIZE and then double the previous value 74 | /// up to MAX_SIZE 75 | struct BufferSizes { 76 | /// The next value to return 77 | next: usize, 78 | } 79 | 80 | impl BufferSizes { 81 | pub fn new() -> Self { 82 | BufferSizes { next: INITIAL_SIZE } 83 | } 84 | } 85 | 86 | impl Iterator for BufferSizes { 87 | type Item = usize; 88 | fn next(&mut self) -> Option { 89 | if self.next > MAX_SIZE { 90 | // Maximum exceeded 91 | None 92 | } else { 93 | let current = self.next; 94 | self.next *= 2; 95 | Some(current) 96 | } 97 | } 98 | } 99 | 100 | pub fn alloc_boxed_slice() -> Box<[T; LEN]> { 101 | use std::convert::TryInto; 102 | match vec![T::default(); LEN].into_boxed_slice().try_into() { 103 | Ok(a) => a, 104 | Err(_) => unreachable!(), 105 | } 106 | } 107 | 108 | #[cfg(test)] 109 | mod tests { 110 | use super::*; 111 | 112 | #[test] 113 | fn buffer_sizes() { 114 | let mut sizes = BufferSizes::new(); 115 | assert_eq!(Some(128), sizes.next()); 116 | assert_eq!(Some(256), sizes.next()); 117 | assert_eq!(Some(512), sizes.next()); 118 | assert_eq!(Some(1024), sizes.next()); 119 | assert_eq!(Some(2048), sizes.next()); 120 | assert_eq!(Some(4096), sizes.next()); 121 | assert_eq!(Some(8192), sizes.next()); 122 | assert_eq!(Some(16384), sizes.next()); 123 | assert_eq!(Some(32768), sizes.next()); 124 | assert_eq!(Some(65536), sizes.next()); 125 | assert_eq!(Some(131072), sizes.next()); 126 | assert_eq!(Some(262144), sizes.next()); 127 | assert_eq!(Some(524288), sizes.next()); 128 | assert_eq!(Some(1048576), sizes.next()); 129 | assert_eq!(None, sizes.next()); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /uhd/src/transmitter/streamer.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::os::raw::c_void; 3 | use std::ptr; 4 | 5 | use crate::{ 6 | error::{check_status, Error}, 7 | usrp::Usrp, 8 | utils::check_equal_buffer_lengths, 9 | TransmitMetadata, 10 | }; 11 | 12 | /// A streamer used to transmit samples from a USRP 13 | /// 14 | /// The type parameter I is the type of sample that this streamer transmits. 15 | #[derive(Debug)] 16 | pub struct TransmitStreamer<'usrp, I> { 17 | /// Streamer handle 18 | handle: uhd_sys::uhd_tx_streamer_handle, 19 | 20 | /// A vector of pointers to buffers (used in transmit() to convert `&mut [&[I]]` to `*mut *const I` 21 | /// without reallocating memory each time 22 | /// 23 | /// Invariant: If this is not empty, its length is equal to the value returned by 24 | /// self.num_channels(). 25 | buffer_pointers: Vec<*const c_void>, 26 | /// Link to the USRP that this streamer is associated with 27 | usrp: PhantomData<&'usrp Usrp>, 28 | /// Item type phantom data 29 | item_phantom: PhantomData, 30 | } 31 | 32 | impl TransmitStreamer<'_, I> { 33 | /// Creates a transmit streamer with a null streamer handle (for internal use only) 34 | /// 35 | /// After creating a streamer with this function, its streamer handle must be initialized. 36 | pub(crate) fn new() -> Self { 37 | TransmitStreamer { 38 | handle: ptr::null_mut(), 39 | buffer_pointers: Vec::new(), 40 | usrp: PhantomData, 41 | item_phantom: PhantomData, 42 | } 43 | } 44 | 45 | /// Returns a reference to the streamer handle 46 | pub(crate) fn handle_mut(&mut self) -> &mut uhd_sys::uhd_tx_streamer_handle { 47 | &mut self.handle 48 | } 49 | /// Returns the streamer handle 50 | pub(crate) fn handle(&mut self) -> uhd_sys::uhd_tx_streamer_handle { 51 | self.handle 52 | } 53 | 54 | /// Returns the number of channels that this streamer is associated with 55 | pub fn num_channels(&self) -> usize { 56 | let mut num_channels = 0usize; 57 | check_status(unsafe { 58 | uhd_sys::uhd_tx_streamer_num_channels( 59 | self.handle, 60 | &mut num_channels as *mut usize as *mut _, 61 | ) 62 | }) 63 | .unwrap(); 64 | num_channels 65 | } 66 | 67 | /// transmits samples from the USRP 68 | /// 69 | /// buffers: One or more buffers (one per channel) containing sample to transmit. All 70 | /// buffers should have the same length. This function will panic if the number of buffers 71 | /// is not equal to self.num_channels(), or if not all buffers have the same length. 72 | /// 73 | /// timeout: The timeout for the transmit operation, in seconds 74 | /// 75 | /// On success, this function returns a transmitMetadata object with information about 76 | /// the number of samples actually transmitd. 77 | pub fn transmit( 78 | &mut self, 79 | buffers: &mut [&[I]], 80 | timeout: f64, 81 | ) -> Result { 82 | let mut metadata = TransmitMetadata::default(); 83 | let mut samples_transmitted = 0usize; 84 | 85 | // Initialize buffer_pointers 86 | if self.buffer_pointers.is_empty() { 87 | self.buffer_pointers 88 | .resize(self.num_channels(), ptr::null_mut()); 89 | } 90 | // Now buffer_pointers.len() is equal to self.num_channels(). 91 | assert_eq!( 92 | buffers.len(), 93 | self.buffer_pointers.len(), 94 | "Number of buffers is not equal to this streamer's number of channels" 95 | ); 96 | // Check that all buffers have the same length 97 | let buffer_length = check_equal_buffer_lengths(buffers); 98 | 99 | // Copy buffer pointers into C-compatible form 100 | for (entry, buffer) in self.buffer_pointers.iter_mut().zip(buffers.iter_mut()) { 101 | *entry = buffer.as_ptr() as *mut c_void; 102 | } 103 | 104 | check_status(unsafe { 105 | uhd_sys::uhd_tx_streamer_send( 106 | self.handle, 107 | self.buffer_pointers.as_mut_ptr(), 108 | buffer_length as _, 109 | metadata.handle_mut(), 110 | timeout, 111 | &mut samples_transmitted as *mut usize as *mut _, 112 | ) 113 | })?; 114 | metadata.set_samples(samples_transmitted); 115 | 116 | Ok(metadata) 117 | } 118 | 119 | /// transmits samples on a single channel with a timeout of 0.1 seconds and 120 | /// one_packet disabled 121 | pub fn transmit_simple(&mut self, buffer: &mut [I]) -> Result { 122 | self.transmit(&mut [buffer], 0.1) 123 | } 124 | } 125 | 126 | impl Drop for TransmitStreamer<'_, I> { 127 | fn drop(&mut self) { 128 | let _ = unsafe { uhd_sys::uhd_tx_streamer_free(&mut self.handle) }; 129 | } 130 | } 131 | 132 | // Thread safety: see https://files.ettus.com/manual/page_general.html#general_threading 133 | // All functions are thread-safe, except that the uhd_tx_streamer send(), uhd_tx_streamer recv(), and 134 | // uhd_tx_streamer recv_async_msg() functions. The corresponding Rust wrapper functions take &mut 135 | // self, which enforces single-thread access. 136 | unsafe impl Send for TransmitStreamer<'_, I> {} 137 | unsafe impl Sync for TransmitStreamer<'_, I> {} 138 | -------------------------------------------------------------------------------- /uhd/src/range.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{check_status, Error}; 2 | use std::ptr; 3 | 4 | /// A range of floating-point values, and a step-by amount 5 | #[derive(Clone)] 6 | pub struct Range(uhd_sys::uhd_range_t); 7 | 8 | impl Default for Range { 9 | fn default() -> Self { 10 | Range(uhd_sys::uhd_range_t { 11 | start: 0.0, 12 | stop: 0.0, 13 | step: 0.0, 14 | }) 15 | } 16 | } 17 | 18 | /// A list of ranges of floating-point values 19 | /// 20 | /// The ranges in a meta-range should be monotonic (the start of each range should be greater 21 | /// than or equal to the end of the preceding range). Gaps between ranges are allowed. 22 | /// 23 | /// Most MetaRange methods will return errors if called on a non-monotonic range. 24 | pub struct MetaRange(uhd_sys::uhd_meta_range_handle); 25 | 26 | impl MetaRange { 27 | /// Creates an empty meta-range 28 | pub fn new() -> Self { 29 | Default::default() 30 | } 31 | 32 | /// Returns the overall start of this meta-range 33 | pub fn start(&self) -> Result { 34 | let mut start = 0.0; 35 | check_status(unsafe { uhd_sys::uhd_meta_range_start(self.0, &mut start) })?; 36 | Ok(start) 37 | } 38 | 39 | /// Returns the overall end (stop) of this meta-range 40 | pub fn stop(&self) -> Result { 41 | let mut stop = 0.0; 42 | check_status(unsafe { uhd_sys::uhd_meta_range_stop(self.0, &mut stop) })?; 43 | Ok(stop) 44 | } 45 | 46 | /// Returns the "overall step value" of this meta-range (the minimum of the step values of 47 | /// each contained range, and the gaps between ranges) 48 | pub fn step(&self) -> Result { 49 | let mut step = 0.0; 50 | check_status(unsafe { uhd_sys::uhd_meta_range_step(self.0, &mut step) })?; 51 | Ok(step) 52 | } 53 | 54 | /// Returns the number of ranges in this meta-range 55 | pub fn len(&self) -> usize { 56 | let mut length = 0usize; 57 | check_status(unsafe { 58 | uhd_sys::uhd_meta_range_size(self.0, &mut length as *mut usize as *mut _) 59 | }) 60 | .unwrap(); 61 | length 62 | } 63 | 64 | /// Returns the range at the provided index, if one exists 65 | pub fn get(&self, index: usize) -> Option { 66 | let mut range = Range::default(); 67 | match check_status(unsafe { uhd_sys::uhd_meta_range_at(self.0, index as _, &mut range.0) }) 68 | { 69 | Ok(()) => Some(range), 70 | Err(e) => match e { 71 | // StdExcept usually indicates a std::out_of_range because index >= length 72 | Error::StdExcept => None, 73 | _ => panic!("Unexpected UHD error: {}", e), 74 | }, 75 | } 76 | } 77 | /// Appends a range to the end of this meta-range 78 | pub fn push(&mut self, range: Range) { 79 | check_status(unsafe { uhd_sys::uhd_meta_range_push_back(self.0, &range.0) }).unwrap(); 80 | } 81 | 82 | /// Returns an iterator over ranges in this meta-range 83 | pub fn iter(&self) -> Iter<'_> { 84 | Iter { 85 | range: self, 86 | next: 0, 87 | length: self.len(), 88 | } 89 | } 90 | 91 | pub(crate) fn handle(&mut self) -> uhd_sys::uhd_meta_range_handle { 92 | self.0 93 | } 94 | } 95 | 96 | impl Default for MetaRange { 97 | /// Creates an empty meta-range 98 | fn default() -> Self { 99 | let mut handle = ptr::null_mut(); 100 | check_status(unsafe { uhd_sys::uhd_meta_range_make(&mut handle) }).unwrap(); 101 | MetaRange(handle) 102 | } 103 | } 104 | 105 | impl Drop for MetaRange { 106 | fn drop(&mut self) { 107 | let _ = unsafe { uhd_sys::uhd_meta_range_free(&mut self.0) }; 108 | } 109 | } 110 | 111 | impl<'m> IntoIterator for &'m MetaRange { 112 | type Item = Range; 113 | type IntoIter = Iter<'m>; 114 | 115 | fn into_iter(self) -> Self::IntoIter { 116 | self.iter() 117 | } 118 | } 119 | 120 | /// An iterator over ranges in a meta-range 121 | pub struct Iter<'m> { 122 | range: &'m MetaRange, 123 | /// The index of the next element to yield 124 | /// Invariant: next <= length 125 | next: usize, 126 | length: usize, 127 | } 128 | 129 | impl Iterator for Iter<'_> { 130 | type Item = Range; 131 | 132 | fn next(&mut self) -> Option { 133 | if self.next == self.length { 134 | None 135 | } else { 136 | let item = self.range.get(self.next).unwrap(); 137 | self.next += 1; 138 | Some(item) 139 | } 140 | } 141 | 142 | fn size_hint(&self) -> (usize, Option) { 143 | let remaining = self.length - self.next; 144 | (remaining, Some(remaining)) 145 | } 146 | 147 | fn count(self) -> usize 148 | where 149 | Self: Sized, 150 | { 151 | self.length - self.next 152 | } 153 | } 154 | 155 | impl ExactSizeIterator for Iter<'_> {} 156 | 157 | mod fmt { 158 | use super::{MetaRange, Range}; 159 | use std::fmt::{Debug, Formatter, Result}; 160 | 161 | impl Debug for MetaRange { 162 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 163 | f.debug_list().entries(self.iter()).finish() 164 | } 165 | } 166 | 167 | impl Debug for Range { 168 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 169 | f.debug_struct("Range") 170 | .field("start", &self.0.start) 171 | .field("stop", &self.0.stop) 172 | .field("step", &self.0.step) 173 | .finish() 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /uhd/src/receiver/streamer.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::os::raw::c_void; 3 | use std::ptr; 4 | 5 | use crate::{ 6 | error::{check_status, Error, Result}, 7 | stream::StreamCommand, 8 | usrp::Usrp, 9 | utils::check_equal_buffer_lengths, 10 | ReceiveMetadata, 11 | }; 12 | 13 | /// A streamer used to receive samples from a USRP 14 | /// 15 | /// The type parameter I is the type of sample that this streamer receives. 16 | #[derive(Debug)] 17 | pub struct ReceiveStreamer<'usrp, I> { 18 | /// Streamer handle 19 | handle: uhd_sys::uhd_rx_streamer_handle, 20 | /// A vector of pointers to buffers (used in receive() to convert `&mut [&mut [I]]` to `*mut *mut I` 21 | /// without reallocating memory each time 22 | /// 23 | /// Invariant: If this is not empty, its length is equal to the value returned by 24 | /// self.num_channels(). 25 | buffer_pointers: Vec<*mut c_void>, 26 | /// Link to the USRP that this streamer is associated with 27 | usrp: PhantomData<&'usrp Usrp>, 28 | /// Item type phantom data 29 | item_phantom: PhantomData, 30 | } 31 | 32 | impl ReceiveStreamer<'_, I> { 33 | /// Creates a receive streamer with a null streamer handle (for internal use only) 34 | /// 35 | /// After creating a streamer with this function, its streamer handle must be initialized. 36 | pub(crate) fn new() -> Self { 37 | ReceiveStreamer { 38 | handle: ptr::null_mut(), 39 | buffer_pointers: Vec::new(), 40 | usrp: PhantomData, 41 | item_phantom: PhantomData, 42 | } 43 | } 44 | 45 | /// Returns a reference to the streamer handle 46 | pub(crate) fn handle_mut(&mut self) -> &mut uhd_sys::uhd_rx_streamer_handle { 47 | &mut self.handle 48 | } 49 | /// Returns the streamer handle 50 | pub(crate) fn handle(&mut self) -> uhd_sys::uhd_rx_streamer_handle { 51 | self.handle 52 | } 53 | 54 | /// Sends a stream command to the USRP 55 | /// 56 | /// This can be used to start or stop streaming 57 | pub fn send_command(&mut self, command: &StreamCommand) -> Result<(), Error> { 58 | let command_c = command.as_c_command(); 59 | check_status(unsafe { uhd_sys::uhd_rx_streamer_issue_stream_cmd(self.handle, &command_c) }) 60 | } 61 | 62 | /// Returns the number of channels that this streamer is associated with 63 | pub fn num_channels(&self) -> usize { 64 | let mut num_channels = 0usize; 65 | check_status(unsafe { 66 | uhd_sys::uhd_rx_streamer_num_channels( 67 | self.handle, 68 | &mut num_channels as *mut usize as *mut _, 69 | ) 70 | }) 71 | .unwrap(); 72 | num_channels 73 | } 74 | 75 | /// Receives samples from the USRP 76 | /// 77 | /// buffers: One or more buffers (one per channel) where the samples will be written. All 78 | /// buffers should have the same length. This function will panic if the number of buffers is 79 | /// not equal to self.num_channels(), or if not all buffers have the same length. 80 | /// 81 | /// timeout: The timeout for the receive operation, in seconds 82 | /// 83 | /// one_packet: If this is true, one call to receive() will not copy samples from more than 84 | /// one packet of the underlying protocol 85 | /// 86 | /// On success, this function returns a ReceiveMetadata object with information about 87 | /// the number of samples actually received. 88 | pub fn receive( 89 | &mut self, 90 | buffers: &mut [&mut [I]], 91 | timeout: f64, 92 | one_packet: bool, 93 | ) -> Result { 94 | let mut metadata = ReceiveMetadata::default(); 95 | let mut samples_received = 0usize; 96 | 97 | // Initialize buffer_pointers 98 | if self.buffer_pointers.is_empty() { 99 | self.buffer_pointers 100 | .resize(self.num_channels(), ptr::null_mut()); 101 | } 102 | // Now buffer_pointers.len() is equal to self.num_channels(). 103 | assert_eq!( 104 | buffers.len(), 105 | self.buffer_pointers.len(), 106 | "Number of buffers is not equal to this streamer's number of channels" 107 | ); 108 | // Check that all buffers have the same length 109 | let buffer_length = check_equal_buffer_lengths(buffers); 110 | 111 | // Copy buffer pointers into C-compatible form 112 | for (entry, buffer) in self.buffer_pointers.iter_mut().zip(buffers.iter_mut()) { 113 | *entry = buffer.as_mut_ptr() as *mut c_void; 114 | } 115 | 116 | check_status(unsafe { 117 | uhd_sys::uhd_rx_streamer_recv( 118 | self.handle, 119 | self.buffer_pointers.as_mut_ptr(), 120 | buffer_length as _, 121 | metadata.handle_mut(), 122 | timeout, 123 | one_packet, 124 | &mut samples_received as *mut usize as *mut _, 125 | ) 126 | })?; 127 | metadata.set_samples(samples_received); 128 | 129 | Ok(metadata) 130 | } 131 | 132 | /// Receives samples on a single channel with a timeout of 0.1 seconds and one_packet disabled 133 | pub fn receive_simple(&mut self, buffer: &mut [I]) -> Result { 134 | self.receive(&mut [buffer], 0.1, false) 135 | } 136 | } 137 | 138 | impl Drop for ReceiveStreamer<'_, I> { 139 | fn drop(&mut self) { 140 | let _ = unsafe { uhd_sys::uhd_rx_streamer_free(&mut self.handle) }; 141 | } 142 | } 143 | 144 | // Thread safety: see https://files.ettus.com/manual/page_general.html#general_threading 145 | // All functions are thread-safe, except that the uhd_tx_streamer send(), uhd_rx_streamer recv(), and 146 | // uhd_rx_streamer recv_async_msg() functions. The corresponding Rust wrapper functions take &mut 147 | // self, which enforces single-thread access. 148 | unsafe impl Send for ReceiveStreamer<'_, I> {} 149 | unsafe impl Sync for ReceiveStreamer<'_, I> {} 150 | -------------------------------------------------------------------------------- /uhd/src/stream/mod.rs: -------------------------------------------------------------------------------- 1 | use num_complex::{Complex, Complex32, Complex64}; 2 | use std::convert::{TryFrom, TryInto}; 3 | use std::ffi::{CString, NulError}; 4 | use std::marker::PhantomData; 5 | 6 | /// Arguments used to create a stream 7 | /// 8 | /// The type parameter I defines the item type and host format. 9 | /// 10 | /// The default stream arguments use wire format `sc16` and host format `fc32`: 11 | /// ``` 12 | /// use uhd::StreamArgs; 13 | /// use num_complex::Complex32; 14 | /// let args = StreamArgs::::new("sc16"); 15 | /// ``` 16 | /// 17 | #[derive(Debug, Clone)] 18 | pub struct StreamArgs { 19 | host_format: PhantomData, 20 | wire_format: String, 21 | args: String, 22 | channels: Vec, 23 | } 24 | 25 | impl StreamArgs { 26 | /// Creates stream arguments with the provided wire format, no additional 27 | /// arguments, and channel 0 28 | pub fn new(wire_format: S) -> Self 29 | where 30 | S: Into, 31 | { 32 | StreamArgs { 33 | wire_format: wire_format.into(), 34 | ..StreamArgs::default() 35 | } 36 | } 37 | 38 | /// Creates a builder, initialized with default arguments, that can be used to configure 39 | /// the stream arguments 40 | pub fn builder() -> StreamArgsBuilder { 41 | StreamArgsBuilder { 42 | args: StreamArgs::default(), 43 | } 44 | } 45 | } 46 | 47 | impl Default for StreamArgs { 48 | /// Creates stream arguments with wire format `sc16`, host format determined by the type `I`, 49 | /// and default arguments and channels 50 | fn default() -> Self { 51 | StreamArgs { 52 | host_format: PhantomData::default(), 53 | wire_format: "sc16".to_string(), 54 | args: "".to_string(), 55 | // Empty list = just channel 0 56 | channels: vec![], 57 | } 58 | } 59 | } 60 | 61 | pub struct StreamArgsBuilder { 62 | args: StreamArgs, 63 | } 64 | 65 | impl StreamArgsBuilder { 66 | /// Sets the wire data format 67 | pub fn wire_format(self, wire_format: String) -> Self { 68 | StreamArgsBuilder { 69 | args: StreamArgs { 70 | wire_format, 71 | ..self.args 72 | }, 73 | } 74 | } 75 | 76 | /// Sets additional arguments for the stream 77 | pub fn args(self, args: String) -> Self { 78 | StreamArgsBuilder { 79 | args: StreamArgs { args, ..self.args }, 80 | } 81 | } 82 | 83 | /// Sets the indexes of channels to stream 84 | pub fn channels(self, channels: Vec) -> Self { 85 | StreamArgsBuilder { 86 | args: StreamArgs { 87 | channels, 88 | ..self.args 89 | }, 90 | } 91 | } 92 | 93 | /// Builds a StreamArgs with the configured options 94 | pub fn build(self) -> StreamArgs { 95 | self.args 96 | } 97 | } 98 | 99 | /// C-compatible version of StreamArgs 100 | pub(crate) struct StreamArgsC<'args> { 101 | pub host_format: CString, 102 | pub wire_format: CString, 103 | pub args: CString, 104 | pub channels: &'args [usize], 105 | } 106 | 107 | impl<'args, I> TryFrom<&'args StreamArgs> for StreamArgsC<'args> 108 | where 109 | I: Item, 110 | { 111 | type Error = NulError; 112 | 113 | fn try_from(args: &'args StreamArgs) -> Result { 114 | Ok(StreamArgsC { 115 | host_format: CString::new(I::FORMAT)?, 116 | wire_format: CString::new(&*args.wire_format)?, 117 | args: CString::new(&*args.args)?, 118 | channels: &args.channels, 119 | }) 120 | } 121 | } 122 | 123 | /// A stream item 124 | pub trait Item { 125 | /// The format name (examples: `fc32` for Complex, `sc16` for Complex) 126 | const FORMAT: &'static str; 127 | } 128 | 129 | impl Item for Complex64 { 130 | const FORMAT: &'static str = "fc64"; 131 | } 132 | impl Item for Complex32 { 133 | const FORMAT: &'static str = "fc32"; 134 | } 135 | impl Item for Complex { 136 | const FORMAT: &'static str = "sc16"; 137 | } 138 | impl Item for Complex { 139 | const FORMAT: &'static str = "sc8"; 140 | } 141 | 142 | /// A stream command that can be sent to a USRP to control streaming 143 | #[derive(Debug, Clone)] 144 | pub struct StreamCommand { 145 | pub time: StreamTime, 146 | pub command_type: StreamCommandType, 147 | } 148 | 149 | #[derive(Debug, Clone)] 150 | pub enum StreamCommandType { 151 | /// UHD_STREAM_MODE_START_CONTINUOUS 152 | StartContinuous, 153 | 154 | /// UHD_STREAM_MODE_STOP_CONTINUOUS 155 | StopContinuous, 156 | 157 | /// UHD_STREAM_MODE_NUM_SAMPS_AND_DONE 158 | CountAndDone(u64), 159 | 160 | /// UHD_STREAM_MODE_NUM_SAMPS_AND_MORE 161 | CountAndMore(u64), 162 | } 163 | 164 | /// When the USRP should begin streaming 165 | #[derive(Debug, Clone)] 166 | pub enum StreamTime { 167 | Now, 168 | Later(std::time::Duration), 169 | } 170 | 171 | impl StreamCommand { 172 | /// Converts this command into a C `uhd_stream_cmd_t` 173 | /// 174 | /// # Panics 175 | /// 176 | /// This function panics if this command is `Later` and contains a time with 177 | /// a seconds field that is too large for a time_t. 178 | pub(crate) fn as_c_command(&self) -> uhd_sys::uhd_stream_cmd_t { 179 | let mut c_cmd = uhd_sys::uhd_stream_cmd_t { 180 | stream_mode: uhd_sys::uhd_stream_mode_t::UHD_STREAM_MODE_START_CONTINUOUS, 181 | num_samps: 0, 182 | stream_now: false, 183 | time_spec_full_secs: 0, 184 | time_spec_frac_secs: 0.0, 185 | }; 186 | 187 | match &self.time { 188 | StreamTime::Now => c_cmd.stream_now = true, 189 | StreamTime::Later(dur) => { 190 | c_cmd.time_spec_full_secs = dur.as_secs() as i64; 191 | c_cmd.time_spec_frac_secs = dur.subsec_millis() as f64 / 1000.0 192 | } 193 | } 194 | 195 | // In some versions of UHD, num_samps is a size_t. In other versions, it's a uint64_t. 196 | // The Rust code always uses u64, and converts here. 197 | 198 | match self.command_type { 199 | StreamCommandType::StartContinuous => { 200 | c_cmd.stream_mode = uhd_sys::uhd_stream_mode_t::UHD_STREAM_MODE_START_CONTINUOUS; 201 | } 202 | StreamCommandType::StopContinuous => { 203 | c_cmd.stream_mode = uhd_sys::uhd_stream_mode_t::UHD_STREAM_MODE_STOP_CONTINUOUS 204 | } 205 | StreamCommandType::CountAndDone(samples) => { 206 | c_cmd.stream_mode = uhd_sys::uhd_stream_mode_t::UHD_STREAM_MODE_NUM_SAMPS_AND_DONE; 207 | c_cmd.num_samps = samples 208 | .try_into() 209 | .expect("Number of samples too large for size_t"); 210 | } 211 | StreamCommandType::CountAndMore(samples) => { 212 | c_cmd.stream_mode = uhd_sys::uhd_stream_mode_t::UHD_STREAM_MODE_NUM_SAMPS_AND_MORE; 213 | c_cmd.num_samps = samples 214 | .try_into() 215 | .expect("Number of samples too large for size_t"); 216 | } 217 | }; 218 | c_cmd 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /uhd/src/string_vector.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryInto; 2 | use std::ffi::{CString, NulError}; 3 | use std::ptr; 4 | 5 | use crate::check_status; 6 | use crate::error::Error; 7 | use crate::utils::copy_string; 8 | 9 | /// A handle to a std::vector of std::strings 10 | pub(crate) struct StringVector(uhd_sys::uhd_string_vector_handle); 11 | 12 | impl StringVector { 13 | /// Creates a new empty string vector 14 | pub fn new() -> Result { 15 | let mut handle: uhd_sys::uhd_string_vector_handle = ptr::null_mut(); 16 | 17 | check_status(unsafe { uhd_sys::uhd_string_vector_make(&mut handle) }) 18 | .map(|_| StringVector(handle)) 19 | } 20 | 21 | /// Returns the number of strings in this vector 22 | pub fn len(&self) -> usize { 23 | let mut len = 0; 24 | let status = unsafe { uhd_sys::uhd_string_vector_size(self.0, &mut len) }; 25 | check_status(status).unwrap(); 26 | len.try_into().expect("Length does not fit into usize") 27 | } 28 | 29 | /// Appends a string to the end of this vector 30 | /// 31 | /// This function returns an error if the provided value contains a null byte. 32 | #[allow(dead_code)] 33 | pub fn push(&mut self, value: String) -> Result<(), NulError> { 34 | let value_c = CString::new(value)?; 35 | let status = unsafe { uhd_sys::uhd_string_vector_push_back(&mut self.0, value_c.as_ptr()) }; 36 | check_status(status).unwrap(); 37 | Ok(()) 38 | } 39 | 40 | pub fn get(&self, index: usize) -> Option> { 41 | let status = copy_string(|buffer, length| unsafe { 42 | uhd_sys::uhd_string_vector_at(self.0, index as _, buffer, length as _) 43 | }); 44 | match status { 45 | Ok(value) => Some(Ok(value)), 46 | Err(e) => match e { 47 | Error::StdExcept => { 48 | // This is most likely an std::out_of_range because the index was >= length. 49 | None 50 | } 51 | _ => Some(Err(e)), 52 | }, 53 | } 54 | } 55 | 56 | /// Returns an iterator over the items in this vector 57 | pub fn iter(&self) -> Iter<'_> { 58 | Iter { 59 | vector: self, 60 | next: 0, 61 | length: self.len(), 62 | } 63 | } 64 | 65 | /// Returns the underlying handle 66 | pub(crate) fn handle_mut(&mut self) -> &mut uhd_sys::uhd_string_vector_handle { 67 | &mut self.0 68 | } 69 | } 70 | 71 | impl Drop for StringVector { 72 | fn drop(&mut self) { 73 | let _ = unsafe { uhd_sys::uhd_string_vector_free(&mut self.0) }; 74 | } 75 | } 76 | 77 | /// An iterator over items in a string vector 78 | pub struct Iter<'v> { 79 | /// Vector being iterated over 80 | vector: &'v StringVector, 81 | /// Index of next item to yield (invariant: next <= length) 82 | next: usize, 83 | /// Number of items in vector 84 | length: usize, 85 | } 86 | 87 | impl Iterator for Iter<'_> { 88 | type Item = Result; 89 | 90 | fn next(&mut self) -> Option { 91 | if self.next == self.length { 92 | None 93 | } else { 94 | let item = self.vector.get(self.next)?; 95 | self.next += 1; 96 | Some(item) 97 | } 98 | } 99 | 100 | fn size_hint(&self) -> (usize, Option) { 101 | let size = self.length - self.next; 102 | (size, Some(size)) 103 | } 104 | 105 | fn count(self) -> usize 106 | where 107 | Self: Sized, 108 | { 109 | self.length - self.next 110 | } 111 | } 112 | 113 | impl ExactSizeIterator for Iter<'_> {} 114 | 115 | impl<'v> IntoIterator for &'v StringVector { 116 | type Item = as Iterator>::Item; 117 | type IntoIter = Iter<'v>; 118 | 119 | fn into_iter(self) -> Self::IntoIter { 120 | self.iter() 121 | } 122 | } 123 | 124 | impl From for Vec { 125 | fn from(strings: StringVector) -> Self { 126 | From::from(&strings) 127 | } 128 | } 129 | impl From<&'_ StringVector> for Vec { 130 | fn from(strings: &StringVector) -> Self { 131 | strings 132 | .iter() 133 | .flat_map(|string_or_err| string_or_err.ok()) 134 | .collect() 135 | } 136 | } 137 | 138 | mod fmt { 139 | use super::StringVector; 140 | use std::fmt::*; 141 | 142 | impl Debug for StringVector { 143 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 144 | f.debug_list() 145 | .entries(self.iter().map(|item| { 146 | // Item may be a normal String or an invalid UTF-8 error 147 | item.unwrap_or_else(|_| "".to_owned()) 148 | })) 149 | .finish() 150 | } 151 | } 152 | } 153 | 154 | #[cfg(test)] 155 | mod tests { 156 | use super::*; 157 | 158 | #[test] 159 | fn string_vector_empty() -> Result<(), Box> { 160 | let vector = StringVector::new()?; 161 | assert!(vector.get(0).is_none()); 162 | Ok(()) 163 | } 164 | 165 | #[test] 166 | fn string_vector_small() -> Result<(), Box> { 167 | let mut vector = StringVector::new()?; 168 | assert!(vector.get(0).is_none()); 169 | let value0 = "slithy toves"; 170 | vector.push(value0.to_owned())?; 171 | 172 | assert!(vector.get(0).unwrap().unwrap() == value0); 173 | Ok(()) 174 | } 175 | 176 | #[test] 177 | fn string_vector_large() -> Result<(), Box> { 178 | let mut vector = StringVector::new()?; 179 | assert!(vector.get(0).is_none()); 180 | let value0 = "mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths \ 181 | outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe \ 182 | mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths \ 183 | outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe \ 184 | mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths \ 185 | outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe \ 186 | mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths \ 187 | outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe \ 188 | mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths \ 189 | outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe \ 190 | mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths \ 191 | outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe \ 192 | mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths \ 193 | outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe \ 194 | mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths \ 195 | outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe \ 196 | mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths \ 197 | outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe \ 198 | mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths \ 199 | outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe \ 200 | mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths \ 201 | outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe \ 202 | mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths \ 203 | outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe \ 204 | mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths \ 205 | outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe \ 206 | mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths \ 207 | outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe mome raths outgrabe \ 208 | mome raths outgrabe mome raths outgrabe mome raths outgrabe "; 209 | vector.push(value0.to_owned())?; 210 | assert!(vector.get(0).unwrap().unwrap() == value0); 211 | Ok(()) 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /uhd/src/receiver/metadata.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use super::error::{ReceiveError, ReceiveErrorKind}; 4 | use crate::error::check_status; 5 | use crate::utils::copy_string; 6 | use crate::TimeSpec; 7 | 8 | /// Data about a receive operation 9 | pub struct ReceiveMetadata { 10 | /// Handle to C++ object 11 | handle: uhd_sys::uhd_rx_metadata_handle, 12 | /// Number of samples received 13 | samples: usize, 14 | } 15 | 16 | impl ReceiveMetadata { 17 | pub fn new() -> Self { 18 | Default::default() 19 | } 20 | 21 | /// Returns the timestamp of (the first?) of the received samples, according to the USRP's 22 | /// internal clock 23 | pub fn time_spec(&self) -> Option { 24 | if self.has_time_spec() { 25 | let mut time = TimeSpec::default(); 26 | let mut seconds_time_t: libc::time_t = Default::default(); 27 | 28 | check_status(unsafe { 29 | uhd_sys::uhd_rx_metadata_time_spec( 30 | self.handle, 31 | &mut seconds_time_t, 32 | &mut time.fraction, 33 | ) 34 | }) 35 | .unwrap(); 36 | // Convert seconds from time_t to i64 37 | time.seconds = seconds_time_t.into(); 38 | Some(time) 39 | } else { 40 | None 41 | } 42 | } 43 | 44 | /// Returns true if this metadata object has a time 45 | fn has_time_spec(&self) -> bool { 46 | let mut has = false; 47 | check_status(unsafe { uhd_sys::uhd_rx_metadata_has_time_spec(self.handle, &mut has) }) 48 | .unwrap(); 49 | has 50 | } 51 | 52 | /// Returns true if the received samples are at the beginning of a burst 53 | pub fn start_of_burst(&self) -> bool { 54 | let mut value = false; 55 | check_status(unsafe { uhd_sys::uhd_rx_metadata_start_of_burst(self.handle, &mut value) }) 56 | .unwrap(); 57 | value 58 | } 59 | 60 | /// Returns true if the received samples are at the end of a burst 61 | pub fn end_of_burst(&self) -> bool { 62 | let mut value = false; 63 | check_status(unsafe { uhd_sys::uhd_rx_metadata_end_of_burst(self.handle, &mut value) }) 64 | .unwrap(); 65 | value 66 | } 67 | 68 | /// Returns true if the provided receive buffer was not large enough to hold a full packet 69 | /// 70 | /// If this is the case, the fragment_offset() function returns the offset from the beginning 71 | /// of the packet to the first sample received 72 | pub fn more_fragments(&self) -> bool { 73 | let mut value = false; 74 | check_status(unsafe { uhd_sys::uhd_rx_metadata_more_fragments(self.handle, &mut value) }) 75 | .unwrap(); 76 | value 77 | } 78 | 79 | /// If more_fragments() returned true, this function returns the offset from the beginning 80 | /// of the packet to the first sample received 81 | pub fn fragment_offset(&self) -> usize { 82 | let mut value = 0usize; 83 | check_status(unsafe { 84 | uhd_sys::uhd_rx_metadata_fragment_offset( 85 | self.handle, 86 | &mut value as *mut usize as *mut _, 87 | ) 88 | }) 89 | .unwrap(); 90 | value 91 | } 92 | 93 | /// Returns true if a packet was dropped or received out of order 94 | pub fn out_of_sequence(&self) -> bool { 95 | let mut value = false; 96 | check_status(unsafe { uhd_sys::uhd_rx_metadata_out_of_sequence(self.handle, &mut value) }) 97 | .unwrap(); 98 | value 99 | } 100 | 101 | /// Returns the number of samples received 102 | pub fn samples(&self) -> usize { 103 | self.samples 104 | } 105 | 106 | /// Sets the number of samples received 107 | pub(crate) fn set_samples(&mut self, samples: usize) { 108 | self.samples = samples 109 | } 110 | 111 | /// Returns the error code associated with the receive operation 112 | fn error_code(&self) -> uhd_sys::uhd_rx_metadata_error_code_t::Type { 113 | let mut code = uhd_sys::uhd_rx_metadata_error_code_t::UHD_RX_METADATA_ERROR_CODE_NONE; 114 | check_status(unsafe { uhd_sys::uhd_rx_metadata_error_code(self.handle, &mut code) }) 115 | .unwrap(); 116 | code 117 | } 118 | 119 | /// Returns the error associated with the receive operation, if any 120 | pub fn last_error(&self) -> Option { 121 | let out_of_sequence = self.out_of_sequence(); 122 | use uhd_sys::uhd_rx_metadata_error_code_t::*; 123 | let kind = match self.error_code() { 124 | UHD_RX_METADATA_ERROR_CODE_TIMEOUT => ReceiveErrorKind::Timeout, 125 | UHD_RX_METADATA_ERROR_CODE_LATE_COMMAND => ReceiveErrorKind::LateCommand, 126 | UHD_RX_METADATA_ERROR_CODE_BROKEN_CHAIN => ReceiveErrorKind::BrokenChain, 127 | UHD_RX_METADATA_ERROR_CODE_OVERFLOW if !out_of_sequence => ReceiveErrorKind::Overflow, 128 | UHD_RX_METADATA_ERROR_CODE_OVERFLOW if out_of_sequence => { 129 | ReceiveErrorKind::OutOfSequence 130 | } 131 | UHD_RX_METADATA_ERROR_CODE_ALIGNMENT => ReceiveErrorKind::Alignment, 132 | UHD_RX_METADATA_ERROR_CODE_BAD_PACKET => ReceiveErrorKind::BadPacket, 133 | UHD_RX_METADATA_ERROR_CODE_NONE => { 134 | // Not actually an error 135 | return None; 136 | } 137 | _ => { 138 | // Some other error 139 | ReceiveErrorKind::Other 140 | } 141 | }; 142 | let message = copy_string(|buffer, length| unsafe { 143 | uhd_sys::uhd_rx_metadata_strerror(self.handle, buffer, length as _) 144 | }) 145 | .ok(); 146 | 147 | Some(ReceiveError { kind, message }) 148 | } 149 | 150 | pub(crate) fn handle_mut(&mut self) -> &mut uhd_sys::uhd_rx_metadata_handle { 151 | &mut self.handle 152 | } 153 | } 154 | 155 | // Thread safety: The uhd_rx_metadata struct just stores data. All exposed functions read fields. 156 | unsafe impl Send for ReceiveMetadata {} 157 | unsafe impl Sync for ReceiveMetadata {} 158 | 159 | impl Default for ReceiveMetadata { 160 | fn default() -> Self { 161 | let mut handle: uhd_sys::uhd_rx_metadata_handle = ptr::null_mut(); 162 | check_status(unsafe { uhd_sys::uhd_rx_metadata_make(&mut handle) }).unwrap(); 163 | ReceiveMetadata { handle, samples: 0 } 164 | } 165 | } 166 | 167 | impl Drop for ReceiveMetadata { 168 | fn drop(&mut self) { 169 | let _ = unsafe { uhd_sys::uhd_rx_metadata_free(&mut self.handle) }; 170 | } 171 | } 172 | 173 | mod fmt { 174 | use super::*; 175 | use super::{ReceiveError, ReceiveMetadata}; 176 | use std::fmt::{Debug, Display, Formatter, Result}; 177 | 178 | impl Debug for ReceiveMetadata { 179 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 180 | f.debug_struct("ReceiveMetadata") 181 | .field("time_spec", &self.time_spec()) 182 | .field("more_fragments", &self.more_fragments()) 183 | .field("fragment_offset", &self.fragment_offset()) 184 | .field("start_of_burst", &self.start_of_burst()) 185 | .field("end_of_burst", &self.end_of_burst()) 186 | .field("received_samples", &self.samples()) 187 | .finish() 188 | } 189 | } 190 | 191 | impl Display for ReceiveError { 192 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 193 | match self.kind { 194 | ReceiveErrorKind::Timeout => write!(f, "No packet received"), 195 | ReceiveErrorKind::LateCommand => write!(f, "Command timestamp was in the past"), 196 | ReceiveErrorKind::BrokenChain => write!(f, "Expected another stream command"), 197 | ReceiveErrorKind::Overflow => { 198 | write!(f, "An internal receive buffer has been filled") 199 | } 200 | ReceiveErrorKind::OutOfSequence => write!(f, "Sequence error"), 201 | ReceiveErrorKind::Alignment => write!(f, "Multi-channel alignment failed"), 202 | ReceiveErrorKind::BadPacket => write!(f, "A packet could not be parsed"), 203 | ReceiveErrorKind::Other => write!(f, "Other error"), 204 | }?; 205 | match self.message { 206 | Some(ref message) if !message.is_empty() => write!(f, ": {}", message)?, 207 | _ => {} 208 | } 209 | Ok(()) 210 | } 211 | } 212 | } 213 | 214 | #[cfg(test)] 215 | mod test { 216 | use super::ReceiveMetadata; 217 | 218 | #[test] 219 | fn default_rx_metadata() { 220 | let metadata = ReceiveMetadata::default(); 221 | assert_eq!(None, metadata.time_spec()); 222 | assert_eq!(false, metadata.start_of_burst()); 223 | assert_eq!(false, metadata.end_of_burst()); 224 | assert_eq!(false, metadata.out_of_sequence()); 225 | assert_eq!(false, metadata.more_fragments()); 226 | assert_eq!(0, metadata.fragment_offset()); 227 | assert!(metadata.last_error().is_none()); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /uhd/src/usrp.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::{check_status, Error}, 3 | motherboard_eeprom::MotherboardEeprom, 4 | range::MetaRange, 5 | stream::{Item, StreamArgs, StreamArgsC}, 6 | string_vector::StringVector, 7 | utils::copy_string, 8 | DaughterBoardEeprom, ReceiveInfo, ReceiveStreamer, TimeSpec, TransmitInfo, TransmitStreamer, 9 | TuneRequest, TuneResult, 10 | }; 11 | 12 | use std::convert::TryInto; 13 | use std::ffi::CString; 14 | use std::ptr; 15 | /// A connection to a USRP device 16 | pub struct Usrp(uhd_sys::uhd_usrp_handle); 17 | 18 | impl Usrp { 19 | pub fn find(args: &str) -> Result, Error> { 20 | let args = CString::new(args)?; 21 | let mut addresses = StringVector::new()?; 22 | check_status(unsafe { uhd_sys::uhd_usrp_find(args.as_ptr(), addresses.handle_mut()) })?; 23 | Ok(addresses.into()) 24 | } 25 | 26 | /// Opens a connection to a USRP 27 | /// 28 | /// args: A string with parameters for the USRP connection. If this is an empty string, 29 | /// one available USRP will be opened with the default settings. Arguments can be specified 30 | /// with the syntax `key=value`, with key-value pairs separated by commas. 31 | /// 32 | /// Frequently used arguments: 33 | /// * `addr`: The IP address of the USRP 34 | /// * `type`: The type of the USRP (allowed values include `usrp2` and others) 35 | /// 36 | pub fn open(args: &str) -> Result { 37 | let mut handle: uhd_sys::uhd_usrp_handle = ptr::null_mut(); 38 | let args_c = CString::new(args)?; 39 | check_status(unsafe { uhd_sys::uhd_usrp_make(&mut handle, args_c.as_ptr()) })?; 40 | Ok(Usrp(handle)) 41 | } 42 | 43 | /// Returns the antennas available for transmission 44 | pub fn get_tx_antennas(&self, channel: usize) -> Result, Error> { 45 | let mut vector = StringVector::new()?; 46 | check_status(unsafe { 47 | uhd_sys::uhd_usrp_get_tx_antennas(self.0, channel as _, vector.handle_mut()) 48 | })?; 49 | Ok(vector.into()) 50 | } 51 | 52 | /// Returns the selected antenna for transmission 53 | pub fn get_tx_antenna(&self, channel: usize) -> Result { 54 | copy_string(|buffer, length| unsafe { 55 | uhd_sys::uhd_usrp_get_tx_antenna(self.0, channel as _, buffer, length as _) 56 | }) 57 | } 58 | 59 | /// Returns the antennas available for receiving 60 | pub fn get_rx_antennas(&self, channel: usize) -> Result, Error> { 61 | let mut vector = StringVector::new()?; 62 | check_status(unsafe { 63 | uhd_sys::uhd_usrp_get_rx_antennas(self.0, channel as _, vector.handle_mut()) 64 | })?; 65 | Ok(vector.into()) 66 | } 67 | 68 | /// Returns the selected antenna for receiving 69 | pub fn get_rx_antenna(&self, channel: usize) -> Result { 70 | copy_string(|buffer, length| unsafe { 71 | uhd_sys::uhd_usrp_get_rx_antenna(self.0, channel as _, buffer, length as _) 72 | }) 73 | } 74 | 75 | /// Returns the current receive front-end bandwidth 76 | pub fn get_rx_bandwidth(&self, channel: usize) -> Result { 77 | let mut value = 0.0; 78 | check_status(unsafe { 79 | uhd_sys::uhd_usrp_get_rx_bandwidth(self.0, channel as _, &mut value) 80 | })?; 81 | Ok(value) 82 | } 83 | 84 | /// Returns the supported range of receive front-end bandwidth 85 | pub fn get_rx_bandwidth_range(&self, channel: usize) -> Result { 86 | let mut range = MetaRange::default(); 87 | check_status(unsafe { 88 | uhd_sys::uhd_usrp_get_rx_bandwidth_range(self.0, channel as _, range.handle()) 89 | })?; 90 | Ok(range) 91 | } 92 | 93 | /// Returns the current transmit front-end bandwidth 94 | pub fn get_tx_bandwidth(&self, channel: usize) -> Result { 95 | let mut value = 0.0; 96 | check_status(unsafe { 97 | uhd_sys::uhd_usrp_get_tx_bandwidth(self.0, channel as _, &mut value) 98 | })?; 99 | Ok(value) 100 | } 101 | 102 | /// Returns the supported range of transmit front-end bandwidth 103 | pub fn get_tx_bandwidth_range(&self, channel: usize) -> Result { 104 | let mut range = MetaRange::default(); 105 | check_status(unsafe { 106 | uhd_sys::uhd_usrp_get_tx_bandwidth_range(self.0, channel as _, range.handle()) 107 | })?; 108 | Ok(range) 109 | } 110 | 111 | /// Returns the current receive frequency 112 | pub fn get_rx_frequency(&self, channel: usize) -> Result { 113 | let mut value = 0.0; 114 | check_status(unsafe { uhd_sys::uhd_usrp_get_rx_freq(self.0, channel as _, &mut value) })?; 115 | Ok(value) 116 | } 117 | 118 | /// Returns the supported range of receive frequencies 119 | pub fn get_rx_frequency_range(&self, channel: usize) -> Result { 120 | let mut range = MetaRange::default(); 121 | check_status(unsafe { 122 | uhd_sys::uhd_usrp_get_rx_freq_range(self.0, channel as _, range.handle()) 123 | })?; 124 | Ok(range) 125 | } 126 | 127 | /// Returns the current transmit frequency 128 | pub fn get_tx_frequency(&self, channel: usize) -> Result { 129 | let mut value = 0.0; 130 | check_status(unsafe { uhd_sys::uhd_usrp_get_tx_freq(self.0, channel as _, &mut value) })?; 131 | Ok(value) 132 | } 133 | 134 | /// Returns the supported range of transmit frequencies 135 | pub fn get_tx_frequency_range(&self, channel: usize) -> Result { 136 | let mut range = MetaRange::default(); 137 | check_status(unsafe { 138 | uhd_sys::uhd_usrp_get_tx_freq_range(self.0, channel as _, range.handle()) 139 | })?; 140 | Ok(range) 141 | } 142 | 143 | /// Returns the current gain of the gain element with the specified name 144 | pub fn get_rx_gain(&self, channel: usize, name: &str) -> Result { 145 | let name = CString::new(name)?; 146 | let mut value = 0.0; 147 | check_status(unsafe { 148 | uhd_sys::uhd_usrp_get_rx_gain(self.0, channel as _, name.as_ptr(), &mut value) 149 | })?; 150 | Ok(value) 151 | } 152 | /// Returns the names of controllable gain elements 153 | pub fn get_rx_gain_names(&self, channel: usize) -> Result, Error> { 154 | let mut names = StringVector::new()?; 155 | check_status(unsafe { 156 | uhd_sys::uhd_usrp_get_rx_gain_names(self.0, channel as _, names.handle_mut()) 157 | })?; 158 | Ok(names.into()) 159 | } 160 | 161 | /// Returns the range(s) of gains for a gain element 162 | pub fn get_rx_gain_range(&self, channel: usize, name: &str) -> Result { 163 | let name = CString::new(name)?; 164 | let mut range = MetaRange::default(); 165 | check_status(unsafe { 166 | uhd_sys::uhd_usrp_get_rx_gain_range(self.0, name.as_ptr(), channel as _, range.handle()) 167 | })?; 168 | Ok(range) 169 | } 170 | 171 | /// Returns the current gain of the gain element with the specified name 172 | pub fn get_tx_gain(&self, channel: usize, name: &str) -> Result { 173 | let name = CString::new(name)?; 174 | let mut value = 0.0; 175 | check_status(unsafe { 176 | uhd_sys::uhd_usrp_get_tx_gain(self.0, channel as _, name.as_ptr(), &mut value) 177 | })?; 178 | Ok(value) 179 | } 180 | /// Returns the names of controllable gain elements 181 | pub fn get_tx_gain_names(&self, channel: usize) -> Result, Error> { 182 | let mut names = StringVector::new()?; 183 | check_status(unsafe { 184 | uhd_sys::uhd_usrp_get_tx_gain_names(self.0, channel as _, names.handle_mut()) 185 | })?; 186 | Ok(names.into()) 187 | } 188 | 189 | /// Returns the range(s) of gains for a gain element 190 | pub fn get_tx_gain_range(&self, channel: usize, name: &str) -> Result { 191 | let name = CString::new(name)?; 192 | let mut range = MetaRange::default(); 193 | check_status(unsafe { 194 | uhd_sys::uhd_usrp_get_tx_gain_range(self.0, name.as_ptr(), channel as _, range.handle()) 195 | })?; 196 | Ok(range) 197 | } 198 | 199 | /// Clears the command time (?), causing stream commands to be sent immediately 200 | pub fn clear_command_time(&mut self, mboard: usize) -> Result<(), Error> { 201 | check_status(unsafe { uhd_sys::uhd_usrp_clear_command_time(self.0, mboard as _) }) 202 | } 203 | 204 | /// Gets the ranges of front-end frequencies for a receive channel 205 | pub fn get_fe_rx_freq_range(&self, channel: usize) -> Result { 206 | let mut range = MetaRange::default(); 207 | check_status(unsafe { 208 | uhd_sys::uhd_usrp_get_fe_rx_freq_range(self.0, channel as _, range.handle()) 209 | })?; 210 | Ok(range) 211 | } 212 | 213 | /// Gets the ranges of front-end frequencies for a transmit channel 214 | pub fn get_fe_tx_freq_range(&self, channel: usize) -> Result { 215 | let mut range = MetaRange::default(); 216 | check_status(unsafe { 217 | uhd_sys::uhd_usrp_get_fe_tx_freq_range(self.0, channel as _, range.handle()) 218 | })?; 219 | Ok(range) 220 | } 221 | 222 | /// Returns the frequency of the master clock 223 | pub fn get_master_clock_rate(&self, mboard: usize) -> Result { 224 | let mut rate = 0.0; 225 | check_status(unsafe { 226 | uhd_sys::uhd_usrp_get_master_clock_rate(self.0, mboard as _, &mut rate) 227 | })?; 228 | Ok(rate) 229 | } 230 | 231 | /// Returns the name of the motherboard 232 | pub fn get_motherboard_name(&self, mboard: usize) -> Result { 233 | copy_string(|buffer, length| unsafe { 234 | uhd_sys::uhd_usrp_get_mboard_name(self.0, mboard as _, buffer, length as _) 235 | }) 236 | } 237 | 238 | /// Returns the transmit gain, normalized to [0, 1] 239 | pub fn get_normalized_tx_gain(&self, channel: usize) -> Result { 240 | let mut value = 0.0; 241 | check_status(unsafe { 242 | uhd_sys::uhd_usrp_get_normalized_tx_gain(self.0, channel as _, &mut value) 243 | })?; 244 | Ok(value) 245 | } 246 | 247 | /// Returns the receive gain, normalized to [0, 1] 248 | pub fn get_normalized_rx_gain(&self, channel: usize) -> Result { 249 | let mut value = 0.0; 250 | check_status(unsafe { 251 | uhd_sys::uhd_usrp_get_normalized_rx_gain(self.0, channel as _, &mut value) 252 | })?; 253 | Ok(value) 254 | } 255 | 256 | /// Returns the number of motherboards that this Usrp object provides access to 257 | pub fn get_num_motherboards(&self) -> Result { 258 | let mut value = 0usize; 259 | check_status(unsafe { 260 | uhd_sys::uhd_usrp_get_num_mboards(self.0, &mut value as *mut usize as *mut _) 261 | })?; 262 | Ok(value) 263 | } 264 | 265 | /// Returns the number of transmit channels 266 | pub fn get_num_tx_channels(&self) -> Result { 267 | let mut value = 0usize; 268 | check_status(unsafe { 269 | uhd_sys::uhd_usrp_get_tx_num_channels(self.0, &mut value as *mut usize as *mut _) 270 | })?; 271 | Ok(value) 272 | } 273 | 274 | /// Returns the number of receive channels 275 | pub fn get_num_rx_channels(&self) -> Result { 276 | let mut value = 0usize; 277 | check_status(unsafe { 278 | uhd_sys::uhd_usrp_get_rx_num_channels(self.0, &mut value as *mut usize as *mut _) 279 | })?; 280 | Ok(value) 281 | } 282 | 283 | /// Writes a user register on the USRP 284 | /// 285 | /// address: The address of the register 286 | /// value: The value to write 287 | /// mboard: The index of the board to write to (normally 0 when there is only one USRP) 288 | pub fn set_user_register( 289 | &mut self, 290 | address: u8, 291 | value: u32, 292 | mboard: usize, 293 | ) -> Result<(), Error> { 294 | check_status(unsafe { 295 | uhd_sys::uhd_usrp_set_user_register(self.0, address, value, mboard as _) 296 | }) 297 | } 298 | 299 | /// Returns the current clock source 300 | pub fn get_clock_source(&self, mboard: usize) -> Result { 301 | copy_string(|buffer, length| unsafe { 302 | uhd_sys::uhd_usrp_get_clock_source(self.0, mboard as _, buffer, length as _) 303 | }) 304 | } 305 | /// Returns the available clock sources 306 | pub fn get_clock_sources(&self, mboard: usize) -> Result, Error> { 307 | let mut vector = StringVector::new()?; 308 | check_status(unsafe { 309 | uhd_sys::uhd_usrp_get_clock_sources(self.0, mboard as _, vector.handle_mut()) 310 | })?; 311 | Ok(vector.into()) 312 | } 313 | 314 | /// Returns the current time source 315 | pub fn get_time_source(&self, mboard: usize) -> Result { 316 | copy_string(|buffer, length| unsafe { 317 | uhd_sys::uhd_usrp_get_time_source(self.0, mboard as _, buffer, length as _) 318 | }) 319 | } 320 | 321 | /// Returns the available time sources 322 | pub fn get_time_sources(&self, mboard: usize) -> Result, Error> { 323 | let mut vector = StringVector::new()?; 324 | check_status(unsafe { 325 | uhd_sys::uhd_usrp_get_time_sources(self.0, mboard as _, vector.handle_mut()) 326 | })?; 327 | Ok(vector.into()) 328 | } 329 | 330 | /// Returns the available sensors on the motherboard 331 | pub fn get_mboard_sensor_names(&self, mboard: usize) -> Result, Error> { 332 | let mut vector = StringVector::new()?; 333 | check_status(unsafe { 334 | uhd_sys::uhd_usrp_get_mboard_sensor_names(self.0, mboard as _, vector.handle_mut()) 335 | })?; 336 | Ok(vector.into()) 337 | } 338 | 339 | /// Returns the values stored in the motherboard EEPROM 340 | pub fn get_motherboard_eeprom(&self, mboard: usize) -> Result { 341 | let mut eeprom = MotherboardEeprom::default(); 342 | check_status(unsafe { 343 | uhd_sys::uhd_usrp_get_mboard_eeprom(self.0, eeprom.handle(), mboard as _) 344 | })?; 345 | Ok(eeprom) 346 | } 347 | 348 | /// Returns the values stored in a daughter board EEPROM 349 | /// 350 | /// Values for unit and slot can be determined by running uhd_usrp_probe --tree. 351 | /// As an example, the entry `/mboards/0/dboards/A/rx_eeprom` corresponds to unit `rx` and 352 | /// slot `A` of mboard 1. 353 | pub fn get_daughter_board_eeprom( 354 | &self, 355 | unit: &str, 356 | slot: &str, 357 | mboard: usize, 358 | ) -> Result { 359 | let unit = CString::new(unit)?; 360 | let slot = CString::new(slot)?; 361 | 362 | let mut eeprom = DaughterBoardEeprom::default(); 363 | 364 | check_status(unsafe { 365 | uhd_sys::uhd_usrp_get_dboard_eeprom( 366 | self.0, 367 | eeprom.handle(), 368 | unit.as_ptr(), 369 | slot.as_ptr(), 370 | mboard as _, 371 | ) 372 | })?; 373 | 374 | Ok(eeprom) 375 | } 376 | 377 | /// Gets information about the receive configuration of a channel 378 | pub fn get_rx_info(&self, channel: usize) -> Result { 379 | let mut info_c = uhd_sys::uhd_usrp_rx_info_t { 380 | mboard_id: ptr::null_mut(), 381 | mboard_name: ptr::null_mut(), 382 | mboard_serial: ptr::null_mut(), 383 | rx_id: ptr::null_mut(), 384 | rx_subdev_name: ptr::null_mut(), 385 | rx_subdev_spec: ptr::null_mut(), 386 | rx_serial: ptr::null_mut(), 387 | rx_antenna: ptr::null_mut(), 388 | }; 389 | unsafe { 390 | check_status(uhd_sys::uhd_usrp_get_rx_info( 391 | self.0, 392 | channel as _, 393 | &mut info_c, 394 | ))?; 395 | let info = ReceiveInfo::from_c(&info_c)?; 396 | uhd_sys::uhd_usrp_rx_info_free(&mut info_c); 397 | Ok(info) 398 | } 399 | } 400 | 401 | /// Gets information about the transmit configuration of a channel 402 | pub fn get_tx_info(&self, channel: usize) -> Result { 403 | let mut info_c = uhd_sys::uhd_usrp_tx_info_t { 404 | mboard_id: ptr::null_mut(), 405 | mboard_name: ptr::null_mut(), 406 | mboard_serial: ptr::null_mut(), 407 | tx_id: ptr::null_mut(), 408 | tx_subdev_name: ptr::null_mut(), 409 | tx_subdev_spec: ptr::null_mut(), 410 | tx_serial: ptr::null_mut(), 411 | tx_antenna: ptr::null_mut(), 412 | }; 413 | unsafe { 414 | check_status(uhd_sys::uhd_usrp_get_tx_info( 415 | self.0, 416 | channel as _, 417 | &mut info_c, 418 | ))?; 419 | let info = TransmitInfo::from_c(&info_c)?; 420 | uhd_sys::uhd_usrp_tx_info_free(&mut info_c); 421 | Ok(info) 422 | } 423 | } 424 | 425 | /// Returns true if the provided local oscillator is exported 426 | pub fn get_rx_lo_export_enabled(&self, channel: usize, name: &str) -> Result { 427 | let name = CString::new(name)?; 428 | let mut enabled = false; 429 | check_status(unsafe { 430 | uhd_sys::uhd_usrp_get_rx_lo_export_enabled( 431 | self.0, 432 | name.as_ptr(), 433 | channel as _, 434 | &mut enabled, 435 | ) 436 | })?; 437 | Ok(enabled) 438 | } 439 | 440 | /// Returns true if the provided local oscillator is exported 441 | pub fn get_tx_lo_export_enabled(&self, channel: usize, name: &str) -> Result { 442 | let name = CString::new(name)?; 443 | let mut enabled = false; 444 | check_status(unsafe { 445 | uhd_sys::uhd_usrp_get_tx_lo_export_enabled( 446 | self.0, 447 | name.as_ptr(), 448 | channel as _, 449 | &mut enabled, 450 | ) 451 | })?; 452 | Ok(enabled) 453 | } 454 | 455 | /// Returns the frequency of a local oscillator 456 | pub fn get_rx_lo_frequency(&self, channel: usize, name: &str) -> Result { 457 | let name = CString::new(name)?; 458 | let mut value = 0.0; 459 | check_status(unsafe { 460 | uhd_sys::uhd_usrp_get_rx_lo_freq(self.0, name.as_ptr(), channel as _, &mut value) 461 | })?; 462 | Ok(value) 463 | } 464 | 465 | /// Returns the names of local oscillators 466 | pub fn get_rx_lo_names(&self, channel: usize) -> Result, Error> { 467 | let mut vector = StringVector::new()?; 468 | check_status(unsafe { 469 | uhd_sys::uhd_usrp_get_rx_lo_names(self.0, channel as _, vector.handle_mut()) 470 | })?; 471 | Ok(vector.into()) 472 | } 473 | 474 | /// Returns the names of sensors that relate to receiving 475 | pub fn get_rx_sensor_names(&self, channel: usize) -> Result, Error> { 476 | let mut vector = StringVector::new()?; 477 | check_status(unsafe { 478 | uhd_sys::uhd_usrp_get_rx_sensor_names(self.0, channel as _, vector.handle_mut()) 479 | })?; 480 | Ok(vector.into()) 481 | } 482 | 483 | /// Returns the frequency of a local oscillator 484 | pub fn get_tx_lo_frequency(&self, channel: usize, name: &str) -> Result { 485 | let name = CString::new(name)?; 486 | let mut value = 0.0; 487 | check_status(unsafe { 488 | uhd_sys::uhd_usrp_get_tx_lo_freq(self.0, name.as_ptr(), channel as _, &mut value) 489 | })?; 490 | Ok(value) 491 | } 492 | 493 | /// Returns the names of local oscillators 494 | pub fn get_tx_lo_names(&self, channel: usize) -> Result, Error> { 495 | let mut vector = StringVector::new()?; 496 | check_status(unsafe { 497 | uhd_sys::uhd_usrp_get_tx_lo_names(self.0, channel as _, vector.handle_mut()) 498 | })?; 499 | Ok(vector.into()) 500 | } 501 | 502 | /// Returns the names of sensors that relate to transmitting 503 | pub fn get_tx_sensor_names(&self, channel: usize) -> Result, Error> { 504 | let mut vector = StringVector::new()?; 505 | check_status(unsafe { 506 | uhd_sys::uhd_usrp_get_tx_sensor_names(self.0, channel as _, vector.handle_mut()) 507 | })?; 508 | Ok(vector.into()) 509 | } 510 | 511 | /// Opens a stream that can be used to receive samples 512 | pub fn get_rx_stream( 513 | &mut self, 514 | args: &StreamArgs, 515 | ) -> Result, Error> 516 | where 517 | I: Item, 518 | { 519 | // Convert arguments 520 | let args: StreamArgsC = args.try_into()?; 521 | // Convert some *T pointers to *mut T pointers. The C API doesn't mark them const, but 522 | // appears to not write to them. 523 | let mut args_c = uhd_sys::uhd_stream_args_t { 524 | cpu_format: args.host_format.as_ptr() as *mut _, 525 | otw_format: args.wire_format.as_ptr() as *mut _, 526 | args: args.args.as_ptr() as *mut _, 527 | channel_list: args.channels.as_ptr() as *mut _, 528 | n_channels: args 529 | .channels 530 | .len() 531 | .try_into() 532 | .expect("Number of channels too large"), 533 | }; 534 | 535 | // Create a streamer 536 | let mut streamer = ReceiveStreamer::new(); 537 | check_status(unsafe { uhd_sys::uhd_rx_streamer_make(streamer.handle_mut()) })?; 538 | // Associate streamer with USRP 539 | check_status(unsafe { 540 | uhd_sys::uhd_usrp_get_rx_stream(self.0, &mut args_c, streamer.handle()) 541 | })?; 542 | 543 | Ok(streamer) 544 | } 545 | 546 | /// Opens a stream that can be used to transmit samples 547 | pub fn get_tx_stream( 548 | &mut self, 549 | args: &StreamArgs, 550 | ) -> Result, Error> 551 | where 552 | I: Item, 553 | { 554 | // Convert arguments 555 | let args: StreamArgsC = args.try_into()?; 556 | // Convert some *T pointers to *mut T pointers. The C API doesn't mark them const, but 557 | // appears to not write to them. 558 | let mut args_c = uhd_sys::uhd_stream_args_t { 559 | cpu_format: args.host_format.as_ptr() as *mut _, 560 | otw_format: args.wire_format.as_ptr() as *mut _, 561 | args: args.args.as_ptr() as *mut _, 562 | channel_list: args.channels.as_ptr() as *mut _, 563 | n_channels: args 564 | .channels 565 | .len() 566 | .try_into() 567 | .expect("Number of channels too large"), 568 | }; 569 | 570 | // Create a streamer 571 | let mut streamer = TransmitStreamer::new(); 572 | check_status(unsafe { uhd_sys::uhd_tx_streamer_make(streamer.handle_mut()) })?; 573 | // Associate streamer with USRP 574 | check_status(unsafe { 575 | uhd_sys::uhd_usrp_get_tx_stream(self.0, &mut args_c, streamer.handle()) 576 | })?; 577 | 578 | Ok(streamer) 579 | } 580 | 581 | /// Returns the current receive sample rate in samples/second 582 | pub fn get_rx_sample_rate(&self, channel: usize) -> Result { 583 | let mut value = 0.0; 584 | check_status(unsafe { uhd_sys::uhd_usrp_get_rx_rate(self.0, channel as _, &mut value) })?; 585 | Ok(value) 586 | } 587 | 588 | /// Returns the ranges of supported sample rates 589 | pub fn get_rx_sample_rates(&self, channel: usize) -> Result { 590 | let mut range = MetaRange::new(); 591 | check_status(unsafe { 592 | uhd_sys::uhd_usrp_get_rx_rates(self.0, channel as _, range.handle()) 593 | })?; 594 | Ok(range) 595 | } 596 | 597 | /// Returns the current transmit sample rate in samples/second 598 | pub fn get_tx_sample_rate(&self, channel: usize) -> Result { 599 | let mut value = 0.0; 600 | check_status(unsafe { uhd_sys::uhd_usrp_get_tx_rate(self.0, channel as _, &mut value) })?; 601 | Ok(value) 602 | } 603 | 604 | /// Returns the ranges of supported sample rates 605 | pub fn get_tx_sample_rates(&self, channel: usize) -> Result { 606 | let mut range = MetaRange::new(); 607 | check_status(unsafe { 608 | uhd_sys::uhd_usrp_get_tx_rates(self.0, channel as _, range.handle()) 609 | })?; 610 | Ok(range) 611 | } 612 | 613 | /// Returns the USRP's current time. Commands can be scheduled relative to this time. 614 | pub fn get_current_time(&self, mboard: usize) -> Result { 615 | let mut time = TimeSpec::default(); 616 | let mut seconds_time_t: libc::time_t = Default::default(); 617 | 618 | check_status(unsafe { 619 | uhd_sys::uhd_usrp_get_time_now( 620 | self.0, 621 | mboard as _, 622 | &mut seconds_time_t, 623 | &mut time.fraction, 624 | ) 625 | })?; 626 | time.seconds = seconds_time_t.into(); 627 | Ok(time) 628 | } 629 | 630 | /// Sets the current clock source 631 | pub fn set_clock_source(&self, source: &str, mboard: usize) -> Result<(), Error> { 632 | let source = CString::new(source)?; 633 | check_status(unsafe { 634 | uhd_sys::uhd_usrp_set_clock_source(self.0, source.as_ptr(), mboard as _) 635 | }) 636 | } 637 | 638 | /// Sets the current time source 639 | pub fn set_time_source(&self, source: &str, mboard: usize) -> Result<(), Error> { 640 | let source = CString::new(source)?; 641 | check_status(unsafe { 642 | uhd_sys::uhd_usrp_set_time_source(self.0, source.as_ptr(), mboard as _) 643 | }) 644 | } 645 | 646 | /// Synchronize the times across all motherboards in this configuration. 647 | pub fn set_time_unknown_pps(&self, full_secs: i64, frac_secs: f64) -> Result<(), Error> { 648 | check_status(unsafe { 649 | uhd_sys::uhd_usrp_set_time_unknown_pps(self.0, full_secs, frac_secs) 650 | })?; 651 | 652 | Ok(()) 653 | } 654 | 655 | /// Set the time registers on the USRP at the next PPS rising edge. 656 | pub fn set_time_next_pps( 657 | &self, 658 | full_secs: i64, 659 | frac_secs: f64, 660 | mboard: usize, 661 | ) -> Result<(), Error> { 662 | check_status(unsafe { 663 | uhd_sys::uhd_usrp_set_time_next_pps(self.0, full_secs, frac_secs, mboard as _) 664 | })?; 665 | 666 | Ok(()) 667 | } 668 | 669 | /// Checks whether the times across all motherboards in this configuration are synchronized 670 | pub fn get_time_synchronized(&self) -> Result { 671 | let mut result = false; 672 | check_status(unsafe { 673 | uhd_sys::uhd_usrp_get_time_synchronized(self.0, &mut result as *mut _) 674 | })?; 675 | 676 | Ok(result) 677 | } 678 | 679 | /// Enables or disables the receive automatic gain control 680 | pub fn set_rx_agc_enabled(&mut self, enabled: bool, channel: usize) -> Result<(), Error> { 681 | check_status(unsafe { uhd_sys::uhd_usrp_set_rx_agc(self.0, enabled, channel as _) }) 682 | } 683 | 684 | /// Sets the antenna used to receive 685 | pub fn set_rx_antenna(&mut self, antenna: &str, channel: usize) -> Result<(), Error> { 686 | let antenna = CString::new(antenna)?; 687 | check_status(unsafe { 688 | uhd_sys::uhd_usrp_set_rx_antenna(self.0, antenna.as_ptr(), channel as _) 689 | }) 690 | } 691 | 692 | /// Sets the receive bandwidth 693 | pub fn set_rx_bandwidth(&mut self, bandwidth: f64, channel: usize) -> Result<(), Error> { 694 | check_status(unsafe { uhd_sys::uhd_usrp_set_rx_bandwidth(self.0, bandwidth, channel as _) }) 695 | } 696 | 697 | /// Enables or disables DC offset correction 698 | pub fn set_rx_dc_offset_enabled(&mut self, enabled: bool, channel: usize) -> Result<(), Error> { 699 | check_status(unsafe { 700 | uhd_sys::uhd_usrp_set_rx_dc_offset_enabled(self.0, enabled, channel as _) 701 | }) 702 | } 703 | 704 | /// Sets the receive center frequency 705 | pub fn set_rx_frequency( 706 | &mut self, 707 | request: &TuneRequest, 708 | channel: usize, 709 | ) -> Result { 710 | let args = CString::new(&*request.args)?; 711 | let mut request_c = uhd_sys::uhd_tune_request_t { 712 | target_freq: request.target_frequency, 713 | rf_freq_policy: request.rf.c_policy(), 714 | rf_freq: request.rf.frequency(), 715 | dsp_freq_policy: request.dsp.c_policy(), 716 | dsp_freq: request.dsp.frequency(), 717 | // Unsafe cast *const c_char to *mut c_char 718 | // The C++ code probably won't modify this. 719 | args: args.as_ptr() as *mut _, 720 | }; 721 | 722 | let mut result = TuneResult::default(); 723 | check_status(unsafe { 724 | uhd_sys::uhd_usrp_set_rx_freq(self.0, &mut request_c, channel as _, result.inner_mut()) 725 | })?; 726 | 727 | Ok(result) 728 | } 729 | 730 | /// Sets the receive gain 731 | pub fn set_rx_gain(&mut self, gain: f64, channel: usize, name: &str) -> Result<(), Error> { 732 | let name = CString::new(name)?; 733 | check_status(unsafe { 734 | uhd_sys::uhd_usrp_set_rx_gain(self.0, gain, channel as _, name.as_ptr()) 735 | }) 736 | } 737 | 738 | /// Sets the receive sample rate 739 | pub fn set_rx_sample_rate(&mut self, rate: f64, channel: usize) -> Result<(), Error> { 740 | check_status(unsafe { uhd_sys::uhd_usrp_set_rx_rate(self.0, rate, channel as _) }) 741 | } 742 | 743 | /// Sets the antenna used to transmit 744 | pub fn set_tx_antenna(&mut self, antenna: &str, channel: usize) -> Result<(), Error> { 745 | let antenna = CString::new(antenna)?; 746 | check_status(unsafe { 747 | uhd_sys::uhd_usrp_set_tx_antenna(self.0, antenna.as_ptr(), channel as _) 748 | }) 749 | } 750 | 751 | /// Sets the transmit bandwidth 752 | pub fn set_tx_bandwidth(&mut self, bandwidth: f64, channel: usize) -> Result<(), Error> { 753 | check_status(unsafe { uhd_sys::uhd_usrp_set_tx_bandwidth(self.0, bandwidth, channel as _) }) 754 | } 755 | 756 | /// Sets the transmit center frequency 757 | pub fn set_tx_frequency( 758 | &mut self, 759 | request: &TuneRequest, 760 | channel: usize, 761 | ) -> Result { 762 | let args = CString::new(&*request.args)?; 763 | let mut request_c = uhd_sys::uhd_tune_request_t { 764 | target_freq: request.target_frequency, 765 | rf_freq_policy: request.rf.c_policy(), 766 | rf_freq: request.rf.frequency(), 767 | dsp_freq_policy: request.dsp.c_policy(), 768 | dsp_freq: request.dsp.frequency(), 769 | // Unsafe cast *const c_char to *mut c_char 770 | // The C++ code probably won't modify this. 771 | args: args.as_ptr() as *mut _, 772 | }; 773 | 774 | let mut result = TuneResult::default(); 775 | check_status(unsafe { 776 | uhd_sys::uhd_usrp_set_tx_freq(self.0, &mut request_c, channel as _, result.inner_mut()) 777 | })?; 778 | 779 | Ok(result) 780 | } 781 | 782 | /// Sets the transmit gain 783 | pub fn set_tx_gain(&mut self, gain: f64, channel: usize, name: &str) -> Result<(), Error> { 784 | let name = CString::new(name)?; 785 | check_status(unsafe { 786 | uhd_sys::uhd_usrp_set_tx_gain(self.0, gain, channel as _, name.as_ptr()) 787 | }) 788 | } 789 | 790 | /// Sets the transmit sample rate 791 | pub fn set_tx_sample_rate(&mut self, rate: f64, channel: usize) -> Result<(), Error> { 792 | check_status(unsafe { uhd_sys::uhd_usrp_set_tx_rate(self.0, rate, channel as _) }) 793 | } 794 | 795 | /// Returns the available GPIO banks 796 | pub fn get_gpio_banks(&self, mboard: usize) -> Result, Error> { 797 | let mut banks = StringVector::new()?; 798 | check_status(unsafe { 799 | uhd_sys::uhd_usrp_get_gpio_banks(self.0, mboard as _, banks.handle_mut()) 800 | })?; 801 | Ok(banks.into()) 802 | } 803 | } 804 | 805 | impl Drop for Usrp { 806 | fn drop(&mut self) { 807 | // Ignore error (what errors could really happen that can be handled?) 808 | let _ = unsafe { uhd_sys::uhd_usrp_free(&mut self.0) }; 809 | } 810 | } 811 | 812 | // Thread safety: see https://files.ettus.com/manual/page_general.html#general_threading 813 | // All functions associated with the Usrp struct are thread-safe 814 | unsafe impl Send for Usrp {} 815 | unsafe impl Sync for Usrp {} 816 | --------------------------------------------------------------------------------