1#![allow(dead_code)]
2
3mod consts;
4pub mod debug;
5mod error;
6pub mod info;
7mod rx;
8mod sweep;
9mod tx;
10use std::ops::Range;
11
12use bytemuck::Pod;
13use nusb::transfer::{ControlIn, ControlOut, ControlType, Recipient};
14use std::sync::mpsc;
15
16use crate::consts::*;
17use crate::debug::Debug;
18use crate::info::Info;
19
20pub use crate::error::{Error, StateChangeError};
21pub use crate::rx::Receive;
22pub use crate::sweep::{Sweep, SweepBuf, SweepParams};
23pub use crate::tx::Transmit;
24
25pub type ComplexI8 = num_complex::Complex<i8>;
27
28pub const PORT_A1: u8 = 0;
30pub const PORT_A2: u8 = 1;
32pub const PORT_A3: u8 = 2;
34pub const PORT_A4: u8 = 3;
36pub const PORT_B1: u8 = 4;
38pub const PORT_B2: u8 = 5;
40pub const PORT_B3: u8 = 6;
42pub const PORT_B4: u8 = 7;
44
45pub struct Buffer {
54 buf: Vec<u8>,
55 pool: mpsc::Sender<Vec<u8>>,
56}
57
58impl Buffer {
59 pub(crate) fn new(buf: Vec<u8>, pool: mpsc::Sender<Vec<u8>>) -> Self {
60 assert!(buf.len() & 0x1FF == 0);
61 Self { buf, pool }
62 }
63
64 pub(crate) fn into_vec(mut self) -> Vec<u8> {
65 core::mem::take(&mut self.buf)
66 }
67
68 pub fn capacity(&self) -> usize {
70 (self.buf.capacity() & !0x1FF) / core::mem::size_of::<ComplexI8>()
73 }
74
75 pub fn clear(&mut self) {
77 self.buf.clear();
78 }
79
80 pub fn len(&self) -> usize {
82 self.buf.len() / core::mem::size_of::<ComplexI8>()
83 }
84
85 pub fn is_empty(&self) -> bool {
87 self.buf.is_empty()
88 }
89
90 pub fn remaining_capacity(&self) -> usize {
92 self.capacity() - self.len()
93 }
94
95 pub fn extend_from_slice(&mut self, slice: &[ComplexI8]) {
100 assert!(self.remaining_capacity() >= slice.len());
101 let slice =
102 unsafe { core::slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * 2) };
103 self.buf.extend_from_slice(slice);
104 }
105
106 pub fn push(&mut self, val: ComplexI8) {
111 assert!(self.remaining_capacity() > 0);
112 let slice: &[u8; 2] = unsafe { &*((&val) as *const ComplexI8 as *const [u8; 2]) };
113 self.buf.extend_from_slice(slice);
114 }
115
116 pub fn bytes(&self) -> &[u8] {
118 &self.buf
119 }
120
121 pub fn bytes_mut(&mut self) -> &mut [u8] {
123 &mut self.buf
124 }
125
126 pub fn samples(&self) -> &[ComplexI8] {
128 let buf: &[u8] = &self.buf;
129 unsafe {
134 core::slice::from_raw_parts(
135 buf.as_ptr() as *const ComplexI8,
136 self.buf.len() / core::mem::size_of::<ComplexI8>(),
137 )
138 }
139 }
140
141 pub fn samples_mut(&mut self) -> &mut [ComplexI8] {
143 let buf: &mut [u8] = &mut self.buf;
144 unsafe {
149 core::slice::from_raw_parts_mut(
150 buf.as_mut_ptr() as *mut ComplexI8,
151 self.buf.len() / core::mem::size_of::<ComplexI8>(),
152 )
153 }
154 }
155}
156
157impl Drop for Buffer {
158 fn drop(&mut self) {
159 let inner = core::mem::take(&mut self.buf);
160 if inner.capacity() > 0 {
161 let _ = self.pool.send(inner);
162 }
163 }
164}
165
166#[derive(Clone, Debug)]
170pub struct BiasTSetting {
171 pub tx: BiasTMode,
173 pub rx: BiasTMode,
175 pub off: BiasTMode,
177}
178
179#[derive(Clone, Copy, Debug, PartialEq, Eq)]
183pub enum BiasTMode {
184 NoChange,
185 Enable,
186 Disable,
187}
188
189impl BiasTMode {
190 fn as_u16(self) -> u16 {
191 match self {
192 Self::NoChange => 0x0,
193 Self::Disable => 0x2,
194 Self::Enable => 0x3,
195 }
196 }
197}
198
199#[repr(u8)]
203#[derive(Clone, Copy, Debug, PartialEq, Eq)]
204pub enum RfPathFilter {
205 Bypass = 0,
207 LowPass = 1,
209 HighPass = 2,
211}
212
213impl std::fmt::Display for RfPathFilter {
214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 match self {
216 Self::Bypass => f.write_str("mixer bypass"),
217 Self::LowPass => f.write_str("low pass filter"),
218 Self::HighPass => f.write_str("high pass filter"),
219 }
220 }
221}
222
223#[derive(Clone, Copy, Debug, PartialEq, Eq)]
236#[repr(u16)]
237pub enum OperacakeMode {
238 Manual = 0,
239 Frequency = 1,
240 Time = 2,
241}
242
243#[derive(Clone, Copy, Debug)]
246pub struct OperacakeFreq {
247 pub min: u16,
249 pub max: u16,
251 pub port: u8,
253}
254
255#[derive(Clone, Copy, Debug)]
268pub struct OperacakeDwell {
269 pub dwell: u32,
271 pub port: u8,
273}
274
275pub struct HackRf {
277 pub(crate) interface: nusb::Interface,
278 pub(crate) version: u16,
279 pub(crate) ty: HackRfType,
280}
281
282pub struct HackRfDescriptor {
284 info: nusb::DeviceInfo,
285}
286
287#[derive(Clone, Copy, Debug, PartialEq, Eq)]
289pub enum HackRfType {
290 Jawbreaker,
291 One,
292 Rad1o,
293}
294
295impl std::fmt::Display for HackRfType {
296 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
297 match self {
298 Self::Jawbreaker => f.write_str("Jawbreaker"),
299 Self::One => f.write_str("HackRF One"),
300 Self::Rad1o => f.write_str("rad1o"),
301 }
302 }
303}
304
305impl HackRfDescriptor {
306 pub fn serial(&self) -> Option<&str> {
308 self.info.serial_number()
309 }
310
311 pub fn radio_type(&self) -> HackRfType {
313 match self.info.product_id() {
314 HACKRF_JAWBREAKER_USB_PID => HackRfType::Jawbreaker,
315 HACKRF_ONE_USB_PID => HackRfType::One,
316 RAD1O_USB_PID => HackRfType::Rad1o,
317 _ => panic!("Created a HackRfDescriptor without using a known product ID"),
318 }
319 }
320
321 pub fn open(self) -> Result<HackRf, std::io::Error> {
323 let version = self.info.device_version();
324 let ty = self.radio_type();
325 let device = self.info.open()?;
326 #[cfg(not(target_os = "windows"))]
327 {
328 if device.active_configuration()?.configuration_value() != 1 {
329 device.detach_kernel_driver(0)?;
330 device.set_configuration(1)?;
331 }
332 }
333 let interface = device.detach_and_claim_interface(0)?;
334
335 Ok(HackRf {
336 interface,
337 version,
338 ty,
339 })
340 }
341}
342
343impl TryFrom<nusb::DeviceInfo> for HackRfDescriptor {
346 type Error = &'static str;
347 fn try_from(value: nusb::DeviceInfo) -> Result<Self, Self::Error> {
348 if value.vendor_id() == HACKRF_USB_VID {
349 if matches!(
350 value.product_id(),
351 HACKRF_JAWBREAKER_USB_PID | HACKRF_ONE_USB_PID | RAD1O_USB_PID
352 ) {
353 Ok(HackRfDescriptor { info: value })
354 } else {
355 Err("VID recognized, PID not recognized")
356 }
357 } else {
358 Err("VID doesn't match for HackRF")
359 }
360 }
361}
362
363pub fn list_hackrf_devices() -> Result<Vec<HackRfDescriptor>, std::io::Error> {
365 Ok(nusb::list_devices()?
366 .filter(|d| {
367 d.vendor_id() == HACKRF_USB_VID
368 && matches!(
369 d.product_id(),
370 HACKRF_JAWBREAKER_USB_PID | HACKRF_ONE_USB_PID | RAD1O_USB_PID
371 )
372 })
373 .map(|d| HackRfDescriptor { info: d })
374 .collect::<Vec<HackRfDescriptor>>())
375}
376
377pub fn open_hackrf() -> Result<HackRf, std::io::Error> {
381 list_hackrf_devices()?
382 .into_iter()
383 .next()
384 .ok_or_else(|| std::io::Error::other("No HackRF devices"))?
385 .open()
386}
387
388impl HackRf {
389 fn api_check(&self, needed: u16) -> Result<(), Error> {
390 if self.version < needed {
391 Err(Error::ApiVersion {
392 needed,
393 actual: self.version,
394 })
395 } else {
396 Ok(())
397 }
398 }
399
400 async fn write_u32(&self, req: ControlRequest, val: u32) -> Result<(), Error> {
401 Ok(self
402 .interface
403 .control_out(ControlOut {
404 control_type: ControlType::Vendor,
405 recipient: Recipient::Device,
406 request: req as u8,
407 value: (val & 0xffff) as u16,
408 index: (val >> 16) as u16,
409 data: &[],
410 })
411 .await
412 .status?)
413 }
414
415 async fn write_u16(&self, req: ControlRequest, idx: u16, val: u16) -> Result<(), Error> {
416 Ok(self
417 .interface
418 .control_out(ControlOut {
419 control_type: ControlType::Vendor,
420 recipient: Recipient::Device,
421 request: req as u8,
422 value: val,
423 index: idx,
424 data: &[],
425 })
426 .await
427 .status?)
428 }
429
430 async fn read_u16(&self, req: ControlRequest, idx: u16) -> Result<u16, Error> {
431 let ret = self
432 .interface
433 .control_in(ControlIn {
434 control_type: ControlType::Vendor,
435 recipient: Recipient::Device,
436 request: req as u8,
437 value: 0,
438 index: idx,
439 length: 2,
440 })
441 .await
442 .into_result()?;
443 let ret: [u8; 2] = ret.as_slice().try_into().map_err(|_| Error::ReturnData)?;
444 Ok(u16::from_le_bytes(ret))
445 }
446
447 async fn write_u8(&self, req: ControlRequest, idx: u16, val: u8) -> Result<(), Error> {
448 self.write_u16(req, idx, val as u16).await?;
449 Ok(())
450 }
451
452 async fn read_u8(&self, req: ControlRequest, idx: u16) -> Result<u8, Error> {
453 let ret = self
454 .interface
455 .control_in(ControlIn {
456 control_type: ControlType::Vendor,
457 recipient: Recipient::Device,
458 request: req as u8,
459 value: 0,
460 index: idx,
461 length: 1,
462 })
463 .await
464 .into_result()?;
465 ret.first().copied().ok_or(Error::ReturnData)
466 }
467
468 async fn write_bytes(&self, req: ControlRequest, data: &[u8]) -> Result<(), Error> {
469 self.interface
470 .control_out(ControlOut {
471 control_type: ControlType::Vendor,
472 recipient: Recipient::Device,
473 request: req as u8,
474 value: 0,
475 index: 0,
476 data,
477 })
478 .await
479 .into_result()?;
480 Ok(())
481 }
482
483 async fn read_bytes(&self, req: ControlRequest, len: usize) -> Result<Vec<u8>, Error> {
484 assert!(len < u16::MAX as usize);
485 Ok(self
486 .interface
487 .control_in(ControlIn {
488 control_type: ControlType::Vendor,
489 recipient: Recipient::Device,
490 request: req as u8,
491 value: 0,
492 index: 0,
493 length: len as u16,
494 })
495 .await
496 .into_result()?)
497 }
498
499 async fn read_struct<T>(&self, req: ControlRequest) -> Result<T, Error>
500 where
501 T: Pod,
502 {
503 let size = core::mem::size_of::<T>();
504 let mut resp = self.read_bytes(req, size).await?;
505 if resp.len() < size {
506 return Err(Error::ReturnData);
507 }
508 resp.truncate(size);
509 Ok(bytemuck::pod_read_unaligned(&resp))
510 }
511
512 async fn set_transceiver_mode(&self, mode: TransceiverMode) -> Result<(), Error> {
513 self.write_u16(ControlRequest::SetTransceiverMode, 0, mode as u16)
514 .await
515 }
516
517 pub async fn set_baseband_filter_bandwidth(&self, bandwidth_hz: u32) -> Result<(), Error> {
529 let bandwidth_hz = baseband_filter_bw(bandwidth_hz);
530 self.write_u32(ControlRequest::BasebandFilterBandwidthSet, bandwidth_hz)
531 .await
532 }
533
534 pub async fn set_tx_underrun_limit(&self, val: u32) -> Result<(), Error> {
540 self.api_check(0x0106)?;
541 self.write_u32(ControlRequest::SetTxUnderrunLimit, val)
542 .await
543 }
544
545 pub async fn set_rx_overrun_limit(&self, val: u32) -> Result<(), Error> {
552 self.api_check(0x0106)?;
553 self.write_u32(ControlRequest::SetRxOverrunLimit, val).await
554 }
555
556 pub fn debug(&mut self) -> Debug<'_> {
558 Debug::new(self)
559 }
560
561 pub fn info(&self) -> Info<'_> {
563 Info::new(self)
564 }
565
566 pub async fn set_freq(&self, freq_hz: u64) -> Result<(), Error> {
572 const ONE_MHZ: u64 = 1_000_000;
573 #[repr(C)]
574 #[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
575 struct FreqParams {
576 mhz: u32,
577 hz: u32,
578 }
579 let mhz = freq_hz / ONE_MHZ;
580 let hz = freq_hz % ONE_MHZ;
581 let params = FreqParams {
582 mhz: (mhz as u32).to_le(),
583 hz: (hz as u32).to_le(),
584 };
585
586 self.write_bytes(ControlRequest::SetFreq, bytemuck::bytes_of(¶ms))
587 .await
588 }
589
590 pub async fn set_freq_explicit(
603 &self,
604 if_freq_hz: u64,
605 lo_freq_hz: u64,
606 path: RfPathFilter,
607 ) -> Result<(), Error> {
608 #[repr(C)]
609 #[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
610 struct FreqParams {
611 if_freq_hz: u64,
612 lo_freq_hz: u64,
613 path: u8,
614 reserved: [u8; 7],
615 }
616
617 const IF_RANGE: Range<u64> = Range {
618 start: 2_000_000_000,
619 end: 3_000_000_001,
620 };
621 const LO_RANGE: Range<u64> = Range {
622 start: 84_375_000,
623 end: 5_400_000_001,
624 };
625
626 if !IF_RANGE.contains(&if_freq_hz) {
627 return Err(Error::TuningRange {
628 range: IF_RANGE,
629 val: if_freq_hz,
630 });
631 }
632 if path != RfPathFilter::Bypass && !LO_RANGE.contains(&lo_freq_hz) {
633 return Err(Error::TuningRange {
634 range: LO_RANGE,
635 val: lo_freq_hz,
636 });
637 }
638
639 let params = FreqParams {
640 if_freq_hz: if_freq_hz.to_le(),
641 lo_freq_hz: lo_freq_hz.to_le(),
642 path: path as u8,
643 reserved: [0u8; 7],
644 };
645
646 self.write_bytes(ControlRequest::SetFreqExplicit, bytemuck::bytes_of(¶ms))
647 .await
648 }
649
650 pub async fn set_sample_rate_manual(&self, freq_hz: u32, divider: u32) -> Result<(), Error> {
664 #[repr(C)]
665 #[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
666 struct FracRateParams {
667 freq_hz: u32,
668 divider: u32,
669 }
670
671 const DIV_RANGE: Range<u32> = Range { start: 1, end: 32 };
672 if !DIV_RANGE.contains(÷r) {
673 return Err(Error::ValueRange {
674 range: DIV_RANGE,
675 val: divider,
676 });
677 }
678
679 let params = FracRateParams {
680 freq_hz: freq_hz.to_le(),
681 divider: divider.to_le(),
682 };
683
684 self.write_bytes(ControlRequest::SampleRateSet, bytemuck::bytes_of(¶ms))
685 .await?;
686
687 let filter_bw = baseband_filter_bw(freq_hz * 3 / (divider * 4));
688 self.set_baseband_filter_bandwidth(filter_bw).await?;
689 Ok(())
690 }
691
692 pub async fn set_sample_rate(&self, freq: f64) -> Result<(), Error> {
704 let freq = freq.clamp(2e6, 20e6);
705
706 let mut freq_hz = 0;
707 let mut divider = 1;
708 let mut diff = f64::MAX;
709
710 for i in 1u32..32 {
713 let new_freq_hz = (freq * (i as f64)).round() as u32;
714 let new_diff = ((freq_hz as f64) / (i as f64) - freq).abs();
715 if new_diff < diff {
716 freq_hz = new_freq_hz;
717 divider = i;
718 diff = new_diff;
719 }
720 }
721
722 self.set_sample_rate_manual(freq_hz, divider).await
723 }
724
725 pub async fn set_amp_enable(&self, enable: bool) -> Result<(), Error> {
730 self.write_u16(ControlRequest::AmpEnable, 0, enable as u16)
731 .await
732 }
733
734 pub async fn set_lna_gain(&self, value: u16) -> Result<(), Error> {
740 if value > 40 {
741 return Err(Error::ValueRange {
742 range: Range { start: 0, end: 41 },
743 val: value as u32,
744 });
745 }
746
747 let ret = self
748 .read_u8(ControlRequest::SetLnaGain, value & (!0x07))
749 .await?;
750 if ret != 0 {
751 return Err(Error::ReturnData);
752 }
753 Ok(())
754 }
755
756 pub async fn set_vga_gain(&self, value: u16) -> Result<(), Error> {
762 if value > 62 {
763 return Err(Error::ValueRange {
764 range: Range { start: 0, end: 63 },
765 val: value as u32,
766 });
767 }
768
769 let ret = self
770 .read_u8(ControlRequest::SetLnaGain, value & (!0x01))
771 .await?;
772 if ret != 0 {
773 return Err(Error::ReturnData);
774 }
775 Ok(())
776 }
777
778 pub async fn set_txvga_gain(&self, value: u16) -> Result<(), Error> {
783 if value > 47 {
784 return Err(Error::ValueRange {
785 range: Range { start: 0, end: 48 },
786 val: value as u32,
787 });
788 }
789
790 let ret = self.read_u8(ControlRequest::SetLnaGain, value).await?;
791 if ret != 0 {
792 return Err(Error::ReturnData);
793 }
794 Ok(())
795 }
796
797 pub async fn set_antenna_enable(&self, enable: bool) -> Result<(), Error> {
806 self.write_u16(ControlRequest::AntennaEnable, 0, enable as u16)
807 .await
808 }
809
810 pub async fn set_hw_sync_mode(&self, enable: bool) -> Result<(), Error> {
820 self.api_check(0x0102)?;
821 self.write_u16(ControlRequest::SetHwSyncMode, 0, enable as u16)
822 .await
823 }
824
825 pub async fn operacake_boards(&self) -> Result<Vec<u8>, Error> {
829 self.api_check(0x0105)?;
830 let mut resp = self
831 .read_bytes(ControlRequest::OperacakeGetBoards, 8)
832 .await?;
833 resp.retain(|&x| x != 0xFF);
834 Ok(resp)
835 }
836
837 pub async fn operacake_set_mode(
844 &self,
845 address: u8,
846 setting: OperacakeMode,
847 ) -> Result<(), Error> {
848 self.api_check(0x0105)?;
849 if address > 7 {
850 return Err(Error::InvalidParameter("Operacake address is out of range"));
851 }
852 self.write_u8(ControlRequest::OperacakeSetMode, setting as u16, address)
853 .await
854 }
855
856 pub async fn operacake_get_mode(&self, address: u8) -> Result<OperacakeMode, Error> {
860 self.api_check(0x0105)?;
861 if address > 7 {
862 return Err(Error::InvalidParameter("Operacake address is out of range"));
863 }
864 let ret = self
865 .interface
866 .control_in(ControlIn {
867 control_type: ControlType::Vendor,
868 recipient: Recipient::Device,
869 request: ControlRequest::OperacakeGetMode as u8,
870 value: address as u16,
871 index: 0,
872 length: 1,
873 })
874 .await
875 .into_result()?;
876 let ret = ret.first().ok_or(Error::ReturnData)?;
877 match ret {
878 0 => Ok(OperacakeMode::Manual),
879 1 => Ok(OperacakeMode::Frequency),
880 2 => Ok(OperacakeMode::Time),
881 _ => Err(Error::ReturnData),
882 }
883 }
884
885 pub async fn operacake_config_manual(&self, address: u8, a: u8, b: u8) -> Result<(), Error> {
892 self.api_check(0x0102)?;
893 if address > 7 {
894 return Err(Error::InvalidParameter("Operacake address is out of range"));
895 }
896
897 if a > 7 || b > 7 {
898 return Err(Error::InvalidParameter(
899 "One or more port numbers is out of range (0-7)",
900 ));
901 }
902 if (a < 4 && b < 4) || (a >= 4 && b >= 4) {
903 return Err(Error::InvalidParameter(
904 "A0 & B0 ports are using same quad of multiplexed ports",
905 ));
906 }
907
908 let a = a as u16;
909 let b = b as u16;
910 self.write_u8(ControlRequest::OperacakeSetPorts, a | (b << 8), address)
911 .await
912 }
913
914 pub async fn operacake_config_freq(&self, freqs: &[OperacakeFreq]) -> Result<(), Error> {
921 self.api_check(0x0103)?;
922 if freqs.len() > 8 {
923 return Err(Error::InvalidParameter(
924 "Operacake can only support 8 frequency bands max",
925 ));
926 }
927 let mut data = Vec::with_capacity(5 * freqs.len());
928 for f in freqs {
929 if f.port > 7 {
930 return Err(Error::InvalidParameter(
931 "Operacake frequency band port selection is out of range",
932 ));
933 }
934 data.push((f.min >> 8) as u8);
935 data.push((f.min & 0xFF) as u8);
936 data.push((f.max >> 8) as u8);
937 data.push((f.max & 0xFF) as u8);
938 data.push(f.port);
939 }
940
941 self.write_bytes(ControlRequest::OperacakeSetRanges, &data)
942 .await
943 }
944
945 pub async fn operacake_config_time(&self, times: &[OperacakeDwell]) -> Result<(), Error> {
952 self.api_check(0x0105)?;
953 if times.len() > 16 {
954 return Err(Error::InvalidParameter(
955 "Operacake can only support 16 time slices max",
956 ));
957 }
958 let mut data = Vec::with_capacity(5 * times.len());
959 for t in times {
960 if t.port > 7 {
961 return Err(Error::InvalidParameter(
962 "Operacake time slice port selection is out of range",
963 ));
964 }
965 data.extend_from_slice(&t.dwell.to_le_bytes());
966 data.push(t.port);
967 }
968 self.write_bytes(ControlRequest::OperacakeSetDwellTimes, &data)
969 .await
970 }
971
972 pub async fn reset(&self) -> Result<(), Error> {
976 self.api_check(0x0102)?;
977 self.write_u16(ControlRequest::Reset, 0, 0).await
978 }
979
980 pub async fn clkout_enable(&self, enable: bool) -> Result<(), Error> {
984 self.api_check(0x0103)?;
985 self.write_u16(ControlRequest::ClkoutEnable, 0, enable as u16)
986 .await
987 }
988
989 pub async fn clkin_status(&self) -> Result<bool, Error> {
995 self.api_check(0x0106)?;
996 Ok(self.read_u8(ControlRequest::GetClkinStatus, 0).await? != 0)
997 }
998
999 pub async fn operacake_gpio_test(&self, address: u8) -> Result<u16, Error> {
1015 self.api_check(0x0103)?;
1016 if address > 7 {
1017 return Err(Error::InvalidParameter("Operacake address is out of range"));
1018 }
1019 let ret = self
1020 .interface
1021 .control_in(ControlIn {
1022 control_type: ControlType::Vendor,
1023 recipient: Recipient::Device,
1024 request: ControlRequest::OperacakeGpioTest as u8,
1025 value: address as u16,
1026 index: 0,
1027 length: 2,
1028 })
1029 .await
1030 .into_result()?;
1031 let ret: [u8; 2] = ret.as_slice().try_into().map_err(|_| Error::ReturnData)?;
1032 Ok(u16::from_le_bytes(ret))
1033 }
1034
1035 pub async fn set_ui_enable(&self, val: u8) -> Result<(), Error> {
1039 self.api_check(0x0104)?;
1040 self.write_u8(ControlRequest::UiEnable, 0, val).await
1041 }
1042
1043 pub async fn set_leds(&self, state: u8) -> Result<(), Error> {
1058 self.api_check(0x0107)?;
1059 self.write_u8(ControlRequest::SetLeds, 0, state).await
1060 }
1061
1062 pub async fn set_user_bias_t_opts(&self, opts: BiasTSetting) -> Result<(), Error> {
1065 self.api_check(0x0108)?;
1066 let state: u16 =
1067 0x124 | opts.off.as_u16() | (opts.rx.as_u16() << 3) | (opts.tx.as_u16() << 6);
1068 self.write_u16(ControlRequest::SetUserBiasTOpts, 0, state)
1069 .await
1070 }
1071
1072 pub async fn start_rx(self, transfer_size: usize) -> Result<Receive, StateChangeError> {
1074 Receive::new(self, transfer_size).await
1075 }
1076
1077 pub async fn start_rx_sweep(self, params: &SweepParams) -> Result<Sweep, StateChangeError> {
1079 Sweep::new(self, params).await
1080 }
1081
1082 pub async fn start_rx_sweep_custom_sample_rate(
1084 self,
1085 params: &SweepParams,
1086 ) -> Result<Sweep, StateChangeError> {
1087 Sweep::new_with_custom_sample_rate(self, params).await
1088 }
1089
1090 pub async fn start_tx(self, max_transfer_size: usize) -> Result<Transmit, StateChangeError> {
1092 Transmit::new(self, max_transfer_size).await
1093 }
1094
1095 pub async fn turn_off(&self) -> Result<(), Error> {
1097 self.set_transceiver_mode(TransceiverMode::Off).await
1098 }
1099}
1100
1101fn baseband_filter_bw(freq: u32) -> u32 {
1102 const MAX2837_FT: &[u32] = &[
1103 1750000, 2500000, 3500000, 5000000, 5500000, 6000000, 7000000, 8000000, 9000000, 10000000,
1104 12000000, 14000000, 15000000, 20000000, 24000000, 28000000,
1105 ];
1106
1107 MAX2837_FT
1108 .iter()
1109 .rev()
1110 .find(|f| freq >= **f)
1111 .copied()
1112 .unwrap_or(MAX2837_FT[0])
1113}
1114
1115#[cfg(test)]
1116mod tests {
1117 use crate::baseband_filter_bw;
1118
1119 #[test]
1120 fn baseband_filter() {
1121 assert_eq!(baseband_filter_bw(1000), 1750000);
1122 assert_eq!(baseband_filter_bw(30_000_000), 28_000_000);
1123 assert_eq!(baseband_filter_bw(3_000_000), 2_500_000);
1124 }
1125}