autd3-driver 38.1.0

AUTD3 driver
Documentation
use autd3_core::{firmware::Segment, link::RxMessage};

/// FPGA state.
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FPGAState(u8);

impl FPGAState {
    const THERMAL_ASSERT_BIT: u8 = 1 << 0;
    const CURRENT_MOD_SEGMENT_BIT: u8 = 1 << 1;
    const CURRENT_STM_SEGMENT_BIT: u8 = 1 << 2;
    const CURRENT_GAIN_SEGMENT_BIT: u8 = 1 << 2;
    const IS_GAIN_MODE_BIT: u8 = 1 << 3;
    const READS_FPGA_STATE_ENABLED: u8 = 1 << 7;

    const fn contains(&self, bit: u8) -> bool {
        (self.0 & bit) != 0
    }
}

impl FPGAState {
    #[doc(hidden)]
    pub const fn bits(&self) -> u8 {
        self.0
    }

    /// `true` if the thermal sensor is asserted.
    #[must_use]
    pub const fn is_thermal_assert(&self) -> bool {
        self.contains(Self::THERMAL_ASSERT_BIT)
    }

    /// The current Modulation segment.
    #[must_use]
    pub const fn current_mod_segment(&self) -> Segment {
        if self.contains(Self::CURRENT_MOD_SEGMENT_BIT) {
            Segment::S1
        } else {
            Segment::S0
        }
    }

    /// The current STM segment. `None` if the current mode is not STM.
    #[must_use]
    pub const fn current_stm_segment(&self) -> Option<Segment> {
        if !self.is_stm_mode() {
            return None;
        }
        if self.contains(Self::CURRENT_STM_SEGMENT_BIT) {
            Some(Segment::S1)
        } else {
            Some(Segment::S0)
        }
    }

    /// The current Gain segment. `None` if the current mode is not Gain.
    #[must_use]
    pub const fn current_gain_segment(&self) -> Option<Segment> {
        if !self.is_gain_mode() {
            return None;
        }
        if self.contains(Self::CURRENT_GAIN_SEGMENT_BIT) {
            Some(Segment::S1)
        } else {
            Some(Segment::S0)
        }
    }

    /// `true` if the current mode is Gain.
    #[must_use]
    pub const fn is_gain_mode(&self) -> bool {
        self.contains(Self::IS_GAIN_MODE_BIT)
    }

    /// `true` if the current mode is STM.
    #[must_use]
    pub const fn is_stm_mode(&self) -> bool {
        !self.is_gain_mode()
    }

    #[doc(hidden)]
    #[must_use]
    pub fn from_rx(msg: &RxMessage) -> Option<Self> {
        let flag = FPGAState(msg.data());
        if !flag.contains(Self::READS_FPGA_STATE_ENABLED) {
            return None;
        }
        Some(flag)
    }
}

#[cfg(test)]
mod tests {
    use std::mem::size_of;

    use super::*;

    #[test]
    fn size() {
        assert_eq!(1, size_of::<FPGAState>());
    }

    #[test]
    fn bits() {
        let state = FPGAState(0b1010_1101);
        assert_eq!(0b1010_1101, state.bits());
    }

    #[rstest::rstest]
    #[case(false, 0b0)]
    #[case(true, 0b1)]
    fn is_thermal_assert(#[case] expected: bool, #[case] state: u8) {
        assert_eq!(expected, FPGAState(state).is_thermal_assert());
    }

    #[rstest::rstest]
    #[case(Segment::S0, 0b00)]
    #[case(Segment::S1, 0b10)]
    fn current_mod_segment(#[case] expected: Segment, #[case] state: u8) {
        assert_eq!(expected, FPGAState(state).current_mod_segment());
    }

    #[rstest::rstest]
    #[case(false, 0b0000)]
    #[case(true, 0b1000)]
    fn is_gain_mode(#[case] expected: bool, #[case] state: u8) {
        assert_eq!(expected, FPGAState(state).is_gain_mode());
    }

    #[rstest::rstest]
    #[case(false, 0b1000)]
    #[case(true, 0b0000)]
    fn is_stm_mode(#[case] expected: bool, #[case] state: u8) {
        assert_eq!(expected, FPGAState(state).is_stm_mode());
    }

    #[rstest::rstest]
    #[case(None, 0b1000)]
    #[case(Some(Segment::S0), 0b0000)]
    #[case(Some(Segment::S1), 0b0100)]
    fn current_stm_segment(#[case] expected: Option<Segment>, #[case] state: u8) {
        assert_eq!(expected, FPGAState(state).current_stm_segment());
    }

    #[rstest::rstest]
    #[case(None, 0b0000)]
    #[case(Some(Segment::S0), 0b1000)]
    #[case(Some(Segment::S1), 0b1100)]
    fn current_gain_segment(#[case] expected: Option<Segment>, #[case] state: u8) {
        assert_eq!(expected, FPGAState(state).current_gain_segment());
    }
}