autd3-rs 0.3.0

Core async client library for the AUTD3 phased-array kit.
Documentation
use super::Command;
use crate::datagram::DatagramBuilder;
use crate::operation::{ChangeModulationBank, ConfigModulation, WriteModulationBuffer};
use crate::value::{LoopBehavior, ModulationBank, SamplingConfig, TransitionMode};

#[derive(Clone, Copy, Debug)]
pub struct Modulation<'a> {
    pub bank: ModulationBank,
    pub config: SamplingConfig,
    pub data: &'a [u8],
    pub loop_behavior: LoopBehavior,
    pub transition_mode: TransitionMode,
}

impl<'a> Modulation<'a> {
    #[must_use]
    pub fn new(config: SamplingConfig, data: &'a [u8]) -> Self {
        Self::with_bank(ModulationBank::B0, config, data)
    }

    #[must_use]
    pub fn with_bank(bank: ModulationBank, config: SamplingConfig, data: &'a [u8]) -> Self {
        Self {
            bank,
            config,
            data,
            loop_behavior: LoopBehavior::Infinite,
            transition_mode: TransitionMode::Immediate,
        }
    }
}

impl<'a> Command<'a> for Modulation<'a> {
    fn expand(self, builder: &mut DatagramBuilder<'a>) {
        let size = self.data.len();
        builder
            .push(WriteModulationBuffer {
                bank: self.bank,
                offset: 0,
                data: self.data,
            })
            .push(ConfigModulation {
                bank: self.bank,
                config: self.config,
                size,
                loop_behavior: self.loop_behavior,
            })
            .push(ChangeModulationBank {
                bank: self.bank,
                transition_mode: self.transition_mode,
            });
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::protocol::Cmd;

    #[test]
    fn modulation_expands_with_size_from_data() {
        let data = vec![0x80u8; 20];
        let mut b = DatagramBuilder::new(1);
        b.push(Modulation::with_bank(
            ModulationBank::B1,
            SamplingConfig::FREQ_4K,
            &data,
        ));
        let datagrams = b.build().unwrap();

        assert_eq!(datagrams.len(), 3);
        assert_eq!(
            datagrams.frame(0).unwrap().datagrams()[0].cmd,
            Cmd::WriteModulationBuffer
        );
        let cfg = datagrams.frame(1).unwrap();
        assert_eq!(cfg.datagrams()[0].cmd, Cmd::ConfigModulation);
        assert_eq!(cfg.datagrams()[0].payload[0], 1, "bank B1");
        assert_eq!(&cfg.datagrams()[0].payload[4..8], &20u32.to_le_bytes());

        let chg = datagrams.frame(2).unwrap();
        assert_eq!(chg.datagrams()[0].cmd, Cmd::ChangeModulationBank);
        assert_eq!(chg.datagrams()[0].payload[0], 1, "bank B1");
        assert_eq!(chg.datagrams()[0].payload[1], 0xFF, "IMMEDIATE");
    }

    #[test]
    fn modulation_defaults_to_infinite_loop() {
        let data = vec![0x80u8; 4];
        let mut b = DatagramBuilder::new(1);
        b.push(Modulation::new(SamplingConfig::FREQ_4K, &data));
        let datagrams = b.build().unwrap();

        let cfg = datagrams.frame(1).unwrap();
        assert_eq!(cfg.datagrams()[0].cmd, Cmd::ConfigModulation);
        assert_eq!(&cfg.datagrams()[0].payload[8..10], &0xFFFFu16.to_le_bytes());
    }

    #[test]
    fn modulation_defaults_to_immediate_transition() {
        let data = vec![0x80u8; 4];
        let mut b = DatagramBuilder::new(1);
        b.push(Modulation::new(SamplingConfig::FREQ_4K, &data));
        let datagrams = b.build().unwrap();

        let chg = datagrams.frame(2).unwrap();
        assert_eq!(chg.datagrams()[0].cmd, Cmd::ChangeModulationBank);
        assert_eq!(chg.datagrams()[0].payload[1], 0xFF, "IMMEDIATE");
        assert_eq!(&chg.datagrams()[0].payload[2..10], &0u64.to_le_bytes());
    }

    #[test]
    fn modulation_transition_mode_encodes_into_change_bank() {
        use crate::value::{DcSysTime, TransitionMode};

        let data = vec![0x80u8; 4];
        let mut b = DatagramBuilder::new(1);
        b.push(Modulation {
            transition_mode: TransitionMode::SysTime {
                time: DcSysTime::from_nanos(0xDEAD_BEEF),
                margin: None,
            },
            ..Modulation::new(SamplingConfig::FREQ_4K, &data)
        });
        let datagrams = b.build().unwrap();

        let chg = datagrams.frame(2).unwrap();
        assert_eq!(chg.datagrams()[0].payload[1], 0x01, "SYS_TIME");
        assert_eq!(
            &chg.datagrams()[0].payload[2..10],
            &0xDEAD_BEEFu64.to_le_bytes()
        );
    }

    #[test]
    fn modulation_finite_loop_encodes_rep() {
        use crate::value::LoopBehavior;
        use core::num::NonZeroU16;

        let data = vec![0x80u8; 4];
        let mut b = DatagramBuilder::new(1);
        b.push(Modulation {
            loop_behavior: LoopBehavior::Finite(NonZeroU16::new(10).unwrap()),
            ..Modulation::new(SamplingConfig::FREQ_4K, &data)
        });
        let datagrams = b.build().unwrap();

        let cfg = datagrams.frame(1).unwrap();
        assert_eq!(&cfg.datagrams()[0].payload[8..10], &9u16.to_le_bytes());
    }
}