autd3-rs 0.9.0

Core async client library for the AUTD3 phased-array kit.
Documentation
use autd3_rs_core::value::DcSysTime;

use super::params::{
    GPIO_O_TYPE_BASE_SIG, GPIO_O_TYPE_DIRECT, GPIO_O_TYPE_FORCE_FAN, GPIO_O_TYPE_IS_STM_MODE,
    GPIO_O_TYPE_MOD_IDX, GPIO_O_TYPE_MOD_SEGMENT, GPIO_O_TYPE_NONE, GPIO_O_TYPE_PWM_OUT,
    GPIO_O_TYPE_STM_IDX, GPIO_O_TYPE_STM_SEGMENT, GPIO_O_TYPE_SYNC, GPIO_O_TYPE_SYNC_DIFF,
    GPIO_O_TYPE_SYS_TIME_EQ, GPIO_O_TYPE_THERMO, GPIO_O_VALUE_MASK,
};

#[must_use]
const fn ec_time_to_gpio_sys_time(ec_time_ns: u64) -> u64 {
    ((ec_time_ns / 3125) << 6) >> 9
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum GpioOut {
    #[default]
    Off,
    BaseSignal,
    Thermo,
    ForceFan,
    Sync,
    ModSegment,
    ModIdx(u16),
    StmSegment,
    StmIdx(u16),
    IsStmMode,
    SysTimeEq(DcSysTime),
    SyncDiff,
    PwmOut(u8),
    Direct(bool),
}

impl GpioOut {
    #[must_use]
    pub const fn encode(self) -> u64 {
        let (tag, value): (u8, u64) = match self {
            GpioOut::Off => (GPIO_O_TYPE_NONE, 0),
            GpioOut::BaseSignal => (GPIO_O_TYPE_BASE_SIG, 0),
            GpioOut::Thermo => (GPIO_O_TYPE_THERMO, 0),
            GpioOut::ForceFan => (GPIO_O_TYPE_FORCE_FAN, 0),
            GpioOut::Sync => (GPIO_O_TYPE_SYNC, 0),
            GpioOut::ModSegment => (GPIO_O_TYPE_MOD_SEGMENT, 0),
            GpioOut::ModIdx(idx) => (GPIO_O_TYPE_MOD_IDX, idx as u64),
            GpioOut::StmSegment => (GPIO_O_TYPE_STM_SEGMENT, 0),
            GpioOut::StmIdx(idx) => (GPIO_O_TYPE_STM_IDX, idx as u64),
            GpioOut::IsStmMode => (GPIO_O_TYPE_IS_STM_MODE, 0),
            GpioOut::SysTimeEq(t) => (
                GPIO_O_TYPE_SYS_TIME_EQ,
                ec_time_to_gpio_sys_time(t.sys_time()),
            ),
            GpioOut::SyncDiff => (GPIO_O_TYPE_SYNC_DIFF, 0),
            GpioOut::PwmOut(tr) => (GPIO_O_TYPE_PWM_OUT, tr as u64),
            GpioOut::Direct(on) => (GPIO_O_TYPE_DIRECT, on as u64),
        };
        ((tag as u64) << 56) | (value & GPIO_O_VALUE_MASK)
    }
}

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

    #[test]
    fn type_tags_match_the_legacy_sdk() {
        let tag = |g: GpioOut| (g.encode() >> 56) as u8;
        assert_eq!(tag(GpioOut::Off), 0x00);
        assert_eq!(tag(GpioOut::BaseSignal), 0x01);
        assert_eq!(tag(GpioOut::Thermo), 0x02);
        assert_eq!(tag(GpioOut::ForceFan), 0x03);
        assert_eq!(tag(GpioOut::Sync), 0x10);
        assert_eq!(tag(GpioOut::ModSegment), 0x20);
        assert_eq!(tag(GpioOut::ModIdx(0)), 0x21);
        assert_eq!(tag(GpioOut::StmSegment), 0x50);
        assert_eq!(tag(GpioOut::StmIdx(0)), 0x51);
        assert_eq!(tag(GpioOut::IsStmMode), 0x52);
        assert_eq!(tag(GpioOut::SysTimeEq(DcSysTime::ZERO)), 0x60);
        assert_eq!(tag(GpioOut::SyncDiff), 0x70);
        assert_eq!(tag(GpioOut::PwmOut(0)), 0xE0);
        assert_eq!(tag(GpioOut::Direct(false)), 0xF0);
    }

    #[test]
    fn values_live_in_the_low_56_bits() {
        assert_eq!(GpioOut::ModIdx(0x1234).encode() & GPIO_O_VALUE_MASK, 0x1234);
        assert_eq!(GpioOut::StmIdx(0xABCD).encode() & GPIO_O_VALUE_MASK, 0xABCD);
        assert_eq!(GpioOut::PwmOut(248).encode() & GPIO_O_VALUE_MASK, 248);
        assert_eq!(GpioOut::Direct(true).encode() & GPIO_O_VALUE_MASK, 1);
        assert_eq!(GpioOut::Direct(false).encode() & GPIO_O_VALUE_MASK, 0);
    }

    #[test]
    fn sys_time_eq_is_scaled_the_same_way_as_the_legacy_sdk() {
        let ns = 3125u64 * 4096;
        assert_eq!(
            GpioOut::SysTimeEq(DcSysTime::from_nanos(ns)).encode() & GPIO_O_VALUE_MASK,
            ((ns / 3125) << 6) >> 9
        );
    }
}