libbladerf-rs 0.3.0

Fully Rust native BladeRF driver
use crate::bladerf1::BladeRf1;
#[cfg(feature = "xb200")]
use crate::bladerf1::board::xb;
#[cfg(feature = "xb200")]
use crate::bladerf1::board::xb::xb200::Xb200Path;
use crate::bladerf1::hardware::lms6002d;
use crate::bladerf1::hardware::lms6002d::frequency::LmsFreq;
use crate::bladerf1::protocol::NiosPktRetuneRequest;
use crate::channel::Channel;
use crate::error::{Error, Result};
use crate::range::{Range, RangeItem};

#[derive(Clone, Copy, Debug)]
pub enum TuningMode {
    Host,
    Fpga,
}

impl BladeRf1 {
    pub fn set_frequency(
        &mut self,
        channel: Channel,
        #[allow(unused_mut)] mut frequency: u64,
        mode: TuningMode,
    ) -> Result<()> {
        log::trace!("Setting Frequency on channel {channel:?} to {frequency}Hz");
        #[cfg(feature = "xb200")]
        if xb::xb200_is_enabled(&mut self.nios)? {
            let freq_min = lms6002d::frequency::get_frequency_min() as u64;
            if frequency < freq_min {
                log::debug!(
                    "Setting path to Mix (freq {} < min {})",
                    frequency,
                    freq_min
                );
                self.xb200_set_path(channel, Xb200Path::Mix)?;
                self.xb200_auto_filter_selection(channel, frequency)?;
                log::debug!(
                    "Converting frequency: 1248000000 - {} = {}",
                    frequency,
                    1_248_000_000 - frequency
                );
                frequency = 1_248_000_000 - frequency;
            } else {
                log::debug!(
                    "Setting path to Bypass (freq {} >= min {})",
                    frequency,
                    freq_min
                );
                self.xb200_set_path(channel, Xb200Path::Bypass)?;
            }
        }
        match mode {
            TuningMode::Host => {
                lms6002d::frequency::set_frequency(&mut self.nios, channel, frequency)?;
                let band = lms6002d::Band::from(frequency);
                self.band_select(channel, band)?;
            }
            TuningMode::Fpga => {
                self.schedule_retune(channel, NiosPktRetuneRequest::RETUNE_NOW, frequency, None)?;
            }
        }
        Ok(())
    }

    pub fn get_frequency(&mut self, channel: Channel) -> Result<u64> {
        let f = lms6002d::frequency::get_frequency(&mut self.nios, channel)?;
        if f.x == 0 {
            log::error!("LMSFreq.x was zero!");
            return Err(Error::HardwareState("LMSFreq.x was zero"));
        }
        #[allow(unused_mut)]
        let mut frequency_hz: u64 = (&f).into();
        log::trace!("Frequency Hz: {frequency_hz}");
        #[cfg(feature = "xb200")]
        if xb::xb200_is_enabled(&mut self.nios)? {
            let path = self.xb200_get_path(channel)?;
            log::trace!("XB200 path detected: {:?}", path);
            if path == Xb200Path::Mix {
                log::debug!("Mix path - converting: 1248000000 - {}", frequency_hz);
                frequency_hz = 1_248_000_000 - frequency_hz;
            }
        }
        Ok(frequency_hz)
    }

    pub fn get_frequency_range(&mut self) -> Result<Range> {
        #[cfg(feature = "xb200")]
        let freq_min = if xb::xb200_is_enabled(&mut self.nios)? {
            0.0
        } else {
            lms6002d::frequency::get_frequency_min() as f64
        };
        #[cfg(not(feature = "xb200"))]
        let freq_min = lms6002d::frequency::get_frequency_min() as f64;
        let freq_max = lms6002d::frequency::get_frequency_max() as f64;
        Ok(Range {
            items: vec![RangeItem::Step(freq_min, freq_max, 1f64, 1f64)],
        })
    }

    pub fn select_band(&mut self, channel: Channel, frequency: u32) -> Result<()> {
        let band = lms6002d::Band::from(frequency);
        self.band_select(channel, band)
    }

    pub fn schedule_retune(
        &mut self,
        channel: Channel,
        timestamp: u64,
        frequency: u64,
        quick_tune: Option<LmsFreq>,
    ) -> Result<LmsFreq> {
        let (lms_freq, _) =
            self.schedule_retune_with_duration(channel, timestamp, frequency, quick_tune)?;
        Ok(lms_freq)
    }

    pub fn schedule_retune_with_duration(
        &mut self,
        channel: Channel,
        timestamp: u64,
        frequency: u64,
        quick_tune: Option<LmsFreq>,
    ) -> Result<(LmsFreq, u64)> {
        let f = if let Some(qt) = quick_tune {
            qt
        } else {
            #[cfg(feature = "xb200")]
            if xb::xb200_is_enabled(&mut self.nios)? {
                log::info!(
                    "Consider supplying the quick_tune parameter to schedule_retune() when the XB-200 is enabled."
                );
            }
            frequency.try_into()?
        };
        log::trace!("{f:?}");
        let band = if f.flags & lms6002d::LMS_FREQ_FLAGS_LOW_BAND != 0 {
            lms6002d::Band::Low
        } else {
            lms6002d::Band::High
        };
        let tune = if (f.flags & lms6002d::LMS_FREQ_FLAGS_FORCE_VCOCAP) != 0 {
            lms6002d::Tune::Quick
        } else {
            lms6002d::Tune::Normal
        };
        let result = self.nios.nios_retune(
            channel, timestamp, f.nint, f.nfrac, f.freqsel, f.vcocap, band, tune, f.xb_gpio,
        )?;
        Ok((f, result.duration))
    }

    pub fn cancel_scheduled_retunes(&mut self, channel: Channel) -> Result<()> {
        self.nios.nios_retune(
            channel,
            NiosPktRetuneRequest::CLEAR_QUEUE,
            0u16,
            0u32,
            0,
            0,
            lms6002d::Band::Low,
            lms6002d::Tune::Normal,
            0,
        )?;
        Ok(())
    }
}