1use soapysdr_sys::*;
2pub use soapysdr_sys::SoapySDRRange as Range;
3use std::slice;
4use std::ptr;
5use std::sync::Arc;
6use std::ffi::{ CStr, CString };
7use std::os::raw::{c_int, c_char};
8use std::os::raw::c_void;
9use std::marker::PhantomData;
10
11use super::{ Args, ArgInfo, StreamSample, Format };
12use crate::arginfo::arg_info_from_c;
13
14#[repr(i32)]
16#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
17#[non_exhaustive]
18pub enum ErrorCode {
19 Timeout = -1,
21
22 StreamError = -2,
24
25 Corruption = -3,
28
29 Overflow = -4,
32
33 NotSupported = -5,
36
37 TimeError = -6,
40
41 Underflow = -7,
44
45 Other = 0,
47}
48
49impl ErrorCode {
50 fn from_c(code: c_int) -> ErrorCode {
51 match code {
52 SOAPY_SDR_TIMEOUT => ErrorCode::Timeout,
53 SOAPY_SDR_STREAM_ERROR => ErrorCode::StreamError,
54 SOAPY_SDR_CORRUPTION => ErrorCode::Corruption,
55 SOAPY_SDR_OVERFLOW => ErrorCode::Overflow,
56 SOAPY_SDR_NOT_SUPPORTED => ErrorCode::NotSupported,
57 SOAPY_SDR_TIME_ERROR => ErrorCode::TimeError,
58 SOAPY_SDR_UNDERFLOW => ErrorCode::Underflow,
59 _ => ErrorCode::Other,
60 }
61 }
62}
63
64#[derive(Clone, Debug, Hash)]
66pub struct Error {
67 pub code: ErrorCode,
68 pub message: String,
69}
70
71impl ::std::fmt::Display for Error {
72 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
73 write!(f, "{:?}: {}", self.code, self.message)
74 }
75}
76
77impl ::std::error::Error for Error {
78 fn description(&self) -> &str {
79 &self.message[..]
80 }
81}
82
83#[repr(u32)]
85#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
86pub enum Direction {
87 Tx = SOAPY_SDR_TX,
89
90 Rx = SOAPY_SDR_RX
92}
93
94impl From<Direction> for c_int {
95 fn from(f: Direction) -> c_int {
96 f as c_int
97 }
98}
99
100struct DeviceInner {
101 ptr: *mut SoapySDRDevice,
102}
103
104unsafe impl Send for DeviceInner {}
106unsafe impl Sync for DeviceInner {}
107
108impl Drop for DeviceInner {
109 fn drop(&mut self) {
110 unsafe {
111 SoapySDRDevice_unmake(self.ptr);
112 }
113 }
114}
115
116#[derive(Clone)]
118pub struct Device {
119 inner: Arc<DeviceInner>,
120}
121
122impl Device {
123 fn from_ptr(ptr: *mut SoapySDRDevice) -> Device {
124 Device {
125 inner: Arc::new(DeviceInner {
126 ptr,
127 }),
128 }
129 }
130}
131
132fn last_error_str() -> String {
133 unsafe {
134 CStr::from_ptr(SoapySDRDevice_lastError()).to_string_lossy().into()
136 }
137}
138
139fn check_error<T>(r: T) -> Result<T, Error> {
140 unsafe {
141 if SoapySDRDevice_lastStatus() == 0 {
142 Ok(r)
143 } else {
144 Err(Error {
145 code: ErrorCode::Other,
146 message: last_error_str(),
147 })
148 }
149 }
150}
151
152fn check_ret_error(r: c_int) -> Result<(), Error> {
153 if r == 0 {
154 Ok(())
155 } else {
156 Err(Error {
157 code: ErrorCode::from_c(r),
158 message: last_error_str(),
159 })
160 }
161}
162
163fn len_result(ret: c_int) -> Result<c_int, Error> {
164 if ret >= 0 {
165 Ok(ret)
166 } else {
167 Err(Error {
168 code: ErrorCode::from_c(ret),
169 message: last_error_str(),
170 })
171 }
172}
173
174unsafe fn string_result(r: *mut c_char) -> Result<String, Error> {
175 let ptr: *mut c_char = check_error(r)?;
176 let ret = CStr::from_ptr(ptr).to_string_lossy().into();
177 SoapySDR_free(ptr as *mut c_void);
178 Ok(ret)
179}
180
181unsafe fn string_list_result<F: FnOnce(*mut usize) -> *mut *mut c_char>(f: F) -> Result<Vec<String>, Error> {
182 let mut len: usize = 0;
183 let mut ptr = check_error(f(&mut len as *mut _))?;
184 let ret = slice::from_raw_parts(ptr, len).iter()
185 .map(|&p| CStr::from_ptr(p).to_string_lossy().into())
186 .collect();
187 SoapySDRStrings_clear(&mut ptr as *mut _, len);
188 Ok(ret)
189}
190
191unsafe fn arg_info_result<F: FnOnce(*mut usize) -> *mut SoapySDRArgInfo>(f: F) -> Result<Vec<ArgInfo>, Error> {
192 let mut len: usize = 0;
193 let ptr = check_error(f(&mut len as *mut _))?;
194 let r = slice::from_raw_parts(ptr, len).iter().map(|x| arg_info_from_c(x)).collect();
195 SoapySDRArgInfoList_clear(ptr, len);
196 Ok(r)
197}
198
199unsafe fn list_result<T: Copy, F: FnOnce(*mut usize) -> *mut T>(f: F) -> Result<Vec<T>, Error> {
200 let mut len: usize = 0;
201 let ptr = check_error(f(&mut len as *mut _))?;
202 let ret = slice::from_raw_parts(ptr, len).to_owned();
203 SoapySDR_free(ptr as *mut c_void);
204 Ok(ret)
205}
206
207fn optional_string_arg<S: AsRef<str>>(optstr: Option<S>) -> CString {
208 match optstr {
209 Some(s) => CString::new(s.as_ref()).expect("Optional arg string contains null"),
210 None => CString::new("").unwrap(),
211 }
212}
213
214pub fn enumerate<A: Into<Args>>(args: A) -> Result<Vec<Args>, Error> {
228 unsafe {
229 let mut len: usize = 0;
230 let devs = check_error(SoapySDRDevice_enumerate(args.into().as_raw_const(), &mut len as *mut _))?;
231 let args = slice::from_raw_parts(devs, len).iter().map(|&arg| Args::from_raw(arg)).collect();
232 SoapySDR_free(devs as *mut c_void);
233 Ok(args)
234 }
235}
236
237impl Device {
238 pub fn new<A: Into<Args>>(args: A) -> Result<Device, Error> {
245 unsafe {
246 let d = check_error(SoapySDRDevice_make(args.into().as_raw_const()))?;
247 Ok(Device::from_ptr(d))
248 }
249 }
250
251 #[doc(hidden)]
252 pub fn null_device() -> Device {
253 Device::new("type=null").unwrap()
254 }
255
256 pub fn driver_key(&self) -> Result<String, Error> {
261 unsafe {
262 string_result(SoapySDRDevice_getDriverKey(self.inner.ptr))
263 }
264 }
265
266 pub fn hardware_key(&self) -> Result<String, Error> {
270 unsafe {
271 string_result(SoapySDRDevice_getDriverKey(self.inner.ptr))
272 }
273 }
274
275 pub fn hardware_info(&self) -> Result<Args, Error> {
283 unsafe {
284 check_error(SoapySDRDevice_getHardwareInfo(self.inner.ptr)).map(|x| Args::from_raw(x))
285 }
286 }
287
288 pub fn frontend_mapping(&self, direction: Direction) -> Result<String, Error> {
290 unsafe {
291 string_result(SoapySDRDevice_getFrontendMapping(self.inner.ptr, direction.into()))
292 }
293 }
294
295 pub fn list_sensors(&self) -> Result<Vec<String>, Error> {
297 unsafe {
298 string_list_result(|len_ptr| SoapySDRDevice_listSensors(self.inner.ptr, len_ptr))
299 }
300 }
301
302 pub fn read_sensor(&self, key: &str) -> Result<String, Error> {
304 let key_c = CString::new(key).expect("key contains null byte");
305 unsafe {
306 string_result(SoapySDRDevice_readSensor(self.inner.ptr, key_c.as_ptr() as *const i8))
307 }
308 }
309
310 pub fn get_channel_sensor_info(&self, dir: Direction, channel: usize, key: &str) -> Result<ArgInfo, Error> {
312 let key_c = CString::new(key).expect("key contains null byte");
313 Ok(unsafe {
314 arg_info_from_c(&SoapySDRDevice_getChannelSensorInfo(
315 self.inner.ptr, dir.into(), channel, key_c.as_ptr() as *const i8))
316 })
317 }
318
319 pub fn list_channel_sensors(&self, dir: Direction, channel: usize) -> Result<Vec<String>, Error> {
321 unsafe {
322 string_list_result(|len_ptr| SoapySDRDevice_listChannelSensors(self.inner.ptr, dir.into(), channel, len_ptr))
323 }
324 }
325
326 pub fn read_channel_sensor(&self, dir: Direction, channel: usize, key: &str) -> Result<String, Error> {
328 let key_c = CString::new(key).expect("key contains null byte");
329 unsafe {
330 string_result(SoapySDRDevice_readChannelSensor(self.inner.ptr, dir.into(), channel, key_c.as_ptr() as *const i8))
331 }
332 }
333
334 pub fn get_sensor_info(&self, key: &str) -> Result<ArgInfo, Error> {
336 let key_c = CString::new(key).expect("key contains null byte");
337 Ok(unsafe {
338 arg_info_from_c(&SoapySDRDevice_getSensorInfo(
339 self.inner.ptr, key_c.as_ptr() as *const i8))
340 })
341 }
342
343 pub fn set_frontend_mapping<S: Into<Vec<u8>>>(&self, direction: Direction, mapping: S) -> Result<(), Error> {
347 unsafe {
348 let mapping_c = CString::new(mapping).expect("Mapping contains null byte");
349 SoapySDRDevice_setFrontendMapping(self.inner.ptr, direction.into(), mapping_c.as_ptr());
350 check_error(())
351 }
352 }
353
354 pub fn num_channels(&self, direction: Direction) -> Result<usize, Error> {
356 unsafe {
357 check_error(SoapySDRDevice_getNumChannels(self.inner.ptr, direction.into()))
358 }
359 }
360
361 pub fn channel_info(&self, direction: Direction, channel: usize) -> Result<Args, Error> {
363 unsafe {
364 check_error(SoapySDRDevice_getChannelInfo(self.inner.ptr, direction.into(), channel)).map(|x| Args::from_raw(x))
365 }
366 }
367
368 pub fn full_duplex(&self, direction: Direction, channel: usize) -> Result<bool, Error> {
372 unsafe {
373 check_error(SoapySDRDevice_getFullDuplex(self.inner.ptr, direction.into(), channel))
374 }
375 }
376
377 pub fn stream_formats(&self, direction: Direction, channel: usize) -> Result<Vec<Format>, Error> {
379 unsafe {
380 let mut len: usize = 0;
381 let mut ptr = check_error(SoapySDRDevice_getStreamFormats(self.inner.ptr, direction.into(), channel, &mut len as *mut _))?;
382 let ret = slice::from_raw_parts(ptr, len).iter()
383 .flat_map(|&p| CStr::from_ptr(p).to_str().ok())
384 .flat_map(|s| s.parse().ok())
385 .collect();
386 SoapySDRStrings_clear(&mut ptr as *mut _, len);
387 Ok(ret)
388 }
389 }
390
391 pub fn native_stream_format(&self, direction: Direction, channel: usize) -> Result<(Format, f64), Error> {
396 unsafe {
397 let mut fullscale: f64 = 0.0;
398 let ptr = check_error(SoapySDRDevice_getNativeStreamFormat(self.inner.ptr, direction.into(), channel, &mut fullscale as *mut _))?;
399
400 let format = CStr::from_ptr(ptr).to_str().ok()
401 .and_then(|s| s.parse().ok())
402 .ok_or_else(|| Error { code: ErrorCode::Other, message: "Invalid stream format returned by SoapySDR".into()})?;
403
404 Ok((format, fullscale))
405 }
406 }
407
408 pub fn stream_args_info(&self, direction: Direction, channel: usize) -> Result<Vec<ArgInfo>, Error> {
410 unsafe {
411 arg_info_result(|len_ptr| SoapySDRDevice_getStreamArgsInfo(self.inner.ptr, direction.into(), channel, len_ptr))
412 }
413 }
414
415 pub fn rx_stream<E: StreamSample>(&self, channels: &[usize]) -> Result<RxStream<E>, Error> {
417 self.rx_stream_args(channels, ())
418 }
419
420 pub fn rx_stream_args<E: StreamSample, A: Into<Args>>(&self, channels: &[usize], args: A) -> Result<RxStream<E>, Error> {
422 unsafe {
423 let mut stream: *mut SoapySDRStream = ptr::null_mut();
424 check_error(SoapySDRDevice_setupStream(self.inner.ptr,
425 &mut stream as *mut _,
426 Direction::Rx.into(),
427 E::STREAM_FORMAT.as_ptr(),
428 channels.as_ptr(), channels.len(),
429 args.into().as_raw_const()
430 )).map(|_| RxStream {
431 device: self.clone(),
432 handle: stream,
433 nchannels: channels.len(),
434 flags: 0,
435 time_ns: 0,
436 active: false,
437 phantom: PhantomData,
438 })
439 }
440 }
441
442 pub fn tx_stream<E: StreamSample>(&self, channels: &[usize]) -> Result<TxStream<E>, Error> {
444 self.tx_stream_args(channels, ())
445 }
446
447 pub fn tx_stream_args<E: StreamSample, A: Into<Args>>(&self, channels: &[usize], args: A) -> Result<TxStream<E>, Error> {
449 unsafe {
450 let mut stream: *mut SoapySDRStream = ptr::null_mut();
451 check_error(SoapySDRDevice_setupStream(self.inner.ptr,
452 &mut stream as *mut _,
453 Direction::Tx.into(),
454 E::STREAM_FORMAT.as_ptr(),
455 channels.as_ptr(), channels.len(),
456 args.into().as_raw_const()
457 )).map(|_| TxStream {
458 device: self.clone(),
459 handle: stream,
460 nchannels: channels.len(),
461 active: false,
462 phantom: PhantomData,
463 })
464 }
465 }
466
467 pub fn antennas(&self, direction: Direction, channel: usize) -> Result<Vec<String>, Error> {
469 unsafe {
470 string_list_result(|len_ptr| SoapySDRDevice_listAntennas(self.inner.ptr, direction.into(), channel, len_ptr))
471 }
472 }
473
474 pub fn set_antenna<S: Into<Vec<u8>>>(&self, direction: Direction, channel: usize, name: S) -> Result<(), Error> {
476 unsafe {
477 let name_c = CString::new(name).expect("Antenna name contains null byte");
478 SoapySDRDevice_setAntenna(self.inner.ptr, direction.into(), channel, name_c.as_ptr());
479 check_error(())
480 }
481 }
482
483 pub fn antenna(&self, direction: Direction, channel: usize) -> Result<String, Error> {
485 unsafe {
486 string_result(SoapySDRDevice_getAntenna(self.inner.ptr, direction.into(), channel))
487 }
488 }
489
490 pub fn has_dc_offset_mode(&self, direction: Direction, channel: usize) -> Result<bool, Error> {
494 unsafe {
495 check_error(SoapySDRDevice_hasDCOffsetMode(self.inner.ptr, direction.into(), channel))
496 }
497 }
498
499 pub fn set_dc_offset_mode(&self, direction: Direction, channel: usize, automatic: bool) -> Result<(), Error> {
501 unsafe {
502 SoapySDRDevice_setDCOffsetMode(self.inner.ptr, direction.into(), channel, automatic);
503 check_error(())
504 }
505 }
506
507 pub fn dc_offset_mode(&self, direction: Direction, channel: usize) -> Result<bool, Error> {
509 unsafe {
510 check_error(SoapySDRDevice_getDCOffsetMode(self.inner.ptr, direction.into(), channel))
511 }
512 }
513
514 pub fn has_dc_offset(&self, direction: Direction, channel: usize) -> Result<bool, Error> {
518 unsafe {
519 check_error(SoapySDRDevice_hasDCOffset(self.inner.ptr, direction.into(), channel))
520 }
521 }
522
523 pub fn set_dc_offset(&self, direction: Direction, channel: usize, offset_i: f64, offset_q: f64) -> Result<(), Error> {
527 unsafe {
528 SoapySDRDevice_setDCOffset(self.inner.ptr, direction.into(), channel, offset_i, offset_q);
529 check_error(())
530 }
531 }
532
533 pub fn dc_offset(&self, direction: Direction, channel: usize) -> Result<(f64, f64), Error> {
535 unsafe {
536 let mut i: f64 = 0.0;
537 let mut q: f64 = 0.0;
538 SoapySDRDevice_getDCOffset(self.inner.ptr, direction.into(), channel, &mut i as *mut _, &mut q as *mut _);
539 check_error((i, q))
540 }
541 }
542
543 pub fn has_iq_balance(&self, direction: Direction, channel: usize) -> Result<bool, Error> {
547 unsafe {
548 check_error(SoapySDRDevice_hasIQBalance(self.inner.ptr, direction.into(), channel))
549 }
550 }
551
552 pub fn set_iq_balance(&self, direction: Direction, channel: usize, balance_i: f64, balance_q: f64) -> Result<(), Error> {
556 unsafe {
557 SoapySDRDevice_setIQBalance(self.inner.ptr, direction.into(), channel, balance_i, balance_q);
558 check_error(())
559 }
560 }
561
562 pub fn iq_balance(&self, direction: Direction, channel: usize) -> Result<(f64, f64), Error> {
564 unsafe {
565 let mut i: f64 = 0.0;
566 let mut q: f64 = 0.0;
567 SoapySDRDevice_getIQBalance(self.inner.ptr, direction.into(), channel, &mut i as *mut _, &mut q as *mut _);
568 check_error((i, q))
569 }
570 }
571
572 pub fn list_gains(&self, direction: Direction, channel: usize) -> Result<Vec<String>, Error> {
576 unsafe {
577 string_list_result(|len_ptr| SoapySDRDevice_listGains(self.inner.ptr, direction.into(), channel, len_ptr))
578 }
579 }
580
581 pub fn has_gain_mode(&self, direction: Direction, channel: usize) -> Result<bool, Error> {
583 unsafe {
584 check_error(SoapySDRDevice_hasGainMode(self.inner.ptr, direction.into(), channel))
585 }
586 }
587
588 pub fn set_gain_mode(&self, direction: Direction, channel: usize, automatic: bool) -> Result<(), Error> {
590 unsafe {
591 SoapySDRDevice_setGainMode(self.inner.ptr, direction.into(), channel, automatic);
592 check_error(())
593 }
594 }
595
596 pub fn gain_mode(&self, direction: Direction, channel: usize) -> Result<bool, Error> {
598 unsafe {
599 check_error(SoapySDRDevice_getGainMode(self.inner.ptr, direction.into(), channel))
600 }
601 }
602
603 pub fn set_gain(&self, direction: Direction, channel: usize, gain: f64) -> Result<(), Error> {
609 unsafe {
610 SoapySDRDevice_setGain(self.inner.ptr, direction.into(), channel, gain);
611 check_error(())
612 }
613 }
614
615 pub fn gain(&self, direction: Direction, channel: usize) -> Result<f64, Error> {
617 unsafe {
618 check_error(SoapySDRDevice_getGain(self.inner.ptr, direction.into(), channel))
619 }
620 }
621
622 pub fn gain_range(&self, direction: Direction, channel: usize) -> Result<Range, Error> {
624 unsafe {
625 check_error(SoapySDRDevice_getGainRange(self.inner.ptr, direction.into(), channel))
626 }
627 }
628
629 pub fn set_gain_element<S: Into<Vec<u8>>>(&self, direction: Direction, channel: usize, name: S, gain: f64) -> Result<(), Error> {
635 unsafe {
636 let name_c = CString::new(name).expect("Gain name contains null byte");
637 SoapySDRDevice_setGainElement(self.inner.ptr, direction.into(), channel, name_c.as_ptr(), gain);
638 check_error(())
639 }
640 }
641
642 pub fn gain_element<S: Into<Vec<u8>>>(&self, direction: Direction, channel: usize, name: S) -> Result<f64, Error> {
644 unsafe {
645 let name_c = CString::new(name).expect("Gain name contains null byte");
646 check_error(SoapySDRDevice_getGainElement(self.inner.ptr, direction.into(), channel, name_c.as_ptr()))
647 }
648 }
649
650 pub fn gain_element_range<S: Into<Vec<u8>>>(&self, direction: Direction, channel: usize, name: S) -> Result<Range, Error> {
652 unsafe {
653 let name_c = CString::new(name).expect("Gain name contains null byte");
654 check_error(SoapySDRDevice_getGainElementRange(self.inner.ptr, direction.into(), channel, name_c.as_ptr()))
655 }
656 }
657
658 pub fn frequency_range(&self, direction: Direction, channel: usize) -> Result<Vec<Range>, Error> {
660 unsafe {
661 list_result(|len_ptr| SoapySDRDevice_getFrequencyRange(self.inner.ptr, direction.into(), channel, len_ptr))
662 }
663 }
664
665 pub fn frequency(&self, direction: Direction, channel: usize) -> Result<f64, Error> {
672 unsafe {
673 check_error(SoapySDRDevice_getFrequency(self.inner.ptr, direction.into(), channel))
674 }
675 }
676
677 pub fn set_frequency<A: Into<Args>>(&self, direction: Direction, channel: usize, frequency: f64, args: A) -> Result<(), Error> {
701 unsafe {
702 SoapySDRDevice_setFrequency(self.inner.ptr, direction.into(), channel, frequency, args.into().as_raw_const());
703 check_error(())
704 }
705 }
706
707 pub fn list_frequencies(&self, direction: Direction, channel: usize) -> Result<Vec<String>, Error> {
711 unsafe {
712 string_list_result(|len_ptr| SoapySDRDevice_listFrequencies(self.inner.ptr, direction.into(), channel, len_ptr))
713 }
714 }
715
716 pub fn component_frequency_range<S: Into<Vec<u8>>>(&self, direction: Direction, channel: usize, name: S) -> Result<Vec<Range>, Error> {
718 unsafe {
719 let name_c = CString::new(name).expect("Component name contains null byte");
720 list_result(|len_ptr| SoapySDRDevice_getFrequencyRangeComponent(self.inner.ptr, direction.into(), channel, name_c.as_ptr(), len_ptr))
721 }
722 }
723
724 pub fn component_frequency<S: Into<Vec<u8>>>(&self, direction: Direction, channel: usize, name: S) -> Result<f64, Error> {
726 unsafe {
727 let name_c = CString::new(name).expect("Component name contains null byte");
728 check_error(SoapySDRDevice_getFrequencyComponent(self.inner.ptr, direction.into(), channel, name_c.as_ptr()))
729 }
730 }
731
732 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> {
744 unsafe {
745 let name_c = CString::new(name).expect("Component name contains null byte");
746 SoapySDRDevice_setFrequencyComponent(self.inner.ptr, direction.into(), channel, name_c.as_ptr(), frequency, args.into().as_raw_const());
747 check_error(())
748 }
749 }
750
751 pub fn frequency_args_info(&self, direction: Direction, channel: usize) -> Result<Vec<ArgInfo>, Error> {
753 unsafe {
754 arg_info_result(|len_ptr| SoapySDRDevice_getFrequencyArgsInfo(self.inner.ptr, direction.into(), channel, len_ptr))
755 }
756 }
757
758 pub fn sample_rate(&self, direction: Direction, channel: usize) -> Result<f64, Error> {
760 unsafe {
761 check_error(SoapySDRDevice_getSampleRate(self.inner.ptr, direction.into(), channel))
762 }
763 }
764
765 pub fn set_sample_rate(&self, direction: Direction, channel: usize, rate: f64) -> Result<(), Error> {
767 unsafe {
768 SoapySDRDevice_setSampleRate(self.inner.ptr, direction.into(), channel, rate);
769 check_error(())
770 }
771 }
772
773 pub fn get_sample_rate_range(&self, direction: Direction, channel: usize) -> Result<Vec<Range>, Error> {
775 unsafe {
776 list_result(|len_ptr| SoapySDRDevice_getSampleRateRange(self.inner.ptr, direction.into(), channel, len_ptr))
777 }
778 }
779
780 pub fn bandwidth(&self, direction: Direction, channel: usize) -> Result<f64, Error> {
782 unsafe {
783 check_error(SoapySDRDevice_getBandwidth(self.inner.ptr, direction.into(), channel))
784 }
785 }
786
787 pub fn set_bandwidth(&self, direction: Direction, channel: usize, bandwidth: f64) -> Result<(), Error> {
789 unsafe {
790 SoapySDRDevice_setBandwidth(self.inner.ptr, direction.into(), channel, bandwidth);
791 check_error(())
792 }
793 }
794
795 pub fn bandwidth_range(&self, direction: Direction, channel: usize) -> Result<Vec<Range>, Error> {
797 unsafe {
798 list_result(|len_ptr| SoapySDRDevice_getBandwidthRange(self.inner.ptr, direction.into(), channel, len_ptr))
799 }
800 }
801
802 pub fn list_time_sources(&self) -> Result<Vec<String>, Error> {
804 unsafe { string_list_result(|len_ptr| SoapySDRDevice_listTimeSources(self.inner.ptr, len_ptr)) }
805 }
806
807 pub fn get_time_source(&self) -> Result<String, Error> {
809 unsafe { string_result(SoapySDRDevice_getTimeSource(self.inner.ptr)) }
810 }
811
812 pub fn set_time_source<S: Into<Vec<u8>>>(&self, time_source: S) -> Result<(), Error> {
814 let time_source = CString::new(time_source).expect("Time source contained null");
815 unsafe {
816 SoapySDRDevice_setTimeSource(self.inner.ptr, time_source.as_ptr());
817 check_error(())
818 }
819 }
820
821 pub fn has_hardware_time(
825 &self,
826 hw_time_source: Option<&str>,
827 ) -> Result<bool, Error> {
828 let hw_time_source = optional_string_arg(hw_time_source);
829 unsafe {
830 let has_hw_time = SoapySDRDevice_hasHardwareTime(self.inner.ptr, hw_time_source.as_ptr());
831 check_error(has_hw_time)
832 }
833 }
834
835 pub fn get_hardware_time(
837 &self,
838 hw_time_source: Option<&str>,
839 ) -> Result<i64, Error> {
840 let hw_time_source = optional_string_arg(hw_time_source);
841 unsafe {
842 let tstamp = SoapySDRDevice_getHardwareTime(self.inner.ptr, hw_time_source.as_ptr());
843 check_error(tstamp)
844 }
845 }
846
847 pub fn set_hardware_time(
850 &self,
851 hw_time_source: Option<&str>,
852 new_time_ns: i64,
853 ) -> Result<(), Error> {
854 let hw_time_source = optional_string_arg(hw_time_source);
855 unsafe {
856 SoapySDRDevice_setHardwareTime(self.inner.ptr, new_time_ns, hw_time_source.as_ptr());
857 check_error(())
858 }
859 }
860
861 pub fn list_clock_sources(&self) -> Result<Vec<String>, Error> {
863 unsafe { string_list_result(|len_ptr| SoapySDRDevice_listClockSources(self.inner.ptr, len_ptr)) }
864 }
865
866 pub fn get_clock_source(&self) -> Result<String, Error> {
868 unsafe { string_result(SoapySDRDevice_getClockSource(self.inner.ptr)) }
869 }
870
871 pub fn set_clock_source<S: Into<Vec<u8>>>(&self, clock_source: S) -> Result<(), Error> {
873 let clock_source = CString::new(clock_source).expect("clock source contained null");
874 unsafe {
875 SoapySDRDevice_setClockSource(self.inner.ptr, clock_source.as_ptr());
876 check_error(())
877 }
878 }
879
880 pub fn write_setting<S: Into<Vec<u8>>>(&self, key: S, value: S) -> Result<(), Error> {
886 let key = CString::new(key).expect("key must not contain null byte");
887 let value = CString::new(value).expect("value must not contain null byte");
888 unsafe {
889 check_ret_error(SoapySDRDevice_writeSetting(self.inner.ptr, key.as_ptr(), value.as_ptr()))?;
890 Ok(())
891 }
892 }
893
894 pub fn read_setting<S: Into<Vec<u8>>>(&self, key: S) -> Result<String, Error> {
896 let key = CString::new(key).expect("key must not contain null byte");
897 unsafe {
898 string_result(SoapySDRDevice_readSetting(self.inner.ptr, key.as_ptr()))
899 }
900 }
901
902 }
911
912pub struct RxStream<E: StreamSample> {
919 device: Device,
920 handle: *mut SoapySDRStream,
921 nchannels: usize,
922 flags: i32,
923 time_ns: i64,
924 active: bool,
925 phantom: PhantomData<fn(&mut[E])>,
926}
927
928unsafe impl<E: StreamSample> Send for RxStream<E> {}
930
931impl<E: StreamSample> Drop for RxStream<E> {
932 fn drop(&mut self) {
933 unsafe {
934 if self.active {
935 self.deactivate(None).ok();
936 }
937 SoapySDRDevice_closeStream(self.device.inner.ptr, self.handle);
938 }
939 }
940}
941
942impl<E: StreamSample> RxStream<E> {
943 pub fn mtu(&self) -> Result<usize, Error> {
949 unsafe {
950 check_error(SoapySDRDevice_getStreamMTU(self.device.inner.ptr, self.handle))
951 }
952 }
953
954 pub fn activate(&mut self, time_ns: Option<i64>) -> Result<(), Error> {
961 if self.active { return Err(Error { code: ErrorCode::Other, message: "Stream is already active".into() }); }
962 unsafe {
963 let flags = if time_ns.is_some() { SOAPY_SDR_HAS_TIME as i32 } else { 0 };
964 check_ret_error(SoapySDRDevice_activateStream(self.device.inner.ptr, self.handle, flags, time_ns.unwrap_or(0), 0))?;
965 self.active = true;
966 Ok(())
967 }
968 }
969
970 pub fn active(&self) -> bool {
972 self.active
973 }
974
975 pub fn deactivate(&mut self, time_ns: Option<i64>) -> Result<(), Error> {
983 if !self.active { return Err(Error { code: ErrorCode::Other, message: "Stream is not active".into() }); }
984 unsafe {
985 let flags = if time_ns.is_some() { SOAPY_SDR_HAS_TIME as i32 } else { 0 };
986 check_ret_error(SoapySDRDevice_deactivateStream(self.device.inner.ptr, self.handle, flags, time_ns.unwrap_or(0)))?;
987 self.active = false;
988 Ok(())
989 }
990 }
991
992 pub fn read(&mut self, buffers: &mut [&mut [E]], timeout_us: i64) -> Result<usize, Error> {
1001 unsafe {
1002 assert!(buffers.len() == self.nchannels);
1003
1004 let num_samples = buffers.iter().map(|b| b.len()).min().unwrap_or(0);
1005
1006 let buf_ptrs = buffers.iter().map(|b| b.as_ptr()).collect::<Vec<_>>();
1008
1009 self.flags = 0;
1010 let len = len_result(SoapySDRDevice_readStream(
1011 self.device.inner.ptr,
1012 self.handle,
1013 buf_ptrs.as_ptr() as *const *mut _,
1014 num_samples,
1015 &mut self.flags as *mut _,
1016 &mut self.time_ns as *mut _,
1017 timeout_us as _
1018 ))?;
1019
1020 Ok(len as usize)
1021 }
1022 }
1023
1024 pub fn time_ns(&self) -> i64 {
1026 self.time_ns
1027 }
1028}
1029
1030pub struct TxStream<E: StreamSample> {
1037 device: Device,
1038 handle: *mut SoapySDRStream,
1039 nchannels: usize,
1040 active: bool,
1041 phantom: PhantomData<fn(&[E])>,
1042}
1043
1044unsafe impl<E: StreamSample> Send for TxStream<E> {}
1046
1047impl<E: StreamSample> Drop for TxStream<E> {
1048 fn drop(&mut self) {
1049 unsafe {
1050 if self.active {
1051 self.deactivate(None).ok();
1052 }
1053 SoapySDRDevice_closeStream(self.device.inner.ptr, self.handle);
1054 }
1055 }
1056}
1057
1058impl<E: StreamSample> TxStream<E> {
1059 pub fn mtu(&self) -> Result<usize, Error> {
1065 unsafe {
1066 check_error(SoapySDRDevice_getStreamMTU(self.device.inner.ptr, self.handle))
1067 }
1068 }
1069
1070 pub fn activate(&mut self, time_ns: Option<i64>) -> Result<(), Error> {
1077 if self.active { return Err(Error { code: ErrorCode::Other, message: "Stream is already active".into() }); }
1078 unsafe {
1079 let flags = if time_ns.is_some() { SOAPY_SDR_HAS_TIME as i32 } else { 0 };
1080 check_ret_error(SoapySDRDevice_activateStream(self.device.inner.ptr, self.handle, flags, time_ns.unwrap_or(0), 0))?;
1081 self.active = true;
1082 Ok(())
1083 }
1084 }
1085
1086 pub fn active(&self) -> bool {
1088 self.active
1089 }
1090
1091 pub fn deactivate(&mut self, time_ns: Option<i64>) -> Result<(), Error> {
1097 if !self.active { return Err(Error { code: ErrorCode::Other, message: "Stream is not active".into() }); }
1098 unsafe {
1099 let flags = if time_ns.is_some() { SOAPY_SDR_HAS_TIME as i32 } else { 0 };
1100 check_ret_error(SoapySDRDevice_deactivateStream(self.device.inner.ptr, self.handle, flags, time_ns.unwrap_or(0)))?;
1101 self.active = false;
1102 Ok(())
1103 }
1104 }
1105
1106 pub fn write(&mut self, buffers: &[&[E]], at_ns: Option<i64>, end_burst: bool, timeout_us: i64) -> Result<usize, Error> {
1123 unsafe {
1124 assert!(buffers.len() == self.nchannels, "Number of buffers must equal number of channels on stream");
1125
1126 let mut buf_ptrs = Vec::with_capacity(self.nchannels);
1127 let num_elems = buffers.get(0).map_or(0, |x| x.len());
1128 for buf in buffers {
1129 assert_eq!(buf.len(), num_elems, "All buffers must be the same length");
1130 buf_ptrs.push(buf.as_ptr());
1131 }
1132
1133 let mut flags = 0;
1134
1135 if at_ns.is_some() {
1136 flags |= SOAPY_SDR_HAS_TIME as i32;
1137 }
1138
1139 if end_burst {
1140 flags |= SOAPY_SDR_END_BURST as i32;
1141 }
1142
1143 let len = len_result(SoapySDRDevice_writeStream(
1144 self.device.inner.ptr,
1145 self.handle,
1146 buf_ptrs.as_ptr() as *const *const _,
1147 num_elems,
1148 &mut flags as *mut _,
1149 at_ns.unwrap_or(0),
1150 timeout_us as _
1151 ))?;
1152
1153 Ok(len as usize)
1154 }
1155 }
1156
1157 pub fn write_all(&mut self, buffers: &[&[E]], at_ns: Option<i64>, end_burst: bool, timeout_us: i64) -> Result<(), Error> {
1175 let mut buffers = buffers.to_owned();
1176 let mut at_ns = at_ns;
1177
1178 while buffers.get(0).map_or(0, |x| x.len()) > 0 {
1179 let written = self.write(&buffers, at_ns.take(), end_burst, timeout_us)?;
1181
1182 for buf in &mut buffers {
1184 *buf = &buf[written..];
1185 }
1186 }
1187
1188 Ok(())
1189 }
1190
1191 pub fn read_status(&mut self, chan_mask: &mut usize, flags: &mut i32, time_ns: &mut i64, timeout_us: i64) -> Result<usize, Error> {
1207 #[allow(clippy::useless_conversion)]
1209 let timeout_us = timeout_us.try_into().unwrap();
1210 unsafe {
1211 let status = len_result(SoapySDRDevice_readStreamStatus(
1212 self.device.inner.ptr,
1213 self.handle,
1214 chan_mask,
1215 flags,
1216 time_ns,
1217 timeout_us
1218 ))?;
1219
1220 Ok(status as usize)
1221 }
1222 }
1223
1224 }