pub use soapysdr_sys::SoapySDRRange as Range;
use soapysdr_sys::*;
use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::os::raw::c_void;
use std::os::raw::{c_char, c_int};
use std::slice;
use std::sync::Arc;
use super::{ArgInfo, Args, Format, StreamSample};
use crate::arginfo::arg_info_from_c;
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
#[non_exhaustive]
pub enum ErrorCode {
Timeout = -1,
StreamError = -2,
Corruption = -3,
Overflow = -4,
NotSupported = -5,
TimeError = -6,
Underflow = -7,
Other = 0,
}
impl ErrorCode {
fn from_c(code: c_int) -> ErrorCode {
match code {
soapysdr_sys::SOAPY_SDR_TIMEOUT => ErrorCode::Timeout,
soapysdr_sys::SOAPY_SDR_STREAM_ERROR => ErrorCode::StreamError,
soapysdr_sys::SOAPY_SDR_CORRUPTION => ErrorCode::Corruption,
soapysdr_sys::SOAPY_SDR_OVERFLOW => ErrorCode::Overflow,
soapysdr_sys::SOAPY_SDR_NOT_SUPPORTED => ErrorCode::NotSupported,
soapysdr_sys::SOAPY_SDR_TIME_ERROR => ErrorCode::TimeError,
soapysdr_sys::SOAPY_SDR_UNDERFLOW => ErrorCode::Underflow,
_ => ErrorCode::Other,
}
}
}
#[derive(Clone, Debug, Hash)]
pub struct Error {
pub code: ErrorCode,
pub message: String,
}
impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "{:?}: {}", self.code, self.message)
}
}
impl ::std::error::Error for Error {
fn description(&self) -> &str {
&self.message[..]
}
}
#[repr(u32)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub enum Direction {
Tx = SOAPY_SDR_TX,
Rx = SOAPY_SDR_RX,
}
impl From<Direction> for c_int {
fn from(f: Direction) -> c_int {
f as c_int
}
}
struct DeviceInner {
ptr: *mut SoapySDRDevice,
}
unsafe impl Send for DeviceInner {}
unsafe impl Sync for DeviceInner {}
impl Drop for DeviceInner {
fn drop(&mut self) {
unsafe {
SoapySDRDevice_unmake(self.ptr);
}
}
}
#[derive(Clone)]
pub struct Device {
inner: Arc<DeviceInner>,
}
impl Device {
fn from_ptr(ptr: *mut SoapySDRDevice) -> Device {
Device {
inner: Arc::new(DeviceInner { ptr }),
}
}
}
fn last_error_str() -> String {
unsafe {
CStr::from_ptr(SoapySDRDevice_lastError())
.to_string_lossy()
.into()
}
}
fn check_error<T>(r: T) -> Result<T, Error> {
unsafe {
if SoapySDRDevice_lastStatus() == 0 {
Ok(r)
} else {
Err(Error {
code: ErrorCode::Other,
message: last_error_str(),
})
}
}
}
fn check_ret_error(r: c_int) -> Result<(), Error> {
if r == 0 {
Ok(())
} else {
Err(Error {
code: ErrorCode::from_c(r),
message: last_error_str(),
})
}
}
fn len_result(ret: c_int) -> Result<c_int, Error> {
if ret >= 0 {
Ok(ret)
} else {
Err(Error {
code: ErrorCode::from_c(ret),
message: last_error_str(),
})
}
}
unsafe fn string_result(r: *mut c_char) -> Result<String, Error> {
unsafe {
let ptr: *mut c_char = check_error(r)?;
let ret = CStr::from_ptr(ptr).to_string_lossy().into();
SoapySDR_free(ptr as *mut c_void);
Ok(ret)
}
}
unsafe fn string_list_result<F: FnOnce(*mut usize) -> *mut *mut c_char>(
f: F,
) -> Result<Vec<String>, Error> {
unsafe {
let mut len: usize = 0;
let mut ptr = check_error(f(&mut len as *mut _))?;
let ret = slice::from_raw_parts(ptr, len)
.iter()
.map(|&p| CStr::from_ptr(p).to_string_lossy().into())
.collect();
SoapySDRStrings_clear(&mut ptr as *mut _, len);
Ok(ret)
}
}
unsafe fn arg_info_result<F: FnOnce(*mut usize) -> *mut SoapySDRArgInfo>(
f: F,
) -> Result<Vec<ArgInfo>, Error> {
unsafe {
let mut len: usize = 0;
let ptr = check_error(f(&mut len as *mut _))?;
let r = slice::from_raw_parts(ptr, len)
.iter()
.map(|x| arg_info_from_c(x))
.collect();
SoapySDRArgInfoList_clear(ptr, len);
Ok(r)
}
}
unsafe fn list_result<T: Copy, F: FnOnce(*mut usize) -> *mut T>(f: F) -> Result<Vec<T>, Error> {
unsafe {
let mut len: usize = 0;
let ptr = check_error(f(&mut len as *mut _))?;
let ret = slice::from_raw_parts(ptr, len).to_owned();
SoapySDR_free(ptr as *mut c_void);
Ok(ret)
}
}
fn optional_string_arg<S: AsRef<str>>(optstr: Option<S>) -> CString {
match optstr {
Some(s) => CString::new(s.as_ref()).expect("Optional arg string contains null"),
None => CString::new("").unwrap(),
}
}
pub fn enumerate<A: Into<Args>>(args: A) -> Result<Vec<Args>, Error> {
unsafe {
let mut len: usize = 0;
let devs = check_error(SoapySDRDevice_enumerate(
args.into().as_raw_const(),
&mut len as *mut _,
))?;
let args = slice::from_raw_parts(devs, len)
.iter()
.map(|&arg| Args::from_raw(arg))
.collect();
SoapySDR_free(devs as *mut c_void);
Ok(args)
}
}
impl Device {
pub fn new<A: Into<Args>>(args: A) -> Result<Device, Error> {
unsafe {
let d = check_error(SoapySDRDevice_make(args.into().as_raw_const()))?;
Ok(Device::from_ptr(d))
}
}
#[doc(hidden)]
pub fn null_device() -> Device {
Device::new("type=null").unwrap()
}
pub fn driver_key(&self) -> Result<String, Error> {
unsafe { string_result(SoapySDRDevice_getDriverKey(self.inner.ptr)) }
}
pub fn hardware_key(&self) -> Result<String, Error> {
unsafe { string_result(SoapySDRDevice_getHardwareKey(self.inner.ptr)) }
}
pub fn hardware_info(&self) -> Result<Args, Error> {
unsafe {
check_error(SoapySDRDevice_getHardwareInfo(self.inner.ptr)).map(|x| Args::from_raw(x))
}
}
pub fn frontend_mapping(&self, direction: Direction) -> Result<String, Error> {
unsafe {
string_result(SoapySDRDevice_getFrontendMapping(
self.inner.ptr,
direction.into(),
))
}
}
pub fn list_sensors(&self) -> Result<Vec<String>, Error> {
unsafe { string_list_result(|len_ptr| SoapySDRDevice_listSensors(self.inner.ptr, len_ptr)) }
}
pub fn read_sensor(&self, key: &str) -> Result<String, Error> {
let key_c = CString::new(key).expect("key contains null byte");
unsafe { string_result(SoapySDRDevice_readSensor(self.inner.ptr, key_c.as_ptr())) }
}
pub fn get_channel_sensor_info(
&self,
dir: Direction,
channel: usize,
key: &str,
) -> Result<ArgInfo, Error> {
let key_c = CString::new(key).expect("key contains null byte");
Ok(unsafe {
arg_info_from_c(&SoapySDRDevice_getChannelSensorInfo(
self.inner.ptr,
dir.into(),
channel,
key_c.as_ptr(),
))
})
}
pub fn list_channel_sensors(
&self,
dir: Direction,
channel: usize,
) -> Result<Vec<String>, Error> {
unsafe {
string_list_result(|len_ptr| {
SoapySDRDevice_listChannelSensors(self.inner.ptr, dir.into(), channel, len_ptr)
})
}
}
pub fn read_channel_sensor(
&self,
dir: Direction,
channel: usize,
key: &str,
) -> Result<String, Error> {
let key_c = CString::new(key).expect("key contains null byte");
unsafe {
string_result(SoapySDRDevice_readChannelSensor(
self.inner.ptr,
dir.into(),
channel,
key_c.as_ptr(),
))
}
}
pub fn get_sensor_info(&self, key: &str) -> Result<ArgInfo, Error> {
let key_c = CString::new(key).expect("key contains null byte");
Ok(unsafe {
arg_info_from_c(&SoapySDRDevice_getSensorInfo(
self.inner.ptr,
key_c.as_ptr(),
))
})
}
pub fn set_frontend_mapping<S: Into<Vec<u8>>>(
&self,
direction: Direction,
mapping: S,
) -> Result<(), Error> {
unsafe {
let mapping_c = CString::new(mapping).expect("Mapping contains null byte");
SoapySDRDevice_setFrontendMapping(self.inner.ptr, direction.into(), mapping_c.as_ptr());
check_error(())
}
}
pub fn num_channels(&self, direction: Direction) -> Result<usize, Error> {
unsafe {
check_error(SoapySDRDevice_getNumChannels(
self.inner.ptr,
direction.into(),
))
}
}
pub fn channel_info(&self, direction: Direction, channel: usize) -> Result<Args, Error> {
unsafe {
check_error(SoapySDRDevice_getChannelInfo(
self.inner.ptr,
direction.into(),
channel,
))
.map(|x| Args::from_raw(x))
}
}
pub fn full_duplex(&self, direction: Direction, channel: usize) -> Result<bool, Error> {
unsafe {
check_error(SoapySDRDevice_getFullDuplex(
self.inner.ptr,
direction.into(),
channel,
))
}
}
pub fn stream_formats(
&self,
direction: Direction,
channel: usize,
) -> Result<Vec<Format>, Error> {
unsafe {
let mut len: usize = 0;
let mut ptr = check_error(SoapySDRDevice_getStreamFormats(
self.inner.ptr,
direction.into(),
channel,
&mut len as *mut _,
))?;
let ret = slice::from_raw_parts(ptr, len)
.iter()
.flat_map(|&p| CStr::from_ptr(p).to_str().ok())
.flat_map(|s| s.parse().ok())
.collect();
SoapySDRStrings_clear(&mut ptr as *mut _, len);
Ok(ret)
}
}
pub fn native_stream_format(
&self,
direction: Direction,
channel: usize,
) -> Result<(Format, f64), Error> {
unsafe {
let mut fullscale: f64 = 0.0;
let ptr = check_error(SoapySDRDevice_getNativeStreamFormat(
self.inner.ptr,
direction.into(),
channel,
&mut fullscale as *mut _,
))?;
let format = CStr::from_ptr(ptr)
.to_str()
.ok()
.and_then(|s| s.parse().ok())
.ok_or_else(|| Error {
code: ErrorCode::Other,
message: "Invalid stream format returned by SoapySDR".into(),
})?;
Ok((format, fullscale))
}
}
pub fn stream_args_info(
&self,
direction: Direction,
channel: usize,
) -> Result<Vec<ArgInfo>, Error> {
unsafe {
arg_info_result(|len_ptr| {
SoapySDRDevice_getStreamArgsInfo(self.inner.ptr, direction.into(), channel, len_ptr)
})
}
}
pub fn rx_stream<E: StreamSample>(&self, channels: &[usize]) -> Result<RxStream<E>, Error> {
self.rx_stream_args(channels, ())
}
pub fn rx_stream_args<E: StreamSample, A: Into<Args>>(
&self,
channels: &[usize],
args: A,
) -> Result<RxStream<E>, Error> {
unsafe {
let stream = check_error(SoapySDRDevice_setupStream(
self.inner.ptr,
Direction::Rx.into(),
E::STREAM_FORMAT.as_ptr(),
channels.as_ptr(),
channels.len(),
args.into().as_raw_const(),
))?;
Ok(RxStream {
device: self.clone(),
handle: stream,
nchannels: channels.len(),
flags: 0,
time_ns: 0,
active: false,
buf_ptrs: vec![std::ptr::null_mut(); channels.len()],
phantom: PhantomData,
})
}
}
pub fn tx_stream<E: StreamSample>(&self, channels: &[usize]) -> Result<TxStream<E>, Error> {
self.tx_stream_args(channels, ())
}
pub fn tx_stream_args<E: StreamSample, A: Into<Args>>(
&self,
channels: &[usize],
args: A,
) -> Result<TxStream<E>, Error> {
unsafe {
let stream = check_error(SoapySDRDevice_setupStream(
self.inner.ptr,
Direction::Tx.into(),
E::STREAM_FORMAT.as_ptr(),
channels.as_ptr(),
channels.len(),
args.into().as_raw_const(),
))?;
Ok(TxStream {
device: self.clone(),
handle: stream,
nchannels: channels.len(),
active: false,
buf_ptrs: vec![std::ptr::null_mut(); channels.len()],
phantom: PhantomData,
})
}
}
pub fn antennas(&self, direction: Direction, channel: usize) -> Result<Vec<String>, Error> {
unsafe {
string_list_result(|len_ptr| {
SoapySDRDevice_listAntennas(self.inner.ptr, direction.into(), channel, len_ptr)
})
}
}
pub fn set_antenna<S: Into<Vec<u8>>>(
&self,
direction: Direction,
channel: usize,
name: S,
) -> Result<(), Error> {
unsafe {
let name_c = CString::new(name).expect("Antenna name contains null byte");
SoapySDRDevice_setAntenna(self.inner.ptr, direction.into(), channel, name_c.as_ptr());
check_error(())
}
}
pub fn antenna(&self, direction: Direction, channel: usize) -> Result<String, Error> {
unsafe {
string_result(SoapySDRDevice_getAntenna(
self.inner.ptr,
direction.into(),
channel,
))
}
}
pub fn has_dc_offset_mode(&self, direction: Direction, channel: usize) -> Result<bool, Error> {
unsafe {
check_error(SoapySDRDevice_hasDCOffsetMode(
self.inner.ptr,
direction.into(),
channel,
))
}
}
pub fn set_dc_offset_mode(
&self,
direction: Direction,
channel: usize,
automatic: bool,
) -> Result<(), Error> {
unsafe {
SoapySDRDevice_setDCOffsetMode(self.inner.ptr, direction.into(), channel, automatic);
check_error(())
}
}
pub fn dc_offset_mode(&self, direction: Direction, channel: usize) -> Result<bool, Error> {
unsafe {
check_error(SoapySDRDevice_getDCOffsetMode(
self.inner.ptr,
direction.into(),
channel,
))
}
}
pub fn has_dc_offset(&self, direction: Direction, channel: usize) -> Result<bool, Error> {
unsafe {
check_error(SoapySDRDevice_hasDCOffset(
self.inner.ptr,
direction.into(),
channel,
))
}
}
pub fn set_dc_offset(
&self,
direction: Direction,
channel: usize,
offset_i: f64,
offset_q: f64,
) -> Result<(), Error> {
unsafe {
SoapySDRDevice_setDCOffset(
self.inner.ptr,
direction.into(),
channel,
offset_i,
offset_q,
);
check_error(())
}
}
pub fn dc_offset(&self, direction: Direction, channel: usize) -> Result<(f64, f64), Error> {
unsafe {
let mut i: f64 = 0.0;
let mut q: f64 = 0.0;
SoapySDRDevice_getDCOffset(
self.inner.ptr,
direction.into(),
channel,
&mut i as *mut _,
&mut q as *mut _,
);
check_error((i, q))
}
}
pub fn has_iq_balance(&self, direction: Direction, channel: usize) -> Result<bool, Error> {
unsafe {
check_error(SoapySDRDevice_hasIQBalance(
self.inner.ptr,
direction.into(),
channel,
))
}
}
pub fn set_iq_balance(
&self,
direction: Direction,
channel: usize,
balance_i: f64,
balance_q: f64,
) -> Result<(), Error> {
unsafe {
SoapySDRDevice_setIQBalance(
self.inner.ptr,
direction.into(),
channel,
balance_i,
balance_q,
);
check_error(())
}
}
pub fn iq_balance(&self, direction: Direction, channel: usize) -> Result<(f64, f64), Error> {
unsafe {
let mut i: f64 = 0.0;
let mut q: f64 = 0.0;
SoapySDRDevice_getIQBalance(
self.inner.ptr,
direction.into(),
channel,
&mut i as *mut _,
&mut q as *mut _,
);
check_error((i, q))
}
}
pub fn list_gains(&self, direction: Direction, channel: usize) -> Result<Vec<String>, Error> {
unsafe {
string_list_result(|len_ptr| {
SoapySDRDevice_listGains(self.inner.ptr, direction.into(), channel, len_ptr)
})
}
}
pub fn has_gain_mode(&self, direction: Direction, channel: usize) -> Result<bool, Error> {
unsafe {
check_error(SoapySDRDevice_hasGainMode(
self.inner.ptr,
direction.into(),
channel,
))
}
}
pub fn set_gain_mode(
&self,
direction: Direction,
channel: usize,
automatic: bool,
) -> Result<(), Error> {
unsafe {
SoapySDRDevice_setGainMode(self.inner.ptr, direction.into(), channel, automatic);
check_error(())
}
}
pub fn gain_mode(&self, direction: Direction, channel: usize) -> Result<bool, Error> {
unsafe {
check_error(SoapySDRDevice_getGainMode(
self.inner.ptr,
direction.into(),
channel,
))
}
}
pub fn set_gain(&self, direction: Direction, channel: usize, gain: f64) -> Result<(), Error> {
unsafe {
SoapySDRDevice_setGain(self.inner.ptr, direction.into(), channel, gain);
check_error(())
}
}
pub fn gain(&self, direction: Direction, channel: usize) -> Result<f64, Error> {
unsafe {
check_error(SoapySDRDevice_getGain(
self.inner.ptr,
direction.into(),
channel,
))
}
}
pub fn gain_range(&self, direction: Direction, channel: usize) -> Result<Range, Error> {
unsafe {
check_error(SoapySDRDevice_getGainRange(
self.inner.ptr,
direction.into(),
channel,
))
}
}
pub fn set_gain_element<S: Into<Vec<u8>>>(
&self,
direction: Direction,
channel: usize,
name: S,
gain: f64,
) -> Result<(), Error> {
unsafe {
let name_c = CString::new(name).expect("Gain name contains null byte");
SoapySDRDevice_setGainElement(
self.inner.ptr,
direction.into(),
channel,
name_c.as_ptr(),
gain,
);
check_error(())
}
}
pub fn gain_element<S: Into<Vec<u8>>>(
&self,
direction: Direction,
channel: usize,
name: S,
) -> Result<f64, Error> {
unsafe {
let name_c = CString::new(name).expect("Gain name contains null byte");
check_error(SoapySDRDevice_getGainElement(
self.inner.ptr,
direction.into(),
channel,
name_c.as_ptr(),
))
}
}
pub fn gain_element_range<S: Into<Vec<u8>>>(
&self,
direction: Direction,
channel: usize,
name: S,
) -> Result<Range, Error> {
unsafe {
let name_c = CString::new(name).expect("Gain name contains null byte");
check_error(SoapySDRDevice_getGainElementRange(
self.inner.ptr,
direction.into(),
channel,
name_c.as_ptr(),
))
}
}
pub fn frequency_range(
&self,
direction: Direction,
channel: usize,
) -> Result<Vec<Range>, Error> {
unsafe {
list_result(|len_ptr| {
SoapySDRDevice_getFrequencyRange(self.inner.ptr, direction.into(), channel, len_ptr)
})
}
}
pub fn frequency(&self, direction: Direction, channel: usize) -> Result<f64, Error> {
unsafe {
check_error(SoapySDRDevice_getFrequency(
self.inner.ptr,
direction.into(),
channel,
))
}
}
pub fn set_frequency<A: Into<Args>>(
&self,
direction: Direction,
channel: usize,
frequency: f64,
args: A,
) -> Result<(), Error> {
unsafe {
SoapySDRDevice_setFrequency(
self.inner.ptr,
direction.into(),
channel,
frequency,
args.into().as_raw_const(),
);
check_error(())
}
}
pub fn list_frequencies(
&self,
direction: Direction,
channel: usize,
) -> Result<Vec<String>, Error> {
unsafe {
string_list_result(|len_ptr| {
SoapySDRDevice_listFrequencies(self.inner.ptr, direction.into(), channel, len_ptr)
})
}
}
pub fn component_frequency_range<S: Into<Vec<u8>>>(
&self,
direction: Direction,
channel: usize,
name: S,
) -> Result<Vec<Range>, Error> {
unsafe {
let name_c = CString::new(name).expect("Component name contains null byte");
list_result(|len_ptr| {
SoapySDRDevice_getFrequencyRangeComponent(
self.inner.ptr,
direction.into(),
channel,
name_c.as_ptr(),
len_ptr,
)
})
}
}
pub fn component_frequency<S: Into<Vec<u8>>>(
&self,
direction: Direction,
channel: usize,
name: S,
) -> Result<f64, Error> {
unsafe {
let name_c = CString::new(name).expect("Component name contains null byte");
check_error(SoapySDRDevice_getFrequencyComponent(
self.inner.ptr,
direction.into(),
channel,
name_c.as_ptr(),
))
}
}
pub fn set_component_frequency<S: Into<Vec<u8>>, A: Into<Args>>(
&self,
direction: Direction,
channel: usize,
name: S,
frequency: f64,
args: A,
) -> Result<(), Error> {
unsafe {
let name_c = CString::new(name).expect("Component name contains null byte");
SoapySDRDevice_setFrequencyComponent(
self.inner.ptr,
direction.into(),
channel,
name_c.as_ptr(),
frequency,
args.into().as_raw_const(),
);
check_error(())
}
}
pub fn frequency_args_info(
&self,
direction: Direction,
channel: usize,
) -> Result<Vec<ArgInfo>, Error> {
unsafe {
arg_info_result(|len_ptr| {
SoapySDRDevice_getFrequencyArgsInfo(
self.inner.ptr,
direction.into(),
channel,
len_ptr,
)
})
}
}
pub fn sample_rate(&self, direction: Direction, channel: usize) -> Result<f64, Error> {
unsafe {
check_error(SoapySDRDevice_getSampleRate(
self.inner.ptr,
direction.into(),
channel,
))
}
}
pub fn set_sample_rate(
&self,
direction: Direction,
channel: usize,
rate: f64,
) -> Result<(), Error> {
unsafe {
SoapySDRDevice_setSampleRate(self.inner.ptr, direction.into(), channel, rate);
check_error(())
}
}
pub fn get_sample_rate_range(
&self,
direction: Direction,
channel: usize,
) -> Result<Vec<Range>, Error> {
unsafe {
list_result(|len_ptr| {
SoapySDRDevice_getSampleRateRange(
self.inner.ptr,
direction.into(),
channel,
len_ptr,
)
})
}
}
pub fn bandwidth(&self, direction: Direction, channel: usize) -> Result<f64, Error> {
unsafe {
check_error(SoapySDRDevice_getBandwidth(
self.inner.ptr,
direction.into(),
channel,
))
}
}
pub fn set_bandwidth(
&self,
direction: Direction,
channel: usize,
bandwidth: f64,
) -> Result<(), Error> {
unsafe {
SoapySDRDevice_setBandwidth(self.inner.ptr, direction.into(), channel, bandwidth);
check_error(())
}
}
pub fn bandwidth_range(
&self,
direction: Direction,
channel: usize,
) -> Result<Vec<Range>, Error> {
unsafe {
list_result(|len_ptr| {
SoapySDRDevice_getBandwidthRange(self.inner.ptr, direction.into(), channel, len_ptr)
})
}
}
pub fn list_time_sources(&self) -> Result<Vec<String>, Error> {
unsafe {
string_list_result(|len_ptr| SoapySDRDevice_listTimeSources(self.inner.ptr, len_ptr))
}
}
pub fn get_time_source(&self) -> Result<String, Error> {
unsafe { string_result(SoapySDRDevice_getTimeSource(self.inner.ptr)) }
}
pub fn set_time_source<S: Into<Vec<u8>>>(&self, time_source: S) -> Result<(), Error> {
let time_source = CString::new(time_source).expect("Time source contained null");
unsafe {
SoapySDRDevice_setTimeSource(self.inner.ptr, time_source.as_ptr());
check_error(())
}
}
pub fn has_hardware_time(&self, hw_time_source: Option<&str>) -> Result<bool, Error> {
let hw_time_source = optional_string_arg(hw_time_source);
unsafe {
let has_hw_time =
SoapySDRDevice_hasHardwareTime(self.inner.ptr, hw_time_source.as_ptr());
check_error(has_hw_time)
}
}
pub fn get_hardware_time(&self, hw_time_source: Option<&str>) -> Result<i64, Error> {
let hw_time_source = optional_string_arg(hw_time_source);
unsafe {
let tstamp = SoapySDRDevice_getHardwareTime(self.inner.ptr, hw_time_source.as_ptr());
check_error(tstamp)
}
}
pub fn set_hardware_time(
&self,
hw_time_source: Option<&str>,
new_time_ns: i64,
) -> Result<(), Error> {
let hw_time_source = optional_string_arg(hw_time_source);
unsafe {
SoapySDRDevice_setHardwareTime(self.inner.ptr, new_time_ns, hw_time_source.as_ptr());
check_error(())
}
}
pub fn list_clock_sources(&self) -> Result<Vec<String>, Error> {
unsafe {
string_list_result(|len_ptr| SoapySDRDevice_listClockSources(self.inner.ptr, len_ptr))
}
}
pub fn get_clock_source(&self) -> Result<String, Error> {
unsafe { string_result(SoapySDRDevice_getClockSource(self.inner.ptr)) }
}
pub fn set_clock_source<S: Into<Vec<u8>>>(&self, clock_source: S) -> Result<(), Error> {
let clock_source = CString::new(clock_source).expect("clock source contained null");
unsafe {
SoapySDRDevice_setClockSource(self.inner.ptr, clock_source.as_ptr());
check_error(())
}
}
pub fn get_master_clock_rate(&self) -> Result<f64, Error> {
unsafe { check_error(SoapySDRDevice_getMasterClockRate(self.inner.ptr)) }
}
pub fn write_register<S: Into<Vec<u8>>>(
&self,
name: S,
address: u32,
value: u32,
) -> Result<(), Error> {
let name = CString::new(name).expect("name must not contain null byte");
unsafe {
SoapySDRDevice_writeRegister(self.inner.ptr, name.as_ptr(), address, value);
check_error(())
}
}
pub fn read_register<S: Into<Vec<u8>>>(&self, name: S, address: u32) -> Result<u32, Error> {
let name = CString::new(name).expect("name must not contain null byte");
unsafe {
let value = SoapySDRDevice_readRegister(self.inner.ptr, name.as_ptr(), address);
check_error(value)
}
}
pub fn write_registers<S: Into<Vec<u8>>>(
&self,
name: S,
address: u32,
value: &[u32],
) -> Result<(), Error> {
let name = CString::new(name).expect("name must not contain null byte");
unsafe {
SoapySDRDevice_writeRegisters(
self.inner.ptr,
name.as_ptr(),
address,
value.as_ptr(),
value.len(),
);
check_error(())
}
}
pub fn list_register_interfaces(&self) -> Result<Vec<String>, Error> {
unsafe {
string_list_result(|len_ptr| {
SoapySDRDevice_listRegisterInterfaces(self.inner.ptr, len_ptr)
})
}
}
pub fn write_setting<S: Into<Vec<u8>>>(&self, key: S, value: S) -> Result<(), Error> {
let key = CString::new(key).expect("key must not contain null byte");
let value = CString::new(value).expect("value must not contain null byte");
unsafe {
check_ret_error(SoapySDRDevice_writeSetting(
self.inner.ptr,
key.as_ptr(),
value.as_ptr(),
))?;
Ok(())
}
}
pub fn read_setting<S: Into<Vec<u8>>>(&self, key: S) -> Result<String, Error> {
let key = CString::new(key).expect("key must not contain null byte");
unsafe { string_result(SoapySDRDevice_readSetting(self.inner.ptr, key.as_ptr())) }
}
}
pub struct RxStream<E: StreamSample> {
device: Device,
handle: *mut SoapySDRStream,
nchannels: usize,
flags: i32,
time_ns: i64,
active: bool,
buf_ptrs: Vec<*mut E>,
phantom: PhantomData<fn(&mut [E])>,
}
unsafe impl<E: StreamSample> Send for RxStream<E> {}
impl<E: StreamSample> Drop for RxStream<E> {
fn drop(&mut self) {
unsafe {
if self.active {
self.deactivate(None).ok();
}
SoapySDRDevice_closeStream(self.device.inner.ptr, self.handle);
}
}
}
impl<E: StreamSample> RxStream<E> {
pub fn mtu(&self) -> Result<usize, Error> {
unsafe {
check_error(SoapySDRDevice_getStreamMTU(
self.device.inner.ptr,
self.handle,
))
}
}
pub fn activate(&mut self, time_ns: Option<i64>) -> Result<(), Error> {
if self.active {
return Err(Error {
code: ErrorCode::Other,
message: "Stream is already active".into(),
});
}
unsafe {
let flags = if time_ns.is_some() {
SOAPY_SDR_HAS_TIME as i32
} else {
0
};
check_ret_error(SoapySDRDevice_activateStream(
self.device.inner.ptr,
self.handle,
flags,
time_ns.unwrap_or(0),
0,
))?;
self.active = true;
Ok(())
}
}
pub fn active(&self) -> bool {
self.active
}
pub fn deactivate(&mut self, time_ns: Option<i64>) -> Result<(), Error> {
if !self.active {
return Err(Error {
code: ErrorCode::Other,
message: "Stream is not active".into(),
});
}
unsafe {
let flags = if time_ns.is_some() {
SOAPY_SDR_HAS_TIME as i32
} else {
0
};
check_ret_error(SoapySDRDevice_deactivateStream(
self.device.inner.ptr,
self.handle,
flags,
time_ns.unwrap_or(0),
))?;
self.active = false;
Ok(())
}
}
pub fn read(&mut self, buffers: &mut [&mut [E]], timeout_us: i64) -> Result<usize, Error> {
unsafe {
assert!(buffers.len() == self.nchannels);
let num_samples = buffers.iter().map(|b| b.len()).min().unwrap_or(0);
for (dst, src) in self.buf_ptrs.iter_mut().zip(buffers.iter_mut()) {
*dst = src.as_mut_ptr();
}
self.flags = 0;
let len = len_result(SoapySDRDevice_readStream(
self.device.inner.ptr,
self.handle,
self.buf_ptrs.as_ptr() as *const *mut _,
num_samples,
&mut self.flags as *mut _,
&mut self.time_ns as *mut _,
timeout_us as _,
))?;
Ok(len as usize)
}
}
pub fn time_ns(&self) -> i64 {
self.time_ns
}
}
pub struct TxStream<E: StreamSample> {
device: Device,
handle: *mut SoapySDRStream,
nchannels: usize,
active: bool,
buf_ptrs: Vec<*const E>,
phantom: PhantomData<fn(&[E])>,
}
unsafe impl<E: StreamSample> Send for TxStream<E> {}
impl<E: StreamSample> Drop for TxStream<E> {
fn drop(&mut self) {
unsafe {
if self.active {
self.deactivate(None).ok();
}
SoapySDRDevice_closeStream(self.device.inner.ptr, self.handle);
}
}
}
impl<E: StreamSample> TxStream<E> {
pub fn mtu(&self) -> Result<usize, Error> {
unsafe {
check_error(SoapySDRDevice_getStreamMTU(
self.device.inner.ptr,
self.handle,
))
}
}
pub fn activate(&mut self, time_ns: Option<i64>) -> Result<(), Error> {
if self.active {
return Err(Error {
code: ErrorCode::Other,
message: "Stream is already active".into(),
});
}
unsafe {
let flags = if time_ns.is_some() {
SOAPY_SDR_HAS_TIME as i32
} else {
0
};
check_ret_error(SoapySDRDevice_activateStream(
self.device.inner.ptr,
self.handle,
flags,
time_ns.unwrap_or(0),
0,
))?;
self.active = true;
Ok(())
}
}
pub fn active(&self) -> bool {
self.active
}
pub fn deactivate(&mut self, time_ns: Option<i64>) -> Result<(), Error> {
if !self.active {
return Err(Error {
code: ErrorCode::Other,
message: "Stream is not active".into(),
});
}
unsafe {
let flags = if time_ns.is_some() {
SOAPY_SDR_HAS_TIME as i32
} else {
0
};
check_ret_error(SoapySDRDevice_deactivateStream(
self.device.inner.ptr,
self.handle,
flags,
time_ns.unwrap_or(0),
))?;
self.active = false;
Ok(())
}
}
pub fn write(
&mut self,
buffers: &[&[E]],
at_ns: Option<i64>,
end_burst: bool,
timeout_us: i64,
) -> Result<usize, Error> {
unsafe {
assert!(
buffers.len() == self.nchannels,
"Number of buffers must equal number of channels on stream"
);
let num_elems = buffers.first().map_or(0, |x| x.len());
for (dst, src) in self.buf_ptrs.iter_mut().zip(buffers) {
assert_eq!(src.len(), num_elems, "All buffers must be the same length");
*dst = src.as_ptr();
}
let mut flags = 0;
if at_ns.is_some() {
flags |= SOAPY_SDR_HAS_TIME as i32;
}
if end_burst {
flags |= SOAPY_SDR_END_BURST as i32;
}
let len = len_result(SoapySDRDevice_writeStream(
self.device.inner.ptr,
self.handle,
self.buf_ptrs.as_ptr() as *const *const _,
num_elems,
&mut flags as *mut _,
at_ns.unwrap_or(0),
timeout_us as _,
))?;
Ok(len as usize)
}
}
pub fn write_all(
&mut self,
buffers: &[&[E]],
at_ns: Option<i64>,
end_burst: bool,
timeout_us: i64,
) -> Result<(), Error> {
let mut buffers = buffers.to_owned();
let mut at_ns = at_ns;
while buffers.first().map_or(0, |x| x.len()) > 0 {
let written = self.write(&buffers, at_ns.take(), end_burst, timeout_us)?;
for buf in &mut buffers {
*buf = &buf[written..];
}
}
Ok(())
}
pub fn read_status(
&mut self,
chan_mask: &mut usize,
flags: &mut i32,
time_ns: &mut i64,
timeout_us: i64,
) -> Result<usize, Error> {
#[allow(clippy::useless_conversion)]
let timeout_us = timeout_us.try_into().unwrap();
unsafe {
let status = len_result(SoapySDRDevice_readStreamStatus(
self.device.inner.ptr,
self.handle,
chan_mask,
flags,
time_ns,
timeout_us,
))?;
Ok(status as usize)
}
}
}