From 5cdb31e36a789b53f417d2590660fb1416e972c9 Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Sat, 11 Jan 2025 11:02:08 +0900 Subject: [PATCH 01/20] rm num-integer --- Cargo.toml | 1 - autd3-firmware-emulator/Cargo.toml | 1 - autd3-firmware-emulator/src/fpga/emulator/stm/foci.rs | 3 +-- autd3-firmware-emulator/src/fpga/emulator/swapchain.rs | 8 +++++--- autd3-firmware-emulator/tests/op/stm/foci.rs | 5 ++--- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 11f29109..a87906a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ itertools = { version = "0.14.0", features = ["use_alloc"], default-features = f libloading = "0.8.6" nalgebra = { version = "0.33.2", default-features = false } num = { version = "0.4.3", default-features = false } -num-integer = { version = "0.1.46", default-features = false } paste = "1.0.15" proc-macro2 = { version = "1.0.89", default-features = false } prost = { version = "0.13.4", features = ["derive"], default-features = false } diff --git a/autd3-firmware-emulator/Cargo.toml b/autd3-firmware-emulator/Cargo.toml index b2007728..491485f7 100644 --- a/autd3-firmware-emulator/Cargo.toml +++ b/autd3-firmware-emulator/Cargo.toml @@ -13,7 +13,6 @@ repository = { workspace = true } autd3-driver = { workspace = true, features = ["derive"] } bitfield-struct = { workspace = true } time = { workspace = true, features = ["std"] } -num-integer = { workspace = true } zerocopy = { workspace = true } [dev-dependencies] diff --git a/autd3-firmware-emulator/src/fpga/emulator/stm/foci.rs b/autd3-firmware-emulator/src/fpga/emulator/stm/foci.rs index bcd325af..c5ac563c 100644 --- a/autd3-firmware-emulator/src/fpga/emulator/stm/foci.rs +++ b/autd3-firmware-emulator/src/fpga/emulator/stm/foci.rs @@ -2,7 +2,6 @@ use autd3_driver::{ derive::Segment, firmware::fpga::{Drive, EmitIntensity, Phase}, }; -use num_integer::Roots; use super::super::{super::params::*, FPGAEmulator}; @@ -67,7 +66,7 @@ impl FPGAEmulator { let d2 = (x - tr_x) * (x - tr_x) + (y - tr_y) * (y - tr_y) + (z - tr_z) * (z - tr_z); - let dist = d2.sqrt() as u32; + let dist = d2.isqrt() as u32; let q = ((dist << 14) / sound_speed as u32) as usize; let q = q + offset as usize; ( diff --git a/autd3-firmware-emulator/src/fpga/emulator/swapchain.rs b/autd3-firmware-emulator/src/fpga/emulator/swapchain.rs index 856956ff..f5c51cf1 100644 --- a/autd3-firmware-emulator/src/fpga/emulator/swapchain.rs +++ b/autd3-firmware-emulator/src/fpga/emulator/swapchain.rs @@ -6,7 +6,6 @@ use autd3_driver::{ ethercat::DcSysTime, firmware::fpga::FPGA_MAIN_CLK_FREQ, }; -use num_integer::Integer; use super::FPGAEmulator; @@ -167,8 +166,11 @@ impl Swapchain { } fn lap_and_idx(&self, segment: Segment, sys_time: DcSysTime) -> (usize, usize) { - (((self.fpga_sys_time(sys_time) >> 8) / self.freq_div[&segment] as u64) as usize) - .div_rem(&self.cycle[&segment]) + let a = ((self.fpga_sys_time(sys_time) >> 8) / self.freq_div[&segment] as u64) as usize; + let b = self.cycle[&segment]; + let lap = a / b; + let idx = a % b; + (lap, idx) } } diff --git a/autd3-firmware-emulator/tests/op/stm/foci.rs b/autd3-firmware-emulator/tests/op/stm/foci.rs index 3635ec03..1e90c807 100644 --- a/autd3-firmware-emulator/tests/op/stm/foci.rs +++ b/autd3-firmware-emulator/tests/op/stm/foci.rs @@ -21,7 +21,6 @@ use autd3_driver::{ use autd3_firmware_emulator::{cpu::params::SYS_TIME_TRANSITION_MARGIN, CPUEmulator}; use crate::{create_geometry, op::gain::TestGain, send}; -use num_integer::Roots; use rand::*; use time::OffsetDateTime; use zerocopy::FromZeros; @@ -115,7 +114,7 @@ fn test_send_foci_stm( let fx = (focus[0].point().x / FOCI_STM_FIXED_NUM_UNIT).round() as i32; let fy = (focus[0].point().y / FOCI_STM_FIXED_NUM_UNIT).round() as i32; let fz = (focus[0].point().z / FOCI_STM_FIXED_NUM_UNIT).round() as i32; - let d = ((tx - fx).pow(2) + (ty - fy).pow(2) + (tz - fz).pow(2)).sqrt() as u32; + let d = ((tx - fx).pow(2) + (ty - fy).pow(2) + (tz - fz).pow(2)).isqrt() as u32; let q = (d << 14) / cpu.fpga().sound_speed(segment) as u32; let sin = (sin_table[q as usize % 256] >> 1) as usize; let cos = (sin_table[(q as usize + 64) % 256] >> 1) as usize; @@ -393,7 +392,7 @@ fn test_send_foci_stm_n() -> anyhow::Result<()> { let fy = (f.point().y / FOCI_STM_FIXED_NUM_UNIT).round() as i32; let fz = (f.point().z / FOCI_STM_FIXED_NUM_UNIT).round() as i32; let d = - ((tx - fx).pow(2) + (ty - fy).pow(2) + (tz - fz).pow(2)).sqrt() as u32; + ((tx - fx).pow(2) + (ty - fy).pow(2) + (tz - fz).pow(2)).isqrt() as u32; let q = (d << 14) / cpu.fpga().sound_speed(Segment::S0) as u32 + (f.phase_offset() - base_offset).value() as u32; let sin = sin_table[q as usize % 256] as usize; From 0f26fad78b4ebf60187a14edb39879c7346d0858 Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Mon, 13 Jan 2025 12:56:09 +0900 Subject: [PATCH 02/20] add `autd3-core` crate --- Cargo.toml | 3 + autd3-core/Cargo.toml | 46 ++ autd3-core/README.md | 7 + autd3-core/src/defined/angle.rs | 65 +++ autd3-core/src/defined/freq/float.rs | 17 + autd3-core/src/defined/freq/int.rs | 17 + autd3-core/src/defined/freq/mod.rs | 37 ++ autd3-core/src/defined/mod.rs | 104 +++++ autd3-core/src/ethercat/mod.rs | 4 + autd3-core/src/gain/drive.rs | 118 +++++ autd3-core/src/gain/emit_intensity.rs | 161 +++++++ autd3-core/src/gain/error.rs | 8 + autd3-core/src/gain/mod.rs | 57 +++ autd3-core/src/gain/phase.rs | 148 ++++++ autd3-core/src/geometry/device.rs | 460 +++++++++++++++++++ autd3-core/src/geometry/mod.rs | 283 ++++++++++++ autd3-core/src/geometry/rotation.rs | 148 ++++++ autd3-core/src/geometry/transducer.rs | 69 +++ autd3-core/src/lib.rs | 24 + autd3-core/src/link/async.rs | 126 +++++ autd3-core/src/link/datagram/header.rs | 27 ++ autd3-core/src/link/datagram/mod.rs | 7 + autd3-core/src/link/datagram/rx.rs | 34 ++ autd3-core/src/link/datagram/tx.rs | 33 ++ autd3-core/src/link/error.rs | 13 + autd3-core/src/link/mod.rs | 14 + autd3-core/src/link/sync.rs | 64 +++ autd3-core/src/modulation/error.rs | 37 ++ autd3-core/src/modulation/loop_behavior.rs | 115 +++++ autd3-core/src/modulation/mod.rs | 27 ++ autd3-core/src/modulation/sampling_config.rs | 302 ++++++++++++ autd3-core/src/utils/float.rs | 38 ++ autd3-core/src/utils/mod.rs | 1 + 33 files changed, 2614 insertions(+) create mode 100644 autd3-core/Cargo.toml create mode 100644 autd3-core/README.md create mode 100644 autd3-core/src/defined/angle.rs create mode 100644 autd3-core/src/defined/freq/float.rs create mode 100644 autd3-core/src/defined/freq/int.rs create mode 100644 autd3-core/src/defined/freq/mod.rs create mode 100644 autd3-core/src/defined/mod.rs create mode 100644 autd3-core/src/ethercat/mod.rs create mode 100644 autd3-core/src/gain/drive.rs create mode 100644 autd3-core/src/gain/emit_intensity.rs create mode 100644 autd3-core/src/gain/error.rs create mode 100644 autd3-core/src/gain/mod.rs create mode 100644 autd3-core/src/gain/phase.rs create mode 100644 autd3-core/src/geometry/device.rs create mode 100644 autd3-core/src/geometry/mod.rs create mode 100644 autd3-core/src/geometry/rotation.rs create mode 100644 autd3-core/src/geometry/transducer.rs create mode 100644 autd3-core/src/lib.rs create mode 100644 autd3-core/src/link/async.rs create mode 100644 autd3-core/src/link/datagram/header.rs create mode 100644 autd3-core/src/link/datagram/mod.rs create mode 100644 autd3-core/src/link/datagram/rx.rs create mode 100644 autd3-core/src/link/datagram/tx.rs create mode 100644 autd3-core/src/link/error.rs create mode 100644 autd3-core/src/link/mod.rs create mode 100644 autd3-core/src/link/sync.rs create mode 100644 autd3-core/src/modulation/error.rs create mode 100644 autd3-core/src/modulation/loop_behavior.rs create mode 100644 autd3-core/src/modulation/mod.rs create mode 100644 autd3-core/src/modulation/sampling_config.rs create mode 100644 autd3-core/src/utils/float.rs create mode 100644 autd3-core/src/utils/mod.rs diff --git a/Cargo.toml b/Cargo.toml index a87906a2..129abbb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "autd3", + "autd3-core", "autd3-driver", "autd3-derive", "autd3-gain-holo", @@ -11,6 +12,7 @@ members = [ "autd3-firmware-emulator", "autd3-protobuf", "examples", + "autd3-core", ] resolver = "2" @@ -68,6 +70,7 @@ spin_sleep = "1.3.0" windows = { version = "0.59.0", default-features = false } zerocopy = { version = "0.8.14", features = ["derive"] } autd3 = { path = "./autd3", version = "29.0.0-rc.14" } +autd3-core = { path = "./autd3-core", version = "29.0.0-rc.14", default-features = false } autd3-driver = { path = "./autd3-driver", version = "29.0.0-rc.14", default-features = false } autd3-derive = { path = "./autd3-derive", version = "29.0.0-rc.14" } autd3-gain-holo = { path = "./autd3-gain-holo", version = "29.0.0-rc.14" } diff --git a/autd3-core/Cargo.toml b/autd3-core/Cargo.toml new file mode 100644 index 00000000..021d150e --- /dev/null +++ b/autd3-core/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "autd3-core" +description = "AUTD3 core traits and types" +readme = "README.md" +keywords = { workspace = true } +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +repository = { workspace = true } + +[dependencies] +async-trait = { workspace = true, optional = true } +autd3-derive = { workspace = true } +bit-vec = { workspace = true } +bvh = { workspace = true } +derive_more = { workspace = true, features = ["add", "debug", "deref", "deref_mut", "display", "into_iterator", "mul"] } +derive-new = { workspace = true } +nalgebra = { workspace = true } +paste = { workspace = true } +thiserror = { workspace = true, default-features = false } +zerocopy = { workspace = true } + +[dev-dependencies] +anyhow = { workspace = true } +approx = { workspace = true } +itertools = { workspace = true } +rand = { workspace = true } +rstest = { workspace = true } + +[features] +default = ["derive"] +async = [] +async-trait = ["dep:async-trait"] +derive = [] +defined = [] +dynamic_freq = [] +ethercat = [] +gain = [] +geometry = [] +left_handed = [] +lightweight = [] +link = [] +modulation = [] +use_meter = [] +utils = [] diff --git a/autd3-core/README.md b/autd3-core/README.md new file mode 100644 index 00000000..5fda9679 --- /dev/null +++ b/autd3-core/README.md @@ -0,0 +1,7 @@ +# autd3-core + +AUTD3 core traits and types. + +# Author + +Shun Suzuki, 2025 diff --git a/autd3-core/src/defined/angle.rs b/autd3-core/src/defined/angle.rs new file mode 100644 index 00000000..800a383c --- /dev/null +++ b/autd3-core/src/defined/angle.rs @@ -0,0 +1,65 @@ +/// \[°\] +#[allow(non_camel_case_types)] +pub struct deg; + +/// \[rad\] +#[allow(non_camel_case_types)] +pub struct rad; + +use derive_more::Debug; + +/// Angle +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum Angle { + #[doc(hidden)] + #[debug("{}°", _0)] + Deg(f32), + #[doc(hidden)] + #[debug("{}rad", _0)] + Rad(f32), +} + +impl Angle { + /// Returns the angle in radian + pub fn radian(self) -> f32 { + match self { + Self::Deg(a) => a.to_radians(), + Self::Rad(a) => a, + } + } + + /// Returns the angle in degree + pub fn degree(self) -> f32 { + match self { + Self::Deg(a) => a, + Self::Rad(a) => a.to_degrees(), + } + } +} + +impl std::ops::Mul for f32 { + type Output = Angle; + + fn mul(self, _rhs: deg) -> Self::Output { + Self::Output::Deg(self) + } +} + +impl std::ops::Mul for f32 { + type Output = Angle; + + fn mul(self, _rhs: rad) -> Self::Output { + Self::Output::Rad(self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn dbg() { + assert_eq!(format!("{:?}", 90.0 * deg), "90°"); + assert_eq!(format!("{:?}", 1.0 * rad), "1rad"); + } +} diff --git a/autd3-core/src/defined/freq/float.rs b/autd3-core/src/defined/freq/float.rs new file mode 100644 index 00000000..6fa1b07f --- /dev/null +++ b/autd3-core/src/defined/freq/float.rs @@ -0,0 +1,17 @@ +use super::{kHz, Freq, Hz}; + +impl std::ops::Mul for f32 { + type Output = Freq; + + fn mul(self, _rhs: Hz) -> Self::Output { + Self::Output { freq: self } + } +} + +impl std::ops::Mul for f32 { + type Output = Freq; + + fn mul(self, _rhs: kHz) -> Self::Output { + Self::Output { freq: self * 1e3 } + } +} diff --git a/autd3-core/src/defined/freq/int.rs b/autd3-core/src/defined/freq/int.rs new file mode 100644 index 00000000..467e5273 --- /dev/null +++ b/autd3-core/src/defined/freq/int.rs @@ -0,0 +1,17 @@ +use super::{kHz, Freq, Hz}; + +impl std::ops::Mul for u32 { + type Output = Freq; + + fn mul(self, _rhs: Hz) -> Self::Output { + Self::Output { freq: self } + } +} + +impl std::ops::Mul for u32 { + type Output = Freq; + + fn mul(self, _rhs: kHz) -> Self::Output { + Self::Output { freq: self * 1000 } + } +} diff --git a/autd3-core/src/defined/freq/mod.rs b/autd3-core/src/defined/freq/mod.rs new file mode 100644 index 00000000..f3bd3c0f --- /dev/null +++ b/autd3-core/src/defined/freq/mod.rs @@ -0,0 +1,37 @@ +mod float; +mod int; + +/// \[Hz\] +pub struct Hz; + +/// \[kHz\] +#[allow(non_camel_case_types)] +pub struct kHz; + +use derive_more::{Add, Debug, Div, Mul, Sub}; + +/// Frequency +#[derive(Clone, Copy, PartialEq, PartialOrd, Add, Div, Mul, Sub, Debug)] +#[debug("{} Hz", freq)] +pub struct Freq { + pub(crate) freq: T, +} + +impl Freq { + #[inline] + /// Returns the frequency in Hz. + pub const fn hz(&self) -> T { + self.freq + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn dbg() { + assert_eq!(format!("{:?}", 100 * Hz), "100 Hz"); + assert_eq!(format!("{:?}", 100 * kHz), "100000 Hz"); + } +} diff --git a/autd3-core/src/defined/mod.rs b/autd3-core/src/defined/mod.rs new file mode 100644 index 00000000..db2e3ac5 --- /dev/null +++ b/autd3-core/src/defined/mod.rs @@ -0,0 +1,104 @@ +mod angle; +mod freq; + +pub use std::f32::consts::PI; + +#[cfg(feature = "use_meter")] +mod unit { + /// meter + pub const METER: f32 = 1.0; +} +#[cfg(not(feature = "use_meter"))] +mod unit { + /// meter + pub const METER: f32 = 1000.0; +} +pub use unit::*; + +pub use angle::*; +pub use freq::*; + +/// millimeter +pub const MILLIMETER: f32 = METER / 1000.0; + +/// a complex number +pub type Complex = nalgebra::Complex; + +/// The absolute threshold of hearing in \[㎩\] +pub const ABSOLUTE_THRESHOLD_OF_HEARING: f32 = 20e-6; + +/// The amplitude of T4010A1 in \[㎩*mm\] +pub const T4010A1_AMPLITUDE: f32 = 275.574_25 * 200.0 * MILLIMETER; // [㎩*mm] + +/// The default timeout duration +pub const DEFAULT_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(200); + +/// The period of ultrasound in discrete time units +pub const ULTRASOUND_PERIOD_COUNT: usize = 256; + +#[cfg(not(feature = "dynamic_freq"))] +mod inner { + use super::Freq; + use std::time::Duration; + + #[inline(always)] + /// The frequency of ultrasound + pub const fn ultrasound_freq() -> Freq { + Freq { freq: 40000 } + } + + #[inline(always)] + /// The period of ultrasound + pub const fn ultrasound_period() -> Duration { + Duration::from_micros(25) + } +} + +#[cfg(feature = "dynamic_freq")] +mod inner { + use std::sync::Once; + + use super::Freq; + use crate::defined::Hz; + + static mut VAL: Freq = Freq { freq: 40000 }; + static FREQ: Once = Once::new(); + + #[inline] + /// The frequency of ultrasound + pub fn ultrasound_freq() -> Freq { + unsafe { + FREQ.call_once(|| { + VAL = match std::env::var("AUTD3_ULTRASOUND_FREQ") { + Ok(freq) => match freq.parse::() { + Ok(freq) => { + tracing::info!("Set ultrasound frequency to {} Hz.", freq); + freq * Hz + } + Err(_) => { + tracing::error!( + "Invalid ultrasound frequency ({} Hz), fallback to 40 kHz.", + freq + ); + Freq { freq: 40000 } + } + }, + Err(_) => { + tracing::warn!("Environment variable AUTD3_ULTRASOUND_FREQ is not set, fallback to 40 kHz."); + Freq { freq: 40000 } + } + }; + }); + VAL + } + } + + #[doc(hidden)] + pub const DRP_ROM_SIZE: usize = 32; +} + +pub use inner::*; + +/// \[㎜\] +#[allow(non_upper_case_globals)] +pub const mm: f32 = MILLIMETER; diff --git a/autd3-core/src/ethercat/mod.rs b/autd3-core/src/ethercat/mod.rs new file mode 100644 index 00000000..e7ebf313 --- /dev/null +++ b/autd3-core/src/ethercat/mod.rs @@ -0,0 +1,4 @@ +/// PDO output frame size +pub const EC_OUTPUT_FRAME_SIZE: usize = 626; +/// PDO input frame size +pub const EC_INPUT_FRAME_SIZE: usize = 2; diff --git a/autd3-core/src/gain/drive.rs b/autd3-core/src/gain/drive.rs new file mode 100644 index 00000000..533a3b0d --- /dev/null +++ b/autd3-core/src/gain/drive.rs @@ -0,0 +1,118 @@ +use autd3_derive::Builder; + +use super::{emit_intensity::EmitIntensity, phase::Phase}; + +use derive_new::new; +use zerocopy::{Immutable, IntoBytes}; + +/// A container for the phase and intensity of the ultrasound. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Builder, new, IntoBytes, Immutable)] +#[repr(C)] +pub struct Drive { + #[get] + /// The phase of the ultrasound. + phase: Phase, + #[get] + /// The intensity of the ultrasound. + intensity: EmitIntensity, +} + +impl Drive { + /// A [`Drive`] with a phase of [`Phase::ZERO`] and an intensity of [`EmitIntensity::MIN`]. + pub const NULL: Self = Self { + phase: Phase::ZERO, + intensity: EmitIntensity::MIN, + }; +} + +impl From<(Phase, EmitIntensity)> for Drive { + fn from((phase, intensity): (Phase, EmitIntensity)) -> Self { + Self { phase, intensity } + } +} + +impl From<(EmitIntensity, Phase)> for Drive { + fn from((intensity, phase): (EmitIntensity, Phase)) -> Self { + Self { phase, intensity } + } +} + +impl From for Drive { + fn from(intensity: EmitIntensity) -> Self { + Self { + phase: Phase::ZERO, + intensity, + } + } +} + +impl From for Drive { + fn from(phase: Phase) -> Self { + Self { + phase, + intensity: EmitIntensity::MAX, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[rstest::rstest] + #[test] + #[case( + Drive::new(Phase::new(0x01), EmitIntensity::new(0x02)), + (Phase::new(0x01), EmitIntensity::new(0x02)) + )] + #[case( + Drive::new(Phase::new(0x01), EmitIntensity::new(0x02)), + (EmitIntensity::new(0x02), Phase::new(0x01)) + )] + #[case( + Drive::new(Phase::ZERO, EmitIntensity::new(0x01)), + EmitIntensity::new(0x01) + )] + #[case(Drive::new(Phase::new(0x01), EmitIntensity::MAX), Phase::new(0x01))] + fn from(#[case] expected: Drive, #[case] target: impl Into) { + assert_eq!(expected, target.into()); + } + + #[rstest::rstest] + #[test] + #[case( + EmitIntensity::new(0x00), + Drive::new(Phase::ZERO, EmitIntensity::new(0x00)) + )] + #[case( + EmitIntensity::new(0x01), + Drive::new(Phase::ZERO, EmitIntensity::new(0x01)) + )] + #[case( + EmitIntensity::new(0xFF), + Drive::new(Phase::ZERO, EmitIntensity::new(0xFF)) + )] + fn test_intensity(#[case] expected: EmitIntensity, #[case] target: Drive) { + assert_eq!(expected, target.intensity()); + } + + #[rstest::rstest] + #[test] + #[case(Phase::ZERO, Drive::new(Phase::ZERO, EmitIntensity::new(0x00)))] + #[case(Phase::new(1), Drive::new(Phase::new(1), EmitIntensity::new(0x00)))] + #[case( + Phase::new(0xFF), + Drive::new(Phase::new(0xFF), EmitIntensity::new(0x00)) + )] + fn test_phase(#[case] expected: Phase, #[case] target: Drive) { + assert_eq!(expected, target.phase()); + } + + #[test] + fn test_null() { + assert_eq!( + Drive::new(Phase::ZERO, EmitIntensity::new(0x00)), + Drive::NULL + ); + } +} diff --git a/autd3-core/src/gain/emit_intensity.rs b/autd3-core/src/gain/emit_intensity.rs new file mode 100644 index 00000000..18d36a5a --- /dev/null +++ b/autd3-core/src/gain/emit_intensity.rs @@ -0,0 +1,161 @@ +use autd3_derive::Builder; + +use derive_more::Debug; +use derive_new::new; +use zerocopy::{Immutable, IntoBytes}; + +/// The intensity of the ultrasound. +#[derive( + Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Builder, new, IntoBytes, Immutable, +)] +#[debug("{:#04X}", self.value)] +#[repr(C)] +pub struct EmitIntensity { + #[get] + /// The value of the intensity. + value: u8, +} + +impl EmitIntensity { + /// Maximum intensity. + pub const MAX: EmitIntensity = EmitIntensity { value: 255 }; + /// Minimum intensity. + pub const MIN: EmitIntensity = EmitIntensity { value: 0 }; +} + +impl From for EmitIntensity { + fn from(v: u8) -> Self { + Self::new(v) + } +} + +impl std::ops::Div for EmitIntensity { + type Output = Self; + + fn div(self, rhs: u8) -> Self::Output { + Self::new(self.value / rhs) + } +} + +impl std::ops::Mul for EmitIntensity { + type Output = EmitIntensity; + + fn mul(self, rhs: u8) -> Self::Output { + Self::Output::new(self.value.saturating_mul(rhs)) + } +} + +impl std::ops::Mul for u8 { + type Output = EmitIntensity; + + fn mul(self, rhs: EmitIntensity) -> Self::Output { + Self::Output::new(self.saturating_mul(rhs.value)) + } +} + +impl std::ops::Add for EmitIntensity { + type Output = Self; + + fn add(self, rhs: EmitIntensity) -> Self::Output { + Self::new(self.value.saturating_add(rhs.value)) + } +} + +impl std::ops::Sub for EmitIntensity { + type Output = Self; + + fn sub(self, rhs: EmitIntensity) -> Self::Output { + Self::new(self.value.saturating_sub(rhs.value)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[rstest::rstest] + #[test] + #[case::value_0(0x00)] + #[case::value_1(0x01)] + #[case::value_ff(0xFF)] + fn test_new(#[case] expected: u8) { + assert_eq!(expected, EmitIntensity::new(expected).value(),); + } + + #[rstest::rstest] + #[test] + #[case::value_1_1(EmitIntensity::new(0x01), EmitIntensity::new(0x01), 1)] + #[case::value_1_2(EmitIntensity::new(0x00), EmitIntensity::new(0x01), 2)] + #[case::value_ff_2(EmitIntensity::new(0x7F), EmitIntensity::new(0xFF), 2)] + fn test_div(#[case] expected: EmitIntensity, #[case] target: EmitIntensity, #[case] div: u8) { + assert_eq!(expected, target / div); + } + + #[rstest::rstest] + #[test] + #[case::value_1_1(EmitIntensity::new(0x01), EmitIntensity::new(0x01), 1)] + #[case::value_1_2(EmitIntensity::new(0x02), EmitIntensity::new(0x01), 2)] + #[case::value_7f_2(EmitIntensity::new(0xFE), EmitIntensity::new(0x7F), 2)] + #[case::value_7f_3(EmitIntensity::new(0xFF), EmitIntensity::new(0x7F), 3)] + fn test_mul(#[case] expected: EmitIntensity, #[case] target: EmitIntensity, #[case] mul: u8) { + assert_eq!(expected, target * mul); + assert_eq!(expected, mul * target); + } + + #[rstest::rstest] + #[test] + #[case::value_1_1( + EmitIntensity::new(0x02), + EmitIntensity::new(0x01), + EmitIntensity::new(0x01) + )] + #[case::value_7f_7f( + EmitIntensity::new(0xFE), + EmitIntensity::new(0x7F), + EmitIntensity::new(0x7F) + )] + #[case::value_7f_ff( + EmitIntensity::new(0xFF), + EmitIntensity::new(0x7F), + EmitIntensity::new(0xFF) + )] + fn test_add( + #[case] expected: EmitIntensity, + #[case] lhs: EmitIntensity, + #[case] rhs: EmitIntensity, + ) { + assert_eq!(expected, lhs + rhs); + } + + #[rstest::rstest] + #[test] + #[case::value_1_1( + EmitIntensity::new(0x00), + EmitIntensity::new(0x01), + EmitIntensity::new(0x01) + )] + #[case::value_7f_7f( + EmitIntensity::new(0x01), + EmitIntensity::new(0x02), + EmitIntensity::new(0x01) + )] + #[case::value_7f_ff( + EmitIntensity::new(0x00), + EmitIntensity::new(0x7F), + EmitIntensity::new(0xFF) + )] + fn test_sub( + #[case] expected: EmitIntensity, + #[case] lhs: EmitIntensity, + #[case] rhs: EmitIntensity, + ) { + assert_eq!(expected, lhs - rhs); + } + + #[test] + fn dbg() { + assert_eq!(format!("{:?}", EmitIntensity::new(0x00)), "0x00"); + assert_eq!(format!("{:?}", EmitIntensity::new(0x01)), "0x01"); + assert_eq!(format!("{:?}", EmitIntensity::new(0xFF)), "0xFF"); + } +} diff --git a/autd3-core/src/gain/error.rs b/autd3-core/src/gain/error.rs new file mode 100644 index 00000000..9bd2c72f --- /dev/null +++ b/autd3-core/src/gain/error.rs @@ -0,0 +1,8 @@ +use derive_more::derive::Display; +use thiserror::Error; + +#[derive(Error, Debug, Display, PartialEq, Clone)] +#[display("{}", msg)] +pub struct GainError { + msg: String, +} diff --git a/autd3-core/src/gain/mod.rs b/autd3-core/src/gain/mod.rs new file mode 100644 index 00000000..b6b0bf52 --- /dev/null +++ b/autd3-core/src/gain/mod.rs @@ -0,0 +1,57 @@ +mod drive; +mod emit_intensity; +mod error; +mod phase; + +use std::collections::HashMap; + +pub use bit_vec::BitVec; +pub use drive::Drive; +pub use emit_intensity::EmitIntensity; +pub use error::GainError; +pub use phase::Phase; + +use crate::geometry::{Device, Geometry, Transducer}; + +/// A trait to calculate the phase and intensity for [`Gain`]. +/// +/// [`Gain`]: crate::datagram::Gain +pub trait GainContext: Send + Sync { + /// Calculates the phase and intensity for the transducer. + fn calc(&self, tr: &Transducer) -> Drive; +} + +impl GainContext for Box { + fn calc(&self, tr: &Transducer) -> Drive { + self.as_ref().calc(tr) + } +} + +/// A trait for generating a context for the gain operation. +pub trait GainContextGenerator { + /// The type of the context that actually performs the calculation. + type Context: GainContext; + + /// Generate a context for the given device. + fn generate(&mut self, device: &Device) -> Self::Context; +} + +/// Trait for calculating the phase/amplitude of each transducer. +/// +/// See also [`Gain`] derive macro. +/// +/// [`Gain`]: autd3_derive::Gain +pub trait Gain: std::fmt::Debug { + /// The type of the context generator. + type G: GainContextGenerator; + + /// Initialize the gain and generate the context generator. + /// + /// `filter` is a hash map that holds a bit vector representing the indices of the enabled transducers for each device index. + /// If `filter` is `None`, all transducers are enabled. + fn init( + self, + geometry: &Geometry, + filter: Option<&HashMap>>, + ) -> Result; +} diff --git a/autd3-core/src/gain/phase.rs b/autd3-core/src/gain/phase.rs new file mode 100644 index 00000000..0d743008 --- /dev/null +++ b/autd3-core/src/gain/phase.rs @@ -0,0 +1,148 @@ +use crate::defined::{rad, Angle, Complex, PI}; + +use autd3_derive::Builder; + +use derive_more::Debug; +use derive_new::new; +use nalgebra::ComplexField; +use zerocopy::{Immutable, IntoBytes}; + +/// The phase of the ultrasound. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Builder, new, IntoBytes, Immutable)] +#[repr(C)] +#[debug("{:#04X}", self.value)] +pub struct Phase { + #[get] + /// The value of the phase. + value: u8, +} + +impl Phase { + /// A phase of zero. + pub const ZERO: Self = Self { value: 0 }; + /// A phase of π. + pub const PI: Self = Self { value: 128 }; + + /// Converts the phase into a radian. + pub fn radian(&self) -> f32 { + self.value as f32 / 256.0 * 2.0 * PI + } +} + +impl From for Phase { + fn from(v: u8) -> Self { + Self::new(v) + } +} + +impl From for Phase { + fn from(v: Angle) -> Self { + Self { + value: (((v.radian() / (2.0 * PI) * 256.0).round() as i32) & 0xFF) as _, + } + } +} + +impl From for Phase { + fn from(v: Complex) -> Self { + Self::from(v.argument() * rad) + } +} + +impl std::ops::Add for Phase { + type Output = Phase; + + fn add(self, rhs: Phase) -> Self::Output { + Self::Output::new(self.value.wrapping_add(rhs.value)) + } +} + +impl std::ops::Sub for Phase { + type Output = Phase; + + fn sub(self, rhs: Phase) -> Self::Output { + Self::Output::new(self.value.wrapping_sub(rhs.value)) + } +} + +impl std::ops::Mul for Phase { + type Output = Phase; + + fn mul(self, rhs: u8) -> Self::Output { + Self::Output::new(self.value.wrapping_mul(rhs)) + } +} + +impl std::ops::Div for Phase { + type Output = Phase; + + fn div(self, rhs: u8) -> Self::Output { + Self::Output::new(self.value.wrapping_div(rhs)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[rstest::rstest] + #[test] + #[case(0x00)] + #[case(0x01)] + #[case(0xFF)] + fn new(#[case] expected: u8) { + assert_eq!(expected, Phase::from(expected).value()); + } + + #[rstest::rstest] + #[test] + #[case(Phase::new(0x02), Phase::new(0x01), Phase::new(0x01))] + #[case(Phase::new(0xFE), Phase::new(0x7F), Phase::new(0x7F))] + #[case(Phase::new(0x7E), Phase::new(0x7F), Phase::new(0xFF))] + fn add(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: Phase) { + assert_eq!(expected, lhs + rhs); + } + + #[rstest::rstest] + #[test] + #[case(Phase::ZERO, Phase::new(0x01), Phase::new(0x01))] + #[case(Phase::new(0x01), Phase::new(0x02), Phase::new(0x01))] + #[case(Phase::new(0x80), Phase::new(0x7F), Phase::new(0xFF))] + fn sub(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: Phase) { + assert_eq!(expected, lhs - rhs); + } + + #[rstest::rstest] + #[test] + #[case(Phase::new(0x02), Phase::new(0x01), 2)] + #[case(Phase::new(0xFE), Phase::new(0x7F), 2)] + #[case(Phase::ZERO, Phase::new(0x80), 2)] + fn mul(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: u8) { + assert_eq!(expected, lhs * rhs); + } + + #[rstest::rstest] + #[test] + #[case(Phase::new(0x01), Phase::new(0x02), 2)] + #[case(Phase::new(0x7F), Phase::new(0xFE), 2)] + #[case(Phase::ZERO, Phase::new(0x01), 2)] + fn div(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: u8) { + assert_eq!(expected, lhs / rhs); + } + + #[rstest::rstest] + #[test] + #[case(0.0, 0)] + #[case(2.0 * PI / 256.0 * 128.0, 128)] + #[case(2.0 * PI / 256.0 * 255.0, 255)] + fn radian(#[case] expect: f32, #[case] value: u8) { + approx::assert_abs_diff_eq!(expect, Phase::new(value).radian()); + } + + #[test] + fn dbg() { + assert_eq!(format!("{:?}", Phase::ZERO), "0x00"); + assert_eq!(format!("{:?}", Phase::new(0x01)), "0x01"); + assert_eq!(format!("{:?}", Phase::new(0xFF)), "0xFF"); + } +} diff --git a/autd3-core/src/geometry/device.rs b/autd3-core/src/geometry/device.rs new file mode 100644 index 00000000..6e95a9da --- /dev/null +++ b/autd3-core/src/geometry/device.rs @@ -0,0 +1,460 @@ +use std::f32::consts::PI; + +use autd3_derive::Builder; +use bvh::aabb::Aabb; +use derive_more::{Deref, IntoIterator}; + +use crate::defined::{ultrasound_freq, METER}; + +use super::{ + Isometry, Point3, Quaternion, Transducer, Translation, UnitQuaternion, UnitVector3, Vector3, +}; + +/// An AUTD device unit. +#[derive(Builder, Deref, IntoIterator)] +pub struct Device { + idx: u16, + #[deref] + #[into_iterator(ref)] + transducers: Vec, + /// enable flag + pub enable: bool, + /// speed of sound + pub sound_speed: f32, + #[get(ref)] + /// The rotation of the device. + rotation: UnitQuaternion, + #[get(ref)] + /// The center of the device. + center: Point3, + #[get(ref)] + /// The x-direction of the device. + x_direction: UnitVector3, + #[get(ref)] + /// The y-direction of the device. + y_direction: UnitVector3, + #[get(ref)] + /// The axial direction of the device. + axial_direction: UnitVector3, + #[get(ref, no_doc)] + inv: Isometry, + #[get(ref)] + /// The Axis Aligned Bounding Box of the device. + aabb: Aabb, +} + +impl Device { + fn init(&mut self) { + self.center = Point3::from( + self.transducers + .iter() + .map(|tr| tr.position().coords) + .sum::() + / self.transducers.len() as f32, + ); + self.x_direction = Self::get_direction(Vector3::x(), &self.rotation); + self.y_direction = Self::get_direction(Vector3::y(), &self.rotation); + self.axial_direction = if cfg!(feature = "left_handed") { + Self::get_direction(-Vector3::z(), &self.rotation) // GRCOV_EXCL_LINE + } else { + Self::get_direction(Vector3::z(), &self.rotation) + }; + self.inv = (nalgebra::Translation3::::from(*self.transducers[0].position()) + * self.rotation) + .inverse(); + self.aabb = self + .transducers + .iter() + .fold(Aabb::empty(), |aabb, tr| aabb.grow(tr.position())); + } + + #[doc(hidden)] + pub fn new(idx: u16, rot: UnitQuaternion, transducers: Vec) -> Self { + let mut dev = Self { + idx, + transducers, + enable: true, + sound_speed: 340.0 * METER, + rotation: rot, + center: Point3::origin(), + x_direction: Vector3::x_axis(), + y_direction: Vector3::y_axis(), + axial_direction: Vector3::z_axis(), + inv: nalgebra::Isometry3::identity(), + aabb: Aabb::empty(), + }; + dev.init(); + dev + } + + /// Gets the index of the device. + pub const fn idx(&self) -> usize { + self.idx as _ + } + + /// Gets the number of transducers of the device. + pub fn num_transducers(&self) -> usize { + self.transducers.len() + } + + /// Translates the device to the target position. + pub fn translate_to(&mut self, t: Point3) { + self.translate(t - self.transducers[0].position()); + } + + /// Rotates the device to the target rotation. + pub fn rotate_to(&mut self, r: UnitQuaternion) { + self.rotate(r * self.rotation.conjugate()); + } + + /// Translates the device. + pub fn translate(&mut self, t: Vector3) { + self.affine(t, UnitQuaternion::identity()); + } + + /// Rotates the device. + pub fn rotate(&mut self, r: UnitQuaternion) { + self.affine(Vector3::zeros(), r); + } + + /// Translates and rotates the device. + pub fn affine(&mut self, t: Vector3, r: UnitQuaternion) { + let isometry = Isometry { + translation: Translation::from(t), + rotation: r, + }; + self.transducers + .iter_mut() + .for_each(|tr| tr.affine(&isometry)); + self.rotation = r * self.rotation; + self.init(); + } + + /// Sets the sound speed of enabled devices from the temperature `t`. + /// + /// This is equivalent to `Self::set_sound_speed_from_temp_with(t, 1.4, 8.314_463, 28.9647e-3)`. + pub fn set_sound_speed_from_temp(&mut self, temp: f32) { + self.set_sound_speed_from_temp_with(temp, 1.4, 8.314_463, 28.9647e-3); + } + + /// Sets the sound speed of enabled devices from the temperature `t`, heat capacity ratio `k`, gas constant `r`, and molar mass `m` [kg/mol]. + pub fn set_sound_speed_from_temp_with(&mut self, temp: f32, k: f32, r: f32, m: f32) { + self.sound_speed = (k * r * (273.15 + temp) / m).sqrt() * METER; + } + + /// Gets the wavelength of the ultrasound. + pub fn wavelength(&self) -> f32 { + self.sound_speed / ultrasound_freq().hz() as f32 + } + + /// Gets the wavenumber of the ultrasound. + pub fn wavenumber(&self) -> f32 { + 2.0 * PI * ultrasound_freq().hz() as f32 / self.sound_speed + } + + fn get_direction(dir: Vector3, rotation: &UnitQuaternion) -> UnitVector3 { + let dir: UnitQuaternion = UnitQuaternion::from_quaternion(Quaternion::from_imag(dir)); + UnitVector3::new_normalize((rotation * dir * rotation.conjugate()).imag()) + } +} + +/// Trait for converting to [`Device`]. +pub trait IntoDevice { + /// Converts to [`Device`]. + fn into_device(self, dev_idx: u16) -> Device; +} + +impl IntoDevice for Device { + fn into_device(mut self, dev_idx: u16) -> Device { + self.idx = dev_idx; + self + } +} + +#[cfg(test)] +pub(crate) mod tests { + use rand::Rng; + + use super::*; + use crate::{ + defined::{mm, PI}, + geometry::tests::{create_device, TestDevice}, + }; + + macro_rules! assert_approx_eq_vec3 { + ($a:expr, $b:expr) => { + approx::assert_abs_diff_eq!($a.x, $b.x, epsilon = 1e-3); + approx::assert_abs_diff_eq!($a.y, $b.y, epsilon = 1e-3); + approx::assert_abs_diff_eq!($a.z, $b.z, epsilon = 1e-3); + }; + } + + macro_rules! assert_approx_eq_quat { + ($a:expr, $b:expr) => { + approx::assert_abs_diff_eq!($a.w, $b.w, epsilon = 1e-3); + approx::assert_abs_diff_eq!($a.i, $b.i, epsilon = 1e-3); + approx::assert_abs_diff_eq!($a.j, $b.j, epsilon = 1e-3); + approx::assert_abs_diff_eq!($a.k, $b.k, epsilon = 1e-3); + }; + } + + #[rstest::rstest] + #[test] + #[case(0)] + #[case(1)] + fn idx(#[case] expect: u16) { + assert_eq!(expect, create_device(expect, 249).idx() as u16); + } + + #[rstest::rstest] + #[test] + #[case(1)] + #[case(249)] + fn num_transducers(#[case] n: u8) { + assert_eq!(n, create_device(0, n).num_transducers() as u8); + } + + #[test] + fn center() { + let device = TestDevice::new_autd3(Point3::origin()).into_device(0); + let expected = + device.iter().map(|t| t.position().coords).sum::() / device.len() as f32; + assert_approx_eq_vec3!(expected, device.center()); + } + + #[rstest::rstest] + #[test] + #[case( + Vector3::new(10., 20., 30.), + Vector3::new(10., 20., 30.), + Point3::origin(), + UnitQuaternion::identity() + )] + #[case( + Vector3::zeros(), + Vector3::new(10., 20., 30.), + Point3::new(10., 20., 30.), + UnitQuaternion::identity() + )] + #[case( + Vector3::new(20., -10., 30.), + Vector3::new(10., 20., 30.), + Point3::origin(), + UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.) + )] + #[case( + Vector3::new(30., 30., -30.), + Vector3::new(40., 50., 60.), + Point3::new(10., 20., 30.), + UnitQuaternion::from_axis_angle(&Vector3::x_axis(), PI / 2.) + )] + fn inv( + #[case] expected: Vector3, + #[case] target: Vector3, + #[case] origin: Point3, + #[case] rot: UnitQuaternion, + ) { + let device = TestDevice::new_autd3_with_rot(origin, rot).into_device(0); + assert_approx_eq_vec3!(expected, device.inv.transform_point(&Point3::from(target))); + } + + #[test] + fn translate_to() { + let mut rng = rand::thread_rng(); + let origin = Point3::new(rng.gen(), rng.gen(), rng.gen()); + + let mut device = TestDevice::new_autd3(origin).into_device(0); + + let t = Point3::new(40., 50., 60.); + device.translate_to(t); + + TestDevice::new_autd3(t) + .into_device(0) + .iter() + .zip(device.iter()) + .for_each(|(expect, tr)| { + assert_approx_eq_vec3!(expect.position(), tr.position()); + }); + } + + #[test] + fn rotate_to() { + let mut rng = rand::thread_rng(); + let mut device = TestDevice::new_autd3_with_rot( + Point3::origin(), + UnitQuaternion::from_axis_angle(&Vector3::x_axis(), rng.gen()) + * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), rng.gen()) + * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), rng.gen()), + ) + .into_device(0); + + let rot = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.) + * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 0.) + * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.); + device.rotate_to(rot); + + let expect_x = Vector3::new(0., 1., 0.); + let expect_y = Vector3::new(-1., 0., 0.); + let expect_z = if cfg!(feature = "left_handed") { + Vector3::new(0., 0., -1.) // GRCOV_EXCL_LINE + } else { + Vector3::new(0., 0., 1.) + }; + assert_approx_eq_quat!(rot, device.rotation()); + assert_approx_eq_vec3!(expect_x, device.x_direction()); + assert_approx_eq_vec3!(expect_y, device.y_direction()); + assert_approx_eq_vec3!(expect_z, device.axial_direction()); + TestDevice::new_autd3_with_rot(Point3::origin(), rot) + .into_device(0) + .iter() + .zip(device.iter()) + .for_each(|(expect, tr)| { + assert_approx_eq_vec3!(expect.position(), tr.position()); + }); + } + + #[test] + fn translate() { + let mut rng = rand::thread_rng(); + let origin = Point3::new(rng.gen(), rng.gen(), rng.gen()); + + let mut device = TestDevice::new_autd3(origin).into_device(0); + + let t = Vector3::new(40., 50., 60.); + device.translate(t); + TestDevice::new_autd3(origin + t) + .into_device(0) + .iter() + .zip(device.iter()) + .for_each(|(expect, tr)| { + assert_approx_eq_vec3!(expect.position(), tr.position()); + }); + } + + #[test] + fn rotate() { + let mut device = TestDevice::new_autd3(Point3::origin()).into_device(0); + + let rot = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.) + * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 0.) + * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.); + device.rotate(rot); + let expect_x = Vector3::new(0., 1., 0.); + let expect_y = Vector3::new(-1., 0., 0.); + let expect_z = if cfg!(feature = "left_handed") { + Vector3::new(0., 0., -1.) // GRCOV_EXCL_LINE + } else { + Vector3::new(0., 0., 1.) + }; + assert_approx_eq_quat!(rot, device.rotation()); + assert_approx_eq_vec3!(expect_x, device.x_direction()); + assert_approx_eq_vec3!(expect_y, device.y_direction()); + assert_approx_eq_vec3!(expect_z, device.axial_direction()); + TestDevice::new_autd3_with_rot(Point3::origin(), rot) + .into_device(0) + .iter() + .zip(device.iter()) + .for_each(|(expect, tr)| { + assert_approx_eq_vec3!(expect.position(), tr.position()); + }); + } + + #[test] + fn affine() { + let mut device = TestDevice::new_autd3(Point3::origin()).into_device(0); + + let t = Vector3::new(40., 50., 60.); + let rot = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.) + * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 0.) + * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.); + device.affine(t, rot); + + let expect_x = Vector3::new(0., 1., 0.); + let expect_y = Vector3::new(-1., 0., 0.); + let expect_z = if cfg!(feature = "left_handed") { + Vector3::new(0., 0., -1.) // GRCOV_EXCL_LINE + } else { + Vector3::new(0., 0., 1.) + }; + assert_approx_eq_quat!(rot, device.rotation()); + assert_approx_eq_vec3!(expect_x, device.x_direction()); + assert_approx_eq_vec3!(expect_y, device.y_direction()); + assert_approx_eq_vec3!(expect_z, device.axial_direction()); + + TestDevice::new_autd3_with_rot(Point3::from(t), rot) + .into_device(0) + .iter() + .zip(device.iter()) + .for_each(|(expect, tr)| { + assert_approx_eq_vec3!(expect.position(), tr.position()); + }); + } + + #[rstest::rstest] + #[test] + #[case(340.29525e3, 15.)] + #[case(343.23497e3, 20.)] + #[case(349.04013e3, 30.)] + fn set_sound_speed_from_temp(#[case] expected: f32, #[case] temp: f32) { + let mut device = create_device(0, 249); + device.set_sound_speed_from_temp(temp); + approx::assert_abs_diff_eq!(expected * mm, device.sound_speed, epsilon = 1e-3); + } + + #[rstest::rstest] + #[test] + #[case(8.5, 340e3)] + #[case(10., 400e3)] + fn wavelength(#[case] expect: f32, #[case] c: f32) { + let mut device = create_device(0, 249); + device.sound_speed = c; + approx::assert_abs_diff_eq!(expect, device.wavelength()); + } + + #[rstest::rstest] + #[test] + #[case(0.739_198_27, 340e3)] + #[case(0.628_318_55, 400e3)] + fn wavenumber(#[case] expect: f32, #[case] c: f32) { + let mut device = create_device(0, 249); + device.sound_speed = c; + approx::assert_abs_diff_eq!(expect, device.wavenumber()); + } + + #[test] + fn into_device() { + let mut rng = rand::thread_rng(); + let t = Vector3::new(rng.gen(), rng.gen(), rng.gen()); + let rot = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), rng.gen()) + * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), rng.gen()) + * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), rng.gen()); + let Device { + idx: _idx, + transducers: expect_transducers, + enable: expect_enable, + sound_speed: expect_sound_speed, + rotation: expect_rotation, + center: expect_center, + x_direction: expect_x_direction, + y_direction: expect_y_direction, + axial_direction: expect_axial_direction, + inv: expect_inv, + aabb: expect_aabb, + } = TestDevice::new_autd3_with_rot(Point3::from(t), rot).into_device(0); + let dev = TestDevice::new_autd3_with_rot(Point3::from(t), rot) + .into_device(0) + .into_device(1); + assert_eq!(1, dev.idx()); + assert_eq!(expect_transducers, dev.transducers); + assert_eq!(expect_enable, dev.enable); + assert_eq!(expect_sound_speed, dev.sound_speed); + assert_eq!(expect_rotation, dev.rotation); + assert_eq!(expect_center, dev.center); + assert_eq!(expect_x_direction, dev.x_direction); + assert_eq!(expect_y_direction, dev.y_direction); + assert_eq!(expect_axial_direction, dev.axial_direction); + assert_eq!(expect_inv, dev.inv); + assert_eq!(expect_aabb.min, dev.aabb.min); + assert_eq!(expect_aabb.max, dev.aabb.max); + } +} diff --git a/autd3-core/src/geometry/mod.rs b/autd3-core/src/geometry/mod.rs new file mode 100644 index 00000000..67d48012 --- /dev/null +++ b/autd3-core/src/geometry/mod.rs @@ -0,0 +1,283 @@ +pub(crate) mod device; +mod rotation; +mod transducer; + +/// 3-dimensional column vector. +pub type Vector3 = nalgebra::Vector3; +/// 3-dimensional unit vector. +pub type UnitVector3 = nalgebra::UnitVector3; +/// 3-dimensional point. +pub type Point3 = nalgebra::Point3; +/// A quaternion. +pub type Quaternion = nalgebra::Quaternion; +/// A unit quaternion. +pub type UnitQuaternion = nalgebra::UnitQuaternion; +pub type Translation = nalgebra::Translation3; +pub type Isometry = nalgebra::Isometry3; + +pub use bvh::aabb::Aabb; + +use autd3_derive::Builder; +pub use device::*; +pub use rotation::*; +pub use transducer::*; + +use derive_more::{Deref, IntoIterator}; +use derive_new::new; + +/// Geometry of the devices. +#[derive(Deref, Builder, IntoIterator, new)] +pub struct Geometry { + #[deref] + #[into_iterator(ref)] + pub(crate) devices: Vec, + #[new(default)] + #[get(no_doc)] + version: usize, + #[get(no_doc)] + default_parallel_threshold: usize, +} + +impl Geometry { + /// Gets the number of enabled devices. + pub fn num_devices(&self) -> usize { + self.devices().count() + } + + /// Gets the number of enabled transducers. + pub fn num_transducers(&self) -> usize { + self.devices().map(|dev| dev.num_transducers()).sum() + } + + /// Gets the center of the enabled transducers. + pub fn center(&self) -> Point3 { + Point3::from( + self.devices().map(|d| d.center().coords).sum::() / self.devices.len() as f32, + ) + } + + /// Gets the iterator of enabled devices. + pub fn devices(&self) -> impl Iterator { + self.iter().filter(|dev| dev.enable) + } + + /// Gets the mutable iterator of enabled devices. + pub fn devices_mut(&mut self) -> impl Iterator { + self.iter_mut().filter(|dev| dev.enable) + } + + /// Sets the sound speed of enabled devices. + pub fn set_sound_speed(&mut self, c: f32) { + self.devices_mut().for_each(|dev| dev.sound_speed = c); + } + + /// Sets the sound speed of enabled devices from the temperature `t`. + /// + /// This is equivalent to `Self::set_sound_speed_from_temp_with(t, 1.4, 8.314_463, 28.9647e-3)`. + pub fn set_sound_speed_from_temp(&mut self, t: f32) { + self.set_sound_speed_from_temp_with(t, 1.4, 8.314_463, 28.9647e-3); + } + + /// Sets the sound speed of enabled devices from the temperature `t`, heat capacity ratio `k`, gas constant `r`, and molar mass `m` [kg/mol]. + pub fn set_sound_speed_from_temp_with(&mut self, t: f32, k: f32, r: f32, m: f32) { + self.devices_mut() + .for_each(|dev| dev.set_sound_speed_from_temp_with(t, k, r, m)); + } + + /// Axis Aligned Bounding Box of enabled devices. + pub fn aabb(&self) -> Aabb { + self.devices() + .fold(Aabb::empty(), |aabb, dev| aabb.join(dev.aabb())) + } + + #[doc(hidden)] + pub fn parallel(&self, threshold: Option) -> bool { + self.num_devices() > threshold.unwrap_or(self.default_parallel_threshold) + } +} + +impl<'a> IntoIterator for &'a mut Geometry { + type Item = &'a mut Device; + type IntoIter = std::slice::IterMut<'a, Device>; + + fn into_iter(self) -> Self::IntoIter { + self.version += 1; + self.devices.iter_mut() + } +} + +impl std::ops::DerefMut for Geometry { + fn deref_mut(&mut self) -> &mut Self::Target { + self.version += 1; + &mut self.devices + } +} + +#[cfg(test)] +pub(crate) mod tests { + use crate::defined::{deg, mm}; + + use super::*; + + macro_rules! assert_approx_eq_vec3 { + ($a:expr, $b:expr) => { + approx::assert_abs_diff_eq!($a.x, $b.x, epsilon = 1e-3); + approx::assert_abs_diff_eq!($a.y, $b.y, epsilon = 1e-3); + approx::assert_abs_diff_eq!($a.z, $b.z, epsilon = 1e-3); + }; + } + + pub struct TestDevice { + pub rotation: UnitQuaternion, + pub transducers: Vec, + } + + impl TestDevice { + pub fn new_autd3(pos: Point3) -> Self { + Self::new_autd3_with_rot(pos, UnitQuaternion::identity()) + } + + pub fn new_autd3_with_rot(pos: Point3, rot: impl Into) -> Self { + Self { + rotation: rot.into(), + transducers: itertools::iproduct!(0..18, 0..14) + .enumerate() + .map(|(i, (y, x))| { + Transducer::new( + i as _, + i as _, + pos + 10.16 * Vector3::new(x as f32, y as f32, 0.), + ) + }) + .collect(), + } + } + } + + impl IntoDevice for TestDevice { + fn into_device(self, dev_idx: u16) -> Device { + Device::new(dev_idx, self.rotation, self.transducers) + } + } + + pub fn create_device(idx: u16, n: u8) -> Device { + Device::new( + idx, + UnitQuaternion::identity(), + (0..n) + .map(|i| Transducer::new(i, idx, Point3::origin())) + .collect(), + ) + } + + pub fn create_geometry(n: u16, num_trans_in_unit: u8) -> Geometry { + Geometry::new( + (0..n) + .map(|i| create_device(i, num_trans_in_unit)) + .collect(), + 4, + ) + } + + #[rstest::rstest] + #[test] + #[case(1, vec![create_device(0, 249)])] + #[case(2, vec![create_device(0, 249), create_device(0, 249)])] + fn test_num_devices(#[case] expected: usize, #[case] devices: Vec) { + let geometry = Geometry::new(devices, 4); + assert_eq!(0, geometry.version()); + assert_eq!(expected, geometry.num_devices()); + assert_eq!(0, geometry.version()); + } + + #[rstest::rstest] + #[test] + #[case(249, vec![create_device(0, 249)])] + #[case(498, vec![create_device(0, 249), create_device(0, 249)])] + fn test_num_transducers(#[case] expected: usize, #[case] devices: Vec) { + let geometry = Geometry::new(devices, 4); + assert_eq!(0, geometry.version()); + assert_eq!(expected, geometry.num_transducers()); + assert_eq!(0, geometry.version()); + } + + #[test] + fn test_center() { + let geometry = Geometry::new( + vec![ + TestDevice::new_autd3(Point3::origin()).into_device(0), + TestDevice::new_autd3(Point3::new(10., 20., 30.)).into_device(1), + ], + 4, + ); + let expect = geometry + .iter() + .map(|dev| dev.center().coords) + .sum::() + / geometry.num_devices() as f32; + assert_eq!(0, geometry.version()); + assert_approx_eq_vec3!(expect, geometry.center()); + assert_eq!(0, geometry.version()); + } + + #[rstest::rstest] + #[test] + #[case(340.29525e3, 15.)] + #[case(343.23497e3, 20.)] + #[case(349.04013e3, 30.)] + fn test_set_sound_speed_from_temp(#[case] expected: f32, #[case] temp: f32) { + let mut geometry = create_geometry(2, 1); + assert_eq!(0, geometry.version()); + geometry.set_sound_speed_from_temp(temp); + assert_eq!(1, geometry.version()); + geometry.iter().for_each(|dev| { + approx::assert_abs_diff_eq!(expected * mm, dev.sound_speed, epsilon = 1e-3); + }); + } + + #[rstest::rstest] + #[test] + #[case(3.402_952_8e5)] + #[case(3.432_35e5)] + #[case(3.490_401_6e5)] + fn test_set_sound_speed(#[case] temp: f32) { + let mut geometry = create_geometry(2, 1); + assert_eq!(0, geometry.version()); + geometry.set_sound_speed(temp * mm); + assert_eq!(1, geometry.version()); + geometry.iter().for_each(|dev| { + assert_eq!(dev.sound_speed, temp * mm); + }); + } + + #[test] + fn into_iter() { + let mut geometry = create_geometry(1, 1); + assert_eq!(0, geometry.version()); + for dev in &mut geometry { + dev.enable = true; + } + assert_eq!(1, geometry.version()); + } + + #[rstest::rstest] + #[test] + #[case(Aabb{min: Point3::origin(), max: Point3::new(172.72 * mm, 132.08 * mm, 0.)}, vec![TestDevice::new_autd3(Point3::origin())])] + #[case(Aabb{min: Point3::new(10. * mm, 20. * mm, 30. * mm), max: Point3::new(182.72 * mm, 152.08 * mm, 30. * mm)}, vec![TestDevice::new_autd3(Point3::new(10., 20., 30.))])] + #[case(Aabb{min: Point3::new(-132.08 * mm, 0., 0.), max: Point3::new(0., 172.72 * mm, 0.)}, vec![TestDevice::new_autd3_with_rot(Point3::origin(), EulerAngle::ZYZ(90. * deg, 0. * deg, 0. * deg))])] + #[case(Aabb{min: Point3::new(-132.08 * mm, -10. * mm, 0.), max: Point3::new(172.72 * mm, 162.72 * mm, 10. * mm)}, vec![ + TestDevice::new_autd3(Point3::origin()), + TestDevice::new_autd3_with_rot(Point3::new(10., 20., 30.), EulerAngle::ZYZ(90. * deg, 0. * deg, 0. * deg)) + ])] + fn aabb(#[case] expect: Aabb, #[case] dev: Vec) { + let geometry = Geometry::new( + dev.into_iter() + .enumerate() + .map(|(idx, d)| d.into_device(idx as _)) + .collect(), + 4, + ); + assert_approx_eq_vec3!(expect.min, geometry.aabb().min); + assert_approx_eq_vec3!(expect.max, geometry.aabb().max); + } +} diff --git a/autd3-core/src/geometry/rotation.rs b/autd3-core/src/geometry/rotation.rs new file mode 100644 index 00000000..e0593c0d --- /dev/null +++ b/autd3-core/src/geometry/rotation.rs @@ -0,0 +1,148 @@ +use crate::defined::Angle; + +use super::{UnitQuaternion, Vector3}; + +use paste::paste; + +macro_rules! make_euler_angle_intrinsic { + ($({$first:ident, $second:ident, $third:ident}),*) => { + paste! { + /// Euler angle (intrinsic) + pub enum EulerAngleIntrinsic { + $( + #[doc = stringify!($first-$second-$third)] + #[doc = "euler angle."] + [<$first:upper $second:upper $third:upper>](Angle, Angle, Angle), + )* + } + + impl From for UnitQuaternion { + fn from(angle: EulerAngleIntrinsic) -> Self { + match angle { + $( + EulerAngleIntrinsic::[<$first:upper $second:upper $third:upper>](first, second, third) => { + UnitQuaternion::from_axis_angle(&Vector3::[<$first _axis>](), first.radian()) + * UnitQuaternion::from_axis_angle(&Vector3::[<$second _axis>](), second.radian()) + * UnitQuaternion::from_axis_angle(&Vector3::[<$third _axis>](), third.radian()) + } + )* + } + } + } + } + } +} + +macro_rules! make_euler_angle_extrinsic { + ($({$first:ident, $second:ident, $third:ident}),*) => { + paste! { + /// Euler angle (extrinsic) + pub enum EulerAngleExtrinsic { + $( + #[doc = stringify!($first-$second-$third)] + #[doc = "euler angle."] + [<$first:upper $second:upper $third:upper>](Angle, Angle, Angle), + )* + } + + impl From for UnitQuaternion { + fn from(angle: EulerAngleExtrinsic) -> Self { + match angle { + $( + EulerAngleExtrinsic::[<$first:upper $second:upper $third:upper>](first, second, third) => { + UnitQuaternion::from_axis_angle(&Vector3::[<$third _axis>](), third.radian()) + * UnitQuaternion::from_axis_angle(&Vector3::[<$second _axis>](), second.radian()) + * UnitQuaternion::from_axis_angle(&Vector3::[<$first _axis>](), first.radian()) + } + )* + } + } + } + } + } +} + +make_euler_angle_intrinsic!({x, y, z}, {x, z, y}, {y, x, z}, {y, z, x}, {z, x, y}, {z, y, x}, {x, y, x}, {x, z, x}, {y, x, y}, {y, z, y}, {z, x, z}, {z, y, z}); +make_euler_angle_extrinsic!({x, y, z}, {x, z, y}, {y, x, z}, {y, z, x}, {z, x, y}, {z, y, x}, {x, y, x}, {x, z, x}, {y, x, y}, {y, z, y}, {z, x, z}, {z, y, z}); + +/// Euler angle (intrinsic) +pub type EulerAngle = EulerAngleIntrinsic; + +#[cfg(test)] +mod tests { + use super::*; + use crate::defined::{deg, rad, PI}; + + macro_rules! assert_approx_eq_quat { + ($a:expr, $b:expr) => { + approx::assert_abs_diff_eq!($a.w, $b.w, epsilon = 1e-3); + approx::assert_abs_diff_eq!($a.i, $b.i, epsilon = 1e-3); + approx::assert_abs_diff_eq!($a.j, $b.j, epsilon = 1e-3); + approx::assert_abs_diff_eq!($a.k, $b.k, epsilon = 1e-3); + }; + } + + #[rstest::rstest] + #[test] + #[case(0., 0. * deg)] + #[case(PI / 2., 90. * deg)] + #[case(0., 0. * rad)] + #[case(PI / 2., PI / 2. * rad)] + fn test_to_radians(#[case] expected: f32, #[case] angle: Angle) { + approx::assert_abs_diff_eq!(expected, angle.radian()); + } + + #[rstest::rstest] + #[test] + #[case(UnitQuaternion::from_axis_angle(&Vector3::x_axis(), PI / 2.), EulerAngle::XYZ(90. * deg, 0. * deg, 0. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngle::XYZ(0. * deg, 90. * deg, 0. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngle::XYZ(0. * deg, 0. * deg, 90. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngle::XYZ(0. * deg, 90. * deg, 90. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::x_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngle::XYZ(90. * deg, 90. * deg, 0. * deg))] + fn test_rotation_xyz(#[case] expected: UnitQuaternion, #[case] angle: EulerAngle) { + let angle: UnitQuaternion = angle.into(); + assert_approx_eq_quat!(expected, angle); + } + + #[rstest::rstest] + #[test] + #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngle::ZYZ(90. * deg, 0. * deg, 0. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngle::ZYZ(0. * deg, 90. * deg, 0. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngle::ZYZ(0. * deg, 0. * deg, 90. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngle::ZYZ(0. * deg, 90. * deg, 90. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngle::ZYZ(90. * deg, 90. * deg, 0. * deg))] + fn test_rotation_zyz(#[case] expected: UnitQuaternion, #[case] angle: EulerAngle) { + let angle: UnitQuaternion = angle.into(); + assert_approx_eq_quat!(expected, angle); + } + + #[rstest::rstest] + #[test] + #[case(UnitQuaternion::from_axis_angle(&Vector3::x_axis(), PI / 2.), EulerAngleExtrinsic::XYZ(90. * deg, 0. * deg, 0. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngleExtrinsic::XYZ(0. * deg, 90. * deg, 0. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngleExtrinsic::XYZ(0. * deg, 0. * deg, 90. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngleExtrinsic::XYZ(0. * deg, 90. * deg, 90. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::x_axis(), PI / 2.), EulerAngleExtrinsic::XYZ(90. * deg, 90. * deg, 0. * deg))] + fn test_rotation_xyz_extrinsic( + #[case] expected: UnitQuaternion, + #[case] angle: EulerAngleExtrinsic, + ) { + let angle: UnitQuaternion = angle.into(); + assert_approx_eq_quat!(expected, angle); + } + + #[rstest::rstest] + #[test] + #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngleExtrinsic::ZYZ(90. * deg, 0. * deg, 0. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngleExtrinsic::ZYZ(0. * deg, 90. * deg, 0. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngleExtrinsic::ZYZ(0. * deg, 0. * deg, 90. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngleExtrinsic::ZYZ(0. * deg, 90. * deg, 90. * deg))] + #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngleExtrinsic::ZYZ(90. * deg, 90. * deg, 0. * deg))] + fn test_rotation_zyz_extrinsic( + #[case] expected: UnitQuaternion, + #[case] angle: EulerAngleExtrinsic, + ) { + let angle: UnitQuaternion = angle.into(); + assert_approx_eq_quat!(expected, angle); + } +} diff --git a/autd3-core/src/geometry/transducer.rs b/autd3-core/src/geometry/transducer.rs new file mode 100644 index 00000000..bb28e2f9 --- /dev/null +++ b/autd3-core/src/geometry/transducer.rs @@ -0,0 +1,69 @@ +use autd3_derive::Builder; + +use super::{Isometry, Point3}; + +use derive_new::new; + +/// A ultrasound transducer. +#[derive(Clone, Debug, PartialEq, Builder, new)] +pub struct Transducer { + idx: u8, + dev_idx: u16, + #[get(ref)] + /// The position of the transducer. + position: Point3, +} + +impl Transducer { + /// Gets the local index of the transducer. + pub const fn idx(&self) -> usize { + self.idx as _ + } + + /// Gets the index of the device to which this transducer belongs. + pub const fn dev_idx(&self) -> usize { + self.dev_idx as _ + } + + pub(super) fn affine(&mut self, isometry: &Isometry) { + self.position = isometry * self.position; + } +} + +#[cfg(test)] +mod tests { + use std::f32::consts::PI; + + use crate::geometry::{Translation, UnitQuaternion, Vector3}; + + use super::*; + + macro_rules! assert_vec3_approx_eq { + ($a:expr, $b:expr) => { + approx::assert_abs_diff_eq!($a.x, $b.x, epsilon = 1e-3); + approx::assert_abs_diff_eq!($a.y, $b.y, epsilon = 1e-3); + approx::assert_abs_diff_eq!($a.z, $b.z, epsilon = 1e-3); + }; + } + + #[rstest::fixture] + fn tr() -> Transducer { + Transducer::new(0, 0, Point3::origin()) + } + + #[rstest::rstest] + #[test] + fn affine(mut tr: Transducer) { + let vector = Vector3::new(40., 50., 60.); + let rotation = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.) + * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 0.) + * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.); + tr.affine(&Isometry { + translation: Translation { vector }, + rotation, + }); + + let expect_pos = vector; + assert_vec3_approx_eq!(expect_pos, tr.position()); + } +} diff --git a/autd3-core/src/lib.rs b/autd3-core/src/lib.rs new file mode 100644 index 00000000..99b6d10e --- /dev/null +++ b/autd3-core/src/lib.rs @@ -0,0 +1,24 @@ +#[cfg(any( + feature = "defined", + feature = "geometry", + feature = "gain", + feature = "modulation", + feature = "link" +))] +/// Common constants and types. +pub mod defined; +#[cfg(any(feature = "ethercat", feature = "link"))] +pub mod ethercat; +#[cfg(feature = "gain")] +pub mod gain; +#[cfg(any(feature = "geometry", feature = "gain", feature = "link"))] +/// Geometry related modules. +pub mod geometry; +#[cfg(feature = "link")] +/// A interface to the device. +pub mod link; +#[cfg(feature = "modulation")] +pub mod modulation; +#[cfg(any(feature = "utils", feature = "modulation"))] +#[doc(hidden)] +pub mod utils; diff --git a/autd3-core/src/link/async.rs b/autd3-core/src/link/async.rs new file mode 100644 index 00000000..e6d2b5bd --- /dev/null +++ b/autd3-core/src/link/async.rs @@ -0,0 +1,126 @@ +use std::time::Duration; + +use crate::{ + error::AUTDDriverError, + firmware::cpu::{RxMessage, TxMessage}, + geometry::Geometry, +}; + +pub use internal::{AsyncLink, AsyncLinkBuilder}; + +#[cfg(feature = "async-trait")] +mod internal { + use super::*; + + /// A trait that provides the interface with the device. + #[async_trait::async_trait] + pub trait AsyncLink: Send { + /// Closes the link. + async fn close(&mut self) -> Result<(), AUTDDriverError>; + + #[doc(hidden)] + async fn update(&mut self, _geometry: &Geometry) -> Result<(), AUTDDriverError> { + Ok(()) + } + + /// Sends a message to the device. + async fn send(&mut self, tx: &[TxMessage]) -> Result; + + /// Receives a message from the device. + async fn receive(&mut self, rx: &mut [RxMessage]) -> Result; + + /// Checks if the link is open. + #[must_use] + fn is_open(&self) -> bool; + + #[doc(hidden)] + fn trace(&mut self, _: Option, _: Option) {} + } + + /// A trait to build a link. + #[async_trait::async_trait] + pub trait AsyncLinkBuilder: Send + Sync { + /// The link type. + type L: AsyncLink; + + /// Opens a link. + async fn open(self, geometry: &Geometry) -> Result; + } + + #[async_trait::async_trait] + impl AsyncLink for Box { + async fn close(&mut self) -> Result<(), AUTDDriverError> { + self.as_mut().close().await + } + + async fn update(&mut self, geometry: &Geometry) -> Result<(), AUTDDriverError> { + self.as_mut().update(geometry).await + } + + async fn send(&mut self, tx: &[TxMessage]) -> Result { + self.as_mut().send(tx).await + } + + async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + self.as_mut().receive(rx).await + } + + fn is_open(&self) -> bool { + self.as_ref().is_open() + } + + fn trace(&mut self, timeout: Option, parallel_threshold: Option) { + self.as_mut().trace(timeout, parallel_threshold) + } + } +} + +#[cfg(not(feature = "async-trait"))] +mod internal { + use super::*; + + /// A trait that provides the interface with the device. + pub trait AsyncLink: Send { + /// Closes the link. + fn close(&mut self) -> impl std::future::Future>; + + #[doc(hidden)] + fn update( + &mut self, + _geometry: &Geometry, + ) -> impl std::future::Future> { + async { Ok(()) } + } + + /// Sends a message to the device. + fn send( + &mut self, + tx: &[TxMessage], + ) -> impl std::future::Future>; + + /// Receives a message from the device. + fn receive( + &mut self, + rx: &mut [RxMessage], + ) -> impl std::future::Future>; + + /// Checks if the link is open. + #[must_use] + fn is_open(&self) -> bool; + + #[doc(hidden)] + fn trace(&mut self, _: Option, _: Option) {} + } + + /// A trait to build a link. + pub trait AsyncLinkBuilder { + /// The link type. + type L: AsyncLink; + + /// Opens a link. + fn open( + self, + geometry: &Geometry, + ) -> impl std::future::Future>; + } +} diff --git a/autd3-core/src/link/datagram/header.rs b/autd3-core/src/link/datagram/header.rs new file mode 100644 index 00000000..370a8b70 --- /dev/null +++ b/autd3-core/src/link/datagram/header.rs @@ -0,0 +1,27 @@ +use derive_more::Debug; +use zerocopy::{FromZeros, Immutable, IntoBytes}; + +#[doc(hidden)] +#[repr(C, align(2))] +#[derive(Clone, Debug, PartialEq, Eq, IntoBytes, Immutable, FromZeros)] +pub struct Header { + pub msg_id: u8, + #[debug(ignore)] + __: u8, + pub slot_2_offset: u16, +} + +#[cfg(test)] +mod tests { + use std::mem::offset_of; + use std::mem::size_of; + + use super::*; + + #[test] + fn test_size() { + assert_eq!(4, size_of::
()); + assert_eq!(0, offset_of!(Header, msg_id)); + assert_eq!(2, offset_of!(Header, slot_2_offset)); + } +} diff --git a/autd3-core/src/link/datagram/mod.rs b/autd3-core/src/link/datagram/mod.rs new file mode 100644 index 00000000..258902c2 --- /dev/null +++ b/autd3-core/src/link/datagram/mod.rs @@ -0,0 +1,7 @@ +mod header; +mod rx; +mod tx; + +pub use header::Header; +pub use rx::RxMessage; +pub use tx::TxMessage; diff --git a/autd3-core/src/link/datagram/rx.rs b/autd3-core/src/link/datagram/rx.rs new file mode 100644 index 00000000..c5424a49 --- /dev/null +++ b/autd3-core/src/link/datagram/rx.rs @@ -0,0 +1,34 @@ +use autd3_derive::Builder; +use derive_more::Display; +use derive_new::new; +use zerocopy::{FromBytes, Immutable, IntoBytes}; + +/// PDO input data representation +#[derive( + Clone, Copy, PartialEq, Eq, Debug, new, Builder, IntoBytes, Immutable, FromBytes, Display, +)] +#[display("{:?}", self)] +#[repr(C)] +pub struct RxMessage { + #[get] + /// Received data + data: u8, + #[get] + /// Acknowledgement + ack: u8, +} + +#[cfg(test)] +mod tests { + use std::mem::offset_of; + use std::mem::size_of; + + use super::*; + + #[test] + fn test_message_size() { + assert_eq!(2, size_of::()); + assert_eq!(0, offset_of!(RxMessage, data)); + assert_eq!(1, offset_of!(RxMessage, ack)); + } +} diff --git a/autd3-core/src/link/datagram/tx.rs b/autd3-core/src/link/datagram/tx.rs new file mode 100644 index 00000000..da414b66 --- /dev/null +++ b/autd3-core/src/link/datagram/tx.rs @@ -0,0 +1,33 @@ +use autd3_derive::Builder; +use derive_more::Display; +use zerocopy::{FromZeros, Immutable, IntoBytes}; + +use crate::ethercat::EC_OUTPUT_FRAME_SIZE; + +use super::header::Header; + +const PAYLOAD_SIZE: usize = EC_OUTPUT_FRAME_SIZE - std::mem::size_of::
(); + +/// PDO output data representation +#[repr(C)] +#[derive(Clone, Debug, PartialEq, Eq, IntoBytes, Immutable, FromZeros, Builder, Display)] +#[display("({:?}, TAG: {:#04X})", header, (payload[0] & 0xFF) as u8)] +pub struct TxMessage { + #[get(ref, ref_mut, no_doc)] + header: Header, + payload: [u16; PAYLOAD_SIZE / size_of::()], // use u16 for alignment +} + +impl TxMessage { + #[doc(hidden)] + #[must_use] + pub fn payload(&self) -> &[u8] { + self.payload.as_bytes() + } + + #[doc(hidden)] + #[must_use] + pub fn payload_mut(&mut self) -> &mut [u8] { + self.payload.as_mut_bytes() + } +} diff --git a/autd3-core/src/link/error.rs b/autd3-core/src/link/error.rs new file mode 100644 index 00000000..8728179a --- /dev/null +++ b/autd3-core/src/link/error.rs @@ -0,0 +1,13 @@ +use thiserror::Error; + +#[derive(Error, Debug, PartialEq, Clone)] +pub enum LinkError { + #[error("Failed to open the link")] + Open, + #[error("Failed to send the data")] + Send, + #[error("Failed to receive the data")] + Receive, + #[error("Failed to close the link")] + Close, +} diff --git a/autd3-core/src/link/mod.rs b/autd3-core/src/link/mod.rs new file mode 100644 index 00000000..b062bd49 --- /dev/null +++ b/autd3-core/src/link/mod.rs @@ -0,0 +1,14 @@ +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +#[cfg(feature = "async")] +mod r#async; +mod datagram; +mod error; +mod sync; + +pub use datagram::*; +pub use error::LinkError; +#[cfg(feature = "async")] +#[doc(inline)] +pub use r#async::*; +#[doc(inline)] +pub use sync::*; diff --git a/autd3-core/src/link/sync.rs b/autd3-core/src/link/sync.rs new file mode 100644 index 00000000..3b595b6c --- /dev/null +++ b/autd3-core/src/link/sync.rs @@ -0,0 +1,64 @@ +use std::time::Duration; + +use crate::geometry::Geometry; + +use super::{error::LinkError, RxMessage, TxMessage}; + +/// A trait that provides the interface with the device. +pub trait Link: Send { + /// Closes the link. + fn close(&mut self) -> Result<(), LinkError>; + + #[doc(hidden)] + fn update(&mut self, _geometry: &Geometry) -> Result<(), LinkError> { + Ok(()) + } + + /// Sends a message to the device. + fn send(&mut self, tx: &[TxMessage]) -> Result; + + /// Receives a message from the device. + fn receive(&mut self, rx: &mut [RxMessage]) -> Result; + + /// Checks if the link is open. + #[must_use] + fn is_open(&self) -> bool; + + #[doc(hidden)] + fn trace(&mut self, _: Option, _: Option) {} +} + +/// A trait to build a link. +pub trait LinkBuilder: Send + Sync { + /// The link type. + type L: Link; + + /// Opens a link. + fn open(self, geometry: &Geometry) -> Result; +} + +impl Link for Box { + fn close(&mut self) -> Result<(), LinkError> { + self.as_mut().close() + } + + fn update(&mut self, geometry: &Geometry) -> Result<(), LinkError> { + self.as_mut().update(geometry) + } + + fn send(&mut self, tx: &[TxMessage]) -> Result { + self.as_mut().send(tx) + } + + fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + self.as_mut().receive(rx) + } + + fn is_open(&self) -> bool { + self.as_ref().is_open() + } + + fn trace(&mut self, timeout: Option, parallel_threshold: Option) { + self.as_mut().trace(timeout, parallel_threshold) + } +} diff --git a/autd3-core/src/modulation/error.rs b/autd3-core/src/modulation/error.rs new file mode 100644 index 00000000..871a7979 --- /dev/null +++ b/autd3-core/src/modulation/error.rs @@ -0,0 +1,37 @@ +use std::time::Duration; + +use derive_more::derive::Display; +use thiserror::Error; + +use crate::defined::Freq; + +#[derive(Error, Debug, Display, PartialEq, Clone)] +#[display("{}", msg)] +pub struct ModulationError { + msg: String, +} + +#[derive(Error, Debug, PartialEq, Clone)] +pub enum SamplingConfigError { + /// Invalid sampling division. + #[error("Sampling division ({0}) must not be zero")] + SamplingDivisionInvalid(u16), + /// Invalid sampling frequency. + #[error("Sampling frequency ({0:?}) must divide theultrasound frequency")] + SamplingFreqInvalid(Freq), + /// Invalid sampling frequency. + #[error("Sampling frequency ({0:?}) must divide the ultrasound frequency")] + SamplingFreqInvalidF(Freq), + /// Invalid sampling period. + #[error("Sampling period ({0:?}) must be a multiple of the ultrasound period")] + SamplingPeriodInvalid(Duration), + /// Sampling frequency is out of range. + #[error("Sampling frequency ({0:?}) is out of range ([{1:?}, {2:?}])")] + SamplingFreqOutOfRange(Freq, Freq, Freq), + /// Sampling frequency is out of range. + #[error("Sampling frequency ({0:?}) is out of range ([{1:?}, {2:?}])")] + SamplingFreqOutOfRangeF(Freq, Freq, Freq), + /// Sampling period is out of range. + #[error("Sampling period ({0:?}) is out of range ([{1:?}, {2:?}])")] + SamplingPeriodOutOfRange(Duration, Duration, Duration), +} diff --git a/autd3-core/src/modulation/loop_behavior.rs b/autd3-core/src/modulation/loop_behavior.rs new file mode 100644 index 00000000..e36a8de6 --- /dev/null +++ b/autd3-core/src/modulation/loop_behavior.rs @@ -0,0 +1,115 @@ +use autd3_derive::Builder; +use derive_more::Debug; + +/// The behavior of the loop for [`Modulation`], [`FociSTM`], and [`GainSTM`]. +/// +/// [`Modulation`]: crate::datagram::Modulation +/// [`FociSTM`]: crate::datagram::FociSTM +/// [`GainSTM`]: crate::datagram::GainSTM +#[derive(Clone, Copy, PartialEq, Eq, Debug, Builder)] +#[debug("{}", match self.rep { 0xFFFF => "Infinite".to_string(), 0 => "Once".to_string(), i => format!("Finite({})", i + 1) })] +#[repr(C)] +pub struct LoopBehavior { + #[get(no_doc)] + pub(crate) rep: u16, +} + +pub trait IntoLoopBehaviorFinite { + type Output; + fn into_loop_behavior(self) -> Self::Output; +} + +impl IntoLoopBehaviorFinite for u16 { + type Output = Option; + fn into_loop_behavior(self) -> Self::Output { + if self == 0 { + None + } else { + Some(LoopBehavior { rep: self - 1 }) + } + } +} + +impl IntoLoopBehaviorFinite for std::num::NonZeroU16 { + type Output = LoopBehavior; + fn into_loop_behavior(self) -> Self::Output { + LoopBehavior { + rep: self.get() - 1, + } + } +} + +impl LoopBehavior { + /// Creates a new [`LoopBehavior`] with an infinite loop. + pub const fn infinite() -> Self { + LoopBehavior { rep: 0xFFFF } + } + + /// Creates a new [`LoopBehavior`] with a finite loop. The value must not be zero. + /// + /// # Example + /// + /// ``` + /// # use autd3_driver::firmware::fpga::LoopBehavior; + /// # use std::num::NonZeroU16; + /// let finite: Option = LoopBehavior::finite(1); + /// assert!(finite.is_some()); + /// let finite: Option = LoopBehavior::finite(0); + /// assert!(finite.is_none()); + /// let finite: LoopBehavior = LoopBehavior::finite(NonZeroU16::new(1).unwrap()); + /// ``` + /// + pub fn finite(repeat: T) -> T::Output { + repeat.into_loop_behavior() + } + + /// Creates a new [`LoopBehavior`] with a single loop. + pub const fn once() -> Self { + Self { rep: 0 } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[rstest::rstest] + #[test] + #[case::infinite(0xFFFF, LoopBehavior::infinite())] + #[case::finite(0x1233, LoopBehavior::finite(0x1234).unwrap())] + #[case::once(0x0000, LoopBehavior::once())] + fn loop_behavior(#[case] expect: u16, #[case] target: LoopBehavior) { + assert_eq!(expect, target.rep()); + } + + #[rstest::rstest] + #[test] + #[case(Some(LoopBehavior{ rep: 0 }), 1)] + #[case(Some(LoopBehavior{ rep: 0xFFFE }), 0xFFFF)] + #[case(None, 0)] + fn into_loop_behavior_u16(#[case] expect: Option, #[case] rep: u16) { + assert_eq!(expect, LoopBehavior::finite(rep)); + } + + #[rstest::rstest] + #[test] + #[case(LoopBehavior{ rep: 0 }, std::num::NonZeroU16::new(1).unwrap())] + #[case(LoopBehavior{ rep: 0xFFFE }, std::num::NonZeroU16::new(0xFFFF).unwrap())] + fn into_loop_behavior_non_zero_u16( + #[case] expect: LoopBehavior, + #[case] rep: std::num::NonZeroU16, + ) { + assert_eq!(expect, LoopBehavior::finite(rep)); + } + + #[test] + fn debug() { + assert_eq!(format!("{:?}", LoopBehavior::infinite()), "Infinite"); + assert_eq!(format!("{:?}", LoopBehavior::once()), "Once"); + assert_eq!( + format!("{:?}", LoopBehavior::finite(0x1234).unwrap()), + "Finite(4660)" + ); + } +} diff --git a/autd3-core/src/modulation/mod.rs b/autd3-core/src/modulation/mod.rs new file mode 100644 index 00000000..70a858b2 --- /dev/null +++ b/autd3-core/src/modulation/mod.rs @@ -0,0 +1,27 @@ +mod error; +mod loop_behavior; +mod sampling_config; + +pub use error::{ModulationError, SamplingConfigError}; +pub use loop_behavior::LoopBehavior; +pub use sampling_config::SamplingConfig; + +/// A trait to get the modulation property. (This trait is automatically implemented by the [`Modulation`] derive macro.) +/// +/// [`Modulation`]: autd3_derive::Modulation +pub trait ModulationProperty { + /// Get the sampling configuration. + fn sampling_config(&self) -> SamplingConfig; + /// Get the loop behavior. + fn loop_behavior(&self) -> LoopBehavior; +} + +/// Trait for applying amplitude modulation. +/// +/// See also [`Modulation`] derive macro. +/// +/// [`Modulation`]: autd3_derive::Modulation +pub trait Modulation: ModulationProperty + std::fmt::Debug { + /// Calculate the modulation data. + fn calc(self) -> Result, ModulationError>; +} diff --git a/autd3-core/src/modulation/sampling_config.rs b/autd3-core/src/modulation/sampling_config.rs new file mode 100644 index 00000000..6f21640a --- /dev/null +++ b/autd3-core/src/modulation/sampling_config.rs @@ -0,0 +1,302 @@ +use std::fmt::Debug; + +use autd3_derive::Builder; + +use crate::{ + defined::{ultrasound_freq, Freq, Hz}, + utils::float::is_integer, +}; + +use super::error::SamplingConfigError; + +/// The configuration for sampling. +#[derive(Clone, Copy, Debug, PartialEq, Builder)] +#[repr(C)] +pub struct SamplingConfig { + #[get] + /// The division number of the sampling frequency. + /// + /// The sampling frequency is [`ultrasound_freq`] / `division`. + division: u16, +} + +pub trait IntoSamplingConfig { + fn into_sampling_config(self) -> Result; +} + +impl IntoSamplingConfig for u16 { + fn into_sampling_config(self) -> Result { + if self == 0 { + return Err(SamplingConfigError::SamplingDivisionInvalid(self)); + } + Ok(SamplingConfig { division: self }) + } +} + +impl IntoSamplingConfig for Freq { + fn into_sampling_config(self) -> Result { + const FREQ_MIN: Freq = Freq { freq: 1 }; + if !(FREQ_MIN..=ultrasound_freq()).contains(&self) { + return Err(SamplingConfigError::SamplingFreqOutOfRange( + self, + FREQ_MIN, + ultrasound_freq(), + )); + } + if ultrasound_freq().hz() % self.hz() != 0 { + return Err(SamplingConfigError::SamplingFreqInvalid(self)); + } + Ok(SamplingConfig { + division: (ultrasound_freq().hz() / self.hz()) as _, + }) + } +} + +impl IntoSamplingConfig for Freq { + fn into_sampling_config(self) -> Result { + let freq_max = ultrasound_freq().hz() as f32 * Hz; + let freq_min = freq_max / u16::MAX as f32; + if !(freq_min..=freq_max).contains(&self) { + return Err(SamplingConfigError::SamplingFreqOutOfRangeF( + self, freq_min, freq_max, + )); + } + let div = ultrasound_freq().hz() as f32 / self.hz(); + if !is_integer(div as _) { + return Err(SamplingConfigError::SamplingFreqInvalidF(self)); + } + Ok(SamplingConfig { division: div as _ }) + } +} + +#[cfg(not(feature = "dynamic_freq"))] +impl IntoSamplingConfig for std::time::Duration { + fn into_sampling_config(self) -> Result { + use crate::defined::ultrasound_period; + + let period_min = ultrasound_period(); + let period_max = std::time::Duration::from_micros( + u16::MAX as u64 * ultrasound_period().as_micros() as u64, + ); + if !(period_min..=period_max).contains(&self) { + return Err(SamplingConfigError::SamplingPeriodOutOfRange( + self, period_min, period_max, + )); + } + if self.as_nanos() % ultrasound_period().as_nanos() != 0 { + return Err(SamplingConfigError::SamplingPeriodInvalid(self)); + } + Ok(SamplingConfig { + division: (self.as_nanos() / ultrasound_period().as_nanos()) as _, + }) + } +} + +pub trait IntoSamplingConfigNearest { + fn into_sampling_config_nearest(self) -> SamplingConfig; +} + +impl IntoSamplingConfigNearest for Freq { + fn into_sampling_config_nearest(self) -> SamplingConfig { + SamplingConfig::new( + (ultrasound_freq().hz() as f32 / self.hz()) + .clamp(1.0, u16::MAX as f32) + .round() as u16, + ) + .unwrap() + } +} + +impl IntoSamplingConfigNearest for Freq { + fn into_sampling_config_nearest(self) -> SamplingConfig { + SamplingConfig::new( + (ultrasound_freq().hz() + self.hz() / 2) + .checked_div(self.hz()) + .unwrap_or(u32::MAX) + .clamp(1, u16::MAX as u32) as u16, + ) + .unwrap() + } +} + +#[cfg(not(feature = "dynamic_freq"))] +impl IntoSamplingConfigNearest for std::time::Duration { + fn into_sampling_config_nearest(self) -> SamplingConfig { + use crate::defined::ultrasound_period; + + SamplingConfig::new( + ((self.as_nanos() + ultrasound_period().as_nanos() / 2) + / ultrasound_period().as_nanos()) + .clamp(1, u16::MAX as u128) as u16, + ) + .unwrap() + } +} + +impl SamplingConfig { + /// A [`SamplingConfig`] of 40kHz. + pub const FREQ_40K: SamplingConfig = SamplingConfig { division: 1 }; + /// A [`SamplingConfig`] of 4kHz. + pub const FREQ_4K: SamplingConfig = SamplingConfig { division: 10 }; + /// A [`SamplingConfig`] of the minimum frequency. + pub const FREQ_MIN: SamplingConfig = SamplingConfig { division: u16::MAX }; + + /// Creates a new [`SamplingConfig`]. + pub fn new(value: impl IntoSamplingConfig) -> Result { + value.into_sampling_config() + } + + /// Creates a new [`SamplingConfig`] with the nearest frequency or period value of the possible values. + pub fn new_nearest(value: impl IntoSamplingConfigNearest) -> Self { + value.into_sampling_config_nearest() + } + + /// Gets the sampling frequency. + pub fn freq(&self) -> Freq { + ultrasound_freq().hz() as f32 / self.division() as f32 * Hz + } + + /// Gets the sampling period. + #[cfg(not(feature = "dynamic_freq"))] + pub fn period(&self) -> std::time::Duration { + crate::defined::ultrasound_period() * self.division() as u32 + } +} + +// GRCOV_EXCL_START +impl TryInto for Freq { + type Error = SamplingConfigError; + + fn try_into(self) -> Result { + SamplingConfig::new(self) + } +} + +impl TryInto for Freq { + type Error = SamplingConfigError; + + fn try_into(self) -> Result { + SamplingConfig::new(self) + } +} + +#[cfg(not(feature = "dynamic_freq"))] +impl TryInto for std::time::Duration { + type Error = SamplingConfigError; + + fn try_into(self) -> Result { + SamplingConfig::new(self) + } +} +// GRCOV_EXCL_STOP + +#[cfg(test)] +mod tests { + use crate::defined::Hz; + + #[cfg(not(feature = "dynamic_freq"))] + use crate::defined::ultrasound_period; + #[cfg(not(feature = "dynamic_freq"))] + use std::time::Duration; + + use super::*; + + #[rstest::rstest] + #[test] + #[case(Ok(1), 1)] + #[case(Ok(u16::MAX), u16::MAX)] + #[case(Ok(1), 40000 * Hz)] + #[case(Ok(10), 4000 * Hz)] + #[case(Ok(1), 40000. * Hz)] + #[case(Ok(10), 4000. * Hz)] + #[case(Err(SamplingConfigError::SamplingDivisionInvalid(0)), 0)] + #[case(Err(SamplingConfigError::SamplingFreqInvalid(ultrasound_freq() - 1 * Hz)), ultrasound_freq() - 1 * Hz)] + #[case(Err(SamplingConfigError::SamplingFreqOutOfRange(0 * Hz, 1 * Hz, ultrasound_freq())), 0 * Hz)] + #[case(Err(SamplingConfigError::SamplingFreqOutOfRange(ultrasound_freq() + 1 * Hz, 1 * Hz, ultrasound_freq())), ultrasound_freq() + 1 * Hz)] + #[case(Err(SamplingConfigError::SamplingFreqInvalidF((ultrasound_freq().hz() as f32 - 1.) * Hz)), (ultrasound_freq().hz() as f32 - 1.) * Hz)] + #[case(Err(SamplingConfigError::SamplingFreqOutOfRangeF(0. * Hz, ultrasound_freq().hz() as f32 * Hz / u16::MAX as f32, ultrasound_freq().hz() as f32 * Hz)), 0. * Hz)] + #[case(Err(SamplingConfigError::SamplingFreqOutOfRangeF(40000. * Hz + 1. * Hz, ultrasound_freq().hz() as f32 * Hz / u16::MAX as f32, ultrasound_freq().hz() as f32 * Hz)), 40000. * Hz + 1. * Hz)] + #[cfg_attr(not(feature = "dynamic_freq"), case(Ok(1), Duration::from_micros(25)))] + #[cfg_attr( + not(feature = "dynamic_freq"), + case(Ok(10), Duration::from_micros(250)) + )] + #[cfg_attr(not(feature = "dynamic_freq"), case(Err(SamplingConfigError::SamplingPeriodInvalid(Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64) - Duration::from_nanos(1))), Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64) - Duration::from_nanos(1)))] + #[cfg_attr(not(feature = "dynamic_freq"), case(Err(SamplingConfigError::SamplingPeriodOutOfRange(ultrasound_period() / 2, ultrasound_period(), Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64))), ultrasound_period() / 2))] + #[cfg_attr(not(feature = "dynamic_freq"), case(Err(SamplingConfigError::SamplingPeriodOutOfRange(Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64) * 2, ultrasound_period(), Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64))), Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64) * 2))] + fn division( + #[case] expect: Result, + #[case] value: impl IntoSamplingConfig, + ) { + assert_eq!(expect, SamplingConfig::new(value).map(|c| c.division())); + } + + #[rstest::rstest] + #[test] + #[case(Ok(40000. * Hz), 1)] + #[case(Ok(0.61036086 * Hz), u16::MAX)] + #[case(Ok(40000. * Hz), 40000 * Hz)] + #[case(Ok(4000. * Hz), 4000 * Hz)] + #[case(Ok(40000. * Hz), 40000. * Hz)] + #[case(Ok(4000. * Hz), 4000. * Hz)] + #[cfg_attr(not(feature = "dynamic_freq"), case(Ok(40000. * Hz), Duration::from_micros(25)))] + #[cfg_attr(not(feature = "dynamic_freq"), case(Ok(4000. * Hz), Duration::from_micros(250)))] + fn freq( + #[case] expect: Result, SamplingConfigError>, + #[case] value: impl IntoSamplingConfig, + ) { + assert_eq!(expect, SamplingConfig::new(value).map(|c| c.freq())); + } + + #[cfg(not(feature = "dynamic_freq"))] + #[rstest::rstest] + #[test] + #[case(Ok(Duration::from_micros(25)), 1)] + #[case(Ok(Duration::from_micros(1638375)), u16::MAX)] + #[case(Ok(Duration::from_micros(25)), 40000 * Hz)] + #[case(Ok(Duration::from_micros(250)), 4000 * Hz)] + #[case(Ok(Duration::from_micros(25)), 40000. * Hz)] + #[case(Ok(Duration::from_micros(250)), 4000. * Hz)] + #[case(Ok(Duration::from_micros(25)), Duration::from_micros(25))] + #[case(Ok(Duration::from_micros(250)), Duration::from_micros(250))] + fn period( + #[case] expect: Result, + #[case] value: impl IntoSamplingConfig, + ) { + assert_eq!(expect, SamplingConfig::new(value).map(|c| c.period())); + } + + #[rstest::rstest] + #[test] + #[case::min(u16::MAX, (40000. / u16::MAX as f32) * Hz)] + #[case::max(1, 40000. * Hz)] + #[case::not_supported_max(1, (ultrasound_freq().hz() as f32 - 1.) * Hz)] + #[case::out_of_range_min(u16::MAX, 0. * Hz)] + #[case::out_of_range_max(1, 40000. * Hz + 1. * Hz)] + fn from_freq_f32_nearest(#[case] expected: u16, #[case] freq: Freq) { + assert_eq!(expected, SamplingConfig::new_nearest(freq).division()); + } + + #[rstest::rstest] + #[test] + #[case::min(40000, 1 * Hz)] + #[case::max(1, 40000 * Hz)] + #[case::not_supported_max(1, ultrasound_freq() - 1 * Hz)] + #[case::out_of_range_min(0xFFFF, 0 * Hz)] + #[case::out_of_range_max(1, ultrasound_freq() + 1 * Hz)] + fn from_freq_u32_nearest(#[case] expected: u16, #[case] freq: Freq) { + assert_eq!(expected, SamplingConfig::new_nearest(freq).division()); + } + + #[cfg(not(feature = "dynamic_freq"))] + #[rstest::rstest] + #[test] + #[case::min(1, ultrasound_period())] + #[case::max(u16::MAX, Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64))] + #[case::not_supported_max(u16::MAX, Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64) - Duration::from_nanos(1))] + #[case::out_of_range_min(1, ultrasound_period() / 2)] + #[case::out_of_range_max(u16::MAX, Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64) * 2)] + fn from_period_nearest(#[case] expected: u16, #[case] p: Duration) { + assert_eq!(expected, SamplingConfig::new_nearest(p).division()); + } +} diff --git a/autd3-core/src/utils/float.rs b/autd3-core/src/utils/float.rs new file mode 100644 index 00000000..0a1be3bc --- /dev/null +++ b/autd3-core/src/utils/float.rs @@ -0,0 +1,38 @@ +const EPSILON: f64 = 1e-6; + +#[doc(hidden)] +pub fn is_integer(a: f64) -> bool { + 0.5 - (a.fract() - 0.5).abs() < EPSILON +} + +#[cfg(test)] +mod tests { + + #[rstest::rstest] + #[test] + #[case(true, 1.0)] + #[case(false, 1.5)] + #[case(true, 1.0 + f32::EPSILON)] + #[case(true, 1.0 - f32::EPSILON)] + #[case(false, 1.0 + 1e-3)] + #[case(false, 1.0 - 1e-3)] + fn is_integer(#[case] expected: bool, #[case] a: f32) { + assert_eq!(super::is_integer(a as f64), expected, "{}", a); + } + + #[test] + fn is_integer_rand() { + use rand::{thread_rng, Rng}; + + const N: usize = 100000; + + let mut rng = thread_rng(); + + (0..N).for_each(|_| { + let a: u32 = rng.gen_range(1..40000); + let m: u32 = rng.gen_range(512..=0xFFFFFFFF); + let b = a as f64 / m as f64; + assert!(super::is_integer(b * m as f64)); + }); + } +} diff --git a/autd3-core/src/utils/mod.rs b/autd3-core/src/utils/mod.rs new file mode 100644 index 00000000..05ed125a --- /dev/null +++ b/autd3-core/src/utils/mod.rs @@ -0,0 +1 @@ +pub mod float; \ No newline at end of file From ea10a21a327c846930e9d78de3b0b35e3cf894b0 Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Mon, 13 Jan 2025 13:11:47 +0900 Subject: [PATCH 03/20] update autd3-core --- autd3-core/Cargo.toml | 1 - autd3-core/src/gain/error.rs | 2 +- autd3-core/src/lib.rs | 3 +++ autd3-core/src/link/async.rs | 32 +++++++++++++++--------------- autd3-core/src/link/error.rs | 16 ++++++--------- autd3-core/src/modulation/error.rs | 2 +- 6 files changed, 27 insertions(+), 29 deletions(-) diff --git a/autd3-core/Cargo.toml b/autd3-core/Cargo.toml index 021d150e..d837fc00 100644 --- a/autd3-core/Cargo.toml +++ b/autd3-core/Cargo.toml @@ -39,7 +39,6 @@ ethercat = [] gain = [] geometry = [] left_handed = [] -lightweight = [] link = [] modulation = [] use_meter = [] diff --git a/autd3-core/src/gain/error.rs b/autd3-core/src/gain/error.rs index 9bd2c72f..03ef4d00 100644 --- a/autd3-core/src/gain/error.rs +++ b/autd3-core/src/gain/error.rs @@ -1,4 +1,4 @@ -use derive_more::derive::Display; +use derive_more::Display; use thiserror::Error; #[derive(Error, Debug, Display, PartialEq, Clone)] diff --git a/autd3-core/src/lib.rs b/autd3-core/src/lib.rs index 99b6d10e..3495476c 100644 --- a/autd3-core/src/lib.rs +++ b/autd3-core/src/lib.rs @@ -22,3 +22,6 @@ pub mod modulation; #[cfg(any(feature = "utils", feature = "modulation"))] #[doc(hidden)] pub mod utils; + +#[cfg(feature = "async-trait")] +pub use async_trait::async_trait; diff --git a/autd3-core/src/link/async.rs b/autd3-core/src/link/async.rs index e6d2b5bd..785bc4ac 100644 --- a/autd3-core/src/link/async.rs +++ b/autd3-core/src/link/async.rs @@ -1,33 +1,33 @@ use std::time::Duration; use crate::{ - error::AUTDDriverError, - firmware::cpu::{RxMessage, TxMessage}, geometry::Geometry, + link::{LinkError, RxMessage, TxMessage}, }; pub use internal::{AsyncLink, AsyncLinkBuilder}; #[cfg(feature = "async-trait")] mod internal { + use super::*; /// A trait that provides the interface with the device. #[async_trait::async_trait] pub trait AsyncLink: Send { /// Closes the link. - async fn close(&mut self) -> Result<(), AUTDDriverError>; + async fn close(&mut self) -> Result<(), LinkError>; #[doc(hidden)] - async fn update(&mut self, _geometry: &Geometry) -> Result<(), AUTDDriverError> { + async fn update(&mut self, _geometry: &Geometry) -> Result<(), LinkError> { Ok(()) } /// Sends a message to the device. - async fn send(&mut self, tx: &[TxMessage]) -> Result; + async fn send(&mut self, tx: &[TxMessage]) -> Result; /// Receives a message from the device. - async fn receive(&mut self, rx: &mut [RxMessage]) -> Result; + async fn receive(&mut self, rx: &mut [RxMessage]) -> Result; /// Checks if the link is open. #[must_use] @@ -44,24 +44,24 @@ mod internal { type L: AsyncLink; /// Opens a link. - async fn open(self, geometry: &Geometry) -> Result; + async fn open(self, geometry: &Geometry) -> Result; } #[async_trait::async_trait] impl AsyncLink for Box { - async fn close(&mut self) -> Result<(), AUTDDriverError> { + async fn close(&mut self) -> Result<(), LinkError> { self.as_mut().close().await } - async fn update(&mut self, geometry: &Geometry) -> Result<(), AUTDDriverError> { + async fn update(&mut self, geometry: &Geometry) -> Result<(), LinkError> { self.as_mut().update(geometry).await } - async fn send(&mut self, tx: &[TxMessage]) -> Result { + async fn send(&mut self, tx: &[TxMessage]) -> Result { self.as_mut().send(tx).await } - async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { self.as_mut().receive(rx).await } @@ -82,13 +82,13 @@ mod internal { /// A trait that provides the interface with the device. pub trait AsyncLink: Send { /// Closes the link. - fn close(&mut self) -> impl std::future::Future>; + fn close(&mut self) -> impl std::future::Future>; #[doc(hidden)] fn update( &mut self, _geometry: &Geometry, - ) -> impl std::future::Future> { + ) -> impl std::future::Future> { async { Ok(()) } } @@ -96,13 +96,13 @@ mod internal { fn send( &mut self, tx: &[TxMessage], - ) -> impl std::future::Future>; + ) -> impl std::future::Future>; /// Receives a message from the device. fn receive( &mut self, rx: &mut [RxMessage], - ) -> impl std::future::Future>; + ) -> impl std::future::Future>; /// Checks if the link is open. #[must_use] @@ -121,6 +121,6 @@ mod internal { fn open( self, geometry: &Geometry, - ) -> impl std::future::Future>; + ) -> impl std::future::Future>; } } diff --git a/autd3-core/src/link/error.rs b/autd3-core/src/link/error.rs index 8728179a..918af19c 100644 --- a/autd3-core/src/link/error.rs +++ b/autd3-core/src/link/error.rs @@ -1,13 +1,9 @@ +use derive_more::Display; +use derive_new::new; use thiserror::Error; -#[derive(Error, Debug, PartialEq, Clone)] -pub enum LinkError { - #[error("Failed to open the link")] - Open, - #[error("Failed to send the data")] - Send, - #[error("Failed to receive the data")] - Receive, - #[error("Failed to close the link")] - Close, +#[derive(new, Error, Debug, Display, PartialEq, Clone)] +#[display("{}", msg)] +pub struct LinkError { + msg: String, } diff --git a/autd3-core/src/modulation/error.rs b/autd3-core/src/modulation/error.rs index 871a7979..10732801 100644 --- a/autd3-core/src/modulation/error.rs +++ b/autd3-core/src/modulation/error.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use derive_more::derive::Display; +use derive_more::Display; use thiserror::Error; use crate::defined::Freq; From b3d422954ec5ebbcda66d4e9298fa2e3f8fc91f9 Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Mon, 13 Jan 2025 13:12:00 +0900 Subject: [PATCH 04/20] update autd3-link-twincat --- autd3-link-twincat/Cargo.toml | 7 ++- autd3-link-twincat/src/error.rs | 10 +++- autd3-link-twincat/src/local/mod.rs | 56 +++++++++---------- .../src/remote/remote_twincat_link.rs | 30 +++++----- 4 files changed, 51 insertions(+), 52 deletions(-) diff --git a/autd3-link-twincat/Cargo.toml b/autd3-link-twincat/Cargo.toml index 05c91be1..18545622 100644 --- a/autd3-link-twincat/Cargo.toml +++ b/autd3-link-twincat/Cargo.toml @@ -10,7 +10,8 @@ license = { workspace = true } repository = { workspace = true } [dependencies] -autd3-driver = { workspace = true, features = ["derive"] } +autd3-core = { workspace = true, features = ["link"] } +autd3-derive = { workspace = true } itertools = { workspace = true, optional = true } libloading = { workspace = true, optional = true } thiserror = { workspace = true } @@ -24,8 +25,8 @@ cc = { workspace = true, optional = true } local = ["libloading"] remote = ["itertools", "cc", "tracing"] default = ["local"] -async = ["autd3-driver/async"] -async-trait = ["async", "autd3-driver/async-trait"] +async = ["autd3-core/async"] +async-trait = ["async", "autd3-core/async-trait"] all = ["local", "remote"] [package.metadata.docs.rs] diff --git a/autd3-link-twincat/src/error.rs b/autd3-link-twincat/src/error.rs index 6cfd3c5a..2861a3a1 100644 --- a/autd3-link-twincat/src/error.rs +++ b/autd3-link-twincat/src/error.rs @@ -1,9 +1,13 @@ -use autd3_driver::error::AUTDDriverError; +use autd3_core::link::LinkError; use thiserror::Error; #[derive(Error, Debug)] #[non_exhaustive] pub enum AdsError { + #[error("TcAdsDll not found. Please install TwinCAT3.")] + DllNotFound, + #[error("Function {0} not found in TcAdsDll")] + FunctionNotFound(String), #[error("Failed to open port")] OpenPort, #[error("Failed to close port")] @@ -24,8 +28,8 @@ pub enum AdsError { InvalidIp(String), } -impl From for AUTDDriverError { +impl From for LinkError { fn from(err: AdsError) -> Self { - AUTDDriverError::LinkError(err.to_string()) + LinkError::new(err.to_string()) } } diff --git a/autd3-link-twincat/src/local/mod.rs b/autd3-link-twincat/src/local/mod.rs index dc64262c..f75fa225 100644 --- a/autd3-link-twincat/src/local/mod.rs +++ b/autd3-link-twincat/src/local/mod.rs @@ -6,10 +6,9 @@ use lib::Library; use zerocopy::IntoBytes; -use autd3_driver::{ - derive::*, - firmware::cpu::{RxMessage, TxMessage}, - link::{Link, LinkBuilder}, +use autd3_core::{ + geometry::Geometry, + link::{Link, LinkBuilder, LinkError, RxMessage, TxMessage}, }; #[repr(C)] @@ -43,20 +42,16 @@ pub struct TwinCAT { } /// A builder for [`TwinCAT`]. -#[derive(Builder)] pub struct TwinCATBuilder {} impl LinkBuilder for TwinCATBuilder { type L = TwinCAT; - fn open(self, _: &Geometry) -> Result { - let dll = unsafe { lib::Library::new("TcAdsDll") }.map_err(|_| { - AUTDDriverError::LinkError("TcAdsDll not found. Please install TwinCAT3".to_owned()) - })?; + fn open(self, _: &Geometry) -> Result { + let dll = unsafe { lib::Library::new("TcAdsDll") }.map_err(|_| AdsError::DllNotFound)?; let port = unsafe { dll.get:: i32>(b"AdsPortOpenEx") - .map_err(|_| AUTDDriverError::LinkError("AdsPortOpenEx not found".to_owned()))?( - ) + .map_err(|_| AdsError::FunctionNotFound("AdsPortOpenEx".to_owned()))?() }; if port == 0 { return Err(AdsError::OpenPort.into()); @@ -65,9 +60,10 @@ impl LinkBuilder for TwinCATBuilder { let mut ams_addr: AmsAddr = unsafe { std::mem::zeroed() }; let n_err = unsafe { dll.get:: i32>(b"AdsGetLocalAddressEx") - .map_err(|_| { - AUTDDriverError::LinkError("AdsGetLocalAddressEx not found".to_owned()) - })?(port, &mut ams_addr as *mut _) + .map_err(|_| AdsError::FunctionNotFound("AdsGetLocalAddressEx".to_owned()))?( + port, + &mut ams_addr as *mut _, + ) }; if n_err != 0 { return Err(AdsError::GetLocalAddress(n_err).into()); @@ -92,19 +88,19 @@ impl TwinCAT { } impl Link for TwinCAT { - fn close(&mut self) -> Result<(), AUTDDriverError> { + fn close(&mut self) -> Result<(), LinkError> { unsafe { self.dll .get:: i32>(b"AdsPortCloseEx") - .map_err(|_| AUTDDriverError::LinkError("AdsPortCloseEx not found".to_owned()))?( - self.port, + .map_err(|_| AdsError::FunctionNotFound("AdsPortCloseEx".to_owned()))?( + self.port ); } self.port = 0; Ok(()) } - fn send(&mut self, tx: &[TxMessage]) -> Result { + fn send(&mut self, tx: &[TxMessage]) -> Result { unsafe { let n_err = self.dll.get:: i32>(b"AdsSyncWriteReqEx").map_err(|_| - AUTDDriverError::LinkError( - "AdsSyncWriteReqEx not found".to_owned(), + AdsError::FunctionNotFound( + "AdsSyncWriteReqEx".to_owned(), ) )? ( @@ -135,7 +131,7 @@ impl Link for TwinCAT { } } - fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + fn receive(&mut self, rx: &mut [RxMessage]) -> Result { let mut read_bytes: u32 = 0; unsafe { let n_err = self @@ -149,9 +145,7 @@ impl Link for TwinCAT { *mut c_void, *mut u32, ) -> i32>(b"AdsSyncReadReqEx2") - .map_err(|_| { - AUTDDriverError::LinkError("AdsSyncReadReqEx2 not found".to_owned()) - })?( + .map_err(|_| AdsError::FunctionNotFound("AdsSyncReadReqEx2".to_owned()))?( self.port, &raw const self.send_addr, INDEX_GROUP, @@ -175,32 +169,32 @@ impl Link for TwinCAT { } #[cfg(feature = "async")] -use autd3_driver::link::{AsyncLink, AsyncLinkBuilder}; +use autd3_core::link::{AsyncLink, AsyncLinkBuilder}; #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] -#[cfg_attr(feature = "async-trait", autd3_driver::async_trait)] +#[cfg_attr(feature = "async-trait", autd3_core::async_trait)] impl AsyncLinkBuilder for TwinCATBuilder { type L = TwinCAT; - async fn open(self, geometry: &Geometry) -> Result { + async fn open(self, geometry: &Geometry) -> Result { ::open(self, geometry) } } #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] -#[cfg_attr(feature = "async-trait", autd3_driver::async_trait)] +#[cfg_attr(feature = "async-trait", autd3_core::async_trait)] impl AsyncLink for TwinCAT { - async fn close(&mut self) -> Result<(), AUTDDriverError> { + async fn close(&mut self) -> Result<(), LinkError> { ::close(self) } - async fn send(&mut self, tx: &[TxMessage]) -> Result { + async fn send(&mut self, tx: &[TxMessage]) -> Result { ::send(self, tx) } - async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { ::receive(self, rx) } diff --git a/autd3-link-twincat/src/remote/remote_twincat_link.rs b/autd3-link-twincat/src/remote/remote_twincat_link.rs index 8d4e8f88..7ce29e89 100644 --- a/autd3-link-twincat/src/remote/remote_twincat_link.rs +++ b/autd3-link-twincat/src/remote/remote_twincat_link.rs @@ -1,13 +1,13 @@ use std::ffi::{c_long, CString}; +use autd3_derive::Builder; use itertools::Itertools; use zerocopy::IntoBytes; -use autd3_driver::{ - derive::*, - firmware::cpu::{RxMessage, TxMessage}, - link::{Link, LinkBuilder}, +use autd3_core::{ + geometry::Geometry, + link::{Link, LinkBuilder, LinkError, RxMessage, TxMessage}, }; use crate::{error::AdsError, remote::native_methods::*}; @@ -47,7 +47,7 @@ impl LinkBuilder for RemoteTwinCATBuilder { type L = RemoteTwinCAT; #[tracing::instrument(level = "debug", skip(_geometry))] - fn open(self, _geometry: &Geometry) -> Result { + fn open(self, _geometry: &Geometry) -> Result { tracing::info!("Connecting to TwinCAT3"); let RemoteTwinCATBuilder { @@ -134,7 +134,7 @@ impl RemoteTwinCAT { } impl Link for RemoteTwinCAT { - fn close(&mut self) -> Result<(), AUTDDriverError> { + fn close(&mut self) -> Result<(), LinkError> { if self.port == 0 { return Ok(()); } @@ -150,7 +150,7 @@ impl Link for RemoteTwinCAT { Ok(()) } - fn send(&mut self, tx: &[TxMessage]) -> Result { + fn send(&mut self, tx: &[TxMessage]) -> Result { let addr = AmsAddr { net_id: self.net_id, port: PORT, @@ -178,7 +178,7 @@ impl Link for RemoteTwinCAT { Err(AdsError::SendData(res as _).into()) } - fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + fn receive(&mut self, rx: &mut [RxMessage]) -> Result { let addr = AmsAddr { net_id: self.net_id, port: PORT, @@ -210,32 +210,32 @@ impl Link for RemoteTwinCAT { } #[cfg(feature = "async")] -use autd3_driver::link::{AsyncLink, AsyncLinkBuilder}; +use autd3_core::link::{AsyncLink, AsyncLinkBuilder}; #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] -#[cfg_attr(feature = "async-trait", autd3_driver::async_trait)] +#[cfg_attr(feature = "async-trait", autd3_core::async_trait)] impl AsyncLinkBuilder for RemoteTwinCATBuilder { type L = RemoteTwinCAT; - async fn open(self, geometry: &Geometry) -> Result { + async fn open(self, geometry: &Geometry) -> Result { ::open(self, geometry) } } #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] -#[cfg_attr(feature = "async-trait", autd3_driver::async_trait)] +#[cfg_attr(feature = "async-trait", autd3_core::async_trait)] impl AsyncLink for RemoteTwinCAT { - async fn close(&mut self) -> Result<(), AUTDDriverError> { + async fn close(&mut self) -> Result<(), LinkError> { ::close(self) } - async fn send(&mut self, tx: &[TxMessage]) -> Result { + async fn send(&mut self, tx: &[TxMessage]) -> Result { ::send(self, tx) } - async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { ::receive(self, rx) } From 7975f054c6a537e04a7500a300a7f818a3e005ea Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Tue, 14 Jan 2025 16:02:13 +0900 Subject: [PATCH 05/20] update autd3-core --- autd3-core/Cargo.toml | 55 +++--- autd3-core/src/acoustics/directivity/mod.rs | 56 +++++++ .../src/acoustics/directivity/sphere.rs | 40 +++++ .../src/acoustics/directivity/t4010a1.rs | 95 +++++++++++ autd3-core/src/acoustics/mod.rs | 112 +++++++++++++ autd3-core/src/datagram/gpio.rs | 27 +++ autd3-core/src/datagram/mod.rs | 83 +++++++++ autd3-core/src/datagram/operation.rs | 34 ++++ autd3-core/src/datagram/segment.rs | 10 ++ autd3-core/src/datagram/transition_mode.rs | 46 +++++ autd3-core/src/datagram/tuple.rs | 158 ++++++++++++++++++ autd3-core/src/defined/mod.rs | 3 - autd3-core/src/ethercat/dc_sys_time.rs | 110 ++++++++++++ autd3-core/src/ethercat/mod.rs | 13 ++ autd3-core/src/gain/error.rs | 3 +- autd3-core/src/gain/mod.rs | 33 +++- autd3-core/src/gain/phase.rs | 7 +- autd3-core/src/geometry/mod.rs | 17 +- autd3-core/src/lib.rs | 63 +++++-- autd3-core/src/modulation/error.rs | 3 +- autd3-core/src/modulation/loop_behavior.rs | 2 +- autd3-core/src/modulation/mod.rs | 13 ++ autd3-core/src/resampler/mod.rs | 25 +++ autd3-core/src/resampler/sinc.rs | 146 ++++++++++++++++ autd3-core/src/resampler/window/blackman.rs | 21 +++ autd3-core/src/resampler/window/mod.rs | 13 ++ .../src/resampler/window/rectangular.rs | 20 +++ 27 files changed, 1164 insertions(+), 44 deletions(-) create mode 100644 autd3-core/src/acoustics/directivity/mod.rs create mode 100644 autd3-core/src/acoustics/directivity/sphere.rs create mode 100644 autd3-core/src/acoustics/directivity/t4010a1.rs create mode 100644 autd3-core/src/acoustics/mod.rs create mode 100644 autd3-core/src/datagram/gpio.rs create mode 100644 autd3-core/src/datagram/mod.rs create mode 100644 autd3-core/src/datagram/operation.rs create mode 100644 autd3-core/src/datagram/segment.rs create mode 100644 autd3-core/src/datagram/transition_mode.rs create mode 100644 autd3-core/src/datagram/tuple.rs create mode 100644 autd3-core/src/ethercat/dc_sys_time.rs create mode 100644 autd3-core/src/resampler/mod.rs create mode 100644 autd3-core/src/resampler/sinc.rs create mode 100644 autd3-core/src/resampler/window/blackman.rs create mode 100644 autd3-core/src/resampler/window/mod.rs create mode 100644 autd3-core/src/resampler/window/rectangular.rs diff --git a/autd3-core/Cargo.toml b/autd3-core/Cargo.toml index d837fc00..16ae93ea 100644 --- a/autd3-core/Cargo.toml +++ b/autd3-core/Cargo.toml @@ -10,16 +10,18 @@ license = { workspace = true } repository = { workspace = true } [dependencies] -async-trait = { workspace = true, optional = true } -autd3-derive = { workspace = true } -bit-vec = { workspace = true } -bvh = { workspace = true } -derive_more = { workspace = true, features = ["add", "debug", "deref", "deref_mut", "display", "into_iterator", "mul"] } -derive-new = { workspace = true } -nalgebra = { workspace = true } -paste = { workspace = true } -thiserror = { workspace = true, default-features = false } -zerocopy = { workspace = true } +async-trait = { workspace = true, default-features = false, optional = true } +autd3-derive = { workspace = true, default-features = false, optional = true } +bit-vec = { workspace = true, default-features = false, optional = true } +bvh = { workspace = true, default-features = false, optional = true } +derive_more = { workspace = true, default-features = false, optional = true } +derive-new = { workspace = true, default-features = false, optional = true } +nalgebra = { workspace = true, default-features = false, optional = true } +paste = { workspace = true, default-features = false, optional = true } +time = { workspace = true, default-features = false, features = ["macros", "std"], optional = true } +thiserror = { workspace = true, default-features = false, optional = true } +tracing = { workspace = true, default-features = false, optional = true } +zerocopy = { workspace = true, default-features = false, features = ["derive"], optional = true } [dev-dependencies] anyhow = { workspace = true } @@ -29,17 +31,32 @@ rand = { workspace = true } rstest = { workspace = true } [features] -default = ["derive"] +acoustics = ["defined", "geometry"] async = [] -async-trait = ["dep:async-trait"] -derive = [] -defined = [] +async-trait = ["async", "dep:async-trait"] +datagram = ["defined", "geometry", "ethercat"] +defined = ["derive_more", "derive_more/add", "derive_more/mul", "derive_more/debug"] +derive = ["datagram", "autd3-derive", "tracing"] dynamic_freq = [] -ethercat = [] -gain = [] -geometry = [] +ethercat = ["time", "thiserror"] +gain = ["defined", "datagram", "geometry", "thiserror", "bit-vec", "zerocopy", "derive_more", "derive_more/display"] +geometry = [ + "nalgebra", + "bvh", + "paste", + "autd3-derive", + "defined", + "derive-new", + "derive_more", + "derive_more/add", + "derive_more/mul", + "derive_more/into_iterator", + "derive_more/deref", + "derive_more/debug", +] left_handed = [] -link = [] -modulation = [] +link = ["zerocopy", "autd3-derive", "ethercat", "geometry", "derive_more", "derive_more/display"] +modulation = ["autd3-derive", "utils", "defined", "datagram", "derive_more", "derive_more/display"] +resampler = ["modulation", "defined", "tracing", "utils"] use_meter = [] utils = [] diff --git a/autd3-core/src/acoustics/directivity/mod.rs b/autd3-core/src/acoustics/directivity/mod.rs new file mode 100644 index 00000000..7836715c --- /dev/null +++ b/autd3-core/src/acoustics/directivity/mod.rs @@ -0,0 +1,56 @@ +mod sphere; +mod t4010a1; + +use crate::{ + defined::{rad, Angle}, + geometry::{UnitVector3, Vector3}, +}; + +pub use sphere::Sphere; +pub use t4010a1::T4010A1; + +/// A trait representing the directivity of ultrasound transducer. +pub trait Directivity: Send + Sync { + /// Calculates the directivity based on the given angle. + /// + /// # Arguments + /// + /// * `theta` - The angle between the axial direction and the target direction. + fn directivity(theta: Angle) -> f32; + + /// Calculates the directivity based on the axial direction and target direction. + fn directivity_from_dir(axial_direction: &UnitVector3, target: &Vector3) -> f32 { + Self::directivity( + (axial_direction.cross(target).norm()).atan2(axial_direction.dot(target)) * rad, + ) + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + + pub(crate) struct TestDirectivity {} + + impl Directivity for TestDirectivity { + fn directivity(t: Angle) -> f32 { + t.degree() + } + } + + #[rstest::rstest] + #[test] + #[case::dir_x(90., Vector3::x(), Vector3::z_axis())] + #[case::dir_y(90., Vector3::y(), Vector3::z_axis())] + #[case::dir_z(0., Vector3::z(), Vector3::z_axis())] + fn test_directivity_from_dir( + #[case] expected: f32, + #[case] target: Vector3, + #[case] dir: UnitVector3, + ) { + approx::assert_abs_diff_eq!( + expected, + TestDirectivity::directivity_from_dir(&dir, &target) + ); + } +} diff --git a/autd3-core/src/acoustics/directivity/sphere.rs b/autd3-core/src/acoustics/directivity/sphere.rs new file mode 100644 index 00000000..203fe725 --- /dev/null +++ b/autd3-core/src/acoustics/directivity/sphere.rs @@ -0,0 +1,40 @@ +use super::*; + +/// Sphere directivity model. +pub struct Sphere {} + +impl Directivity for Sphere { + #[inline] + fn directivity(_: Angle) -> f32 { + 1. + } + + #[inline] + fn directivity_from_dir(_: &UnitVector3, _: &Vector3) -> f32 { + 1. + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use rand::prelude::*; + + #[test] + fn test_directivity() { + let mut rng = rand::thread_rng(); + assert_eq!(1.0, Sphere::directivity(rng.gen::() * rad)); + } + + #[rstest::rstest] + #[test] + #[case::dir_x(1., Vector3::x())] + #[case::dir_y(1., Vector3::y())] + #[case::dir_z(1., Vector3::z())] + fn test_directivity_sphere_from_dir(#[case] expected: f32, #[case] target: Vector3) { + let mut rng = rand::thread_rng(); + let dir = UnitVector3::new_unchecked(Vector3::new(rng.gen(), rng.gen(), rng.gen())); + assert_eq!(expected, Sphere::directivity_from_dir(&dir, &target)); + } +} diff --git a/autd3-core/src/acoustics/directivity/t4010a1.rs b/autd3-core/src/acoustics/directivity/t4010a1.rs new file mode 100644 index 00000000..d7487889 --- /dev/null +++ b/autd3-core/src/acoustics/directivity/t4010a1.rs @@ -0,0 +1,95 @@ +use super::*; + +#[allow(clippy::excessive_precision, clippy::unreadable_literal)] +static DIR_COEF_A: &[f32] = &[ + 1.0, + 1.0, + 1.0, + 0.891250938, + 0.707945784, + 0.501187234, + 0.354813389, + 0.251188643, + 0.199526231, +]; + +#[allow(clippy::excessive_precision, clippy::unreadable_literal)] +static DIR_COEF_B: &[f32] = &[ + 0., + 0., + -0.00459648054721, + -0.0155520765675, + -0.0208114779827, + -0.0182211227016, + -0.0122437497109, + -0.00780345575475, + -0.00312857467007, +]; + +#[allow(clippy::excessive_precision, clippy::unreadable_literal)] +static DIR_COEF_C: &[f32] = &[ + 0., + 0., + -0.000787968093807, + -0.000307591508224, + -0.000218348633296, + 0.00047738416141, + 0.000120353137658, + 0.000323676257958, + 0.000143850511, +]; + +#[allow(clippy::excessive_precision, clippy::unreadable_literal)] +static DIR_COEF_D: &[f32] = &[ + 0., + 0., + 1.60125528528e-05, + 2.9747624976e-06, + 2.31910931569e-05, + -1.1901034125e-05, + 6.77743734332e-06, + -5.99548024824e-06, + -4.79372835035e-06, +]; + +/// [T4010A1/B4](https://www.nicera.co.jp/en/products/ultrasonic-sensor/open-aperture-type) directivity model. +pub struct T4010A1 {} + +impl Directivity for T4010A1 { + fn directivity(theta: Angle) -> f32 { + let theta_deg = theta.degree().abs() % 180.0; + let theta_deg = 90.0 - (theta_deg - 90.0).abs(); + let i = (theta_deg / 10.0).ceil() as usize; + if i == 0 { + 1.0 + } else { + let x = theta_deg - (i as f32 - 1.0) * 10.0; + ((DIR_COEF_D[i - 1] * x + DIR_COEF_C[i - 1]) * x + DIR_COEF_B[i - 1]) * x + + DIR_COEF_A[i - 1] + } + } +} + +#[cfg(test)] +mod tests { + use crate::defined::deg; + + use super::*; + + #[rstest::rstest] + #[test] + #[case::deg_0(1.0, 0.0)] + #[case::deg_10(1.0, 10.0)] + #[case::deg_20(1.0, 20.0)] + #[case::deg_30(0.891251, 30.0)] + #[case::deg_40(0.70794576, 40.0)] + #[case::deg_50(0.5011872, 50.0)] + #[case::deg_60(0.35481337, 60.0)] + #[case::deg_70(0.25118864, 70.0)] + #[case::deg_80(0.19952622, 80.0)] + #[case::deg_90(0.17783181, 90.0)] + #[case::deg_100(0.19952622, 100.0)] + fn test_directivity(#[case] expected: f32, #[case] theta_deg: f32) { + approx::assert_abs_diff_eq!(expected, T4010A1::directivity(theta_deg * deg)); + } +} diff --git a/autd3-core/src/acoustics/mod.rs b/autd3-core/src/acoustics/mod.rs new file mode 100644 index 00000000..202cee44 --- /dev/null +++ b/autd3-core/src/acoustics/mod.rs @@ -0,0 +1,112 @@ +/// directivity module +pub mod directivity; + +use std::f32::consts::PI; + +use crate::{ + defined::T4010A1_AMPLITUDE, + geometry::{Complex, Point3, Transducer, UnitVector3}, +}; + +use directivity::Directivity; + +/// Calculate the pressure at the target position. +#[inline] +pub fn propagate( + tr: &Transducer, + wavenumber: f32, + dir: &UnitVector3, + target_pos: &Point3, +) -> Complex { + const P0: f32 = T4010A1_AMPLITUDE / (4. * PI); + let diff = target_pos - tr.position(); + let dist = diff.norm(); + Complex::from_polar( + P0 / dist * D::directivity_from_dir(dir, &diff), + wavenumber * dist, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + use rand::Rng; + + use crate::{ + defined::mm, + geometry::{Device, UnitQuaternion, Vector3}, + }; + use directivity::tests::TestDirectivity; + + macro_rules! assert_complex_approx_eq { + ($a:expr, $b:expr) => { + approx::assert_abs_diff_eq!($a.re, $b.re, epsilon = 1e-3 / mm); + approx::assert_abs_diff_eq!($a.im, $b.im, epsilon = 1e-3 / mm); + }; + } + + #[rstest::fixture] + fn tr() -> Transducer { + let mut rng = rand::thread_rng(); + Transducer::new( + 0, + 0, + Point3::new( + rng.gen_range(-100.0..100.0), + rng.gen_range(-100.0..100.0), + rng.gen_range(-100.0..100.0), + ), + ) + } + + #[rstest::fixture] + fn rot() -> UnitQuaternion { + let mut rng = rand::thread_rng(); + UnitQuaternion::from_axis_angle( + &Vector3::x_axis(), + rng.gen_range::(-180.0..180.0).to_radians(), + ) * UnitQuaternion::from_axis_angle( + &Vector3::y_axis(), + rng.gen_range::(-180.0..180.0).to_radians(), + ) * UnitQuaternion::from_axis_angle( + &Vector3::z_axis(), + rng.gen_range::(-180.0..180.0).to_radians(), + ) + } + + #[rstest::fixture] + fn target() -> Point3 { + let mut rng = rand::thread_rng(); + Point3::new( + rng.gen_range(-100.0..100.0), + rng.gen_range(-100.0..100.0), + rng.gen_range(-100.0..100.0), + ) + } + + #[rstest::fixture] + fn sound_speed() -> f32 { + let mut rng = rand::thread_rng(); + rng.gen_range(300e3..400e3) + } + + #[rstest::rstest] + #[test] + fn test_propagate(tr: Transducer, rot: UnitQuaternion, target: Point3, sound_speed: f32) { + let mut device = Device::new(0, rot, vec![tr.clone()]); + device.sound_speed = sound_speed; + let wavenumber = device.wavenumber(); + assert_complex_approx_eq!( + { + let diff = target - tr.position(); + let dist = diff.norm(); + let r = T4010A1_AMPLITUDE / (4. * PI) / dist + * TestDirectivity::directivity_from_dir(device.axial_direction(), &diff); + let phase = wavenumber * dist; + Complex::new(r * phase.cos(), r * phase.sin()) + }, + super::propagate::(&tr, wavenumber, device.axial_direction(), &target) + ); + } +} diff --git a/autd3-core/src/datagram/gpio.rs b/autd3-core/src/datagram/gpio.rs new file mode 100644 index 00000000..3d291088 --- /dev/null +++ b/autd3-core/src/datagram/gpio.rs @@ -0,0 +1,27 @@ +/// GPIO output pin. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum GPIOOut { + /// Output 0 + O0 = 0, + /// Output 1 + O1 = 1, + /// Output 2 + O2 = 2, + /// Output 3 + O3 = 3, +} + +/// GPIO input pin. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum GPIOIn { + /// Input 0 + I0 = 0, + /// Input 1 + I1 = 1, + /// Input 2 + I2 = 2, + /// Input 3 + I3 = 3, +} diff --git a/autd3-core/src/datagram/mod.rs b/autd3-core/src/datagram/mod.rs new file mode 100644 index 00000000..01e1d4af --- /dev/null +++ b/autd3-core/src/datagram/mod.rs @@ -0,0 +1,83 @@ +mod gpio; +mod operation; +mod segment; +mod transition_mode; +mod tuple; + +pub use gpio::{GPIOIn, GPIOOut}; +pub use operation::{NullOp, Operation}; +pub use segment::Segment; +pub use transition_mode::{TransitionMode, TRANSITION_MODE_NONE}; +pub use tuple::{CombinedError, CombinedOperationGenerator}; + +use std::time::Duration; + +use crate::{defined::DEFAULT_TIMEOUT, geometry::Geometry}; + +/// [`Datagram`] represents the data sent to the device. +pub trait Datagram: std::fmt::Debug { + #[doc(hidden)] + type G; + #[doc(hidden)] + type Error: std::error::Error; + + #[doc(hidden)] + fn operation_generator(self, geometry: &Geometry) -> Result; + + /// Returns the timeout duration. + fn timeout(&self) -> Option { + Some(DEFAULT_TIMEOUT) + } + + /// Returns the parallel threshold. + fn parallel_threshold(&self) -> Option { + Some(usize::MAX) + } +} + +/// [`DatagramS`] represents a [`Datagram`] that can specify [`Segment`] to write the data. +pub trait DatagramS: std::fmt::Debug { + #[doc(hidden)] + type G; + #[doc(hidden)] + type Error: std::error::Error; + + #[doc(hidden)] + fn operation_generator_with_segment( + self, + geometry: &Geometry, + segment: Segment, + transition_mode: Option, + ) -> Result; + + /// Returns the timeout duration. + fn timeout(&self) -> Option { + Some(DEFAULT_TIMEOUT) + } + + /// Returns the parallel threshold. + fn parallel_threshold(&self) -> Option { + Some(usize::MAX) + } +} + +impl Datagram for D { + type G = D::G; + type Error = D::Error; + + fn operation_generator(self, geometry: &Geometry) -> Result { + self.operation_generator_with_segment( + geometry, + Segment::S0, + Some(TransitionMode::Immediate), + ) + } + + fn timeout(&self) -> Option { + ::timeout(self) + } + + fn parallel_threshold(&self) -> Option { + ::parallel_threshold(self) + } +} diff --git a/autd3-core/src/datagram/operation.rs b/autd3-core/src/datagram/operation.rs new file mode 100644 index 00000000..dbb7365a --- /dev/null +++ b/autd3-core/src/datagram/operation.rs @@ -0,0 +1,34 @@ +use crate::geometry::Device; + +#[doc(hidden)] +pub trait Operation: Send + Sync { + type Error: std::error::Error; + + fn required_size(&self, device: &Device) -> usize; + fn pack(&mut self, device: &Device, tx: &mut [u8]) -> Result; + fn is_done(&self) -> bool; +} + +pub struct NullOp; + +impl Operation for NullOp { + type Error = std::convert::Infallible; + + fn required_size(&self, _: &Device) -> usize { + 0 + } + + fn pack(&mut self, _: &Device, _: &mut [u8]) -> Result { + unreachable!() + } + + fn is_done(&self) -> bool { + true + } +} + +impl Default for Box> { + fn default() -> Self { + Box::new(NullOp) + } +} diff --git a/autd3-core/src/datagram/segment.rs b/autd3-core/src/datagram/segment.rs new file mode 100644 index 00000000..c131ae9d --- /dev/null +++ b/autd3-core/src/datagram/segment.rs @@ -0,0 +1,10 @@ +/// Segment of the FPGA memory +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[repr(u8)] +pub enum Segment { + /// Segment 0 + #[default] + S0 = 0, + /// Segment 1 + S1 = 1, +} diff --git a/autd3-core/src/datagram/transition_mode.rs b/autd3-core/src/datagram/transition_mode.rs new file mode 100644 index 00000000..98f07018 --- /dev/null +++ b/autd3-core/src/datagram/transition_mode.rs @@ -0,0 +1,46 @@ +use crate::ethercat::DcSysTime; + +use super::gpio::GPIOIn; + +pub(crate) const TRANSITION_MODE_SYNC_IDX: u8 = 0x00; +pub(crate) const TRANSITION_MODE_SYS_TIME: u8 = 0x01; +pub(crate) const TRANSITION_MODE_GPIO: u8 = 0x02; +pub(crate) const TRANSITION_MODE_EXT: u8 = 0xF0; +pub const TRANSITION_MODE_NONE: u8 = 0xFE; +pub(crate) const TRANSITION_MODE_IMMEDIATE: u8 = 0xFF; + +/// Transition mode of segment +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum TransitionMode { + /// Transites when the sampling index in the destination segment is 0. + SyncIdx, + /// Transites when the system time is the specified time. + SysTime(DcSysTime), + /// Transites when the specified GPIO pin is high. + GPIO(GPIOIn), + /// Transites to the next segment automatically when the data in the current segment is finished. + Ext, + /// Transites immediately. + Immediate, +} + +impl TransitionMode { + pub const fn mode(&self) -> u8 { + match self { + TransitionMode::SyncIdx => TRANSITION_MODE_SYNC_IDX, + TransitionMode::SysTime(_) => TRANSITION_MODE_SYS_TIME, + TransitionMode::GPIO(_) => TRANSITION_MODE_GPIO, + TransitionMode::Ext => TRANSITION_MODE_EXT, + TransitionMode::Immediate => TRANSITION_MODE_IMMEDIATE, + } + } + + pub const fn value(&self) -> u64 { + match self { + TransitionMode::SyncIdx | TransitionMode::Ext | TransitionMode::Immediate => 0, + TransitionMode::GPIO(gpio) => *gpio as u64, + TransitionMode::SysTime(time) => time.sys_time(), + } + } +} diff --git a/autd3-core/src/datagram/tuple.rs b/autd3-core/src/datagram/tuple.rs new file mode 100644 index 00000000..5088df31 --- /dev/null +++ b/autd3-core/src/datagram/tuple.rs @@ -0,0 +1,158 @@ +use std::time::Duration; + +use thiserror::Error; + +use crate::geometry::Geometry; + +use super::Datagram; + +pub struct CombinedOperationGenerator { + pub o1: O1, + pub o2: O2, +} + +#[derive(Error, Debug, PartialEq)] +pub enum CombinedError +where + E1: std::error::Error, + E2: std::error::Error, +{ + #[error("{0}")] + E1(E1), + #[error("{0}")] + E2(E2), +} + +impl Datagram for (D1, D2) +where + D1: Datagram, + D2: Datagram, + E1: std::error::Error, + E2: std::error::Error, +{ + type G = CombinedOperationGenerator; + type Error = CombinedError; + + fn operation_generator(self, geometry: &Geometry) -> Result { + match ( + self.0.operation_generator(geometry), + self.1.operation_generator(geometry), + ) { + (Ok(g1), Ok(g2)) => Ok(CombinedOperationGenerator { o1: g1, o2: g2 }), + (Err(e1), _) => Err(Self::Error::E1(e1)), + (_, Err(e2)) => Err(Self::Error::E2(e2)), + } + } + + fn timeout(&self) -> Option { + self.0.timeout().into_iter().chain(self.1.timeout()).max() + } + + fn parallel_threshold(&self) -> Option { + self.0 + .parallel_threshold() + .into_iter() + .chain(self.1.parallel_threshold()) + .min() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Debug)] + pub struct NullDatagram { + pub timeout: Option, + pub parallel_threshold: Option, + } + + impl Datagram for NullDatagram { + type G = (); + type Error = std::convert::Infallible; + + fn operation_generator(self, _: &Geometry) -> Result { + Ok(()) + } + + fn timeout(&self) -> Option { + self.timeout + } + + fn parallel_threshold(&self) -> Option { + self.parallel_threshold + } + } + + #[rstest::rstest] + #[case(None, None, None)] + #[case( + Some(Duration::from_millis(100)), + Some(Duration::from_millis(100)), + None + )] + #[case( + Some(Duration::from_millis(100)), + None, + Some(Duration::from_millis(100)) + )] + #[case( + Some(Duration::from_millis(200)), + Some(Duration::from_millis(100)), + Some(Duration::from_millis(200)) + )] + #[case( + Some(Duration::from_millis(200)), + Some(Duration::from_millis(200)), + Some(Duration::from_millis(100)) + )] + #[test] + fn timeout( + #[case] expect: Option, + #[case] timeout1: Option, + #[case] timeout2: Option, + ) { + assert_eq!( + expect, + ( + NullDatagram { + timeout: timeout1, + parallel_threshold: None, + }, + NullDatagram { + timeout: timeout2, + parallel_threshold: None, + } + ) + .timeout() + ); + } + + #[rstest::rstest] + #[case(None, None, None)] + #[case(Some(100), Some(100), None)] + #[case(Some(100), None, Some(100))] + #[case(Some(100), Some(100), Some(200))] + #[case(Some(100), Some(200), Some(100))] + #[test] + fn parallel_threshold( + #[case] expect: Option, + #[case] threshold1: Option, + #[case] threshold2: Option, + ) { + assert_eq!( + expect, + ( + NullDatagram { + timeout: None, + parallel_threshold: threshold1, + }, + NullDatagram { + timeout: None, + parallel_threshold: threshold2, + } + ) + .parallel_threshold() + ); + } +} diff --git a/autd3-core/src/defined/mod.rs b/autd3-core/src/defined/mod.rs index db2e3ac5..83bf1247 100644 --- a/autd3-core/src/defined/mod.rs +++ b/autd3-core/src/defined/mod.rs @@ -21,9 +21,6 @@ pub use freq::*; /// millimeter pub const MILLIMETER: f32 = METER / 1000.0; -/// a complex number -pub type Complex = nalgebra::Complex; - /// The absolute threshold of hearing in \[㎩\] pub const ABSOLUTE_THRESHOLD_OF_HEARING: f32 = 20e-6; diff --git a/autd3-core/src/ethercat/dc_sys_time.rs b/autd3-core/src/ethercat/dc_sys_time.rs new file mode 100644 index 00000000..9e71389e --- /dev/null +++ b/autd3-core/src/ethercat/dc_sys_time.rs @@ -0,0 +1,110 @@ +use time::OffsetDateTime; + +use thiserror::Error; + +#[derive(Error, Debug, PartialEq, Clone)] +#[error("Invalid date time")] +pub struct InvalidDateTime; + +use super::ECAT_DC_SYS_TIME_BASE; + +/// The system time of the Distributed Clock +/// +/// The system time is the time expressed in 1ns units with 2000-01-01 0:00:00 UTC as the reference. +/// It is expressed as a 64-bit unsigned integer and can represent about 584 years of time. +/// See [EtherCAT Distributed Clock](https://infosys.beckhoff.com/english.php?content=../content/1033/ethercatsystem/2469118347.html) for more information. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[repr(C)] +pub struct DcSysTime { + dc_sys_time: u64, +} + +impl DcSysTime { + /// The zero point of the DcSysTime (2000-01-01 0:00:00 UTC) + pub const ZERO: Self = Self { dc_sys_time: 0 }; + + /// Returns the system time in nanoseconds + pub const fn sys_time(&self) -> u64 { + self.dc_sys_time + } + + /// Converts the system time to the UTC time + pub fn to_utc(&self) -> OffsetDateTime { + ECAT_DC_SYS_TIME_BASE + std::time::Duration::from_nanos(self.dc_sys_time) + } + + /// Creates a new instance from the UTC time + pub fn from_utc(utc: OffsetDateTime) -> Result { + Ok(Self { + dc_sys_time: u64::try_from((utc - ECAT_DC_SYS_TIME_BASE).whole_nanoseconds()) + .map_err(|_| InvalidDateTime)?, + }) + } + + /// Returns the system time of now + pub fn now() -> Self { + Self::from_utc(OffsetDateTime::now_utc()).unwrap() + } +} + +impl std::ops::Add for DcSysTime { + type Output = Self; + + fn add(self, rhs: std::time::Duration) -> Self::Output { + Self { + dc_sys_time: self.dc_sys_time + rhs.as_nanos() as u64, + } + } +} + +impl std::ops::Sub for DcSysTime { + type Output = Self; + + fn sub(self, rhs: std::time::Duration) -> Self::Output { + Self { + dc_sys_time: self.dc_sys_time - rhs.as_nanos() as u64, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn now_dc_sys_time() { + let t = DcSysTime::now(); + assert!(t.sys_time() > 0); + } + + #[rstest::rstest] + #[test] + #[case(Ok(DcSysTime { dc_sys_time: 0 }), time::macros::datetime!(2000-01-01 0:0:0 UTC))] + #[case(Ok(DcSysTime { dc_sys_time: 1000000000 }), time::macros::datetime!(2000-01-01 0:0:1 UTC))] + #[case(Ok(DcSysTime { dc_sys_time: 31622400000000000 }), time::macros::datetime!(2001-01-01 0:0:0 UTC))] + #[case(Err(InvalidDateTime), time::macros::datetime!(1999-01-01 0:0:1 UTC))] + #[case(Err(InvalidDateTime), time::macros::datetime!(9999-01-01 0:0:1 UTC))] + fn from_utc(#[case] expect: Result, #[case] utc: OffsetDateTime) { + assert_eq!(expect, DcSysTime::from_utc(utc)); + } + + #[rstest::rstest] + #[test] + #[case(time::macros::datetime!(2000-01-01 0:0:1 UTC))] + #[case(time::macros::datetime!(2001-01-01 0:0:0 UTC))] + fn to_utc(#[case] utc: OffsetDateTime) { + assert_eq!(utc, DcSysTime::from_utc(utc).unwrap().to_utc()); + } + + #[test] + fn addsub() { + let utc = time::macros::datetime!(2000-01-01 0:0:0 UTC); + let t = DcSysTime::from_utc(utc); + assert!(t.is_ok()); + let t = t.unwrap() + std::time::Duration::from_secs(1); + assert_eq!(1000000000, t.sys_time()); + + let t = t - std::time::Duration::from_secs(1); + assert_eq!(0, t.sys_time()); + } +} diff --git a/autd3-core/src/ethercat/mod.rs b/autd3-core/src/ethercat/mod.rs index e7ebf313..55bd29f7 100644 --- a/autd3-core/src/ethercat/mod.rs +++ b/autd3-core/src/ethercat/mod.rs @@ -1,4 +1,17 @@ +mod dc_sys_time; + +pub use dc_sys_time::DcSysTime; + +use std::time::Duration; + /// PDO output frame size pub const EC_OUTPUT_FRAME_SIZE: usize = 626; /// PDO input frame size pub const EC_INPUT_FRAME_SIZE: usize = 2; + +/// The base unit of the EtherCAT +pub const EC_CYCLE_TIME_BASE: Duration = Duration::from_micros(500); + +/// The base point of system time +pub const ECAT_DC_SYS_TIME_BASE: time::OffsetDateTime = + time::macros::datetime!(2000-01-01 0:00 UTC); diff --git a/autd3-core/src/gain/error.rs b/autd3-core/src/gain/error.rs index 03ef4d00..be59f968 100644 --- a/autd3-core/src/gain/error.rs +++ b/autd3-core/src/gain/error.rs @@ -1,7 +1,8 @@ use derive_more::Display; +use derive_new::new; use thiserror::Error; -#[derive(Error, Debug, Display, PartialEq, Clone)] +#[derive(new, Error, Debug, Display, PartialEq, Clone)] #[display("{}", msg)] pub struct GainError { msg: String, diff --git a/autd3-core/src/gain/mod.rs b/autd3-core/src/gain/mod.rs index b6b0bf52..4008692a 100644 --- a/autd3-core/src/gain/mod.rs +++ b/autd3-core/src/gain/mod.rs @@ -5,13 +5,17 @@ mod phase; use std::collections::HashMap; -pub use bit_vec::BitVec; +pub type BitVec = bit_vec::BitVec; + pub use drive::Drive; pub use emit_intensity::EmitIntensity; pub use error::GainError; pub use phase::Phase; -use crate::geometry::{Device, Geometry, Transducer}; +use crate::{ + datagram::{Segment, TransitionMode}, + geometry::{Device, Geometry, Transducer}, +}; /// A trait to calculate the phase and intensity for [`Gain`]. /// @@ -52,6 +56,29 @@ pub trait Gain: std::fmt::Debug { fn init( self, geometry: &Geometry, - filter: Option<&HashMap>>, + filter: Option<&HashMap>, ) -> Result; } + +#[doc(hidden)] +pub struct GainOperationGenerator { + pub generator: G, + pub segment: Segment, + pub transition: Option, +} + +impl GainOperationGenerator { + pub fn new>( + gain: T, + geometry: &Geometry, + segment: Segment, + transition: Option, + ) -> Result { + Ok(Self { + generator: gain.init(geometry, None)?, + segment, + transition, + }) + } +} + diff --git a/autd3-core/src/gain/phase.rs b/autd3-core/src/gain/phase.rs index 0d743008..bbf99087 100644 --- a/autd3-core/src/gain/phase.rs +++ b/autd3-core/src/gain/phase.rs @@ -1,4 +1,9 @@ -use crate::defined::{rad, Angle, Complex, PI}; +use std::f32::consts::PI; + +use crate::{ + defined::{rad, Angle}, + geometry::Complex, +}; use autd3_derive::Builder; diff --git a/autd3-core/src/geometry/mod.rs b/autd3-core/src/geometry/mod.rs index 67d48012..0bc01440 100644 --- a/autd3-core/src/geometry/mod.rs +++ b/autd3-core/src/geometry/mod.rs @@ -2,6 +2,8 @@ pub(crate) mod device; mod rotation; mod transducer; +/// a complex number +pub type Complex = nalgebra::Complex; /// 3-dimensional column vector. pub type Vector3 = nalgebra::Vector3; /// 3-dimensional unit vector. @@ -138,15 +140,20 @@ pub(crate) mod tests { } pub fn new_autd3_with_rot(pos: Point3, rot: impl Into) -> Self { + let rotation = rot.into(); + let isometry = Isometry { + rotation, + translation: Translation::from(pos), + }; Self { - rotation: rot.into(), - transducers: itertools::iproduct!(0..18, 0..14) + rotation, + transducers: itertools::iproduct!(0..14, 0..18) .enumerate() .map(|(i, (y, x))| { Transducer::new( i as _, i as _, - pos + 10.16 * Vector3::new(x as f32, y as f32, 0.), + (isometry * (10.16 * mm * Point3::new(x as f32, y as f32, 0.))).xyz(), ) }) .collect(), @@ -263,11 +270,11 @@ pub(crate) mod tests { #[rstest::rstest] #[test] #[case(Aabb{min: Point3::origin(), max: Point3::new(172.72 * mm, 132.08 * mm, 0.)}, vec![TestDevice::new_autd3(Point3::origin())])] - #[case(Aabb{min: Point3::new(10. * mm, 20. * mm, 30. * mm), max: Point3::new(182.72 * mm, 152.08 * mm, 30. * mm)}, vec![TestDevice::new_autd3(Point3::new(10., 20., 30.))])] + #[case(Aabb{min: Point3::new(10. * mm, 20. * mm, 30. * mm), max: Point3::new(182.72 * mm, 152.08 * mm, 30. * mm)}, vec![TestDevice::new_autd3(Point3::new(10. * mm, 20. * mm, 30. * mm))])] #[case(Aabb{min: Point3::new(-132.08 * mm, 0., 0.), max: Point3::new(0., 172.72 * mm, 0.)}, vec![TestDevice::new_autd3_with_rot(Point3::origin(), EulerAngle::ZYZ(90. * deg, 0. * deg, 0. * deg))])] #[case(Aabb{min: Point3::new(-132.08 * mm, -10. * mm, 0.), max: Point3::new(172.72 * mm, 162.72 * mm, 10. * mm)}, vec![ TestDevice::new_autd3(Point3::origin()), - TestDevice::new_autd3_with_rot(Point3::new(10., 20., 30.), EulerAngle::ZYZ(90. * deg, 0. * deg, 0. * deg)) + TestDevice::new_autd3_with_rot(Point3::new(0., -10. * mm, 10. * mm), EulerAngle::ZYZ(90. * deg, 0. * deg, 0. * deg)) ])] fn aabb(#[case] expect: Aabb, #[case] dev: Vec) { let geometry = Geometry::new( diff --git a/autd3-core/src/lib.rs b/autd3-core/src/lib.rs index 3495476c..ca586565 100644 --- a/autd3-core/src/lib.rs +++ b/autd3-core/src/lib.rs @@ -1,17 +1,16 @@ -#[cfg(any( - feature = "defined", - feature = "geometry", - feature = "gain", - feature = "modulation", - feature = "link" -))] +#[cfg(feature = "acoustics")] +/// Utilities for acoustics. +pub mod acoustics; +#[cfg(feature = "datagram")] +pub mod datagram; +#[cfg(feature = "defined")] /// Common constants and types. pub mod defined; -#[cfg(any(feature = "ethercat", feature = "link"))] +#[cfg(feature = "ethercat")] pub mod ethercat; #[cfg(feature = "gain")] pub mod gain; -#[cfg(any(feature = "geometry", feature = "gain", feature = "link"))] +#[cfg(feature = "geometry")] /// Geometry related modules. pub mod geometry; #[cfg(feature = "link")] @@ -19,9 +18,53 @@ pub mod geometry; pub mod link; #[cfg(feature = "modulation")] pub mod modulation; -#[cfg(any(feature = "utils", feature = "modulation"))] +#[cfg(feature = "resampler")] +/// Resampler module. +pub mod resampler; +#[cfg(feature = "utils")] #[doc(hidden)] pub mod utils; #[cfg(feature = "async-trait")] pub use async_trait::async_trait; + +/// Utilities for user-defined [`Gain`] and [`Modulation`]. +/// +/// [`Gain`]: crate::datagram::Gain +/// [`Modulation`]: crate::datagram::Modulation +#[cfg_attr(docsrs, doc(cfg(feature = "derive")))] +#[cfg(feature = "derive")] +pub mod derive { + #[cfg(any(feature = "gain", feature = "modulation"))] + mod common { + pub use crate::datagram::{DatagramS, Segment, TransitionMode}; + pub use crate::{defined::DEFAULT_TIMEOUT, geometry::Geometry}; + pub use tracing; + } + #[cfg(any(feature = "gain", feature = "modulation"))] + pub use common::*; + + #[cfg(feature = "gain")] + mod gain { + pub use crate::gain::{ + BitVec, Drive, EmitIntensity, Gain, GainContext, GainContextGenerator, GainError, + GainOperationGenerator, Phase, + }; + pub use crate::geometry::{Device, Transducer}; + pub use autd3_derive::Gain; + } + #[cfg(feature = "gain")] + pub use gain::*; + + #[cfg(feature = "modulation")] + mod modulation { + pub use crate::modulation::{ + LoopBehavior, Modulation, ModulationError, ModulationOperationGenerator, + ModulationProperty, SamplingConfig, + }; + pub use autd3_derive::Modulation; + pub use std::{collections::HashMap, sync::Arc}; + } + #[cfg(feature = "modulation")] + pub use modulation::*; +} diff --git a/autd3-core/src/modulation/error.rs b/autd3-core/src/modulation/error.rs index 10732801..88e6b430 100644 --- a/autd3-core/src/modulation/error.rs +++ b/autd3-core/src/modulation/error.rs @@ -1,11 +1,12 @@ use std::time::Duration; use derive_more::Display; +use derive_new::new; use thiserror::Error; use crate::defined::Freq; -#[derive(Error, Debug, Display, PartialEq, Clone)] +#[derive(new, Error, Debug, Display, PartialEq, Clone)] #[display("{}", msg)] pub struct ModulationError { msg: String, diff --git a/autd3-core/src/modulation/loop_behavior.rs b/autd3-core/src/modulation/loop_behavior.rs index e36a8de6..180e1cd4 100644 --- a/autd3-core/src/modulation/loop_behavior.rs +++ b/autd3-core/src/modulation/loop_behavior.rs @@ -50,7 +50,7 @@ impl LoopBehavior { /// # Example /// /// ``` - /// # use autd3_driver::firmware::fpga::LoopBehavior; + /// # use autd3_core::modulation::LoopBehavior; /// # use std::num::NonZeroU16; /// let finite: Option = LoopBehavior::finite(1); /// assert!(finite.is_some()); diff --git a/autd3-core/src/modulation/mod.rs b/autd3-core/src/modulation/mod.rs index 70a858b2..483dc52f 100644 --- a/autd3-core/src/modulation/mod.rs +++ b/autd3-core/src/modulation/mod.rs @@ -2,10 +2,14 @@ mod error; mod loop_behavior; mod sampling_config; +use std::sync::Arc; + pub use error::{ModulationError, SamplingConfigError}; pub use loop_behavior::LoopBehavior; pub use sampling_config::SamplingConfig; +use crate::datagram::{Segment, TransitionMode}; + /// A trait to get the modulation property. (This trait is automatically implemented by the [`Modulation`] derive macro.) /// /// [`Modulation`]: autd3_derive::Modulation @@ -25,3 +29,12 @@ pub trait Modulation: ModulationProperty + std::fmt::Debug { /// Calculate the modulation data. fn calc(self) -> Result, ModulationError>; } + +#[doc(hidden)] +pub struct ModulationOperationGenerator { + pub g: Arc>, + pub config: SamplingConfig, + pub loop_behavior: LoopBehavior, + pub segment: Segment, + pub transition_mode: Option, +} diff --git a/autd3-core/src/resampler/mod.rs b/autd3-core/src/resampler/mod.rs new file mode 100644 index 00000000..2da92b46 --- /dev/null +++ b/autd3-core/src/resampler/mod.rs @@ -0,0 +1,25 @@ +mod sinc; +mod window; + +use crate::{defined::Freq, modulation::SamplingConfig}; +pub use sinc::SincInterpolation; +pub use window::*; + +/// Resampler trait. +pub trait Resampler: std::fmt::Debug + Send + Sync { + /// Upsample the buffer. + fn upsample(&self, buffer: &[u8], ratio: f64) -> Vec; + /// Downsample the buffer. + fn downsample(&self, buffer: &[u8], ratio: f64) -> Vec; + /// Resample the buffer. + fn resample(&self, buffer: &[u8], source: Freq, target: SamplingConfig) -> Vec { + let src_fs = source.hz().abs() as f64; + let target_fs = target.freq().hz().abs() as f64; + let ratio = target_fs / src_fs; + if ratio > 1.0 { + self.upsample(buffer, ratio) + } else { + self.downsample(buffer, ratio) + } + } +} diff --git a/autd3-core/src/resampler/sinc.rs b/autd3-core/src/resampler/sinc.rs new file mode 100644 index 00000000..cff0baee --- /dev/null +++ b/autd3-core/src/resampler/sinc.rs @@ -0,0 +1,146 @@ +use std::{f64::consts::PI, num::NonZeroUsize}; + +use crate::utils::float::is_integer; + +use super::{window::InterpolationWindow, Blackman, Resampler}; + +/// Sinc interpolation resampler. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct SincInterpolation { + /// Window function. + pub window: T, +} + +impl Default for SincInterpolation { + fn default() -> Self { + Self { + window: Blackman { + size: NonZeroUsize::new(32).unwrap(), + }, + } + } +} + +#[inline] +fn sinc(x: f64) -> f64 { + if x == 0.0 { + 1.0 + } else { + (x * PI).sin() / (x * PI) + } +} + +#[inline] +fn modf(lhs: f64) -> (isize, f64) { + let int = lhs.floor() as isize; + let frac = lhs - int as f64; + (int, frac) +} + +impl Resampler for SincInterpolation { + fn upsample(&self, buffer: &[u8], ratio: f64) -> Vec { + let source_len = buffer.len(); + let window_size = self.window.window_size(); + let target_size = source_len as f64 * ratio; + // GRCOV_EXCL_START + if !is_integer(target_size) { + tracing::warn!( + "The target size ({}) is not an integer, ceiling to {}.", + target_size, + target_size.ceil() + ); + } + // GRCOV_EXCL_STOP + (0..target_size.ceil() as usize) + .map(|m| { + let (n, frac) = modf(m as f64 / ratio); + (0..window_size) + .map(|k| { + let kk = k as isize - window_size as isize / 2; + let idx = ((n + kk).rem_euclid(source_len as isize)) as usize; + buffer[idx] as f64 * sinc(kk as f64 - frac) * self.window.value(k) + }) + .sum::() + .round() as u8 + }) + .collect() + } + + fn downsample(&self, buffer: &[u8], ratio: f64) -> Vec { + let source_len = buffer.len(); + let window_size = self.window.window_size(); + let target_size = source_len as f64 * ratio; + // GRCOV_EXCL_START + if !is_integer(target_size) { + tracing::warn!( + "The target size ({}) is not an integer, ceiling to {}.", + target_size, + target_size.ceil() + ); + } + // GRCOV_EXCL_STOP + (0..target_size.ceil() as usize) + .map(|m| { + let (n, frac) = modf(m as f64 / ratio); + (0..window_size) + .map(|k| { + let kk = k as isize - window_size as isize / 2; + let idx = ((n + kk).rem_euclid(source_len as isize)) as usize; + ratio + * (buffer[idx] as f64 + * sinc((kk as f64 - frac) * ratio) + * self.window.value(k)) + }) + .sum::() + .round() as u8 + }) + .collect() + } +} + +#[cfg(test)] +mod tests { + use crate::resampler::{Blackman, Rectangular}; + + use super::*; + + #[rstest::rstest] + #[test] + #[case(vec![127, 217, 255, 223, 127, 42, 0, 37], vec![127, 255, 127, 0], 2.0, Rectangular { size: NonZeroUsize::new(32).unwrap() })] + #[case(vec![127, 217, 255, 217, 127, 37, 0, 37], vec![127, 255, 127, 0], 2.0, Rectangular { size: NonZeroUsize::new(4096).unwrap() })] + #[case(vec![127, 130, 127, 130], vec![127, 127], 2.0, Rectangular { size: NonZeroUsize::new(32).unwrap() })] + #[case(vec![127, 127, 127, 127], vec![127, 127], 2.0, Rectangular { size: NonZeroUsize::new(4096).unwrap() })] + #[case(vec![127, 217, 255, 217, 127, 37, 0, 37], vec![127, 255, 127, 0], 2.0, Blackman { size: NonZeroUsize::new(32).unwrap() })] + #[case(vec![127, 217, 255, 217, 127, 37, 0, 37], vec![127, 255, 127, 0], 2.0, Blackman { size: NonZeroUsize::new(4096).unwrap() })] + #[case(vec![127, 126, 127, 126], vec![127, 127], 2.0, Blackman { size: NonZeroUsize::new(32).unwrap() })] + #[case(vec![127, 127, 127, 127], vec![127, 127], 2.0, Blackman { size: NonZeroUsize::new(4096).unwrap() })] + fn upsample( + #[case] expect: Vec, + #[case] buffer: Vec, + #[case] ratio: f64, + #[case] window: impl InterpolationWindow, + ) { + let resampler = SincInterpolation { window }; + assert_eq!(expect, resampler.upsample(&buffer, ratio)); + } + + #[rstest::rstest] + #[test] + #[case(vec![124, 249, 124, 1], vec![127, 217, 255, 217, 127, 37, 0, 37], 0.5, Rectangular { size: NonZeroUsize::new(32).unwrap() })] + #[case(vec![127, 255, 127, 0], vec![127, 217, 255, 217, 127, 37, 0, 37], 0.5, Rectangular { size: NonZeroUsize::new(4096).unwrap() })] + #[case(vec![124, 124], vec![127, 127, 127, 127], 0.5, Rectangular { size: NonZeroUsize::new(32).unwrap() })] + #[case(vec![127, 127], vec![127, 127, 127, 127], 0.5, Rectangular { size: NonZeroUsize::new(4096).unwrap() })] + #[case(vec![127, 255, 127, 0], vec![127, 217, 255, 217, 127, 37, 0, 37], 0.5, Blackman { size: NonZeroUsize::new(32).unwrap() })] + #[case(vec![127, 255, 127, 0], vec![127, 217, 255, 217, 127, 37, 0, 37], 0.5, Blackman { size: NonZeroUsize::new(4096).unwrap() })] + #[case(vec![127, 127], vec![127, 127, 127, 127], 0.5, Blackman { size: NonZeroUsize::new(32).unwrap() })] + #[case(vec![127, 127], vec![127, 127, 127, 127], 0.5, Blackman { size: NonZeroUsize::new(4096).unwrap() })] + fn downsample( + #[case] expect: Vec, + #[case] buffer: Vec, + #[case] ratio: f64, + #[case] window: impl InterpolationWindow, + ) { + let resampler = SincInterpolation { window }; + assert_eq!(expect, resampler.downsample(&buffer, ratio)); + } +} diff --git a/autd3-core/src/resampler/window/blackman.rs b/autd3-core/src/resampler/window/blackman.rs new file mode 100644 index 00000000..de96781a --- /dev/null +++ b/autd3-core/src/resampler/window/blackman.rs @@ -0,0 +1,21 @@ +use std::{f64::consts::PI, num::NonZeroUsize}; + +use super::InterpolationWindow; + +/// Blackman window. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Blackman { + /// Window size. + pub size: NonZeroUsize, +} + +impl InterpolationWindow for Blackman { + fn value(&self, idx: usize) -> f64 { + let x = idx as f64 / self.window_size() as f64; + 0.42 - 0.5 * (2.0 * PI * x).cos() + 0.08 * (4.0 * PI * x).cos() + } + + fn window_size(&self) -> usize { + self.size.get() + } +} diff --git a/autd3-core/src/resampler/window/mod.rs b/autd3-core/src/resampler/window/mod.rs new file mode 100644 index 00000000..4c9bb7ae --- /dev/null +++ b/autd3-core/src/resampler/window/mod.rs @@ -0,0 +1,13 @@ +mod blackman; +mod rectangular; + +pub use blackman::Blackman; +pub use rectangular::Rectangular; + +/// Interpolation window trait. +pub trait InterpolationWindow: std::fmt::Debug + Clone + Copy + PartialEq + Send + Sync { + /// Get the window size. + fn window_size(&self) -> usize; + /// Get the value of the window at given index. + fn value(&self, idx: usize) -> f64; +} diff --git a/autd3-core/src/resampler/window/rectangular.rs b/autd3-core/src/resampler/window/rectangular.rs new file mode 100644 index 00000000..9d61ff27 --- /dev/null +++ b/autd3-core/src/resampler/window/rectangular.rs @@ -0,0 +1,20 @@ +use std::num::NonZeroUsize; + +use super::InterpolationWindow; + +/// Rectangular window. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Rectangular { + /// Window size. + pub size: NonZeroUsize, +} + +impl InterpolationWindow for Rectangular { + fn value(&self, _idx: usize) -> f64 { + 1.0 + } + + fn window_size(&self) -> usize { + self.size.get() + } +} From 5e88402576d9a8ee245fd9d8b27f5292bb3f3ed4 Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Tue, 14 Jan 2025 16:02:27 +0900 Subject: [PATCH 06/20] update autd3-driver --- autd3-driver/Cargo.toml | 19 +- autd3-driver/benches/gain.rs | 16 +- autd3-driver/src/acoustics/directivity/mod.rs | 56 --- .../src/acoustics/directivity/sphere.rs | 40 -- .../src/acoustics/directivity/t4010a1.rs | 95 ---- autd3-driver/src/acoustics/mod.rs | 110 ----- autd3-driver/src/datagram/clear.rs | 7 +- autd3-driver/src/datagram/clock.rs | 9 +- autd3-driver/src/datagram/cpu_gpio_out.rs | 7 +- autd3-driver/src/datagram/debug.rs | 7 +- autd3-driver/src/datagram/force_fan.rs | 7 +- autd3-driver/src/datagram/gain/boxed.rs | 42 +- autd3-driver/src/datagram/gain/mod.rs | 84 +--- autd3-driver/src/datagram/gpio_in.rs | 7 +- autd3-driver/src/datagram/info.rs | 8 +- autd3-driver/src/datagram/mod.rs | 53 +- autd3-driver/src/datagram/modulation/boxed.rs | 16 +- autd3-driver/src/datagram/modulation/mod.rs | 56 +-- autd3-driver/src/datagram/phase_corr.rs | 9 +- .../src/datagram/pulse_width_encoder.rs | 7 +- autd3-driver/src/datagram/reads_fpga_state.rs | 7 +- autd3-driver/src/datagram/segment.rs | 7 +- autd3-driver/src/datagram/silencer.rs | 12 +- .../src/datagram/stm/foci/implement.rs | 12 +- autd3-driver/src/datagram/stm/foci/mod.rs | 19 +- .../src/datagram/stm/gain/implement.rs | 32 +- autd3-driver/src/datagram/stm/gain/mod.rs | 27 +- .../src/datagram/stm/sampling_config.rs | 53 +- autd3-driver/src/datagram/synchronize.rs | 7 +- autd3-driver/src/datagram/tuple.rs | 130 +---- .../src/datagram/with_parallel_threshold.rs | 10 +- autd3-driver/src/datagram/with_segment.rs | 52 +- autd3-driver/src/datagram/with_timeout.rs | 10 +- autd3-driver/src/defined/angle.rs | 65 --- autd3-driver/src/defined/freq/float.rs | 17 - autd3-driver/src/defined/freq/int.rs | 17 - autd3-driver/src/defined/freq/mod.rs | 37 -- autd3-driver/src/defined/mod.rs | 104 ---- autd3-driver/src/error.rs | 64 +-- autd3-driver/src/ethercat/consts.rs | 13 - autd3-driver/src/ethercat/dc_sys_time.rs | 106 ---- autd3-driver/src/ethercat/mod.rs | 8 +- autd3-driver/src/firmware/cpu/datagram/mod.rs | 52 -- autd3-driver/src/firmware/cpu/datagram/rx.rs | 57 --- autd3-driver/src/firmware/cpu/datagram/tx.rs | 31 -- autd3-driver/src/firmware/cpu/header.rs | 29 -- autd3-driver/src/firmware/cpu/mod.rs | 64 ++- autd3-driver/src/firmware/fpga/drive.rs | 118 ----- .../src/firmware/fpga/emit_intensity.rs | 161 ------ autd3-driver/src/firmware/fpga/fpga_state.rs | 11 + autd3-driver/src/firmware/fpga/gpio.rs | 27 - .../src/firmware/fpga/loop_behavior.rs | 115 ----- autd3-driver/src/firmware/fpga/mod.rs | 30 +- autd3-driver/src/firmware/fpga/phase.rs | 148 ------ .../src/firmware/fpga/sampling_config.rs | 301 ----------- autd3-driver/src/firmware/fpga/segment.rs | 10 - .../src/firmware/fpga/transition_mode.rs | 46 -- autd3-driver/src/firmware/operation/boxed.rs | 67 +++ autd3-driver/src/firmware/operation/clear.rs | 9 +- .../src/firmware/operation/clock/mod.rs | 4 +- .../src/firmware/operation/cpu_gpio_out.rs | 9 +- autd3-driver/src/firmware/operation/debug.rs | 9 +- .../src/firmware/operation/force_fan.rs | 9 +- autd3-driver/src/firmware/operation/gain.rs | 19 +- .../src/firmware/operation/gpio_in.rs | 9 +- autd3-driver/src/firmware/operation/info.rs | 9 +- autd3-driver/src/firmware/operation/mod.rs | 91 ++-- .../src/firmware/operation/modulation.rs | 8 +- autd3-driver/src/firmware/operation/null.rs | 45 -- .../src/firmware/operation/phase_corr.rs | 9 +- .../firmware/operation/pulse_width_encoder.rs | 9 +- .../firmware/operation/reads_fpga_state.rs | 9 +- .../src/firmware/operation/segment.rs | 6 +- .../operation/silencer/completion_steps.rs | 9 +- .../operation/silencer/completion_time.rs | 5 +- .../operation/silencer/update_rate.rs | 9 +- .../src/firmware/operation/stm/foci.rs | 14 +- .../src/firmware/operation/stm/gain.rs | 18 +- autd3-driver/src/firmware/operation/sync.rs | 9 +- autd3-driver/src/geometry/device.rs | 467 ------------------ autd3-driver/src/geometry/mod.rs | 272 ---------- autd3-driver/src/geometry/rotation.rs | 148 ------ autd3-driver/src/geometry/transducer.rs | 69 --- autd3-driver/src/lib.rs | 39 +- autd3-driver/src/link/async.rs | 126 ----- autd3-driver/src/link/mod.rs | 10 - autd3-driver/src/link/sync.rs | 66 --- autd3-driver/src/utils/float.rs | 38 -- autd3-driver/src/utils/mod.rs | 1 - 89 files changed, 586 insertions(+), 3699 deletions(-) delete mode 100644 autd3-driver/src/acoustics/directivity/mod.rs delete mode 100644 autd3-driver/src/acoustics/directivity/sphere.rs delete mode 100644 autd3-driver/src/acoustics/directivity/t4010a1.rs delete mode 100644 autd3-driver/src/acoustics/mod.rs delete mode 100644 autd3-driver/src/defined/angle.rs delete mode 100644 autd3-driver/src/defined/freq/float.rs delete mode 100644 autd3-driver/src/defined/freq/int.rs delete mode 100644 autd3-driver/src/defined/freq/mod.rs delete mode 100644 autd3-driver/src/defined/mod.rs delete mode 100644 autd3-driver/src/ethercat/consts.rs delete mode 100644 autd3-driver/src/ethercat/dc_sys_time.rs delete mode 100644 autd3-driver/src/firmware/cpu/datagram/mod.rs delete mode 100644 autd3-driver/src/firmware/cpu/datagram/rx.rs delete mode 100644 autd3-driver/src/firmware/cpu/datagram/tx.rs delete mode 100644 autd3-driver/src/firmware/cpu/header.rs delete mode 100644 autd3-driver/src/firmware/fpga/drive.rs delete mode 100644 autd3-driver/src/firmware/fpga/emit_intensity.rs delete mode 100644 autd3-driver/src/firmware/fpga/gpio.rs delete mode 100644 autd3-driver/src/firmware/fpga/loop_behavior.rs delete mode 100644 autd3-driver/src/firmware/fpga/phase.rs delete mode 100644 autd3-driver/src/firmware/fpga/sampling_config.rs delete mode 100644 autd3-driver/src/firmware/fpga/segment.rs delete mode 100644 autd3-driver/src/firmware/fpga/transition_mode.rs create mode 100644 autd3-driver/src/firmware/operation/boxed.rs delete mode 100644 autd3-driver/src/firmware/operation/null.rs delete mode 100644 autd3-driver/src/geometry/device.rs delete mode 100644 autd3-driver/src/geometry/mod.rs delete mode 100644 autd3-driver/src/geometry/rotation.rs delete mode 100644 autd3-driver/src/geometry/transducer.rs delete mode 100644 autd3-driver/src/link/async.rs delete mode 100644 autd3-driver/src/link/mod.rs delete mode 100644 autd3-driver/src/link/sync.rs delete mode 100644 autd3-driver/src/utils/float.rs diff --git a/autd3-driver/Cargo.toml b/autd3-driver/Cargo.toml index 285fc3c5..dce8a397 100644 --- a/autd3-driver/Cargo.toml +++ b/autd3-driver/Cargo.toml @@ -10,18 +10,16 @@ license = { workspace = true } repository = { workspace = true } [dependencies] +autd3-core = { workspace = true, features = ["gain", "geometry", "derive", "link", "ethercat", "modulation"] } +autd3-derive = { workspace = true } serde = { workspace = true, optional = true } bitflags = { workspace = true } -bvh = { workspace = true } thiserror = { workspace = true } nalgebra = { workspace = true } -bit-vec = { workspace = true } time = { workspace = true, features = ["macros", "std"] } -autd3-derive = { workspace = true } -async-trait = { workspace = true, optional = true } bitfield-struct = { workspace = true } rayon = { workspace = true } -itertools = { workspace = true } +itertools = { workspace = true, features = ["use_std"] } derive_more = { workspace = true, features = ["add", "debug", "deref", "deref_mut", "display", "into_iterator", "mul"] } tracing = { workspace = true } seq-macro = { workspace = true } @@ -41,15 +39,10 @@ rstest = { workspace = true } criterion = { workspace = true } [features] -default = ["derive"] -async = [] +default = [] lightweight = [] -async-trait = ["dep:async-trait"] -use_meter = [] -left_handed = [] serde = ["dep:serde"] -derive = [] -dynamic_freq = [] +dynamic_freq = ["autd3-core/dynamic_freq"] [lib] bench = false @@ -60,5 +53,5 @@ path = "benches/gain.rs" harness = false [package.metadata.docs.rs] -features = ["lightweight", "async", "serde", "derive"] +features = ["serde"] rustdoc-args = ["--cfg", "docsrs"] diff --git a/autd3-driver/benches/gain.rs b/autd3-driver/benches/gain.rs index 478e1d2c..e9b69e0d 100644 --- a/autd3-driver/benches/gain.rs +++ b/autd3-driver/benches/gain.rs @@ -1,22 +1,18 @@ use std::collections::HashMap; -use autd3_derive::Gain; +use autd3_core::derive::*; use autd3_driver::{ autd3_device::AUTD3, - datagram::{ - Datagram, DatagramS, Gain, GainContextGenerator, GainOperationGenerator, IntoBoxedGain, - }, + datagram::{Datagram, IntoBoxedGain}, defined::rad, - error::AUTDDriverError, firmware::{ cpu::TxMessage, - fpga::{Drive, EmitIntensity, Phase, Segment, TransitionMode}, - operation::{GainContext, OperationHandler}, + fpga::{Drive, EmitIntensity, Phase}, + operation::OperationHandler, }, geometry::{Device, Geometry, IntoDevice, Point3, Transducer}, }; -use bit_vec::BitVec; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use zerocopy::FromZeros; @@ -84,8 +80,8 @@ impl Gain for Focus { fn init( self, _geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { Ok(self) } } diff --git a/autd3-driver/src/acoustics/directivity/mod.rs b/autd3-driver/src/acoustics/directivity/mod.rs deleted file mode 100644 index 7836715c..00000000 --- a/autd3-driver/src/acoustics/directivity/mod.rs +++ /dev/null @@ -1,56 +0,0 @@ -mod sphere; -mod t4010a1; - -use crate::{ - defined::{rad, Angle}, - geometry::{UnitVector3, Vector3}, -}; - -pub use sphere::Sphere; -pub use t4010a1::T4010A1; - -/// A trait representing the directivity of ultrasound transducer. -pub trait Directivity: Send + Sync { - /// Calculates the directivity based on the given angle. - /// - /// # Arguments - /// - /// * `theta` - The angle between the axial direction and the target direction. - fn directivity(theta: Angle) -> f32; - - /// Calculates the directivity based on the axial direction and target direction. - fn directivity_from_dir(axial_direction: &UnitVector3, target: &Vector3) -> f32 { - Self::directivity( - (axial_direction.cross(target).norm()).atan2(axial_direction.dot(target)) * rad, - ) - } -} - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - - pub(crate) struct TestDirectivity {} - - impl Directivity for TestDirectivity { - fn directivity(t: Angle) -> f32 { - t.degree() - } - } - - #[rstest::rstest] - #[test] - #[case::dir_x(90., Vector3::x(), Vector3::z_axis())] - #[case::dir_y(90., Vector3::y(), Vector3::z_axis())] - #[case::dir_z(0., Vector3::z(), Vector3::z_axis())] - fn test_directivity_from_dir( - #[case] expected: f32, - #[case] target: Vector3, - #[case] dir: UnitVector3, - ) { - approx::assert_abs_diff_eq!( - expected, - TestDirectivity::directivity_from_dir(&dir, &target) - ); - } -} diff --git a/autd3-driver/src/acoustics/directivity/sphere.rs b/autd3-driver/src/acoustics/directivity/sphere.rs deleted file mode 100644 index 203fe725..00000000 --- a/autd3-driver/src/acoustics/directivity/sphere.rs +++ /dev/null @@ -1,40 +0,0 @@ -use super::*; - -/// Sphere directivity model. -pub struct Sphere {} - -impl Directivity for Sphere { - #[inline] - fn directivity(_: Angle) -> f32 { - 1. - } - - #[inline] - fn directivity_from_dir(_: &UnitVector3, _: &Vector3) -> f32 { - 1. - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use rand::prelude::*; - - #[test] - fn test_directivity() { - let mut rng = rand::thread_rng(); - assert_eq!(1.0, Sphere::directivity(rng.gen::() * rad)); - } - - #[rstest::rstest] - #[test] - #[case::dir_x(1., Vector3::x())] - #[case::dir_y(1., Vector3::y())] - #[case::dir_z(1., Vector3::z())] - fn test_directivity_sphere_from_dir(#[case] expected: f32, #[case] target: Vector3) { - let mut rng = rand::thread_rng(); - let dir = UnitVector3::new_unchecked(Vector3::new(rng.gen(), rng.gen(), rng.gen())); - assert_eq!(expected, Sphere::directivity_from_dir(&dir, &target)); - } -} diff --git a/autd3-driver/src/acoustics/directivity/t4010a1.rs b/autd3-driver/src/acoustics/directivity/t4010a1.rs deleted file mode 100644 index d7487889..00000000 --- a/autd3-driver/src/acoustics/directivity/t4010a1.rs +++ /dev/null @@ -1,95 +0,0 @@ -use super::*; - -#[allow(clippy::excessive_precision, clippy::unreadable_literal)] -static DIR_COEF_A: &[f32] = &[ - 1.0, - 1.0, - 1.0, - 0.891250938, - 0.707945784, - 0.501187234, - 0.354813389, - 0.251188643, - 0.199526231, -]; - -#[allow(clippy::excessive_precision, clippy::unreadable_literal)] -static DIR_COEF_B: &[f32] = &[ - 0., - 0., - -0.00459648054721, - -0.0155520765675, - -0.0208114779827, - -0.0182211227016, - -0.0122437497109, - -0.00780345575475, - -0.00312857467007, -]; - -#[allow(clippy::excessive_precision, clippy::unreadable_literal)] -static DIR_COEF_C: &[f32] = &[ - 0., - 0., - -0.000787968093807, - -0.000307591508224, - -0.000218348633296, - 0.00047738416141, - 0.000120353137658, - 0.000323676257958, - 0.000143850511, -]; - -#[allow(clippy::excessive_precision, clippy::unreadable_literal)] -static DIR_COEF_D: &[f32] = &[ - 0., - 0., - 1.60125528528e-05, - 2.9747624976e-06, - 2.31910931569e-05, - -1.1901034125e-05, - 6.77743734332e-06, - -5.99548024824e-06, - -4.79372835035e-06, -]; - -/// [T4010A1/B4](https://www.nicera.co.jp/en/products/ultrasonic-sensor/open-aperture-type) directivity model. -pub struct T4010A1 {} - -impl Directivity for T4010A1 { - fn directivity(theta: Angle) -> f32 { - let theta_deg = theta.degree().abs() % 180.0; - let theta_deg = 90.0 - (theta_deg - 90.0).abs(); - let i = (theta_deg / 10.0).ceil() as usize; - if i == 0 { - 1.0 - } else { - let x = theta_deg - (i as f32 - 1.0) * 10.0; - ((DIR_COEF_D[i - 1] * x + DIR_COEF_C[i - 1]) * x + DIR_COEF_B[i - 1]) * x - + DIR_COEF_A[i - 1] - } - } -} - -#[cfg(test)] -mod tests { - use crate::defined::deg; - - use super::*; - - #[rstest::rstest] - #[test] - #[case::deg_0(1.0, 0.0)] - #[case::deg_10(1.0, 10.0)] - #[case::deg_20(1.0, 20.0)] - #[case::deg_30(0.891251, 30.0)] - #[case::deg_40(0.70794576, 40.0)] - #[case::deg_50(0.5011872, 50.0)] - #[case::deg_60(0.35481337, 60.0)] - #[case::deg_70(0.25118864, 70.0)] - #[case::deg_80(0.19952622, 80.0)] - #[case::deg_90(0.17783181, 90.0)] - #[case::deg_100(0.19952622, 100.0)] - fn test_directivity(#[case] expected: f32, #[case] theta_deg: f32) { - approx::assert_abs_diff_eq!(expected, T4010A1::directivity(theta_deg * deg)); - } -} diff --git a/autd3-driver/src/acoustics/mod.rs b/autd3-driver/src/acoustics/mod.rs deleted file mode 100644 index 7abdfba0..00000000 --- a/autd3-driver/src/acoustics/mod.rs +++ /dev/null @@ -1,110 +0,0 @@ -/// directivity module -pub mod directivity; - -use crate::{ - defined::{Complex, PI, T4010A1_AMPLITUDE}, - geometry::{Point3, Transducer, UnitVector3}, -}; - -use directivity::Directivity; - -/// Calculate the pressure at the target position. -#[inline] -pub fn propagate( - tr: &Transducer, - wavenumber: f32, - dir: &UnitVector3, - target_pos: &Point3, -) -> Complex { - const P0: f32 = T4010A1_AMPLITUDE / (4. * PI); - let diff = target_pos - tr.position(); - let dist = diff.norm(); - Complex::from_polar( - P0 / dist * D::directivity_from_dir(dir, &diff), - wavenumber * dist, - ) -} - -#[cfg(test)] -mod tests { - use super::*; - - use rand::Rng; - - use crate::{ - defined::mm, - geometry::{Device, UnitQuaternion, Vector3}, - }; - use directivity::tests::TestDirectivity; - - macro_rules! assert_complex_approx_eq { - ($a:expr, $b:expr) => { - approx::assert_abs_diff_eq!($a.re, $b.re, epsilon = 1e-3 / mm); - approx::assert_abs_diff_eq!($a.im, $b.im, epsilon = 1e-3 / mm); - }; - } - - #[rstest::fixture] - fn tr() -> Transducer { - let mut rng = rand::thread_rng(); - Transducer::new( - 0, - 0, - Point3::new( - rng.gen_range(-100.0..100.0), - rng.gen_range(-100.0..100.0), - rng.gen_range(-100.0..100.0), - ), - ) - } - - #[rstest::fixture] - fn rot() -> UnitQuaternion { - let mut rng = rand::thread_rng(); - UnitQuaternion::from_axis_angle( - &Vector3::x_axis(), - rng.gen_range::(-180.0..180.0).to_radians(), - ) * UnitQuaternion::from_axis_angle( - &Vector3::y_axis(), - rng.gen_range::(-180.0..180.0).to_radians(), - ) * UnitQuaternion::from_axis_angle( - &Vector3::z_axis(), - rng.gen_range::(-180.0..180.0).to_radians(), - ) - } - - #[rstest::fixture] - fn target() -> Point3 { - let mut rng = rand::thread_rng(); - Point3::new( - rng.gen_range(-100.0..100.0), - rng.gen_range(-100.0..100.0), - rng.gen_range(-100.0..100.0), - ) - } - - #[rstest::fixture] - fn sound_speed() -> f32 { - let mut rng = rand::thread_rng(); - rng.gen_range(300e3..400e3) - } - - #[rstest::rstest] - #[test] - fn test_propagate(tr: Transducer, rot: UnitQuaternion, target: Point3, sound_speed: f32) { - let mut device = Device::new(0, rot, vec![tr.clone()]); - device.sound_speed = sound_speed; - let wavenumber = device.wavenumber(); - assert_complex_approx_eq!( - { - let diff = target - tr.position(); - let dist = diff.norm(); - let r = T4010A1_AMPLITUDE / (4. * PI) / dist - * TestDirectivity::directivity_from_dir(device.axial_direction(), &diff); - let phase = wavenumber * dist; - Complex::new(r * phase.cos(), r * phase.sin()) - }, - super::propagate::(&tr, wavenumber, device.axial_direction(), &target) - ); - } -} diff --git a/autd3-driver/src/datagram/clear.rs b/autd3-driver/src/datagram/clear.rs index 7b9440c9..8d63248a 100644 --- a/autd3-driver/src/datagram/clear.rs +++ b/autd3-driver/src/datagram/clear.rs @@ -1,3 +1,5 @@ +use std::convert::Infallible; + use crate::firmware::operation::ClearOp; use crate::datagram::*; @@ -14,14 +16,15 @@ impl OperationGenerator for ClearOpGenerator { type O2 = NullOp; fn generate(&mut self, _: &Device) -> (Self::O1, Self::O2) { - (Self::O1::new(), Self::O2::new()) + (Self::O1::new(), Self::O2 {}) } } impl Datagram for Clear { type G = ClearOpGenerator; + type Error = Infallible; - fn operation_generator(self, _: &Geometry) -> Result { + fn operation_generator(self, _: &Geometry) -> Result { Ok(ClearOpGenerator {}) } } diff --git a/autd3-driver/src/datagram/clock.rs b/autd3-driver/src/datagram/clock.rs index 125e0192..1bf98f4c 100644 --- a/autd3-driver/src/datagram/clock.rs +++ b/autd3-driver/src/datagram/clock.rs @@ -1,6 +1,6 @@ -use crate::{defined::ultrasound_freq, firmware::operation::ConfigureClockOp}; +use std::convert::Infallible; -use crate::datagram::*; +use crate::{datagram::*, defined::ultrasound_freq, firmware::operation::ConfigureClockOp}; #[derive(Default, Debug)] #[doc(hidden)] @@ -19,14 +19,15 @@ impl OperationGenerator for ConfigureClockOpGenerator { type O2 = NullOp; fn generate(&mut self, _: &Device) -> (Self::O1, Self::O2) { - (Self::O1::new(ultrasound_freq()), Self::O2::new()) + (Self::O1::new(ultrasound_freq()), Self::O2 {}) } } impl Datagram for ConfigureFPGAClock { type G = ConfigureClockOpGenerator; + type Error = Infallible; - fn operation_generator(self, _: &Geometry) -> Result { + fn operation_generator(self, _: &Geometry) -> Result { Ok(ConfigureClockOpGenerator {}) } } diff --git a/autd3-driver/src/datagram/cpu_gpio_out.rs b/autd3-driver/src/datagram/cpu_gpio_out.rs index 9bc679f4..be88f85e 100644 --- a/autd3-driver/src/datagram/cpu_gpio_out.rs +++ b/autd3-driver/src/datagram/cpu_gpio_out.rs @@ -1,3 +1,5 @@ +use std::convert::Infallible; + use crate::firmware::operation::CpuGPIOOutOp; use crate::datagram::*; @@ -29,14 +31,15 @@ impl CpuGPIOPort + Send + Sync> OperationGenerator for CpuGPIO fn generate(&mut self, device: &Device) -> (Self::O1, Self::O2) { let port = (self.f)(device); - (CpuGPIOOutOp::new(port.pa5, port.pa7), Self::O2::new()) + (CpuGPIOOutOp::new(port.pa5, port.pa7), Self::O2 {}) } } impl CpuGPIOPort + Send + Sync> Datagram for CpuGPIO { type G = CpuGPIOOutOpGenerator; + type Error = Infallible; - fn operation_generator(self, _: &Geometry) -> Result { + fn operation_generator(self, _: &Geometry) -> Result { Ok(Self::G { f: self.f }) } } diff --git a/autd3-driver/src/datagram/debug.rs b/autd3-driver/src/datagram/debug.rs index ca718359..c85e1e18 100644 --- a/autd3-driver/src/datagram/debug.rs +++ b/autd3-driver/src/datagram/debug.rs @@ -1,3 +1,5 @@ +use std::convert::Infallible; + use crate::firmware::{ fpga::{DebugType, GPIOOut}, operation::DebugSettingOp, @@ -43,15 +45,16 @@ impl DebugType + Send + Sync> OperationGenerator [GPIOOut::O0, GPIOOut::O1, GPIOOut::O2, GPIOOut::O3] .map(|gpio| (self.f)(device, gpio).into()), ), - Self::O2::new(), + Self::O2 {}, ) } } impl DebugType + Send + Sync> Datagram for DebugSettings { type G = DebugSettingOpGenerator; + type Error = Infallible; - fn operation_generator(self, _: &Geometry) -> Result { + fn operation_generator(self, _: &Geometry) -> Result { Ok(DebugSettingOpGenerator { f: self.f }) } } diff --git a/autd3-driver/src/datagram/force_fan.rs b/autd3-driver/src/datagram/force_fan.rs index 2dedb09e..ea91765d 100644 --- a/autd3-driver/src/datagram/force_fan.rs +++ b/autd3-driver/src/datagram/force_fan.rs @@ -1,3 +1,5 @@ +use std::convert::Infallible; + use crate::{datagram::*, firmware::operation::ForceFanOp}; use autd3_derive::Builder; @@ -21,14 +23,15 @@ impl bool> OperationGenerator for ForceFanOpGenerator { type O2 = NullOp; fn generate(&mut self, device: &Device) -> (Self::O1, Self::O2) { - (Self::O1::new((self.f)(device)), Self::O2::new()) + (Self::O1::new((self.f)(device)), Self::O2 {}) } } impl bool> Datagram for ForceFan { type G = ForceFanOpGenerator; + type Error = Infallible; - fn operation_generator(self, _: &Geometry) -> Result { + fn operation_generator(self, _: &Geometry) -> Result { Ok(ForceFanOpGenerator { f: self.f }) } } diff --git a/autd3-driver/src/datagram/gain/boxed.rs b/autd3-driver/src/datagram/gain/boxed.rs index 3bdd2801..4323c3c9 100644 --- a/autd3-driver/src/datagram/gain/boxed.rs +++ b/autd3-driver/src/datagram/gain/boxed.rs @@ -1,25 +1,10 @@ use std::{collections::HashMap, mem::MaybeUninit}; -use super::{Gain, GainContextGenerator, GainOperationGenerator}; - -use crate::error::AUTDDriverError; -pub use crate::{ - datagram::DatagramS, - firmware::{ - fpga::{Drive, Segment, TransitionMode}, - operation::GainContext, - }, - geometry::{Device, Geometry, Transducer}, -}; - -use autd3_derive::Gain; -use bit_vec::BitVec; - -impl GainContext for Box { - fn calc(&self, tr: &Transducer) -> Drive { - self.as_ref().calc(tr) - } -} +use super::{Gain, GainContextGenerator}; + +pub use crate::geometry::{Device, Geometry}; + +use autd3_core::derive::*; pub trait DGainContextGenerator { fn dyn_generate(&mut self, device: &Device) -> Box; @@ -50,8 +35,8 @@ trait DGain { fn dyn_init( &mut self, geometry: &Geometry, - filter: Option<&HashMap>>, - ) -> Result, AUTDDriverError>; + filter: Option<&HashMap>, + ) -> Result, GainError>; fn dyn_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result; } @@ -64,8 +49,8 @@ impl< fn dyn_init( &mut self, geometry: &Geometry, - filter: Option<&HashMap>>, - ) -> Result, AUTDDriverError> { + filter: Option<&HashMap>, + ) -> Result, GainError> { let mut tmp: MaybeUninit = MaybeUninit::uninit(); std::mem::swap(&mut tmp, self); // SAFETY: This function is called only once from `Gain::init`. @@ -105,8 +90,8 @@ impl Gain for BoxedGain { fn init( self, geometry: &Geometry, - filter: Option<&HashMap>>, - ) -> Result { + filter: Option<&HashMap>, + ) -> Result { let Self { mut g } = self; Ok(DynGainContextGenerator { g: g.dyn_init(geometry, filter)?, @@ -134,11 +119,12 @@ impl< #[cfg(test)] pub mod tests { + use autd3_core::gain::Drive; + use super::*; use crate::datagram::gain::tests::TestGain; use crate::firmware::fpga::{EmitIntensity, Phase}; - use crate::geometry::tests::create_geometry; const NUM_TRANSDUCERS: usize = 2; @@ -172,6 +158,8 @@ pub mod tests { #[case] enabled: Vec, #[case] n: u16, ) -> anyhow::Result<()> { + use crate::datagram::tests::create_geometry; + let mut geometry = create_geometry(n, NUM_TRANSDUCERS as _); geometry .iter_mut() diff --git a/autd3-driver/src/datagram/gain/mod.rs b/autd3-driver/src/datagram/gain/mod.rs index 2a116095..09c964f4 100644 --- a/autd3-driver/src/datagram/gain/mod.rs +++ b/autd3-driver/src/datagram/gain/mod.rs @@ -1,70 +1,13 @@ mod boxed; +use autd3_core::gain::{Gain, GainContextGenerator, GainOperationGenerator}; pub use boxed::{BoxedGain, IntoBoxedGain}; -use std::collections::HashMap; - -pub use crate::firmware::operation::GainContext; use crate::{ - error::AUTDDriverError, - firmware::fpga::{Segment, TransitionMode}, firmware::operation::{GainOp, NullOp, OperationGenerator}, - geometry::{Device, Geometry}, + geometry::Device, }; -use bit_vec::BitVec; - -/// A trait for generating a context for the gain operation. -pub trait GainContextGenerator { - /// The type of the context that actually performs the calculation. - type Context: GainContext; - - /// Generate a context for the given device. - fn generate(&mut self, device: &Device) -> Self::Context; -} - -/// Trait for calculating the phase/amplitude of each transducer. -/// -/// See also [`Gain`] derive macro. -/// -/// [`Gain`]: autd3_derive::Gain -pub trait Gain: std::fmt::Debug { - /// The type of the context generator. - type G: GainContextGenerator; - - /// Initialize the gain and generate the context generator. - /// - /// `filter` is a hash map that holds a bit vector representing the indices of the enabled transducers for each device index. - /// If `filter` is `None`, all transducers are enabled. - fn init( - self, - geometry: &Geometry, - filter: Option<&HashMap>>, - ) -> Result; -} - -#[doc(hidden)] -pub struct GainOperationGenerator { - pub generator: G, - pub segment: Segment, - pub transition: Option, -} - -impl GainOperationGenerator { - pub fn new>( - gain: T, - geometry: &Geometry, - segment: Segment, - transition: Option, - ) -> Result { - Ok(Self { - generator: gain.init(geometry, None)?, - segment, - transition, - }) - } -} - impl OperationGenerator for GainOperationGenerator { type O1 = GainOp; type O2 = NullOp; @@ -73,24 +16,25 @@ impl OperationGenerator for GainOperationGenerator { let context = self.generator.generate(device); ( Self::O1::new(self.segment, self.transition, context), - Self::O2::new(), + Self::O2 {}, ) } } #[cfg(test)] pub mod tests { - use autd3_derive::Gain; + use std::collections::HashMap; + + use autd3_core::{ + gain::{BitVec, GainContext, GainError}, + geometry::{Geometry, Transducer}, + }; use super::*; - use crate::{ - datagram::DatagramS, - firmware::fpga::{Drive, EmitIntensity, Phase}, - geometry::{tests::create_geometry, Device, Transducer}, - }; + use crate::firmware::fpga::{Drive, EmitIntensity, Phase}; - #[derive(Gain, Clone, Debug)] + #[derive(Clone, Debug)] pub struct TestGain { pub data: HashMap>, } @@ -141,8 +85,8 @@ pub mod tests { fn init( self, _geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { Ok(self) } } @@ -179,6 +123,8 @@ pub mod tests { #[case] enabled: Vec, #[case] n: u16, ) -> anyhow::Result<()> { + use crate::datagram::tests::create_geometry; + let mut geometry = create_geometry(n, NUM_TRANSDUCERS as _); geometry .iter_mut() diff --git a/autd3-driver/src/datagram/gpio_in.rs b/autd3-driver/src/datagram/gpio_in.rs index a0710cc9..6485c8e5 100644 --- a/autd3-driver/src/datagram/gpio_in.rs +++ b/autd3-driver/src/datagram/gpio_in.rs @@ -1,3 +1,5 @@ +use std::convert::Infallible; + use crate::{ datagram::*, firmware::{fpga::GPIOIn, operation::EmulateGPIOInOp}, @@ -27,15 +29,16 @@ impl bool + Send + Sync, F: Fn(&Device) -> H> OperationGenerato let h = (self.f)(device); ( Self::O1::new([h(GPIOIn::I0), h(GPIOIn::I1), h(GPIOIn::I2), h(GPIOIn::I3)]), - Self::O2::new(), + Self::O2 {}, ) } } impl bool + Send + Sync, F: Fn(&Device) -> H> Datagram for EmulateGPIOIn { type G = EmulateGPIOInOpGenerator; + type Error = Infallible; - fn operation_generator(self, _: &Geometry) -> Result { + fn operation_generator(self, _: &Geometry) -> Result { Ok(EmulateGPIOInOpGenerator { f: self.f }) } } diff --git a/autd3-driver/src/datagram/info.rs b/autd3-driver/src/datagram/info.rs index 19b00eec..23bbb2ad 100644 --- a/autd3-driver/src/datagram/info.rs +++ b/autd3-driver/src/datagram/info.rs @@ -1,6 +1,7 @@ +use std::convert::Infallible; + use crate::{ datagram::*, - error::AUTDDriverError, firmware::operation::{FirmInfoOp, FirmwareVersionType}, geometry::Geometry, }; @@ -16,14 +17,15 @@ impl OperationGenerator for FetchFirmwareInfoOpGenerator { type O2 = NullOp; fn generate(&mut self, _: &Device) -> (Self::O1, Self::O2) { - (Self::O1::new(self.inner), Self::O2::new()) + (Self::O1::new(self.inner), Self::O2 {}) } } impl Datagram for FirmwareVersionType { type G = FetchFirmwareInfoOpGenerator; + type Error = Infallible; - fn operation_generator(self, _: &Geometry) -> Result { + fn operation_generator(self, _: &Geometry) -> Result { Ok(Self::G { inner: self }) } } diff --git a/autd3-driver/src/datagram/mod.rs b/autd3-driver/src/datagram/mod.rs index f331fe82..efd6ab21 100644 --- a/autd3-driver/src/datagram/mod.rs +++ b/autd3-driver/src/datagram/mod.rs @@ -31,13 +31,10 @@ pub use clock::ConfigureFPGAClock; pub use cpu_gpio_out::{CpuGPIO, CpuGPIOPort}; pub use debug::DebugSettings; pub use force_fan::ForceFan; -pub use gain::{BoxedGain, Gain, GainContextGenerator, GainOperationGenerator, IntoBoxedGain}; +pub use gain::{BoxedGain, IntoBoxedGain}; #[doc(hidden)] pub use gpio_in::EmulateGPIOIn; -pub use modulation::{ - BoxedModulation, IntoBoxedModulation, Modulation, ModulationOperationGenerator, - ModulationProperty, -}; +pub use modulation::{BoxedModulation, IntoBoxedModulation}; pub use phase_corr::PhaseCorrection; pub use pulse_width_encoder::PulseWidthEncoder; pub use reads_fpga_state::ReadsFPGAState; @@ -53,43 +50,40 @@ pub use synchronize::Synchronize; pub use with_parallel_threshold::{ DatagramWithParallelThreshold, IntoDatagramWithParallelThreshold, }; -pub use with_segment::{DatagramS, DatagramWithSegment, IntoDatagramWithSegment}; +pub use with_segment::{DatagramWithSegment, IntoDatagramWithSegment}; pub use with_timeout::{DatagramWithTimeout, IntoDatagramWithTimeout}; +pub use autd3_core::datagram::Datagram; + use crate::{ - defined::DEFAULT_TIMEOUT, firmware::operation::NullOp, geometry::{Device, Geometry}, }; -use std::time::Duration; use crate::{error::AUTDDriverError, firmware::operation::OperationGenerator}; -/// [`Datagram`] represents the data sent to the device. -pub trait Datagram: std::fmt::Debug { - #[doc(hidden)] - type G: OperationGenerator; - - #[doc(hidden)] - fn operation_generator(self, geometry: &Geometry) -> Result; - - /// Returns the timeout duration. - fn timeout(&self) -> Option { - Some(DEFAULT_TIMEOUT) - } - - /// Returns the parallel threshold. - fn parallel_threshold(&self) -> Option { - Some(usize::MAX) - } -} - #[cfg(test)] pub(crate) mod tests { - use crate::firmware::fpga::{Segment, TransitionMode}; + use std::time::Duration; + + use autd3_core::datagram::DatagramS; + + use crate::firmware::{ + fpga::{Segment, TransitionMode}, + operation::tests::create_device, + }; use super::*; + pub fn create_geometry(n: u16, num_trans_in_unit: u8) -> Geometry { + Geometry::new( + (0..n) + .map(|i| create_device(i, num_trans_in_unit)) + .collect(), + 4, + ) + } + #[derive(Debug)] pub struct NullDatagram { pub timeout: Option, @@ -104,13 +98,14 @@ pub(crate) mod tests { // GRCOV_EXCL_START fn generate(&mut self, _device: &Device) -> (Self::O1, Self::O2) { - (Self::O1::new(), Self::O2::new()) + (Self::O1 {}, Self::O2 {}) } // GRCOV_EXCL_STOP } impl DatagramS for NullDatagram { type G = NullOperationGenerator; + type Error = AUTDDriverError; fn operation_generator_with_segment( self, diff --git a/autd3-driver/src/datagram/modulation/boxed.rs b/autd3-driver/src/datagram/modulation/boxed.rs index c714b32b..fa245ed7 100644 --- a/autd3-driver/src/datagram/modulation/boxed.rs +++ b/autd3-driver/src/datagram/modulation/boxed.rs @@ -1,18 +1,10 @@ use std::mem::MaybeUninit; -use autd3_derive::Modulation; - -use super::{Modulation, ModulationOperationGenerator, ModulationProperty}; -use crate::{ - datagram::DatagramS, - error::AUTDDriverError, - firmware::fpga::{LoopBehavior, SamplingConfig, Segment, TransitionMode}, - geometry::Geometry, -}; +use autd3_core::derive::*; /// A dyn-compatible version of [`Modulation`]. pub trait DModulation { - fn dyn_calc(&mut self) -> Result, AUTDDriverError>; + fn dyn_calc(&mut self) -> Result, ModulationError>; fn dyn_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result; } @@ -21,7 +13,7 @@ impl< #[cfg(feature = "lightweight")] T: Modulation + Send + Sync, > DModulation for MaybeUninit { - fn dyn_calc(&mut self) -> Result, AUTDDriverError> { + fn dyn_calc(&mut self) -> Result, ModulationError> { let mut tmp: MaybeUninit = MaybeUninit::uninit(); std::mem::swap(&mut tmp, self); // SAFETY: This function is called only once from `Modulation::calc`. @@ -58,7 +50,7 @@ impl std::fmt::Debug for BoxedModulation { } impl Modulation for BoxedModulation { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { let Self { mut m, .. } = self; m.dyn_calc() } diff --git a/autd3-driver/src/datagram/modulation/mod.rs b/autd3-driver/src/datagram/modulation/mod.rs index 003beb47..23e35b1e 100644 --- a/autd3-driver/src/datagram/modulation/mod.rs +++ b/autd3-driver/src/datagram/modulation/mod.rs @@ -1,39 +1,17 @@ mod boxed; +use autd3_core::{derive::ModulationOperationGenerator, modulation::Modulation}; pub use boxed::{BoxedModulation, IntoBoxedModulation}; -use std::sync::Arc; - use super::silencer::HasSamplingConfig; use crate::{ - error::AUTDDriverError, firmware::{ - fpga::{LoopBehavior, SamplingConfig, Segment, TransitionMode}, + fpga::SamplingConfig, operation::{ModulationOp, NullOp, OperationGenerator}, }, geometry::Device, }; -/// A trait to get the modulation property. (This trait is automatically implemented by the [`Modulation`] derive macro.) -/// -/// [`Modulation`]: autd3_derive::Modulation -pub trait ModulationProperty { - /// Get the sampling configuration. - fn sampling_config(&self) -> SamplingConfig; - /// Get the loop behavior. - fn loop_behavior(&self) -> LoopBehavior; -} - -/// Trait for applying amplitude modulation. -/// -/// See also [`Modulation`] derive macro. -/// -/// [`Modulation`]: autd3_derive::Modulation -pub trait Modulation: ModulationProperty + std::fmt::Debug { - /// Calculate the modulation data. - fn calc(self) -> Result, AUTDDriverError>; -} - impl HasSamplingConfig for M { fn intensity(&self) -> Option { Some(self.sampling_config()) @@ -43,15 +21,6 @@ impl HasSamplingConfig for M { } } -#[doc(hidden)] -pub struct ModulationOperationGenerator { - pub g: Arc>, - pub config: SamplingConfig, - pub loop_behavior: LoopBehavior, - pub segment: Segment, - pub transition_mode: Option, -} - impl OperationGenerator for ModulationOperationGenerator { type O1 = ModulationOp; type O2 = NullOp; @@ -66,26 +35,37 @@ impl OperationGenerator for ModulationOperationGenerator { self.segment, self.transition_mode, ), - Self::O2::new(), + Self::O2 {}, ) } } #[cfg(test)] pub mod tests { - use autd3_derive::Modulation; + use autd3_core::{ + derive::LoopBehavior, + modulation::{ModulationError, ModulationProperty}, + }; use super::*; - use crate::{datagram::DatagramS, geometry::Geometry}; - #[derive(Modulation, Clone, PartialEq, Debug)] + #[derive(Clone, PartialEq, Debug)] pub struct TestModulation { pub config: SamplingConfig, pub loop_behavior: LoopBehavior, } + impl ModulationProperty for TestModulation { + fn sampling_config(&self) -> SamplingConfig { + self.config + } + fn loop_behavior(&self) -> LoopBehavior { + self.loop_behavior + } + } + impl Modulation for TestModulation { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { Ok(vec![0; 2]) } } diff --git a/autd3-driver/src/datagram/phase_corr.rs b/autd3-driver/src/datagram/phase_corr.rs index f1a4b3f5..3f8e6782 100644 --- a/autd3-driver/src/datagram/phase_corr.rs +++ b/autd3-driver/src/datagram/phase_corr.rs @@ -1,3 +1,5 @@ +use std::convert::Infallible; + use crate::{ datagram::*, firmware::{fpga::Phase, operation::PhaseCorrectionOp}, @@ -16,7 +18,7 @@ use derive_new::new; /// /// ``` /// # use autd3_driver::datagram::PhaseCorrection; -/// # use autd3_driver::derive::Phase; +/// # use autd3_driver::firmware::fpga::Phase; /// PhaseCorrection::new(|_dev| |_tr| Phase::PI); /// ``` #[derive(Builder, Debug, new)] @@ -36,7 +38,7 @@ impl Phase + Send + Sync, F: Fn(&Device) -> FT> Operation type O2 = NullOp; fn generate(&mut self, device: &Device) -> (Self::O1, Self::O2) { - (Self::O1::new((self.f)(device)), Self::O2::new()) + (Self::O1::new((self.f)(device)), Self::O2 {}) } } @@ -44,8 +46,9 @@ impl Phase + Send + Sync, F: Fn(&Device) -> FT> Datagram for PhaseCorrection { type G = PhaseCorrectionOpGenerator; + type Error = Infallible; - fn operation_generator(self, _: &Geometry) -> Result { + fn operation_generator(self, _: &Geometry) -> Result { Ok(Self::G { f: self.f }) } } diff --git a/autd3-driver/src/datagram/pulse_width_encoder.rs b/autd3-driver/src/datagram/pulse_width_encoder.rs index ece09aa6..3c3a6c0f 100644 --- a/autd3-driver/src/datagram/pulse_width_encoder.rs +++ b/autd3-driver/src/datagram/pulse_width_encoder.rs @@ -1,3 +1,5 @@ +use std::convert::Infallible; + use crate::{ datagram::*, firmware::{fpga::PWE_BUF_SIZE, operation::PulseWidthEncoderOp}, @@ -59,14 +61,15 @@ impl u8 + Send + Sync, F: Fn(&Device) -> H> OperationGenerator type O2 = NullOp; fn generate(&mut self, device: &Device) -> (Self::O1, Self::O2) { - (Self::O1::new((self.f)(device)), Self::O2::new()) + (Self::O1::new((self.f)(device)), Self::O2 {}) } } impl u8 + Send + Sync, F: Fn(&Device) -> H> Datagram for PulseWidthEncoder { type G = PulseWidthEncoderOpGenerator; + type Error = Infallible; - fn operation_generator(self, _: &Geometry) -> Result { + fn operation_generator(self, _: &Geometry) -> Result { Ok(PulseWidthEncoderOpGenerator { f: self.f }) } diff --git a/autd3-driver/src/datagram/reads_fpga_state.rs b/autd3-driver/src/datagram/reads_fpga_state.rs index 6ecce042..235c3e78 100644 --- a/autd3-driver/src/datagram/reads_fpga_state.rs +++ b/autd3-driver/src/datagram/reads_fpga_state.rs @@ -1,3 +1,5 @@ +use std::convert::Infallible; + use crate::{datagram::*, firmware::operation::ReadsFPGAStateOp}; use autd3_derive::Builder; @@ -21,14 +23,15 @@ impl bool> OperationGenerator for ReadsFPGAStateOpGenerator type O2 = NullOp; fn generate(&mut self, device: &Device) -> (Self::O1, Self::O2) { - (Self::O1::new((self.f)(device)), Self::O2::new()) + (Self::O1::new((self.f)(device)), Self::O2 {}) } } impl bool> Datagram for ReadsFPGAState { type G = ReadsFPGAStateOpGenerator; + type Error = Infallible; - fn operation_generator(self, _: &Geometry) -> Result { + fn operation_generator(self, _: &Geometry) -> Result { Ok(ReadsFPGAStateOpGenerator { f: self.f }) } } diff --git a/autd3-driver/src/datagram/segment.rs b/autd3-driver/src/datagram/segment.rs index e7d4804c..75db74d3 100644 --- a/autd3-driver/src/datagram/segment.rs +++ b/autd3-driver/src/datagram/segment.rs @@ -1,3 +1,5 @@ +use std::convert::Infallible; + use crate::{datagram::*, firmware::operation::SwapSegmentOp}; use super::OperationGenerator; @@ -11,14 +13,15 @@ impl OperationGenerator for SwapSegmentOpGenerator { type O2 = NullOp; fn generate(&mut self, _: &Device) -> (Self::O1, Self::O2) { - (Self::O1::new(self.segment), Self::O2::new()) + (Self::O1::new(self.segment), Self::O2 {}) } } impl Datagram for SwapSegment { type G = SwapSegmentOpGenerator; + type Error = Infallible; - fn operation_generator(self, _: &Geometry) -> Result { + fn operation_generator(self, _: &Geometry) -> Result { Ok(SwapSegmentOpGenerator { segment: self }) } } diff --git a/autd3-driver/src/datagram/silencer.rs b/autd3-driver/src/datagram/silencer.rs index e80a4ba3..44ef77f0 100644 --- a/autd3-driver/src/datagram/silencer.rs +++ b/autd3-driver/src/datagram/silencer.rs @@ -1,9 +1,8 @@ -use std::num::NonZeroU16; +use std::{convert::Infallible, num::NonZeroU16}; use autd3_derive::Builder; use crate::{ - error::AUTDDriverError, firmware::{ fpga::{ SamplingConfig, SilencerTarget, SILENCER_STEPS_INTENSITY_DEFAULT, @@ -194,7 +193,7 @@ impl OperationGenerator for SilencerOpGenerator { fn generate(&mut self, _: &Device) -> (Self::O1, Self::O2) { ( Self::O1::new(self.config.intensity, self.config.phase, self.target), - Self::O2::new(), + Self::O2 {}, ) } } @@ -212,7 +211,7 @@ impl OperationGenerator for SilencerOpGenerator { self.strict_mode, self.target, ), - Self::O2::new(), + Self::O2 {}, ) } } @@ -229,7 +228,7 @@ impl OperationGenerator for SilencerOpGenerator { self.strict_mode, self.target, ), - Self::O2::new(), + Self::O2 {}, ) } } @@ -239,8 +238,9 @@ where SilencerOpGenerator: OperationGenerator, { type G = SilencerOpGenerator; + type Error = Infallible; - fn operation_generator(self, _: &Geometry) -> Result { + fn operation_generator(self, _: &Geometry) -> Result { Ok(Self::G { config: self.config, strict_mode: self.strict_mode, diff --git a/autd3-driver/src/datagram/stm/foci/implement.rs b/autd3-driver/src/datagram/stm/foci/implement.rs index e45f0b3f..bf9aef2c 100644 --- a/autd3-driver/src/datagram/stm/foci/implement.rs +++ b/autd3-driver/src/datagram/stm/foci/implement.rs @@ -59,6 +59,8 @@ mod tests { #[cfg(not(feature = "dynamic_freq"))] use std::time::Duration; + use autd3_core::modulation::SamplingConfigError; + use super::{super::FociSTM, *}; use crate::{ defined::{kHz, Freq, Hz}, @@ -73,12 +75,12 @@ mod tests { #[case((20. * Hz).try_into(), 2.*Hz, 10)] #[case((2. * 0.49*Hz).try_into(), 0.49*Hz, 2)] fn from_freq( - #[case] expect: Result, + #[case] expect: Result, #[case] freq: Freq, #[case] n: usize, ) { assert_eq!( - expect, + expect.map_err(AUTDDriverError::from), FociSTM::new(freq, (0..n).map(|_| Point3::origin())).map(|f| f.sampling_config()) ); } @@ -104,17 +106,17 @@ mod tests { #[rstest::rstest] #[test] #[case( - Duration::from_millis(1000).try_into(), + Duration::from_millis(1000).try_into().map_err(AUTDDriverError::from), Duration::from_millis(2000), 2 )] #[case( - Duration::from_millis(100).try_into(), + Duration::from_millis(100).try_into().map_err(AUTDDriverError::from), Duration::from_millis(1000), 10 )] #[case( - Duration::from_millis(50).try_into(), + Duration::from_millis(50).try_into().map_err(AUTDDriverError::from), Duration::from_millis(500), 10 )] diff --git a/autd3-driver/src/datagram/stm/foci/mod.rs b/autd3-driver/src/datagram/stm/foci/mod.rs index 86ddee6e..80f8e3b1 100644 --- a/autd3-driver/src/datagram/stm/foci/mod.rs +++ b/autd3-driver/src/datagram/stm/foci/mod.rs @@ -12,6 +12,7 @@ use crate::{ pub use crate::firmware::operation::FociSTMContext; +use autd3_core::datagram::DatagramS; use autd3_derive::Builder; use derive_more::{Deref, DerefMut}; use silencer::HasSamplingConfig; @@ -84,16 +85,13 @@ impl> FociSTM { Self::new_from_sampling_config(config.into(), iter).unwrap() } - fn new_from_sampling_config( - config: T, + fn new_from_sampling_config( + config: impl IntoSamplingConfigSTM, iter: impl IntoFociSTMGenerator, - ) -> Result - where - SamplingConfig: TryFrom<(T, usize), Error = AUTDDriverError>, - { + ) -> Result { let gen = iter.into(); Ok(Self { - sampling_config: (config, gen.len()).try_into()?, + sampling_config: config.into_sampling_config(gen.len())?, gen, loop_behavior: LoopBehavior::infinite(), }) @@ -145,7 +143,7 @@ impl> FociSTM { /// # } /// ``` #[cfg(not(feature = "dynamic_freq"))] - pub fn period(&self) -> Duration { + pub fn period(&self) -> std::time::Duration { self.sampling_config().period() * self.gen.len() as u32 } } @@ -185,20 +183,21 @@ impl> OperationGenerator self.segment, self.transition_mode, ), - Self::O2::new(), + Self::O2 {}, ) } } impl> DatagramS for FociSTM { type G = FociSTMOperationGenerator; + type Error = AUTDDriverError; fn operation_generator_with_segment( self, _geometry: &Geometry, segment: Segment, transition_mode: Option, - ) -> Result { + ) -> Result { let size = self.gen.len(); Ok(FociSTMOperationGenerator { gen: self.gen.init()?, diff --git a/autd3-driver/src/datagram/stm/gain/implement.rs b/autd3-driver/src/datagram/stm/gain/implement.rs index 8f7131c9..2f794e60 100644 --- a/autd3-driver/src/datagram/stm/gain/implement.rs +++ b/autd3-driver/src/datagram/stm/gain/implement.rs @@ -1,16 +1,10 @@ use std::{collections::HashMap, iter::Peekable}; -use bit_vec::BitVec; +use autd3_core::gain::{BitVec, Gain, GainContext, GainContextGenerator, GainError}; -use crate::{ - error::AUTDDriverError, - geometry::{Device, Geometry}, -}; +use crate::geometry::{Device, Geometry}; -use super::{ - gain::GainContext, Gain, GainContextGenerator, GainSTMContext, GainSTMContextGenerator, - GainSTMGenerator, IntoGainSTMGenerator, -}; +use super::{GainSTMContext, GainSTMContextGenerator, GainSTMGenerator, IntoGainSTMGenerator}; pub struct VecGainSTMContext { gains: Peekable>, @@ -46,8 +40,8 @@ impl GainSTMGenerator for Vec { fn init( self, geometry: &Geometry, - filter: Option<&HashMap>>, - ) -> Result { + filter: Option<&HashMap>, + ) -> Result { self.into_iter() .map(|g| g.init(geometry, filter)) .collect::, _>>() @@ -70,13 +64,17 @@ where #[cfg(test)] mod tests { + #[cfg(not(feature = "dynamic_freq"))] use std::time::Duration; - use super::{super::GainSTM, *}; + use autd3_core::modulation::SamplingConfigError; + + use super::super::GainSTM; use crate::{ datagram::gain::tests::TestGain, defined::{kHz, Freq, Hz}, + error::AUTDDriverError, firmware::{ cpu::GainSTMMode, fpga::{LoopBehavior, SamplingConfig}, @@ -90,12 +88,12 @@ mod tests { #[case((20. * Hz).try_into(), 2.*Hz, 10)] #[case((2. * 0.49*Hz).try_into(), 0.49*Hz, 2)] fn from_freq( - #[case] expect: Result, + #[case] expect: Result, #[case] freq: Freq, #[case] n: usize, ) { assert_eq!( - expect, + expect.map_err(AUTDDriverError::from), GainSTM::new(freq, (0..n).map(|_| TestGain::null())).map(|g| g.sampling_config()) ); } @@ -121,17 +119,17 @@ mod tests { #[rstest::rstest] #[test] #[case( - Duration::from_millis(1000).try_into(), + Duration::from_millis(1000).try_into().map_err(AUTDDriverError::from), Duration::from_millis(2000), 2 )] #[case( - Duration::from_millis(100).try_into(), + Duration::from_millis(100).try_into().map_err(AUTDDriverError::from), Duration::from_millis(1000), 10 )] #[case( - Duration::from_millis(50).try_into(), + Duration::from_millis(50).try_into().map_err(AUTDDriverError::from), Duration::from_millis(500), 10 )] diff --git a/autd3-driver/src/datagram/stm/gain/mod.rs b/autd3-driver/src/datagram/stm/gain/mod.rs index 48a8dab5..4b810bda 100644 --- a/autd3-driver/src/datagram/stm/gain/mod.rs +++ b/autd3-driver/src/datagram/stm/gain/mod.rs @@ -14,8 +14,11 @@ use crate::{ }, }; +use autd3_core::{ + derive::DatagramS, + gain::{BitVec, GainContextGenerator, GainError}, +}; use autd3_derive::Builder; -use bit_vec::BitVec; use derive_more::{Deref, DerefMut}; use silencer::HasSamplingConfig; @@ -40,8 +43,8 @@ pub trait GainSTMGenerator: std::fmt::Debug { fn init( self, geometry: &Geometry, - filter: Option<&HashMap>>, - ) -> Result; + filter: Option<&HashMap>, + ) -> Result; /// Returns the length of the sequence of gains. fn len(&self) -> usize; } @@ -105,16 +108,13 @@ impl GainSTM { Self::new_from_sampling_config(config.into(), iter).unwrap() } - fn new_from_sampling_config>( - config: S, + fn new_from_sampling_config>( + config: impl IntoSamplingConfigSTM, iter: T, - ) -> Result - where - SamplingConfig: TryFrom<(S, usize), Error = AUTDDriverError>, - { + ) -> Result { let gen = iter.into(); Ok(Self { - sampling_config: (config, gen.len()).try_into()?, + sampling_config: config.into_sampling_config(gen.len())?, loop_behavior: LoopBehavior::infinite(), mode: GainSTMMode::default(), gen, @@ -132,7 +132,7 @@ impl GainSTM { /// /// [`FociSTM::period`]: crate::datagram::FociSTM::period #[cfg(not(feature = "dynamic_freq"))] - pub fn period(&self) -> Duration { + pub fn period(&self) -> std::time::Duration { self.sampling_config().period() * self.gen.len() as u32 } } @@ -162,20 +162,21 @@ impl OperationGenerator for GainSTMOperationGenerato self.segment, self.transition_mode, ), - Self::O2::new(), + Self::O2 {}, ) } } impl DatagramS for GainSTM { type G = GainSTMOperationGenerator; + type Error = AUTDDriverError; fn operation_generator_with_segment( self, geometry: &Geometry, segment: Segment, transition_mode: Option, - ) -> Result { + ) -> Result { let size = self.gen.len(); let config = self.sampling_config; let loop_behavior = self.loop_behavior; diff --git a/autd3-driver/src/datagram/stm/sampling_config.rs b/autd3-driver/src/datagram/stm/sampling_config.rs index e3b9f7ee..2383e202 100644 --- a/autd3-driver/src/datagram/stm/sampling_config.rs +++ b/autd3-driver/src/datagram/stm/sampling_config.rs @@ -31,31 +31,29 @@ pub enum STMConfigNearest { Period(Duration), } -impl TryFrom<(STMConfig, usize)> for SamplingConfig { - type Error = AUTDDriverError; +pub(crate) trait IntoSamplingConfigSTM { + fn into_sampling_config(self, size: usize) -> Result; +} - fn try_from(value: (STMConfig, usize)) -> Result { - let (config, size) = value; - match config { - STMConfig::Freq(f) => SamplingConfig::new(f * size as f32), +impl IntoSamplingConfigSTM for STMConfig { + fn into_sampling_config(self, size: usize) -> Result { + match self { + STMConfig::Freq(f) => Ok(SamplingConfig::new(f * size as f32)?), #[cfg(not(feature = "dynamic_freq"))] STMConfig::Period(p) => { if p.as_nanos() % size as u128 != 0 { return Err(AUTDDriverError::STMPeriodInvalid(size, p)); } - SamplingConfig::new(p / size as u32) + Ok(SamplingConfig::new(p / size as u32)?) } STMConfig::SamplingConfig(s) => Ok(s), } } } -impl TryFrom<(STMConfigNearest, usize)> for SamplingConfig { - type Error = AUTDDriverError; - - fn try_from(value: (STMConfigNearest, usize)) -> Result { - let (config, size) = value; - match config { +impl IntoSamplingConfigSTM for STMConfigNearest { + fn into_sampling_config(self, size: usize) -> Result { + match self { STMConfigNearest::Freq(f) => Ok(SamplingConfig::new_nearest(f.hz() * size as f32 * Hz)), #[cfg(not(feature = "dynamic_freq"))] STMConfigNearest::Period(p) => Ok(SamplingConfig::new_nearest(p / size as u32)), @@ -97,6 +95,8 @@ impl From for STMConfigNearest { #[cfg(test)] mod tests { + use autd3_core::modulation::SamplingConfigError; + use super::*; use crate::{defined::Hz, firmware::fpga::SamplingConfig}; @@ -107,11 +107,14 @@ mod tests { #[case((40000. * Hz).try_into(), 40000. * Hz, 1)] #[case((4000.5 * Hz).try_into(), 4000.5 * Hz, 1)] fn frequency( - #[case] expect: Result, + #[case] expect: Result, #[case] freq: Freq, #[case] size: usize, ) { - assert_eq!(expect, (STMConfig::Freq(freq), size).try_into()); + assert_eq!( + expect.map_err(AUTDDriverError::from), + STMConfig::Freq(freq).into_sampling_config(size) + ); } #[rstest::rstest] @@ -123,7 +126,7 @@ mod tests { fn sampling(#[case] config: SamplingConfig, #[case] size: usize) { assert_eq!( Ok(config), - (STMConfig::SamplingConfig(config), size).try_into() + STMConfig::SamplingConfig(config).into_sampling_config(size) ); } @@ -131,17 +134,17 @@ mod tests { #[rstest::rstest] #[test] #[case( - Duration::from_micros(250).try_into(), + Duration::from_micros(250).try_into().map_err(AUTDDriverError::from), Duration::from_micros(250), 1 )] #[case( - Duration::from_micros(125).try_into(), + Duration::from_micros(125).try_into().map_err(AUTDDriverError::from), Duration::from_micros(250), 2 )] #[case( - Duration::from_micros(25).try_into(), + Duration::from_micros(25).try_into().map_err(AUTDDriverError::from), Duration::from_micros(25), 1 )] @@ -155,7 +158,7 @@ mod tests { #[case] p: Duration, #[case] size: usize, ) { - assert_eq!(expect, (STMConfig::Period(p), size).try_into()); + assert_eq!(expect, STMConfig::Period(p).into_sampling_config(size)); } #[rstest::rstest] @@ -169,7 +172,10 @@ mod tests { #[case] freq: Freq, #[case] size: usize, ) { - assert_eq!(expect, (STMConfigNearest::Freq(freq), size).try_into()); + assert_eq!( + expect, + STMConfigNearest::Freq(freq).into_sampling_config(size) + ); } #[cfg(not(feature = "dynamic_freq"))] @@ -200,6 +206,9 @@ mod tests { #[case] p: Duration, #[case] size: usize, ) { - assert_eq!(expect, (STMConfigNearest::Period(p), size).try_into()); + assert_eq!( + expect, + STMConfigNearest::Period(p).into_sampling_config(size) + ); } } diff --git a/autd3-driver/src/datagram/synchronize.rs b/autd3-driver/src/datagram/synchronize.rs index 816dce72..fdb04d81 100644 --- a/autd3-driver/src/datagram/synchronize.rs +++ b/autd3-driver/src/datagram/synchronize.rs @@ -1,3 +1,5 @@ +use std::convert::Infallible; + use crate::firmware::operation::SyncOp; use crate::datagram::*; @@ -14,14 +16,15 @@ impl OperationGenerator for SynchronizeOpGenerator { type O2 = NullOp; fn generate(&mut self, _: &Device) -> (Self::O1, Self::O2) { - (Self::O1::new(), Self::O2::new()) + (Self::O1::new(), Self::O2 {}) } } impl Datagram for Synchronize { type G = SynchronizeOpGenerator; + type Error = Infallible; - fn operation_generator(self, _: &Geometry) -> Result { + fn operation_generator(self, _: &Geometry) -> Result { Ok(SynchronizeOpGenerator {}) } } diff --git a/autd3-driver/src/datagram/tuple.rs b/autd3-driver/src/datagram/tuple.rs index cf69d937..20a1606d 100644 --- a/autd3-driver/src/datagram/tuple.rs +++ b/autd3-driver/src/datagram/tuple.rs @@ -1,26 +1,14 @@ -use std::time::Duration; - -use crate::{ - error::AUTDDriverError, - firmware::operation::{NullOp, OperationGenerator}, - geometry::{Device, Geometry}, +use autd3_core::{ + datagram::{CombinedOperationGenerator, NullOp}, + geometry::Device, }; -use super::Datagram; - -pub struct CombinedOperationGenerator -where - O1: OperationGenerator, - O2: OperationGenerator, -{ - o1: O1, - o2: O2, -} +use crate::firmware::operation::OperationGenerator; impl OperationGenerator for CombinedOperationGenerator where O1: OperationGenerator, - O2: OperationGenerator, + O2: OperationGenerator, { type O1 = O1::O1; type O2 = O2::O1; @@ -31,111 +19,3 @@ where (o1, o2) } } - -impl Datagram for (D1, D2) -where - D1: Datagram, - D2: Datagram, - G1: OperationGenerator, - G2: OperationGenerator, -{ - type G = CombinedOperationGenerator; - - fn operation_generator(self, geometry: &Geometry) -> Result { - Ok(CombinedOperationGenerator { - o1: self.0.operation_generator(geometry)?, - o2: self.1.operation_generator(geometry)?, - }) - } - - fn timeout(&self) -> Option { - self.0.timeout().into_iter().chain(self.1.timeout()).max() - } - - fn parallel_threshold(&self) -> Option { - self.0 - .parallel_threshold() - .into_iter() - .chain(self.1.parallel_threshold()) - .min() - } -} - -#[cfg(test)] -mod tests { - use crate::datagram::tests::NullDatagram; - - use super::*; - - #[rstest::rstest] - #[case(None, None, None)] - #[case( - Some(Duration::from_millis(100)), - Some(Duration::from_millis(100)), - None - )] - #[case( - Some(Duration::from_millis(100)), - None, - Some(Duration::from_millis(100)) - )] - #[case( - Some(Duration::from_millis(200)), - Some(Duration::from_millis(100)), - Some(Duration::from_millis(200)) - )] - #[case( - Some(Duration::from_millis(200)), - Some(Duration::from_millis(200)), - Some(Duration::from_millis(100)) - )] - #[test] - fn timeout( - #[case] expect: Option, - #[case] timeout1: Option, - #[case] timeout2: Option, - ) { - assert_eq!( - expect, - ( - NullDatagram { - timeout: timeout1, - parallel_threshold: None, - }, - NullDatagram { - timeout: timeout2, - parallel_threshold: None, - } - ) - .timeout() - ); - } - - #[rstest::rstest] - #[case(None, None, None)] - #[case(Some(100), Some(100), None)] - #[case(Some(100), None, Some(100))] - #[case(Some(100), Some(100), Some(200))] - #[case(Some(100), Some(200), Some(100))] - #[test] - fn parallel_threshold( - #[case] expect: Option, - #[case] threshold1: Option, - #[case] threshold2: Option, - ) { - assert_eq!( - expect, - ( - NullDatagram { - timeout: None, - parallel_threshold: threshold1, - }, - NullDatagram { - timeout: None, - parallel_threshold: threshold2, - } - ) - .parallel_threshold() - ); - } -} diff --git a/autd3-driver/src/datagram/with_parallel_threshold.rs b/autd3-driver/src/datagram/with_parallel_threshold.rs index f7106046..1912fce8 100644 --- a/autd3-driver/src/datagram/with_parallel_threshold.rs +++ b/autd3-driver/src/datagram/with_parallel_threshold.rs @@ -1,4 +1,4 @@ -use crate::{error::AUTDDriverError, geometry::Geometry}; +use crate::geometry::Geometry; use super::Datagram; @@ -14,8 +14,9 @@ pub struct DatagramWithParallelThreshold { impl Datagram for DatagramWithParallelThreshold { type G = D::G; + type Error = D::Error; - fn operation_generator(self, geometry: &Geometry) -> Result { + fn operation_generator(self, geometry: &Geometry) -> Result { self.datagram.operation_generator(geometry) } @@ -47,10 +48,7 @@ impl IntoDatagramWithParallelThreshold for D { mod tests { use super::*; - use crate::{ - datagram::tests::{NullDatagram, NullOperationGenerator}, - geometry::tests::create_geometry, - }; + use crate::datagram::tests::{create_geometry, NullDatagram, NullOperationGenerator}; #[test] fn with_parallel_threshold() { diff --git a/autd3-driver/src/datagram/with_segment.rs b/autd3-driver/src/datagram/with_segment.rs index 04107a37..b29fdaf9 100644 --- a/autd3-driver/src/datagram/with_segment.rs +++ b/autd3-driver/src/datagram/with_segment.rs @@ -1,40 +1,15 @@ use std::time::Duration; -use super::{Datagram, OperationGenerator}; +use super::Datagram; use crate::{ - defined::DEFAULT_TIMEOUT, - error::AUTDDriverError, firmware::fpga::{Segment, TransitionMode}, geometry::Geometry, }; +use autd3_core::datagram::DatagramS; use autd3_derive::Builder; use derive_more::Deref; -/// [`DatagramS`] represents a [`Datagram`] that can specify [`Segment`] to write the data. -pub trait DatagramS: std::fmt::Debug { - #[doc(hidden)] - type G: OperationGenerator; - - #[doc(hidden)] - fn operation_generator_with_segment( - self, - geometry: &Geometry, - segment: Segment, - transition_mode: Option, - ) -> Result; - - /// Returns the timeout duration. - fn timeout(&self) -> Option { - Some(DEFAULT_TIMEOUT) - } - - /// Returns the parallel threshold. - fn parallel_threshold(&self) -> Option { - Some(usize::MAX) - } -} - /// A wrapper to set [`Segment`] of [`DatagramS`]. #[derive(Builder, Clone, Deref, Debug)] pub struct DatagramWithSegment { @@ -51,8 +26,9 @@ pub struct DatagramWithSegment { impl Datagram for DatagramWithSegment { type G = D::G; + type Error = D::Error; - fn operation_generator(self, geometry: &Geometry) -> Result { + fn operation_generator(self, geometry: &Geometry) -> Result { self.datagram .operation_generator_with_segment(geometry, self.segment, self.transition_mode) } @@ -66,26 +42,6 @@ impl Datagram for DatagramWithSegment { } } -impl Datagram for D { - type G = D::G; - - fn operation_generator(self, geometry: &Geometry) -> Result { - self.operation_generator_with_segment( - geometry, - Segment::S0, - Some(TransitionMode::Immediate), - ) - } - - fn timeout(&self) -> Option { - ::timeout(self) - } - - fn parallel_threshold(&self) -> Option { - ::parallel_threshold(self) - } -} - /// A trait to convert [`DatagramS`] to [`DatagramWithSegment`]. pub trait IntoDatagramWithSegment { /// Convert [`DatagramS`] to [`DatagramWithSegment`]. diff --git a/autd3-driver/src/datagram/with_timeout.rs b/autd3-driver/src/datagram/with_timeout.rs index 5fb76264..727d2e0d 100644 --- a/autd3-driver/src/datagram/with_timeout.rs +++ b/autd3-driver/src/datagram/with_timeout.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use crate::{error::AUTDDriverError, geometry::Geometry}; +use crate::geometry::Geometry; use super::Datagram; @@ -16,8 +16,9 @@ pub struct DatagramWithTimeout { impl Datagram for DatagramWithTimeout { type G = D::G; + type Error = D::Error; - fn operation_generator(self, geometry: &Geometry) -> Result { + fn operation_generator(self, geometry: &Geometry) -> Result { self.datagram.operation_generator(geometry) } @@ -49,10 +50,7 @@ impl IntoDatagramWithTimeout for D { mod tests { use super::*; - use crate::{ - datagram::tests::{NullDatagram, NullOperationGenerator}, - geometry::tests::create_geometry, - }; + use crate::datagram::tests::{create_geometry, NullDatagram, NullOperationGenerator}; #[test] fn with_timeout() { diff --git a/autd3-driver/src/defined/angle.rs b/autd3-driver/src/defined/angle.rs deleted file mode 100644 index 800a383c..00000000 --- a/autd3-driver/src/defined/angle.rs +++ /dev/null @@ -1,65 +0,0 @@ -/// \[°\] -#[allow(non_camel_case_types)] -pub struct deg; - -/// \[rad\] -#[allow(non_camel_case_types)] -pub struct rad; - -use derive_more::Debug; - -/// Angle -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum Angle { - #[doc(hidden)] - #[debug("{}°", _0)] - Deg(f32), - #[doc(hidden)] - #[debug("{}rad", _0)] - Rad(f32), -} - -impl Angle { - /// Returns the angle in radian - pub fn radian(self) -> f32 { - match self { - Self::Deg(a) => a.to_radians(), - Self::Rad(a) => a, - } - } - - /// Returns the angle in degree - pub fn degree(self) -> f32 { - match self { - Self::Deg(a) => a, - Self::Rad(a) => a.to_degrees(), - } - } -} - -impl std::ops::Mul for f32 { - type Output = Angle; - - fn mul(self, _rhs: deg) -> Self::Output { - Self::Output::Deg(self) - } -} - -impl std::ops::Mul for f32 { - type Output = Angle; - - fn mul(self, _rhs: rad) -> Self::Output { - Self::Output::Rad(self) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn dbg() { - assert_eq!(format!("{:?}", 90.0 * deg), "90°"); - assert_eq!(format!("{:?}", 1.0 * rad), "1rad"); - } -} diff --git a/autd3-driver/src/defined/freq/float.rs b/autd3-driver/src/defined/freq/float.rs deleted file mode 100644 index 6fa1b07f..00000000 --- a/autd3-driver/src/defined/freq/float.rs +++ /dev/null @@ -1,17 +0,0 @@ -use super::{kHz, Freq, Hz}; - -impl std::ops::Mul for f32 { - type Output = Freq; - - fn mul(self, _rhs: Hz) -> Self::Output { - Self::Output { freq: self } - } -} - -impl std::ops::Mul for f32 { - type Output = Freq; - - fn mul(self, _rhs: kHz) -> Self::Output { - Self::Output { freq: self * 1e3 } - } -} diff --git a/autd3-driver/src/defined/freq/int.rs b/autd3-driver/src/defined/freq/int.rs deleted file mode 100644 index 467e5273..00000000 --- a/autd3-driver/src/defined/freq/int.rs +++ /dev/null @@ -1,17 +0,0 @@ -use super::{kHz, Freq, Hz}; - -impl std::ops::Mul for u32 { - type Output = Freq; - - fn mul(self, _rhs: Hz) -> Self::Output { - Self::Output { freq: self } - } -} - -impl std::ops::Mul for u32 { - type Output = Freq; - - fn mul(self, _rhs: kHz) -> Self::Output { - Self::Output { freq: self * 1000 } - } -} diff --git a/autd3-driver/src/defined/freq/mod.rs b/autd3-driver/src/defined/freq/mod.rs deleted file mode 100644 index f3bd3c0f..00000000 --- a/autd3-driver/src/defined/freq/mod.rs +++ /dev/null @@ -1,37 +0,0 @@ -mod float; -mod int; - -/// \[Hz\] -pub struct Hz; - -/// \[kHz\] -#[allow(non_camel_case_types)] -pub struct kHz; - -use derive_more::{Add, Debug, Div, Mul, Sub}; - -/// Frequency -#[derive(Clone, Copy, PartialEq, PartialOrd, Add, Div, Mul, Sub, Debug)] -#[debug("{} Hz", freq)] -pub struct Freq { - pub(crate) freq: T, -} - -impl Freq { - #[inline] - /// Returns the frequency in Hz. - pub const fn hz(&self) -> T { - self.freq - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn dbg() { - assert_eq!(format!("{:?}", 100 * Hz), "100 Hz"); - assert_eq!(format!("{:?}", 100 * kHz), "100000 Hz"); - } -} diff --git a/autd3-driver/src/defined/mod.rs b/autd3-driver/src/defined/mod.rs deleted file mode 100644 index db2e3ac5..00000000 --- a/autd3-driver/src/defined/mod.rs +++ /dev/null @@ -1,104 +0,0 @@ -mod angle; -mod freq; - -pub use std::f32::consts::PI; - -#[cfg(feature = "use_meter")] -mod unit { - /// meter - pub const METER: f32 = 1.0; -} -#[cfg(not(feature = "use_meter"))] -mod unit { - /// meter - pub const METER: f32 = 1000.0; -} -pub use unit::*; - -pub use angle::*; -pub use freq::*; - -/// millimeter -pub const MILLIMETER: f32 = METER / 1000.0; - -/// a complex number -pub type Complex = nalgebra::Complex; - -/// The absolute threshold of hearing in \[㎩\] -pub const ABSOLUTE_THRESHOLD_OF_HEARING: f32 = 20e-6; - -/// The amplitude of T4010A1 in \[㎩*mm\] -pub const T4010A1_AMPLITUDE: f32 = 275.574_25 * 200.0 * MILLIMETER; // [㎩*mm] - -/// The default timeout duration -pub const DEFAULT_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(200); - -/// The period of ultrasound in discrete time units -pub const ULTRASOUND_PERIOD_COUNT: usize = 256; - -#[cfg(not(feature = "dynamic_freq"))] -mod inner { - use super::Freq; - use std::time::Duration; - - #[inline(always)] - /// The frequency of ultrasound - pub const fn ultrasound_freq() -> Freq { - Freq { freq: 40000 } - } - - #[inline(always)] - /// The period of ultrasound - pub const fn ultrasound_period() -> Duration { - Duration::from_micros(25) - } -} - -#[cfg(feature = "dynamic_freq")] -mod inner { - use std::sync::Once; - - use super::Freq; - use crate::defined::Hz; - - static mut VAL: Freq = Freq { freq: 40000 }; - static FREQ: Once = Once::new(); - - #[inline] - /// The frequency of ultrasound - pub fn ultrasound_freq() -> Freq { - unsafe { - FREQ.call_once(|| { - VAL = match std::env::var("AUTD3_ULTRASOUND_FREQ") { - Ok(freq) => match freq.parse::() { - Ok(freq) => { - tracing::info!("Set ultrasound frequency to {} Hz.", freq); - freq * Hz - } - Err(_) => { - tracing::error!( - "Invalid ultrasound frequency ({} Hz), fallback to 40 kHz.", - freq - ); - Freq { freq: 40000 } - } - }, - Err(_) => { - tracing::warn!("Environment variable AUTD3_ULTRASOUND_FREQ is not set, fallback to 40 kHz."); - Freq { freq: 40000 } - } - }; - }); - VAL - } - } - - #[doc(hidden)] - pub const DRP_ROM_SIZE: usize = 32; -} - -pub use inner::*; - -/// \[㎜\] -#[allow(non_upper_case_globals)] -pub const mm: f32 = MILLIMETER; diff --git a/autd3-driver/src/error.rs b/autd3-driver/src/error.rs index 1eaf52e2..6d84f027 100644 --- a/autd3-driver/src/error.rs +++ b/autd3-driver/src/error.rs @@ -1,8 +1,14 @@ -use std::time::Duration; - +use std::{convert::Infallible, time::Duration}; + +use autd3_core::{ + datagram::CombinedError, + gain::GainError, + link::LinkError, + modulation::{ModulationError, SamplingConfigError}, +}; use thiserror::Error; -use crate::{defined::Freq, firmware::cpu::GainSTMMode, firmware::fpga::*}; +use crate::{firmware::cpu::GainSTMMode, firmware::fpga::*}; /// A interface for error handling in autd3-driver. #[derive(Error, Debug, PartialEq, Clone)] @@ -33,27 +39,9 @@ pub enum AUTDDriverError { #[error("Unused group key({0})")] UnusedKey(String), - /// Invalid sampling division. - #[error("Sampling division ({0}) must not be zero")] - SamplingDivisionInvalid(u16), - /// Invalid sampling frequency. - #[error("Sampling frequency ({0:?}) must divide theultrasound frequency")] - SamplingFreqInvalid(Freq), - /// Invalid sampling frequency. - #[error("Sampling frequency ({0:?}) must divide the ultrasound frequency")] - SamplingFreqInvalidF(Freq), - /// Invalid sampling period. - #[error("Sampling period ({0:?}) must be a multiple of the ultrasound period")] - SamplingPeriodInvalid(Duration), - /// Sampling frequency is out of range. - #[error("Sampling frequency ({0:?}) is out of range ([{1:?}, {2:?}])")] - SamplingFreqOutOfRange(Freq, Freq, Freq), - /// Sampling frequency is out of range. - #[error("Sampling frequency ({0:?}) is out of range ([{1:?}, {2:?}])")] - SamplingFreqOutOfRangeF(Freq, Freq, Freq), - /// Sampling period is out of range. - #[error("Sampling period ({0:?}) is out of range ([{1:?}, {2:?}])")] - SamplingPeriodOutOfRange(Duration, Duration, Duration), + /// Sampling config error + #[error("{0}")] + SamplingConfig(#[from] SamplingConfigError), /// Invalid STM period. #[error("STM sampling period ({1:?}/{0}) must be integer")] @@ -97,13 +85,13 @@ pub enum AUTDDriverError { /// Error in the modulation. #[error("{0}")] - ModulationError(String), + Modulation(#[from] ModulationError), /// Error in the gain. #[error("{0}")] - GainError(String), + Gain(#[from] GainError), /// Error in the Link. #[error("{0}")] - LinkError(String), + Link(#[from] LinkError), /// Link is closed. #[error("Link is closed")] @@ -122,7 +110,7 @@ pub enum AUTDDriverError { #[cfg(feature = "dynamic_freq")] #[error("Ultrasound frequency ({0:?}) is not supported")] /// Invalid ultrasound frequency. - InvalidFrequency(Freq), + InvalidFrequency(autd3_core::defined::Freq), /// Not supported tag. /// @@ -172,6 +160,26 @@ impl AUTDDriverError { } } +impl From for AUTDDriverError { + fn from(_: Infallible) -> Self { + unreachable!() + } +} + +impl From> for AUTDDriverError +where + E1: std::error::Error, + E2: std::error::Error, + AUTDDriverError: From + From, +{ + fn from(err: CombinedError) -> Self { + match err { + CombinedError::E1(e) => AUTDDriverError::from(e), + CombinedError::E2(e) => AUTDDriverError::from(e), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/autd3-driver/src/ethercat/consts.rs b/autd3-driver/src/ethercat/consts.rs deleted file mode 100644 index c682601a..00000000 --- a/autd3-driver/src/ethercat/consts.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::time::Duration; - -/// PDO output frame size -pub const EC_OUTPUT_FRAME_SIZE: usize = 626; -/// PDO input frame size -pub const EC_INPUT_FRAME_SIZE: usize = 2; - -/// The base unit of the EtherCAT -pub const EC_CYCLE_TIME_BASE: Duration = Duration::from_micros(500); - -/// The base point of system time -pub const ECAT_DC_SYS_TIME_BASE: time::OffsetDateTime = - time::macros::datetime!(2000-01-01 0:00 UTC); diff --git a/autd3-driver/src/ethercat/dc_sys_time.rs b/autd3-driver/src/ethercat/dc_sys_time.rs deleted file mode 100644 index 3378afed..00000000 --- a/autd3-driver/src/ethercat/dc_sys_time.rs +++ /dev/null @@ -1,106 +0,0 @@ -use time::OffsetDateTime; - -use crate::error::AUTDDriverError; - -use super::ECAT_DC_SYS_TIME_BASE; - -/// The system time of the Distributed Clock -/// -/// The system time is the time expressed in 1ns units with 2000-01-01 0:00:00 UTC as the reference. -/// It is expressed as a 64-bit unsigned integer and can represent about 584 years of time. -/// See [EtherCAT Distributed Clock](https://infosys.beckhoff.com/english.php?content=../content/1033/ethercatsystem/2469118347.html) for more information. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -#[repr(C)] -pub struct DcSysTime { - dc_sys_time: u64, -} - -impl DcSysTime { - /// The zero point of the DcSysTime (2000-01-01 0:00:00 UTC) - pub const ZERO: Self = Self { dc_sys_time: 0 }; - - /// Returns the system time in nanoseconds - pub const fn sys_time(&self) -> u64 { - self.dc_sys_time - } - - /// Converts the system time to the UTC time - pub fn to_utc(&self) -> OffsetDateTime { - ECAT_DC_SYS_TIME_BASE + std::time::Duration::from_nanos(self.dc_sys_time) - } - - /// Creates a new instance from the UTC time - pub fn from_utc(utc: OffsetDateTime) -> Result { - Ok(Self { - dc_sys_time: u64::try_from((utc - ECAT_DC_SYS_TIME_BASE).whole_nanoseconds()) - .map_err(|_| AUTDDriverError::InvalidDateTime)?, - }) - } - - /// Returns the system time of now - pub fn now() -> Self { - Self::from_utc(OffsetDateTime::now_utc()).unwrap() - } -} - -impl std::ops::Add for DcSysTime { - type Output = Self; - - fn add(self, rhs: std::time::Duration) -> Self::Output { - Self { - dc_sys_time: self.dc_sys_time + rhs.as_nanos() as u64, - } - } -} - -impl std::ops::Sub for DcSysTime { - type Output = Self; - - fn sub(self, rhs: std::time::Duration) -> Self::Output { - Self { - dc_sys_time: self.dc_sys_time - rhs.as_nanos() as u64, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn now_dc_sys_time() { - let t = DcSysTime::now(); - assert!(t.sys_time() > 0); - } - - #[rstest::rstest] - #[test] - #[case(Ok(DcSysTime { dc_sys_time: 0 }), time::macros::datetime!(2000-01-01 0:0:0 UTC))] - #[case(Ok(DcSysTime { dc_sys_time: 1000000000 }), time::macros::datetime!(2000-01-01 0:0:1 UTC))] - #[case(Ok(DcSysTime { dc_sys_time: 31622400000000000 }), time::macros::datetime!(2001-01-01 0:0:0 UTC))] - #[case(Err(AUTDDriverError::InvalidDateTime), time::macros::datetime!(1999-01-01 0:0:1 UTC))] - #[case(Err(AUTDDriverError::InvalidDateTime), time::macros::datetime!(9999-01-01 0:0:1 UTC))] - fn from_utc(#[case] expect: Result, #[case] utc: OffsetDateTime) { - assert_eq!(expect, DcSysTime::from_utc(utc)); - } - - #[rstest::rstest] - #[test] - #[case(time::macros::datetime!(2000-01-01 0:0:1 UTC))] - #[case(time::macros::datetime!(2001-01-01 0:0:0 UTC))] - fn to_utc(#[case] utc: OffsetDateTime) { - assert_eq!(utc, DcSysTime::from_utc(utc).unwrap().to_utc()); - } - - #[test] - fn addsub() { - let utc = time::macros::datetime!(2000-01-01 0:0:0 UTC); - let t = DcSysTime::from_utc(utc); - assert!(t.is_ok()); - let t = t.unwrap() + std::time::Duration::from_secs(1); - assert_eq!(1000000000, t.sys_time()); - - let t = t - std::time::Duration::from_secs(1); - assert_eq!(0, t.sys_time()); - } -} diff --git a/autd3-driver/src/ethercat/mod.rs b/autd3-driver/src/ethercat/mod.rs index 9cc28ef7..a2b8795b 100644 --- a/autd3-driver/src/ethercat/mod.rs +++ b/autd3-driver/src/ethercat/mod.rs @@ -1,7 +1,7 @@ -mod consts; -mod dc_sys_time; mod sync_mode; -pub use consts::*; -pub use dc_sys_time::DcSysTime; +pub use autd3_core::ethercat::DcSysTime; +pub use autd3_core::ethercat::{ + ECAT_DC_SYS_TIME_BASE, EC_CYCLE_TIME_BASE, EC_INPUT_FRAME_SIZE, EC_OUTPUT_FRAME_SIZE, +}; pub use sync_mode::SyncMode; diff --git a/autd3-driver/src/firmware/cpu/datagram/mod.rs b/autd3-driver/src/firmware/cpu/datagram/mod.rs deleted file mode 100644 index 27067429..00000000 --- a/autd3-driver/src/firmware/cpu/datagram/mod.rs +++ /dev/null @@ -1,52 +0,0 @@ -mod rx; -mod tx; - -pub use rx::RxMessage; -pub use tx::TxMessage; - -#[doc(hidden)] -pub fn check_if_msg_is_processed<'a>( - tx: &'a [TxMessage], - rx: &'a [RxMessage], -) -> impl Iterator + 'a { - tx.iter() - .zip(rx.iter()) - .map(|(tx, r)| tx.header().msg_id == r.ack()) -} - -#[cfg(test)] -mod tests { - use itertools::Itertools; - use zerocopy::FromZeros; - - use super::*; - - #[rstest::fixture] - fn tx() -> Vec { - let mut tx = vec![TxMessage::new_zeroed(); 3]; - tx[0].header_mut().msg_id = 0; - tx[1].header_mut().msg_id = 1; - tx[2].header_mut().msg_id = 2; - tx - } - - #[rstest::rstest] - #[test] - #[case::success(vec![ - RxMessage::new(0,0), - RxMessage::new(0,1), - RxMessage::new(0,2), - ], vec![true, true, true])] - #[case::success(vec![ - RxMessage::new(0, 1), - RxMessage::new(0, 1), - RxMessage::new(0, 1), - ], vec![false, true, false])] - fn test_check_if_msg_is_processed( - #[case] rx: Vec, - #[case] expect: Vec, - tx: Vec, - ) { - assert_eq!(expect, check_if_msg_is_processed(&tx, &rx).collect_vec()); - } -} diff --git a/autd3-driver/src/firmware/cpu/datagram/rx.rs b/autd3-driver/src/firmware/cpu/datagram/rx.rs deleted file mode 100644 index c058c401..00000000 --- a/autd3-driver/src/firmware/cpu/datagram/rx.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::{error::AUTDDriverError, firmware::fpga::FPGAState}; -use autd3_derive::Builder; -use derive_more::Display; -use derive_new::new; -use zerocopy::{FromBytes, Immutable, IntoBytes}; - -const READS_FPGA_STATE_ENABLED_BIT: u8 = 7; -const READS_FPGA_STATE_ENABLED: u8 = 1 << READS_FPGA_STATE_ENABLED_BIT; - -/// PDO input data representation -#[derive( - Clone, Copy, PartialEq, Eq, Debug, new, Builder, IntoBytes, Immutable, FromBytes, Display, -)] -#[display("{:?}", self)] -#[repr(C)] -pub struct RxMessage { - #[get] - /// Received data - data: u8, - #[get] - /// Acknowledgement - ack: u8, -} - -impl From<&RxMessage> for Option { - fn from(msg: &RxMessage) -> Self { - if msg.data & READS_FPGA_STATE_ENABLED != 0 { - Some(FPGAState { state: msg.data }) - } else { - None - } - } -} - -impl From<&RxMessage> for Result<(), AUTDDriverError> { - fn from(msg: &RxMessage) -> Self { - if msg.ack & 0x80 != 0 { - return Err(AUTDDriverError::firmware_err(msg.ack)); - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use std::mem::offset_of; - use std::mem::size_of; - - use super::*; - - #[test] - fn test_message_size() { - assert_eq!(2, size_of::()); - assert_eq!(0, offset_of!(RxMessage, data)); - assert_eq!(1, offset_of!(RxMessage, ack)); - } -} diff --git a/autd3-driver/src/firmware/cpu/datagram/tx.rs b/autd3-driver/src/firmware/cpu/datagram/tx.rs deleted file mode 100644 index edf74d71..00000000 --- a/autd3-driver/src/firmware/cpu/datagram/tx.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::{ethercat::EC_OUTPUT_FRAME_SIZE, firmware::cpu::Header}; - -use autd3_derive::Builder; -use derive_more::Display; -use zerocopy::{FromZeros, Immutable, IntoBytes}; - -const PAYLOAD_SIZE: usize = EC_OUTPUT_FRAME_SIZE - std::mem::size_of::
(); - -/// PDO output data representation -#[repr(C)] -#[derive(Clone, Debug, PartialEq, Eq, IntoBytes, Immutable, FromZeros, Builder, Display)] -#[display("({:?}, TAG: {:#04X})", header, (payload[0] & 0xFF) as u8)] -pub struct TxMessage { - #[get(ref, ref_mut, no_doc)] - header: Header, - payload: [u16; PAYLOAD_SIZE / size_of::()], // use u16 for alignment -} - -impl TxMessage { - #[doc(hidden)] - #[must_use] - pub fn payload(&self) -> &[u8] { - self.payload.as_bytes() - } - - #[doc(hidden)] - #[must_use] - pub fn payload_mut(&mut self) -> &mut [u8] { - self.payload.as_mut_bytes() - } -} diff --git a/autd3-driver/src/firmware/cpu/header.rs b/autd3-driver/src/firmware/cpu/header.rs deleted file mode 100644 index 195a45e7..00000000 --- a/autd3-driver/src/firmware/cpu/header.rs +++ /dev/null @@ -1,29 +0,0 @@ -pub(crate) const MSG_ID_MAX: u8 = 0x7F; - -use derive_more::Debug; -use zerocopy::{FromZeros, Immutable, IntoBytes}; - -#[doc(hidden)] -#[repr(C, align(2))] -#[derive(Clone, Debug, PartialEq, Eq, IntoBytes, Immutable, FromZeros)] -pub struct Header { - pub msg_id: u8, - #[debug(ignore)] - __: u8, - pub slot_2_offset: u16, -} - -#[cfg(test)] -mod tests { - use std::mem::offset_of; - use std::mem::size_of; - - use super::*; - - #[test] - fn test_size() { - assert_eq!(4, size_of::
()); - assert_eq!(0, offset_of!(Header, msg_id)); - assert_eq!(2, offset_of!(Header, slot_2_offset)); - } -} diff --git a/autd3-driver/src/firmware/cpu/mod.rs b/autd3-driver/src/firmware/cpu/mod.rs index 70c95634..fc9393a8 100644 --- a/autd3-driver/src/firmware/cpu/mod.rs +++ b/autd3-driver/src/firmware/cpu/mod.rs @@ -1,7 +1,63 @@ -mod datagram; mod gain_stm_mode; -mod header; -pub use datagram::*; +pub use autd3_core::link::{Header, RxMessage, TxMessage}; pub use gain_stm_mode::*; -pub use header::*; + +use crate::error::AUTDDriverError; + +pub(crate) const MSG_ID_MAX: u8 = 0x7F; + +#[doc(hidden)] +pub fn check_firmware_err(msg: &RxMessage) -> Result<(), AUTDDriverError> { + if msg.ack() & 0x80 != 0 { + return Err(AUTDDriverError::firmware_err(msg.ack())); + } + Ok(()) +} + +#[doc(hidden)] +pub fn check_if_msg_is_processed<'a>( + tx: &'a [TxMessage], + rx: &'a [RxMessage], +) -> impl Iterator + 'a { + tx.iter() + .zip(rx.iter()) + .map(|(tx, r)| tx.header().msg_id == r.ack()) +} + +#[cfg(test)] +mod tests { + use itertools::Itertools; + use zerocopy::FromZeros; + + use super::*; + + #[rstest::fixture] + fn tx() -> Vec { + let mut tx = vec![TxMessage::new_zeroed(); 3]; + tx[0].header_mut().msg_id = 0; + tx[1].header_mut().msg_id = 1; + tx[2].header_mut().msg_id = 2; + tx + } + + #[rstest::rstest] + #[test] + #[case::success(vec![ + RxMessage::new(0,0), + RxMessage::new(0,1), + RxMessage::new(0,2), + ], vec![true, true, true])] + #[case::success(vec![ + RxMessage::new(0, 1), + RxMessage::new(0, 1), + RxMessage::new(0, 1), + ], vec![false, true, false])] + fn test_check_if_msg_is_processed( + #[case] rx: Vec, + #[case] expect: Vec, + tx: Vec, + ) { + assert_eq!(expect, check_if_msg_is_processed(&tx, &rx).collect_vec()); + } +} diff --git a/autd3-driver/src/firmware/fpga/drive.rs b/autd3-driver/src/firmware/fpga/drive.rs deleted file mode 100644 index 19286334..00000000 --- a/autd3-driver/src/firmware/fpga/drive.rs +++ /dev/null @@ -1,118 +0,0 @@ -use autd3_derive::Builder; - -use super::{EmitIntensity, Phase}; - -use derive_new::new; -use zerocopy::{Immutable, IntoBytes}; - -/// A container for the phase and intensity of the ultrasound. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Builder, new, IntoBytes, Immutable)] -#[repr(C)] -pub struct Drive { - #[get] - /// The phase of the ultrasound. - phase: Phase, - #[get] - /// The intensity of the ultrasound. - intensity: EmitIntensity, -} - -impl Drive { - /// A [`Drive`] with a phase of [`Phase::ZERO`] and an intensity of [`EmitIntensity::MIN`]. - pub const NULL: Self = Self { - phase: Phase::ZERO, - intensity: EmitIntensity::MIN, - }; -} - -impl From<(Phase, EmitIntensity)> for Drive { - fn from((phase, intensity): (Phase, EmitIntensity)) -> Self { - Self { phase, intensity } - } -} - -impl From<(EmitIntensity, Phase)> for Drive { - fn from((intensity, phase): (EmitIntensity, Phase)) -> Self { - Self { phase, intensity } - } -} - -impl From for Drive { - fn from(intensity: EmitIntensity) -> Self { - Self { - phase: Phase::ZERO, - intensity, - } - } -} - -impl From for Drive { - fn from(phase: Phase) -> Self { - Self { - phase, - intensity: EmitIntensity::MAX, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[rstest::rstest] - #[test] - #[case( - Drive::new(Phase::new(0x01), EmitIntensity::new(0x02)), - (Phase::new(0x01), EmitIntensity::new(0x02)) - )] - #[case( - Drive::new(Phase::new(0x01), EmitIntensity::new(0x02)), - (EmitIntensity::new(0x02), Phase::new(0x01)) - )] - #[case( - Drive::new(Phase::ZERO, EmitIntensity::new(0x01)), - EmitIntensity::new(0x01) - )] - #[case(Drive::new(Phase::new(0x01), EmitIntensity::MAX), Phase::new(0x01))] - fn from(#[case] expected: Drive, #[case] target: impl Into) { - assert_eq!(expected, target.into()); - } - - #[rstest::rstest] - #[test] - #[case( - EmitIntensity::new(0x00), - Drive::new(Phase::ZERO, EmitIntensity::new(0x00)) - )] - #[case( - EmitIntensity::new(0x01), - Drive::new(Phase::ZERO, EmitIntensity::new(0x01)) - )] - #[case( - EmitIntensity::new(0xFF), - Drive::new(Phase::ZERO, EmitIntensity::new(0xFF)) - )] - fn test_intensity(#[case] expected: EmitIntensity, #[case] target: Drive) { - assert_eq!(expected, target.intensity()); - } - - #[rstest::rstest] - #[test] - #[case(Phase::ZERO, Drive::new(Phase::ZERO, EmitIntensity::new(0x00)))] - #[case(Phase::new(1), Drive::new(Phase::new(1), EmitIntensity::new(0x00)))] - #[case( - Phase::new(0xFF), - Drive::new(Phase::new(0xFF), EmitIntensity::new(0x00)) - )] - fn test_phase(#[case] expected: Phase, #[case] target: Drive) { - assert_eq!(expected, target.phase()); - } - - #[test] - fn test_null() { - assert_eq!( - Drive::new(Phase::ZERO, EmitIntensity::new(0x00)), - Drive::NULL - ); - } -} diff --git a/autd3-driver/src/firmware/fpga/emit_intensity.rs b/autd3-driver/src/firmware/fpga/emit_intensity.rs deleted file mode 100644 index 18d36a5a..00000000 --- a/autd3-driver/src/firmware/fpga/emit_intensity.rs +++ /dev/null @@ -1,161 +0,0 @@ -use autd3_derive::Builder; - -use derive_more::Debug; -use derive_new::new; -use zerocopy::{Immutable, IntoBytes}; - -/// The intensity of the ultrasound. -#[derive( - Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Builder, new, IntoBytes, Immutable, -)] -#[debug("{:#04X}", self.value)] -#[repr(C)] -pub struct EmitIntensity { - #[get] - /// The value of the intensity. - value: u8, -} - -impl EmitIntensity { - /// Maximum intensity. - pub const MAX: EmitIntensity = EmitIntensity { value: 255 }; - /// Minimum intensity. - pub const MIN: EmitIntensity = EmitIntensity { value: 0 }; -} - -impl From for EmitIntensity { - fn from(v: u8) -> Self { - Self::new(v) - } -} - -impl std::ops::Div for EmitIntensity { - type Output = Self; - - fn div(self, rhs: u8) -> Self::Output { - Self::new(self.value / rhs) - } -} - -impl std::ops::Mul for EmitIntensity { - type Output = EmitIntensity; - - fn mul(self, rhs: u8) -> Self::Output { - Self::Output::new(self.value.saturating_mul(rhs)) - } -} - -impl std::ops::Mul for u8 { - type Output = EmitIntensity; - - fn mul(self, rhs: EmitIntensity) -> Self::Output { - Self::Output::new(self.saturating_mul(rhs.value)) - } -} - -impl std::ops::Add for EmitIntensity { - type Output = Self; - - fn add(self, rhs: EmitIntensity) -> Self::Output { - Self::new(self.value.saturating_add(rhs.value)) - } -} - -impl std::ops::Sub for EmitIntensity { - type Output = Self; - - fn sub(self, rhs: EmitIntensity) -> Self::Output { - Self::new(self.value.saturating_sub(rhs.value)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[rstest::rstest] - #[test] - #[case::value_0(0x00)] - #[case::value_1(0x01)] - #[case::value_ff(0xFF)] - fn test_new(#[case] expected: u8) { - assert_eq!(expected, EmitIntensity::new(expected).value(),); - } - - #[rstest::rstest] - #[test] - #[case::value_1_1(EmitIntensity::new(0x01), EmitIntensity::new(0x01), 1)] - #[case::value_1_2(EmitIntensity::new(0x00), EmitIntensity::new(0x01), 2)] - #[case::value_ff_2(EmitIntensity::new(0x7F), EmitIntensity::new(0xFF), 2)] - fn test_div(#[case] expected: EmitIntensity, #[case] target: EmitIntensity, #[case] div: u8) { - assert_eq!(expected, target / div); - } - - #[rstest::rstest] - #[test] - #[case::value_1_1(EmitIntensity::new(0x01), EmitIntensity::new(0x01), 1)] - #[case::value_1_2(EmitIntensity::new(0x02), EmitIntensity::new(0x01), 2)] - #[case::value_7f_2(EmitIntensity::new(0xFE), EmitIntensity::new(0x7F), 2)] - #[case::value_7f_3(EmitIntensity::new(0xFF), EmitIntensity::new(0x7F), 3)] - fn test_mul(#[case] expected: EmitIntensity, #[case] target: EmitIntensity, #[case] mul: u8) { - assert_eq!(expected, target * mul); - assert_eq!(expected, mul * target); - } - - #[rstest::rstest] - #[test] - #[case::value_1_1( - EmitIntensity::new(0x02), - EmitIntensity::new(0x01), - EmitIntensity::new(0x01) - )] - #[case::value_7f_7f( - EmitIntensity::new(0xFE), - EmitIntensity::new(0x7F), - EmitIntensity::new(0x7F) - )] - #[case::value_7f_ff( - EmitIntensity::new(0xFF), - EmitIntensity::new(0x7F), - EmitIntensity::new(0xFF) - )] - fn test_add( - #[case] expected: EmitIntensity, - #[case] lhs: EmitIntensity, - #[case] rhs: EmitIntensity, - ) { - assert_eq!(expected, lhs + rhs); - } - - #[rstest::rstest] - #[test] - #[case::value_1_1( - EmitIntensity::new(0x00), - EmitIntensity::new(0x01), - EmitIntensity::new(0x01) - )] - #[case::value_7f_7f( - EmitIntensity::new(0x01), - EmitIntensity::new(0x02), - EmitIntensity::new(0x01) - )] - #[case::value_7f_ff( - EmitIntensity::new(0x00), - EmitIntensity::new(0x7F), - EmitIntensity::new(0xFF) - )] - fn test_sub( - #[case] expected: EmitIntensity, - #[case] lhs: EmitIntensity, - #[case] rhs: EmitIntensity, - ) { - assert_eq!(expected, lhs - rhs); - } - - #[test] - fn dbg() { - assert_eq!(format!("{:?}", EmitIntensity::new(0x00)), "0x00"); - assert_eq!(format!("{:?}", EmitIntensity::new(0x01)), "0x01"); - assert_eq!(format!("{:?}", EmitIntensity::new(0xFF)), "0xFF"); - } -} diff --git a/autd3-driver/src/firmware/fpga/fpga_state.rs b/autd3-driver/src/firmware/fpga/fpga_state.rs index 4eeca3b1..24970203 100644 --- a/autd3-driver/src/firmware/fpga/fpga_state.rs +++ b/autd3-driver/src/firmware/fpga/fpga_state.rs @@ -1,5 +1,6 @@ use crate::firmware::fpga::Segment; +use autd3_core::link::RxMessage; use autd3_derive::Builder; const THERMAL_ASSERT_BIT: u8 = 1 << 0; @@ -7,6 +8,7 @@ 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; /// FPGA state. #[repr(C)] @@ -61,6 +63,15 @@ impl FPGAState { pub const fn is_stm_mode(&self) -> bool { !self.is_gain_mode() } + + #[doc(hidden)] + pub fn from_rx(msg: &RxMessage) -> Option { + if msg.data() & READS_FPGA_STATE_ENABLED != 0 { + Some(FPGAState { state: msg.data() }) + } else { + None + } + } } #[cfg(test)] diff --git a/autd3-driver/src/firmware/fpga/gpio.rs b/autd3-driver/src/firmware/fpga/gpio.rs deleted file mode 100644 index 3d291088..00000000 --- a/autd3-driver/src/firmware/fpga/gpio.rs +++ /dev/null @@ -1,27 +0,0 @@ -/// GPIO output pin. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -pub enum GPIOOut { - /// Output 0 - O0 = 0, - /// Output 1 - O1 = 1, - /// Output 2 - O2 = 2, - /// Output 3 - O3 = 3, -} - -/// GPIO input pin. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -pub enum GPIOIn { - /// Input 0 - I0 = 0, - /// Input 1 - I1 = 1, - /// Input 2 - I2 = 2, - /// Input 3 - I3 = 3, -} diff --git a/autd3-driver/src/firmware/fpga/loop_behavior.rs b/autd3-driver/src/firmware/fpga/loop_behavior.rs deleted file mode 100644 index e36a8de6..00000000 --- a/autd3-driver/src/firmware/fpga/loop_behavior.rs +++ /dev/null @@ -1,115 +0,0 @@ -use autd3_derive::Builder; -use derive_more::Debug; - -/// The behavior of the loop for [`Modulation`], [`FociSTM`], and [`GainSTM`]. -/// -/// [`Modulation`]: crate::datagram::Modulation -/// [`FociSTM`]: crate::datagram::FociSTM -/// [`GainSTM`]: crate::datagram::GainSTM -#[derive(Clone, Copy, PartialEq, Eq, Debug, Builder)] -#[debug("{}", match self.rep { 0xFFFF => "Infinite".to_string(), 0 => "Once".to_string(), i => format!("Finite({})", i + 1) })] -#[repr(C)] -pub struct LoopBehavior { - #[get(no_doc)] - pub(crate) rep: u16, -} - -pub trait IntoLoopBehaviorFinite { - type Output; - fn into_loop_behavior(self) -> Self::Output; -} - -impl IntoLoopBehaviorFinite for u16 { - type Output = Option; - fn into_loop_behavior(self) -> Self::Output { - if self == 0 { - None - } else { - Some(LoopBehavior { rep: self - 1 }) - } - } -} - -impl IntoLoopBehaviorFinite for std::num::NonZeroU16 { - type Output = LoopBehavior; - fn into_loop_behavior(self) -> Self::Output { - LoopBehavior { - rep: self.get() - 1, - } - } -} - -impl LoopBehavior { - /// Creates a new [`LoopBehavior`] with an infinite loop. - pub const fn infinite() -> Self { - LoopBehavior { rep: 0xFFFF } - } - - /// Creates a new [`LoopBehavior`] with a finite loop. The value must not be zero. - /// - /// # Example - /// - /// ``` - /// # use autd3_driver::firmware::fpga::LoopBehavior; - /// # use std::num::NonZeroU16; - /// let finite: Option = LoopBehavior::finite(1); - /// assert!(finite.is_some()); - /// let finite: Option = LoopBehavior::finite(0); - /// assert!(finite.is_none()); - /// let finite: LoopBehavior = LoopBehavior::finite(NonZeroU16::new(1).unwrap()); - /// ``` - /// - pub fn finite(repeat: T) -> T::Output { - repeat.into_loop_behavior() - } - - /// Creates a new [`LoopBehavior`] with a single loop. - pub const fn once() -> Self { - Self { rep: 0 } - } -} - -#[cfg(test)] -mod tests { - - use super::*; - - #[rstest::rstest] - #[test] - #[case::infinite(0xFFFF, LoopBehavior::infinite())] - #[case::finite(0x1233, LoopBehavior::finite(0x1234).unwrap())] - #[case::once(0x0000, LoopBehavior::once())] - fn loop_behavior(#[case] expect: u16, #[case] target: LoopBehavior) { - assert_eq!(expect, target.rep()); - } - - #[rstest::rstest] - #[test] - #[case(Some(LoopBehavior{ rep: 0 }), 1)] - #[case(Some(LoopBehavior{ rep: 0xFFFE }), 0xFFFF)] - #[case(None, 0)] - fn into_loop_behavior_u16(#[case] expect: Option, #[case] rep: u16) { - assert_eq!(expect, LoopBehavior::finite(rep)); - } - - #[rstest::rstest] - #[test] - #[case(LoopBehavior{ rep: 0 }, std::num::NonZeroU16::new(1).unwrap())] - #[case(LoopBehavior{ rep: 0xFFFE }, std::num::NonZeroU16::new(0xFFFF).unwrap())] - fn into_loop_behavior_non_zero_u16( - #[case] expect: LoopBehavior, - #[case] rep: std::num::NonZeroU16, - ) { - assert_eq!(expect, LoopBehavior::finite(rep)); - } - - #[test] - fn debug() { - assert_eq!(format!("{:?}", LoopBehavior::infinite()), "Infinite"); - assert_eq!(format!("{:?}", LoopBehavior::once()), "Once"); - assert_eq!( - format!("{:?}", LoopBehavior::finite(0x1234).unwrap()), - "Finite(4660)" - ); - } -} diff --git a/autd3-driver/src/firmware/fpga/mod.rs b/autd3-driver/src/firmware/fpga/mod.rs index 059c595f..8ab17627 100644 --- a/autd3-driver/src/firmware/fpga/mod.rs +++ b/autd3-driver/src/firmware/fpga/mod.rs @@ -1,37 +1,21 @@ mod debug_type; -mod drive; -mod emit_intensity; mod fpga_state; -mod gpio; -mod loop_behavior; -mod phase; -mod sampling_config; -mod segment; mod silencer_target; mod stm_focus; -mod transition_mode; + +pub use autd3_core::{ + datagram::{GPIOIn, GPIOOut, Segment, TransitionMode, TRANSITION_MODE_NONE}, + gain::{Drive, EmitIntensity, Phase}, + modulation::{LoopBehavior, SamplingConfig}, +}; pub use debug_type::DebugType; pub(crate) use debug_type::DebugValue; -pub use drive::Drive; -pub use emit_intensity::EmitIntensity; pub use fpga_state::FPGAState; -pub use gpio::*; -pub use loop_behavior::LoopBehavior; -pub use phase::Phase; -pub use sampling_config::SamplingConfig; -pub use segment::Segment; pub use silencer_target::SilencerTarget; pub(crate) use stm_focus::STMFocus; -pub use transition_mode::*; - -use crate::{ - defined::{mm, Freq}, - ethercat::DcSysTime, -}; -/// FPGA main clock frequency. -pub const FPGA_MAIN_CLK_FREQ: Freq = Freq { freq: 10240000 }; +use crate::{defined::mm, ethercat::DcSysTime}; /// The unit of the fixed-point number used in the [`FociSTM`]. /// diff --git a/autd3-driver/src/firmware/fpga/phase.rs b/autd3-driver/src/firmware/fpga/phase.rs deleted file mode 100644 index 0d743008..00000000 --- a/autd3-driver/src/firmware/fpga/phase.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::defined::{rad, Angle, Complex, PI}; - -use autd3_derive::Builder; - -use derive_more::Debug; -use derive_new::new; -use nalgebra::ComplexField; -use zerocopy::{Immutable, IntoBytes}; - -/// The phase of the ultrasound. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Builder, new, IntoBytes, Immutable)] -#[repr(C)] -#[debug("{:#04X}", self.value)] -pub struct Phase { - #[get] - /// The value of the phase. - value: u8, -} - -impl Phase { - /// A phase of zero. - pub const ZERO: Self = Self { value: 0 }; - /// A phase of π. - pub const PI: Self = Self { value: 128 }; - - /// Converts the phase into a radian. - pub fn radian(&self) -> f32 { - self.value as f32 / 256.0 * 2.0 * PI - } -} - -impl From for Phase { - fn from(v: u8) -> Self { - Self::new(v) - } -} - -impl From for Phase { - fn from(v: Angle) -> Self { - Self { - value: (((v.radian() / (2.0 * PI) * 256.0).round() as i32) & 0xFF) as _, - } - } -} - -impl From for Phase { - fn from(v: Complex) -> Self { - Self::from(v.argument() * rad) - } -} - -impl std::ops::Add for Phase { - type Output = Phase; - - fn add(self, rhs: Phase) -> Self::Output { - Self::Output::new(self.value.wrapping_add(rhs.value)) - } -} - -impl std::ops::Sub for Phase { - type Output = Phase; - - fn sub(self, rhs: Phase) -> Self::Output { - Self::Output::new(self.value.wrapping_sub(rhs.value)) - } -} - -impl std::ops::Mul for Phase { - type Output = Phase; - - fn mul(self, rhs: u8) -> Self::Output { - Self::Output::new(self.value.wrapping_mul(rhs)) - } -} - -impl std::ops::Div for Phase { - type Output = Phase; - - fn div(self, rhs: u8) -> Self::Output { - Self::Output::new(self.value.wrapping_div(rhs)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[rstest::rstest] - #[test] - #[case(0x00)] - #[case(0x01)] - #[case(0xFF)] - fn new(#[case] expected: u8) { - assert_eq!(expected, Phase::from(expected).value()); - } - - #[rstest::rstest] - #[test] - #[case(Phase::new(0x02), Phase::new(0x01), Phase::new(0x01))] - #[case(Phase::new(0xFE), Phase::new(0x7F), Phase::new(0x7F))] - #[case(Phase::new(0x7E), Phase::new(0x7F), Phase::new(0xFF))] - fn add(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: Phase) { - assert_eq!(expected, lhs + rhs); - } - - #[rstest::rstest] - #[test] - #[case(Phase::ZERO, Phase::new(0x01), Phase::new(0x01))] - #[case(Phase::new(0x01), Phase::new(0x02), Phase::new(0x01))] - #[case(Phase::new(0x80), Phase::new(0x7F), Phase::new(0xFF))] - fn sub(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: Phase) { - assert_eq!(expected, lhs - rhs); - } - - #[rstest::rstest] - #[test] - #[case(Phase::new(0x02), Phase::new(0x01), 2)] - #[case(Phase::new(0xFE), Phase::new(0x7F), 2)] - #[case(Phase::ZERO, Phase::new(0x80), 2)] - fn mul(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: u8) { - assert_eq!(expected, lhs * rhs); - } - - #[rstest::rstest] - #[test] - #[case(Phase::new(0x01), Phase::new(0x02), 2)] - #[case(Phase::new(0x7F), Phase::new(0xFE), 2)] - #[case(Phase::ZERO, Phase::new(0x01), 2)] - fn div(#[case] expected: Phase, #[case] lhs: Phase, #[case] rhs: u8) { - assert_eq!(expected, lhs / rhs); - } - - #[rstest::rstest] - #[test] - #[case(0.0, 0)] - #[case(2.0 * PI / 256.0 * 128.0, 128)] - #[case(2.0 * PI / 256.0 * 255.0, 255)] - fn radian(#[case] expect: f32, #[case] value: u8) { - approx::assert_abs_diff_eq!(expect, Phase::new(value).radian()); - } - - #[test] - fn dbg() { - assert_eq!(format!("{:?}", Phase::ZERO), "0x00"); - assert_eq!(format!("{:?}", Phase::new(0x01)), "0x01"); - assert_eq!(format!("{:?}", Phase::new(0xFF)), "0xFF"); - } -} diff --git a/autd3-driver/src/firmware/fpga/sampling_config.rs b/autd3-driver/src/firmware/fpga/sampling_config.rs deleted file mode 100644 index 04e198ac..00000000 --- a/autd3-driver/src/firmware/fpga/sampling_config.rs +++ /dev/null @@ -1,301 +0,0 @@ -use std::fmt::Debug; - -use autd3_derive::Builder; - -use crate::{ - defined::{ultrasound_freq, Freq, Hz}, - error::AUTDDriverError, - utils::float::is_integer, -}; - -/// The configuration for sampling. -#[derive(Clone, Copy, Debug, PartialEq, Builder)] -#[repr(C)] -pub struct SamplingConfig { - #[get] - /// The division number of the sampling frequency. - /// - /// The sampling frequency is [`ultrasound_freq`] / `division`. - division: u16, -} - -pub trait IntoSamplingConfig { - fn into_sampling_config(self) -> Result; -} - -impl IntoSamplingConfig for u16 { - fn into_sampling_config(self) -> Result { - if self == 0 { - return Err(AUTDDriverError::SamplingDivisionInvalid(self)); - } - Ok(SamplingConfig { division: self }) - } -} - -impl IntoSamplingConfig for Freq { - fn into_sampling_config(self) -> Result { - const FREQ_MIN: Freq = Freq { freq: 1 }; - if !(FREQ_MIN..=ultrasound_freq()).contains(&self) { - return Err(AUTDDriverError::SamplingFreqOutOfRange( - self, - FREQ_MIN, - ultrasound_freq(), - )); - } - if ultrasound_freq().hz() % self.hz() != 0 { - return Err(AUTDDriverError::SamplingFreqInvalid(self)); - } - Ok(SamplingConfig { - division: (ultrasound_freq().hz() / self.hz()) as _, - }) - } -} - -impl IntoSamplingConfig for Freq { - fn into_sampling_config(self) -> Result { - let freq_max = ultrasound_freq().hz() as f32 * Hz; - let freq_min = freq_max / u16::MAX as f32; - if !(freq_min..=freq_max).contains(&self) { - return Err(AUTDDriverError::SamplingFreqOutOfRangeF( - self, freq_min, freq_max, - )); - } - let div = ultrasound_freq().hz() as f32 / self.hz(); - if !is_integer(div as _) { - return Err(AUTDDriverError::SamplingFreqInvalidF(self)); - } - Ok(SamplingConfig { division: div as _ }) - } -} - -#[cfg(not(feature = "dynamic_freq"))] -impl IntoSamplingConfig for std::time::Duration { - fn into_sampling_config(self) -> Result { - use crate::defined::ultrasound_period; - - let period_min = ultrasound_period(); - let period_max = std::time::Duration::from_micros( - u16::MAX as u64 * ultrasound_period().as_micros() as u64, - ); - if !(period_min..=period_max).contains(&self) { - return Err(AUTDDriverError::SamplingPeriodOutOfRange( - self, period_min, period_max, - )); - } - if self.as_nanos() % ultrasound_period().as_nanos() != 0 { - return Err(AUTDDriverError::SamplingPeriodInvalid(self)); - } - Ok(SamplingConfig { - division: (self.as_nanos() / ultrasound_period().as_nanos()) as _, - }) - } -} - -pub trait IntoSamplingConfigNearest { - fn into_sampling_config_nearest(self) -> SamplingConfig; -} - -impl IntoSamplingConfigNearest for Freq { - fn into_sampling_config_nearest(self) -> SamplingConfig { - SamplingConfig::new( - (ultrasound_freq().hz() as f32 / self.hz()) - .clamp(1.0, u16::MAX as f32) - .round() as u16, - ) - .unwrap() - } -} - -impl IntoSamplingConfigNearest for Freq { - fn into_sampling_config_nearest(self) -> SamplingConfig { - SamplingConfig::new( - (ultrasound_freq().hz() + self.hz() / 2) - .checked_div(self.hz()) - .unwrap_or(u32::MAX) - .clamp(1, u16::MAX as u32) as u16, - ) - .unwrap() - } -} - -#[cfg(not(feature = "dynamic_freq"))] -impl IntoSamplingConfigNearest for std::time::Duration { - fn into_sampling_config_nearest(self) -> SamplingConfig { - use crate::defined::ultrasound_period; - - SamplingConfig::new( - ((self.as_nanos() + ultrasound_period().as_nanos() / 2) - / ultrasound_period().as_nanos()) - .clamp(1, u16::MAX as u128) as u16, - ) - .unwrap() - } -} - -impl SamplingConfig { - /// A [`SamplingConfig`] of 40kHz. - pub const FREQ_40K: SamplingConfig = SamplingConfig { division: 1 }; - /// A [`SamplingConfig`] of 4kHz. - pub const FREQ_4K: SamplingConfig = SamplingConfig { division: 10 }; - /// A [`SamplingConfig`] of the minimum frequency. - pub const FREQ_MIN: SamplingConfig = SamplingConfig { division: u16::MAX }; - - /// Creates a new [`SamplingConfig`]. - pub fn new(value: impl IntoSamplingConfig) -> Result { - value.into_sampling_config() - } - - /// Creates a new [`SamplingConfig`] with the nearest frequency or period value of the possible values. - pub fn new_nearest(value: impl IntoSamplingConfigNearest) -> Self { - value.into_sampling_config_nearest() - } - - /// Gets the sampling frequency. - pub fn freq(&self) -> Freq { - ultrasound_freq().hz() as f32 / self.division() as f32 * Hz - } - - /// Gets the sampling period. - #[cfg(not(feature = "dynamic_freq"))] - pub fn period(&self) -> std::time::Duration { - crate::defined::ultrasound_period() * self.division() as u32 - } -} - -// GRCOV_EXCL_START -impl TryInto for Freq { - type Error = AUTDDriverError; - - fn try_into(self) -> Result { - SamplingConfig::new(self) - } -} - -impl TryInto for Freq { - type Error = AUTDDriverError; - - fn try_into(self) -> Result { - SamplingConfig::new(self) - } -} - -#[cfg(not(feature = "dynamic_freq"))] -impl TryInto for std::time::Duration { - type Error = AUTDDriverError; - - fn try_into(self) -> Result { - SamplingConfig::new(self) - } -} -// GRCOV_EXCL_STOP - -#[cfg(test)] -mod tests { - use crate::defined::Hz; - - #[cfg(not(feature = "dynamic_freq"))] - use crate::defined::ultrasound_period; - #[cfg(not(feature = "dynamic_freq"))] - use std::time::Duration; - - use super::*; - - #[rstest::rstest] - #[test] - #[case(Ok(1), 1)] - #[case(Ok(u16::MAX), u16::MAX)] - #[case(Ok(1), 40000 * Hz)] - #[case(Ok(10), 4000 * Hz)] - #[case(Ok(1), 40000. * Hz)] - #[case(Ok(10), 4000. * Hz)] - #[case(Err(AUTDDriverError::SamplingDivisionInvalid(0)), 0)] - #[case(Err(AUTDDriverError::SamplingFreqInvalid(ultrasound_freq() - 1 * Hz)), ultrasound_freq() - 1 * Hz)] - #[case(Err(AUTDDriverError::SamplingFreqOutOfRange(0 * Hz, 1 * Hz, ultrasound_freq())), 0 * Hz)] - #[case(Err(AUTDDriverError::SamplingFreqOutOfRange(ultrasound_freq() + 1 * Hz, 1 * Hz, ultrasound_freq())), ultrasound_freq() + 1 * Hz)] - #[case(Err(AUTDDriverError::SamplingFreqInvalidF((ultrasound_freq().hz() as f32 - 1.) * Hz)), (ultrasound_freq().hz() as f32 - 1.) * Hz)] - #[case(Err(AUTDDriverError::SamplingFreqOutOfRangeF(0. * Hz, ultrasound_freq().hz() as f32 * Hz / u16::MAX as f32, ultrasound_freq().hz() as f32 * Hz)), 0. * Hz)] - #[case(Err(AUTDDriverError::SamplingFreqOutOfRangeF(40000. * Hz + 1. * Hz, ultrasound_freq().hz() as f32 * Hz / u16::MAX as f32, ultrasound_freq().hz() as f32 * Hz)), 40000. * Hz + 1. * Hz)] - #[cfg_attr(not(feature = "dynamic_freq"), case(Ok(1), Duration::from_micros(25)))] - #[cfg_attr( - not(feature = "dynamic_freq"), - case(Ok(10), Duration::from_micros(250)) - )] - #[cfg_attr(not(feature = "dynamic_freq"), case(Err(AUTDDriverError::SamplingPeriodInvalid(Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64) - Duration::from_nanos(1))), Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64) - Duration::from_nanos(1)))] - #[cfg_attr(not(feature = "dynamic_freq"), case(Err(AUTDDriverError::SamplingPeriodOutOfRange(ultrasound_period() / 2, ultrasound_period(), Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64))), ultrasound_period() / 2))] - #[cfg_attr(not(feature = "dynamic_freq"), case(Err(AUTDDriverError::SamplingPeriodOutOfRange(Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64) * 2, ultrasound_period(), Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64))), Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64) * 2))] - fn division( - #[case] expect: Result, - #[case] value: impl IntoSamplingConfig, - ) { - assert_eq!(expect, SamplingConfig::new(value).map(|c| c.division())); - } - - #[rstest::rstest] - #[test] - #[case(Ok(40000. * Hz), 1)] - #[case(Ok(0.61036086 * Hz), u16::MAX)] - #[case(Ok(40000. * Hz), 40000 * Hz)] - #[case(Ok(4000. * Hz), 4000 * Hz)] - #[case(Ok(40000. * Hz), 40000. * Hz)] - #[case(Ok(4000. * Hz), 4000. * Hz)] - #[cfg_attr(not(feature = "dynamic_freq"), case(Ok(40000. * Hz), Duration::from_micros(25)))] - #[cfg_attr(not(feature = "dynamic_freq"), case(Ok(4000. * Hz), Duration::from_micros(250)))] - fn freq( - #[case] expect: Result, AUTDDriverError>, - #[case] value: impl IntoSamplingConfig, - ) { - assert_eq!(expect, SamplingConfig::new(value).map(|c| c.freq())); - } - - #[cfg(not(feature = "dynamic_freq"))] - #[rstest::rstest] - #[test] - #[case(Ok(Duration::from_micros(25)), 1)] - #[case(Ok(Duration::from_micros(1638375)), u16::MAX)] - #[case(Ok(Duration::from_micros(25)), 40000 * Hz)] - #[case(Ok(Duration::from_micros(250)), 4000 * Hz)] - #[case(Ok(Duration::from_micros(25)), 40000. * Hz)] - #[case(Ok(Duration::from_micros(250)), 4000. * Hz)] - #[case(Ok(Duration::from_micros(25)), Duration::from_micros(25))] - #[case(Ok(Duration::from_micros(250)), Duration::from_micros(250))] - fn period( - #[case] expect: Result, - #[case] value: impl IntoSamplingConfig, - ) { - assert_eq!(expect, SamplingConfig::new(value).map(|c| c.period())); - } - - #[rstest::rstest] - #[test] - #[case::min(u16::MAX, (40000. / u16::MAX as f32) * Hz)] - #[case::max(1, 40000. * Hz)] - #[case::not_supported_max(1, (ultrasound_freq().hz() as f32 - 1.) * Hz)] - #[case::out_of_range_min(u16::MAX, 0. * Hz)] - #[case::out_of_range_max(1, 40000. * Hz + 1. * Hz)] - fn from_freq_f32_nearest(#[case] expected: u16, #[case] freq: Freq) { - assert_eq!(expected, SamplingConfig::new_nearest(freq).division()); - } - - #[rstest::rstest] - #[test] - #[case::min(40000, 1 * Hz)] - #[case::max(1, 40000 * Hz)] - #[case::not_supported_max(1, ultrasound_freq() - 1 * Hz)] - #[case::out_of_range_min(0xFFFF, 0 * Hz)] - #[case::out_of_range_max(1, ultrasound_freq() + 1 * Hz)] - fn from_freq_u32_nearest(#[case] expected: u16, #[case] freq: Freq) { - assert_eq!(expected, SamplingConfig::new_nearest(freq).division()); - } - - #[cfg(not(feature = "dynamic_freq"))] - #[rstest::rstest] - #[test] - #[case::min(1, ultrasound_period())] - #[case::max(u16::MAX, Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64))] - #[case::not_supported_max(u16::MAX, Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64) - Duration::from_nanos(1))] - #[case::out_of_range_min(1, ultrasound_period() / 2)] - #[case::out_of_range_max(u16::MAX, Duration::from_micros(u16::MAX as u64 * ultrasound_period().as_micros() as u64) * 2)] - fn from_period_nearest(#[case] expected: u16, #[case] p: Duration) { - assert_eq!(expected, SamplingConfig::new_nearest(p).division()); - } -} diff --git a/autd3-driver/src/firmware/fpga/segment.rs b/autd3-driver/src/firmware/fpga/segment.rs deleted file mode 100644 index c131ae9d..00000000 --- a/autd3-driver/src/firmware/fpga/segment.rs +++ /dev/null @@ -1,10 +0,0 @@ -/// Segment of the FPGA memory -#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[repr(u8)] -pub enum Segment { - /// Segment 0 - #[default] - S0 = 0, - /// Segment 1 - S1 = 1, -} diff --git a/autd3-driver/src/firmware/fpga/transition_mode.rs b/autd3-driver/src/firmware/fpga/transition_mode.rs deleted file mode 100644 index fc6fa459..00000000 --- a/autd3-driver/src/firmware/fpga/transition_mode.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::ethercat::DcSysTime; - -use super::GPIOIn; - -pub(crate) const TRANSITION_MODE_SYNC_IDX: u8 = 0x00; -pub(crate) const TRANSITION_MODE_SYS_TIME: u8 = 0x01; -pub(crate) const TRANSITION_MODE_GPIO: u8 = 0x02; -pub(crate) const TRANSITION_MODE_EXT: u8 = 0xF0; -pub(crate) const TRANSITION_MODE_NONE: u8 = 0xFE; -pub(crate) const TRANSITION_MODE_IMMEDIATE: u8 = 0xFF; - -/// Transition mode of segment -#[non_exhaustive] -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum TransitionMode { - /// Transites when the sampling index in the destination segment is 0. - SyncIdx, - /// Transites when the system time is the specified time. - SysTime(DcSysTime), - /// Transites when the specified GPIO pin is high. - GPIO(GPIOIn), - /// Transites to the next segment automatically when the data in the current segment is finished. - Ext, - /// Transites immediately. - Immediate, -} - -impl TransitionMode { - pub(crate) const fn mode(&self) -> u8 { - match self { - TransitionMode::SyncIdx => TRANSITION_MODE_SYNC_IDX, - TransitionMode::SysTime(_) => TRANSITION_MODE_SYS_TIME, - TransitionMode::GPIO(_) => TRANSITION_MODE_GPIO, - TransitionMode::Ext => TRANSITION_MODE_EXT, - TransitionMode::Immediate => TRANSITION_MODE_IMMEDIATE, - } - } - - pub(crate) const fn value(&self) -> u64 { - match self { - TransitionMode::SyncIdx | TransitionMode::Ext | TransitionMode::Immediate => 0, - TransitionMode::GPIO(gpio) => *gpio as u64, - TransitionMode::SysTime(time) => time.sys_time(), - } - } -} diff --git a/autd3-driver/src/firmware/operation/boxed.rs b/autd3-driver/src/firmware/operation/boxed.rs new file mode 100644 index 00000000..888ae2e8 --- /dev/null +++ b/autd3-driver/src/firmware/operation/boxed.rs @@ -0,0 +1,67 @@ +use autd3_core::{ + datagram::{NullOp, Operation}, + geometry::Device, +}; + +use crate::error::AUTDDriverError; + +trait DOperation: Send + Sync { + fn required_size(&self, device: &Device) -> usize; + fn pack(&mut self, device: &Device, tx: &mut [u8]) -> Result; + fn is_done(&self) -> bool; +} + +impl> DOperation for O +where + AUTDDriverError: From, +{ + fn required_size(&self, device: &Device) -> usize { + O::required_size(self, device) + } + + fn pack(&mut self, device: &Device, tx: &mut [u8]) -> Result { + Ok(O::pack(self, device, tx)?) + } + + fn is_done(&self) -> bool { + O::is_done(self) + } +} + +#[doc(hidden)] +pub struct BoxedOperation { + inner: Box, +} + +impl BoxedOperation { + pub fn new + 'static>(op: O) -> Self + where + AUTDDriverError: From, + { + Self { + inner: Box::new(op), + } + } +} + +impl Operation for BoxedOperation { + type Error = AUTDDriverError; + + fn required_size(&self, device: &Device) -> usize { + self.inner.required_size(device) + } + + fn pack(&mut self, device: &Device, tx: &mut [u8]) -> Result { + self.inner.pack(device, tx) + } + + fn is_done(&self) -> bool { + self.inner.is_done() + } +} + +impl Default for BoxedOperation { + fn default() -> Self { + Self::new(NullOp {}) + } +} diff --git a/autd3-driver/src/firmware/operation/clear.rs b/autd3-driver/src/firmware/operation/clear.rs index cffd0c09..a37d0a71 100644 --- a/autd3-driver/src/firmware/operation/clear.rs +++ b/autd3-driver/src/firmware/operation/clear.rs @@ -1,5 +1,6 @@ +use std::convert::Infallible; + use crate::{ - error::AUTDDriverError, firmware::operation::{Operation, TypeTag}, geometry::Device, }; @@ -22,7 +23,9 @@ pub struct ClearOp { } impl Operation for ClearOp { - fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { + type Error = Infallible; + + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { super::write_to_tx( tx, Clear { @@ -49,7 +52,7 @@ mod tests { use std::mem::size_of; use super::*; - use crate::geometry::tests::create_device; + use crate::firmware::operation::tests::create_device; const NUM_TRANS_IN_UNIT: u8 = 249; diff --git a/autd3-driver/src/firmware/operation/clock/mod.rs b/autd3-driver/src/firmware/operation/clock/mod.rs index bbb2412a..d68d934b 100644 --- a/autd3-driver/src/firmware/operation/clock/mod.rs +++ b/autd3-driver/src/firmware/operation/clock/mod.rs @@ -75,6 +75,8 @@ impl ConfigureClockOp { } impl Operation for ConfigureClockOp { + type Error = AUTDDriverError; + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { let sent = DRP_ROM_SIZE - self.remains; @@ -199,7 +201,7 @@ mod tests { use crate::{ defined::{Freq, Hz}, - geometry::tests::create_device, + firmware::operation::tests::create_device, }; use super::*; diff --git a/autd3-driver/src/firmware/operation/cpu_gpio_out.rs b/autd3-driver/src/firmware/operation/cpu_gpio_out.rs index 33832001..54a02d75 100644 --- a/autd3-driver/src/firmware/operation/cpu_gpio_out.rs +++ b/autd3-driver/src/firmware/operation/cpu_gpio_out.rs @@ -1,7 +1,6 @@ -use std::mem::size_of; +use std::{convert::Infallible, mem::size_of}; use crate::{ - error::AUTDDriverError, firmware::operation::{Operation, TypeTag}, geometry::Device, }; @@ -26,7 +25,9 @@ pub struct CpuGPIOOutOp { } impl Operation for CpuGPIOOutOp { - fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { + type Error = Infallible; + + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { super::write_to_tx( tx, CpuGPIOOut { @@ -50,7 +51,7 @@ impl Operation for CpuGPIOOutOp { #[cfg(test)] mod tests { - use crate::geometry::tests::create_device; + use crate::firmware::operation::tests::create_device; use super::*; diff --git a/autd3-driver/src/firmware/operation/debug.rs b/autd3-driver/src/firmware/operation/debug.rs index ee30fcde..6164b3ca 100644 --- a/autd3-driver/src/firmware/operation/debug.rs +++ b/autd3-driver/src/firmware/operation/debug.rs @@ -1,7 +1,6 @@ -use std::mem::size_of; +use std::{convert::Infallible, mem::size_of}; use crate::{ - error::AUTDDriverError, firmware::{ fpga::DebugValue, operation::{Operation, TypeTag}, @@ -29,7 +28,9 @@ pub struct DebugSettingOp { } impl Operation for DebugSettingOp { - fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { + type Error = Infallible; + + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { super::write_to_tx( tx, DebugSetting { @@ -54,7 +55,7 @@ impl Operation for DebugSettingOp { #[cfg(test)] mod tests { - use crate::geometry::tests::create_device; + use crate::firmware::operation::tests::create_device; use super::*; diff --git a/autd3-driver/src/firmware/operation/force_fan.rs b/autd3-driver/src/firmware/operation/force_fan.rs index be3718ed..8f5c191a 100644 --- a/autd3-driver/src/firmware/operation/force_fan.rs +++ b/autd3-driver/src/firmware/operation/force_fan.rs @@ -1,5 +1,6 @@ +use std::convert::Infallible; + use crate::{ - error::AUTDDriverError, firmware::operation::{Operation, TypeTag}, geometry::Device, }; @@ -23,7 +24,9 @@ pub struct ForceFanOp { } impl Operation for ForceFanOp { - fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { + type Error = Infallible; + + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { super::write_to_tx( tx, ForceFan { @@ -50,7 +53,7 @@ mod tests { use std::mem::offset_of; use super::*; - use crate::geometry::tests::create_device; + use crate::firmware::operation::tests::create_device; const NUM_TRANS_IN_UNIT: u8 = 249; diff --git a/autd3-driver/src/firmware/operation/gain.rs b/autd3-driver/src/firmware/operation/gain.rs index 22fe9542..44d8e84d 100644 --- a/autd3-driver/src/firmware/operation/gain.rs +++ b/autd3-driver/src/firmware/operation/gain.rs @@ -6,9 +6,10 @@ use crate::{ fpga::{Drive, Segment, TransitionMode}, operation::{Operation, TypeTag}, }, - geometry::{Device, Transducer}, + geometry::Device, }; +use autd3_core::gain::GainContext; use derive_new::new; use zerocopy::{Immutable, IntoBytes}; @@ -32,14 +33,6 @@ struct Gain { __: u8, } -/// A trait to calculate the phase and intensity for [`Gain`]. -/// -/// [`Gain`]: crate::datagram::Gain -pub trait GainContext: Send + Sync { - /// Calculates the phase and intensity for the transducer. - fn calc(&self, tr: &Transducer) -> Drive; -} - #[derive(new)] #[new(visibility = "pub(crate)")] pub struct GainOp { @@ -51,11 +44,13 @@ pub struct GainOp { } impl Operation for GainOp { + type Error = AUTDDriverError; + fn required_size(&self, device: &Device) -> usize { size_of::() + device.num_transducers() * size_of::() } - fn pack(&mut self, device: &Device, tx: &mut [u8]) -> Result { + fn pack(&mut self, device: &Device, tx: &mut [u8]) -> Result { super::write_to_tx( tx, Gain { @@ -91,14 +86,14 @@ impl Operation for GainOp { #[cfg(test)] mod tests { - use size_of; + use autd3_core::geometry::Transducer; use rand::prelude::*; use super::*; use crate::{ firmware::fpga::{EmitIntensity, Phase}, - geometry::tests::create_device, + firmware::operation::tests::create_device, }; const NUM_TRANS_IN_UNIT: usize = 249; diff --git a/autd3-driver/src/firmware/operation/gpio_in.rs b/autd3-driver/src/firmware/operation/gpio_in.rs index ece80619..f6dde1c0 100644 --- a/autd3-driver/src/firmware/operation/gpio_in.rs +++ b/autd3-driver/src/firmware/operation/gpio_in.rs @@ -1,5 +1,6 @@ +use std::convert::Infallible; + use crate::{ - error::AUTDDriverError, firmware::operation::{Operation, TypeTag}, geometry::Device, }; @@ -37,7 +38,9 @@ pub struct EmulateGPIOInOp { } impl Operation for EmulateGPIOInOp { - fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { + type Error = Infallible; + + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { let mut flag = GPIOInFlags::NONE; seq_macro::seq!(N in 0..4 {#(flag.set(GPIOInFlags::GPIO_IN_~N, self.value[N]);)*}); @@ -67,7 +70,7 @@ mod tests { use std::mem::{offset_of, size_of}; use super::*; - use crate::geometry::tests::create_device; + use crate::firmware::operation::tests::create_device; const NUM_TRANS_IN_UNIT: u8 = 249; diff --git a/autd3-driver/src/firmware/operation/info.rs b/autd3-driver/src/firmware/operation/info.rs index 190bf1c5..71c6ec72 100644 --- a/autd3-driver/src/firmware/operation/info.rs +++ b/autd3-driver/src/firmware/operation/info.rs @@ -1,5 +1,6 @@ +use std::convert::Infallible; + use crate::{ - error::AUTDDriverError, firmware::operation::{Operation, TypeTag}, geometry::Device, }; @@ -35,7 +36,9 @@ pub struct FirmInfoOp { } impl Operation for FirmInfoOp { - fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { + type Error = Infallible; + + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { super::write_to_tx( tx, FirmInfo { @@ -59,7 +62,7 @@ impl Operation for FirmInfoOp { #[cfg(test)] mod tests { use super::*; - use crate::geometry::tests::create_device; + use crate::firmware::operation::tests::create_device; const NUM_TRANS_IN_UNIT: u8 = 249; diff --git a/autd3-driver/src/firmware/operation/mod.rs b/autd3-driver/src/firmware/operation/mod.rs index 0394d42e..538cf2b1 100644 --- a/autd3-driver/src/firmware/operation/mod.rs +++ b/autd3-driver/src/firmware/operation/mod.rs @@ -1,3 +1,4 @@ +mod boxed; mod clear; #[cfg(feature = "dynamic_freq")] mod clock; @@ -8,7 +9,6 @@ mod gain; mod gpio_in; mod info; mod modulation; -mod null; mod phase_corr; mod pulse_width_encoder; mod reads_fpga_state; @@ -17,19 +17,19 @@ mod silencer; mod stm; mod sync; +pub(crate) use autd3_core::datagram::NullOp; +pub use boxed::BoxedOperation; pub(crate) use clear::*; #[cfg(feature = "dynamic_freq")] pub(crate) use clock::*; pub(crate) use cpu_gpio_out::*; pub(crate) use debug::*; pub(crate) use force_fan::*; -pub use gain::GainContext; pub(crate) use gain::*; pub(crate) use gpio_in::*; pub use info::FirmwareVersionType; pub(crate) use info::*; pub(crate) use modulation::*; -pub(crate) use null::*; pub(crate) use phase_corr::*; pub(crate) use pulse_width_encoder::*; pub(crate) use reads_fpga_state::*; @@ -76,12 +76,7 @@ pub(crate) enum TypeTag { CpuGPIOOut = 0xF2, } -#[doc(hidden)] -pub trait Operation: Send + Sync { - fn required_size(&self, device: &Device) -> usize; - fn pack(&mut self, device: &Device, tx: &mut [u8]) -> Result; - fn is_done(&self) -> bool; -} +pub use autd3_core::datagram::Operation; #[doc(hidden)] pub trait OperationGenerator { @@ -90,26 +85,6 @@ pub trait OperationGenerator { fn generate(&mut self, device: &Device) -> (Self::O1, Self::O2); } -impl Operation for Box { - fn required_size(&self, device: &Device) -> usize { - self.as_ref().required_size(device) - } - - fn pack(&mut self, device: &Device, tx: &mut [u8]) -> Result { - self.as_mut().pack(device, tx) - } - - fn is_done(&self) -> bool { - self.as_ref().is_done() - } -} - -impl Default for Box { - fn default() -> Self { - Box::new(NullOp::new()) - } -} - #[doc(hidden)] pub struct OperationHandler {} @@ -122,12 +97,17 @@ impl OperationHandler { operations.iter().all(|op| op.0.is_done() && op.1.is_done()) } - pub fn pack( - operations: &mut [(impl Operation, impl Operation)], + pub fn pack( + operations: &mut [(O1, O2)], geometry: &Geometry, tx: &mut [TxMessage], parallel: bool, - ) -> Result<(), AUTDDriverError> { + ) -> Result<(), AUTDDriverError> + where + O1: Operation, + O2: Operation, + AUTDDriverError: From + From, + { if parallel { geometry .iter() @@ -152,12 +132,17 @@ impl OperationHandler { } } - fn pack_op2( - op1: &mut impl Operation, - op2: &mut impl Operation, + fn pack_op2( + op1: &mut O1, + op2: &mut O2, dev: &Device, tx: &mut TxMessage, - ) -> Result<(), AUTDDriverError> { + ) -> Result<(), AUTDDriverError> + where + O1: Operation, + O2: Operation, + AUTDDriverError: From + From, + { match (op1.is_done(), op2.is_done()) { (true, true) => Result::<_, AUTDDriverError>::Ok(()), (true, false) => Self::pack_op(op2, dev, tx).map(|_| Ok(()))?, @@ -173,15 +158,15 @@ impl OperationHandler { } } - fn pack_op( - op: &mut impl Operation, - dev: &Device, - tx: &mut TxMessage, - ) -> Result { + fn pack_op(op: &mut O, dev: &Device, tx: &mut TxMessage) -> Result + where + O: Operation, + AUTDDriverError: From, + { tx.header_mut().msg_id += 1; tx.header_mut().msg_id &= MSG_ID_MAX; tx.header_mut().slot_2_offset = 0; - op.pack(dev, tx.payload_mut()) + Ok(op.pack(dev, tx.payload_mut())?) } } @@ -205,6 +190,16 @@ pub(crate) mod tests { use super::*; + pub fn create_device(idx: u16, n: u8) -> Device { + Device::new( + idx, + UnitQuaternion::identity(), + (0..n) + .map(|i| Transducer::new(i, idx, Point3::origin())) + .collect(), + ) + } + struct OperationMock { pub pack_size: usize, pub required_size: usize, @@ -213,13 +208,15 @@ pub(crate) mod tests { } impl Operation for OperationMock { + type Error = AUTDDriverError; + fn required_size(&self, _: &Device) -> usize { self.required_size } fn pack(&mut self, _: &Device, _: &mut [u8]) -> Result { if self.broken { - return Err(AUTDDriverError::LinkError("test".to_owned())); + return Err(AUTDDriverError::NotSupportedTag); } self.num_frames -= 1; Ok(self.pack_size) @@ -393,7 +390,7 @@ pub(crate) mod tests { let mut tx = vec![TxMessage::new_zeroed(); 1]; assert_eq!( - Err(AUTDDriverError::LinkError("test".to_owned())), + Err(AUTDDriverError::NotSupportedTag), OperationHandler::pack(&mut op, &geometry, &mut tx, false) ); @@ -401,14 +398,14 @@ pub(crate) mod tests { op[0].1.broken = true; assert_eq!( - Err(AUTDDriverError::LinkError("test".to_owned())), + Err(AUTDDriverError::NotSupportedTag), OperationHandler::pack(&mut op, &geometry, &mut tx, false) ); op[0].0.num_frames = 0; assert_eq!( - Err(AUTDDriverError::LinkError("test".to_owned())), + Err(AUTDDriverError::NotSupportedTag), OperationHandler::pack(&mut op, &geometry, &mut tx, false) ); @@ -419,7 +416,7 @@ pub(crate) mod tests { op[0].1.num_frames = 0; assert_eq!( - Err(AUTDDriverError::LinkError("test".to_owned())), + Err(AUTDDriverError::NotSupportedTag), OperationHandler::pack(&mut op, &geometry, &mut tx, false) ); } diff --git a/autd3-driver/src/firmware/operation/modulation.rs b/autd3-driver/src/firmware/operation/modulation.rs index 4f3880e2..5680b366 100644 --- a/autd3-driver/src/firmware/operation/modulation.rs +++ b/autd3-driver/src/firmware/operation/modulation.rs @@ -64,6 +64,8 @@ pub struct ModulationOp { } impl Operation for ModulationOp { + type Error = AUTDDriverError; + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { let is_first = self.sent == 0; @@ -154,7 +156,7 @@ mod tests { use rand::prelude::*; use super::*; - use crate::{ethercat::DcSysTime, geometry::tests::create_device}; + use crate::{ethercat::DcSysTime, firmware::operation::tests::create_device}; const NUM_TRANS_IN_UNIT: usize = 249; @@ -171,7 +173,7 @@ mod tests { let buf: Vec = (0..MOD_SIZE).map(|_| rng.gen()).collect(); let freq_div = rng.gen_range(0x0001..=0xFFFF); let loop_behavior = LoopBehavior::infinite(); - let rep = loop_behavior.rep; + let rep = loop_behavior.rep(); let segment = Segment::S0; let transition_mode = TransitionMode::SysTime( DcSysTime::from_utc( @@ -184,7 +186,7 @@ mod tests { let mut op = ModulationOp::new( Arc::new(buf.clone()), SamplingConfig::new(freq_div).unwrap(), - LoopBehavior { rep }, + loop_behavior, segment, Some(transition_mode), ); diff --git a/autd3-driver/src/firmware/operation/null.rs b/autd3-driver/src/firmware/operation/null.rs deleted file mode 100644 index 405918ba..00000000 --- a/autd3-driver/src/firmware/operation/null.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::{error::AUTDDriverError, firmware::operation::Operation, geometry::Device}; - -use derive_new::new; - -#[derive(new)] -#[new(visibility = "pub(crate)")] -pub struct NullOp { - #[new(default)] - __: (), -} - -impl Operation for NullOp { - // GRCOV_EXCL_START - fn pack(&mut self, _: &Device, _: &mut [u8]) -> Result { - unreachable!() - } - // GRCOV_EXCL_STOP - - fn required_size(&self, _: &Device) -> usize { - 0 - } - - fn is_done(&self) -> bool { - true - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::geometry::tests::create_device; - - const NUM_TRANS_IN_UNIT: u8 = 249; - - #[test] - fn test() { - let device = create_device(0, NUM_TRANS_IN_UNIT); - - let op = NullOp::new(); - - assert_eq!(op.required_size(&device), 0); - - assert!(op.is_done()); - } -} diff --git a/autd3-driver/src/firmware/operation/phase_corr.rs b/autd3-driver/src/firmware/operation/phase_corr.rs index 43732ec8..66b02d52 100644 --- a/autd3-driver/src/firmware/operation/phase_corr.rs +++ b/autd3-driver/src/firmware/operation/phase_corr.rs @@ -1,7 +1,6 @@ -use std::mem::size_of; +use std::{convert::Infallible, mem::size_of}; use crate::{ - error::AUTDDriverError, firmware::{ fpga::Phase, operation::{Operation, TypeTag}, @@ -28,7 +27,9 @@ pub struct PhaseCorrectionOp Phase> { } impl Phase + Send + Sync> Operation for PhaseCorrectionOp { - fn pack(&mut self, dev: &Device, tx: &mut [u8]) -> Result { + type Error = Infallible; + + fn pack(&mut self, dev: &Device, tx: &mut [u8]) -> Result { super::write_to_tx( tx, PhaseCorr { @@ -63,7 +64,7 @@ mod tests { use std::mem::size_of; use super::*; - use crate::geometry::tests::create_device; + use crate::firmware::operation::tests::create_device; const NUM_TRANS_IN_UNIT: u8 = 249; diff --git a/autd3-driver/src/firmware/operation/pulse_width_encoder.rs b/autd3-driver/src/firmware/operation/pulse_width_encoder.rs index ef2de82c..602b2a6e 100644 --- a/autd3-driver/src/firmware/operation/pulse_width_encoder.rs +++ b/autd3-driver/src/firmware/operation/pulse_width_encoder.rs @@ -1,7 +1,6 @@ -use std::mem::size_of; +use std::{convert::Infallible, mem::size_of}; use crate::{ - error::AUTDDriverError, firmware::{ fpga::PWE_BUF_SIZE, operation::{Operation, TypeTag}, @@ -28,7 +27,9 @@ pub struct PulseWidthEncoderOp u8> { } impl u8 + Send + Sync> Operation for PulseWidthEncoderOp { - fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { + type Error = Infallible; + + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { super::write_to_tx( tx, Pwe { @@ -64,7 +65,7 @@ mod tests { use std::mem::size_of; use super::*; - use crate::geometry::tests::create_device; + use crate::firmware::operation::tests::create_device; const NUM_TRANS_IN_UNIT: u8 = 249; diff --git a/autd3-driver/src/firmware/operation/reads_fpga_state.rs b/autd3-driver/src/firmware/operation/reads_fpga_state.rs index dd40ee7f..1a5994f3 100644 --- a/autd3-driver/src/firmware/operation/reads_fpga_state.rs +++ b/autd3-driver/src/firmware/operation/reads_fpga_state.rs @@ -1,5 +1,6 @@ +use std::convert::Infallible; + use crate::{ - error::AUTDDriverError, firmware::operation::{Operation, TypeTag}, geometry::Device, }; @@ -23,7 +24,9 @@ pub struct ReadsFPGAStateOp { } impl Operation for ReadsFPGAStateOp { - fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { + type Error = Infallible; + + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { super::write_to_tx( tx, ReadsFPGAState { @@ -50,7 +53,7 @@ mod tests { use std::mem::size_of; use super::*; - use crate::geometry::tests::create_device; + use crate::firmware::operation::tests::create_device; const NUM_TRANS_IN_UNIT: u8 = 249; diff --git a/autd3-driver/src/firmware/operation/segment.rs b/autd3-driver/src/firmware/operation/segment.rs index f1eb7d62..f9208526 100644 --- a/autd3-driver/src/firmware/operation/segment.rs +++ b/autd3-driver/src/firmware/operation/segment.rs @@ -63,7 +63,9 @@ pub struct SwapSegmentOp { } impl Operation for SwapSegmentOp { - fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { + type Error = AUTDDriverError; + + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { self.is_done = true; let tag = match self.segment { @@ -122,7 +124,7 @@ impl Operation for SwapSegmentOp { #[cfg(test)] mod tests { - use crate::{ethercat::DcSysTime, geometry::tests::create_device}; + use crate::{ethercat::DcSysTime, firmware::operation::tests::create_device}; use super::*; diff --git a/autd3-driver/src/firmware/operation/silencer/completion_steps.rs b/autd3-driver/src/firmware/operation/silencer/completion_steps.rs index 82b39670..c77d30fd 100644 --- a/autd3-driver/src/firmware/operation/silencer/completion_steps.rs +++ b/autd3-driver/src/firmware/operation/silencer/completion_steps.rs @@ -1,7 +1,6 @@ -use std::num::NonZeroU16; +use std::{convert::Infallible, num::NonZeroU16}; use crate::{ - error::AUTDDriverError, firmware::{ fpga::SilencerTarget, operation::{Operation, TypeTag}, @@ -35,7 +34,9 @@ pub struct SilencerFixedCompletionStepsOp { } impl Operation for SilencerFixedCompletionStepsOp { - fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { + type Error = Infallible; + + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { super::super::write_to_tx( tx, SilencerFixedCompletionSteps { @@ -71,7 +72,7 @@ mod tests { use std::mem::size_of; use super::*; - use crate::{firmware::fpga::SilencerTarget, geometry::tests::create_device}; + use crate::{firmware::fpga::SilencerTarget, firmware::operation::tests::create_device}; const NUM_TRANS_IN_UNIT: u8 = 249; diff --git a/autd3-driver/src/firmware/operation/silencer/completion_time.rs b/autd3-driver/src/firmware/operation/silencer/completion_time.rs index a7042175..978c04f2 100644 --- a/autd3-driver/src/firmware/operation/silencer/completion_time.rs +++ b/autd3-driver/src/firmware/operation/silencer/completion_time.rs @@ -36,6 +36,8 @@ pub struct SilencerFixedCompletionTimeOp { } impl Operation for SilencerFixedCompletionTimeOp { + type Error = AUTDDriverError; + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { let validate = |value: Duration| { const NANOSEC: u128 = 1_000_000_000; @@ -89,7 +91,8 @@ mod tests { use super::*; use crate::{ - defined::ultrasound_period, firmware::fpga::SilencerTarget, geometry::tests::create_device, + defined::ultrasound_period, firmware::fpga::SilencerTarget, + firmware::operation::tests::create_device, }; const NUM_TRANS_IN_UNIT: u8 = 249; diff --git a/autd3-driver/src/firmware/operation/silencer/update_rate.rs b/autd3-driver/src/firmware/operation/silencer/update_rate.rs index 8ff7ac61..7aa9e831 100644 --- a/autd3-driver/src/firmware/operation/silencer/update_rate.rs +++ b/autd3-driver/src/firmware/operation/silencer/update_rate.rs @@ -1,7 +1,6 @@ -use std::num::NonZeroU16; +use std::{convert::Infallible, num::NonZeroU16}; use crate::{ - error::AUTDDriverError, firmware::{ fpga::SilencerTarget, operation::{Operation, TypeTag}, @@ -34,7 +33,9 @@ pub struct SilencerFixedUpdateRateOp { } impl Operation for SilencerFixedUpdateRateOp { - fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { + type Error = Infallible; + + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { super::super::write_to_tx( tx, SilencerFixedUpdateRate { @@ -67,7 +68,7 @@ mod tests { use std::mem::size_of; use super::*; - use crate::geometry::tests::create_device; + use crate::firmware::operation::tests::create_device; const NUM_TRANS_IN_UNIT: u8 = 249; diff --git a/autd3-driver/src/firmware/operation/stm/foci.rs b/autd3-driver/src/firmware/operation/stm/foci.rs index 082e988b..aaaa6d9e 100644 --- a/autd3-driver/src/firmware/operation/stm/foci.rs +++ b/autd3-driver/src/firmware/operation/stm/foci.rs @@ -78,6 +78,8 @@ pub struct FociSTMOp> { } impl> Operation for FociSTMOp { + type Error = AUTDDriverError; + fn pack(&mut self, device: &Device, tx: &mut [u8]) -> Result { if N == 0 || N > FOCI_STM_FOCI_NUM_MAX { return Err(AUTDDriverError::FociSTMNumFociOutOfRange(N)); @@ -199,9 +201,9 @@ mod tests { ethercat::DcSysTime, firmware::{ fpga::{FOCI_STM_FIXED_NUM_UNIT, FOCI_STM_FIXED_NUM_UPPER_X}, - operation::ControlPoint, + operation::{tests::create_device, ControlPoint}, }, - geometry::{tests::create_device, Point3}, + geometry::Point3, }; const NUM_TRANS_IN_UNIT: u8 = 249; @@ -258,7 +260,7 @@ mod tests { }, FOCI_STM_SIZE, SamplingConfig::new(freq_div).unwrap(), - LoopBehavior { rep }, + LoopBehavior::infinite(), segment, Some(transition_mode), ); @@ -363,7 +365,7 @@ mod tests { }, FOCI_STM_SIZE, SamplingConfig::new(freq_div).unwrap(), - LoopBehavior { rep }, + LoopBehavior::infinite(), segment, Some(transition_mode), ); @@ -457,7 +459,7 @@ mod tests { .into() }) .collect(); - let freq_div = rng.gen_range(0x0001..=0xFFFF); + let freq_div = rng.gen_range(0x0001..0xFFFF); let rep = rng.gen_range(0x0001..=0xFFFF); let segment = Segment::S1; @@ -467,7 +469,7 @@ mod tests { }, FOCI_STM_SIZE, SamplingConfig::new(freq_div).unwrap(), - LoopBehavior { rep }, + LoopBehavior::finite(rep + 1).unwrap(), segment, None, ); diff --git a/autd3-driver/src/firmware/operation/stm/gain.rs b/autd3-driver/src/firmware/operation/stm/gain.rs index e7328ee4..784e67bc 100644 --- a/autd3-driver/src/firmware/operation/stm/gain.rs +++ b/autd3-driver/src/firmware/operation/stm/gain.rs @@ -10,11 +10,12 @@ use crate::{ Drive, LoopBehavior, SamplingConfig, Segment, TransitionMode, GAIN_STM_BUF_SIZE_MAX, STM_BUF_SIZE_MIN, TRANSITION_MODE_NONE, }, - operation::{write_to_tx, GainContext, Operation, TypeTag}, + operation::{write_to_tx, Operation, TypeTag}, }, geometry::Device, }; +use autd3_core::gain::GainContext; use derive_new::new; use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; @@ -98,6 +99,8 @@ pub struct GainSTMOp> { } impl> Operation for GainSTMOp { + type Error = AUTDDriverError; + fn required_size(&self, device: &Device) -> usize { if self.sent == 0 { size_of::() + device.num_transducers() * size_of::() @@ -235,8 +238,9 @@ mod tests { firmware::{ cpu::TxMessage, fpga::{EmitIntensity, Phase}, + operation::tests::create_device, }, - geometry::{tests::create_device, Transducer}, + geometry::Transducer, }; const NUM_TRANS_IN_UNIT: usize = 249; @@ -288,7 +292,7 @@ mod tests { .collect(); let freq_div = rng.gen_range(0x0001..=0xFFFF); - let rep = rng.gen_range(0x000..=0xFFFF); + let rep = rng.gen_range(0x0001..0xFFFF); let segment = Segment::S0; let transition_value = 0x0123456789ABCDEF; let transition_mode = TransitionMode::SysTime( @@ -308,7 +312,7 @@ mod tests { GAIN_STM_SIZE, GainSTMMode::PhaseIntensityFull, SamplingConfig::new(freq_div).unwrap(), - LoopBehavior { rep }, + LoopBehavior::finite(rep + 1).unwrap(), segment, Some(transition_mode), ); @@ -449,7 +453,7 @@ mod tests { GAIN_STM_SIZE, GainSTMMode::PhaseFull, SamplingConfig::new(freq_div).unwrap(), - LoopBehavior { rep }, + LoopBehavior::finite(rep).unwrap(), segment, None, ); @@ -575,7 +579,7 @@ mod tests { .collect(); let freq_div = rng.gen_range(0x0001..=0xFFFF); - let rep = rng.gen_range(0x001..=0xFFFF); + let rep = rng.gen_range(0x0001..=0xFFFF); let segment = Segment::S0; let mut op = GainSTMOp::new( STMContext { @@ -584,7 +588,7 @@ mod tests { GAIN_STM_SIZE, GainSTMMode::PhaseHalf, SamplingConfig::new(freq_div).unwrap(), - LoopBehavior { rep }, + LoopBehavior::finite(rep).unwrap(), segment, Some(TransitionMode::SyncIdx), ); diff --git a/autd3-driver/src/firmware/operation/sync.rs b/autd3-driver/src/firmware/operation/sync.rs index 829cbe18..845b6269 100644 --- a/autd3-driver/src/firmware/operation/sync.rs +++ b/autd3-driver/src/firmware/operation/sync.rs @@ -1,6 +1,7 @@ +use std::convert::Infallible; + use crate::{ defined::ultrasound_freq, - error::AUTDDriverError, firmware::operation::{Operation, TypeTag}, geometry::Device, }; @@ -25,7 +26,9 @@ pub struct SyncOp { } impl Operation for SyncOp { - fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { + type Error = Infallible; + + fn pack(&mut self, _: &Device, tx: &mut [u8]) -> Result { let ultrasound_freq = ultrasound_freq().hz(); let mult = ultrasound_freq / 125; let base_cnt = (ultrasound_freq as u64 * 256 * 500) / 1000000; @@ -59,7 +62,7 @@ mod tests { use std::mem::size_of; use super::*; - use crate::geometry::tests::create_device; + use crate::firmware::operation::tests::create_device; const NUM_TRANS_IN_UNIT: u8 = 249; diff --git a/autd3-driver/src/geometry/device.rs b/autd3-driver/src/geometry/device.rs deleted file mode 100644 index f574c882..00000000 --- a/autd3-driver/src/geometry/device.rs +++ /dev/null @@ -1,467 +0,0 @@ -use std::f32::consts::PI; - -use autd3_derive::Builder; -use bvh::aabb::Aabb; -use derive_more::{Deref, IntoIterator}; - -use crate::defined::{ultrasound_freq, METER}; - -use super::{ - Isometry, Point3, Quaternion, Transducer, Translation, UnitQuaternion, UnitVector3, Vector3, -}; - -/// An AUTD device unit. -#[derive(Builder, Deref, IntoIterator)] -pub struct Device { - idx: u16, - #[deref] - #[into_iterator(ref)] - transducers: Vec, - /// enable flag - pub enable: bool, - /// speed of sound - pub sound_speed: f32, - #[get(ref)] - /// The rotation of the device. - rotation: UnitQuaternion, - #[get(ref)] - /// The center of the device. - center: Point3, - #[get(ref)] - /// The x-direction of the device. - x_direction: UnitVector3, - #[get(ref)] - /// The y-direction of the device. - y_direction: UnitVector3, - #[get(ref)] - /// The axial direction of the device. - axial_direction: UnitVector3, - #[get(ref, no_doc)] - inv: Isometry, - #[get(ref)] - /// The Axis Aligned Bounding Box of the device. - aabb: Aabb, -} - -impl Device { - fn init(&mut self) { - self.center = Point3::from( - self.transducers - .iter() - .map(|tr| tr.position().coords) - .sum::() - / self.transducers.len() as f32, - ); - self.x_direction = Self::get_direction(Vector3::x(), &self.rotation); - self.y_direction = Self::get_direction(Vector3::y(), &self.rotation); - self.axial_direction = if cfg!(feature = "left_handed") { - Self::get_direction(-Vector3::z(), &self.rotation) // GRCOV_EXCL_LINE - } else { - Self::get_direction(Vector3::z(), &self.rotation) - }; - self.inv = (nalgebra::Translation3::::from(*self.transducers[0].position()) - * self.rotation) - .inverse(); - self.aabb = self - .transducers - .iter() - .fold(Aabb::empty(), |aabb, tr| aabb.grow(tr.position())); - } - - #[doc(hidden)] - pub fn new(idx: u16, rot: UnitQuaternion, transducers: Vec) -> Self { - let mut dev = Self { - idx, - transducers, - enable: true, - sound_speed: 340.0 * METER, - rotation: rot, - center: Point3::origin(), - x_direction: Vector3::x_axis(), - y_direction: Vector3::y_axis(), - axial_direction: Vector3::z_axis(), - inv: nalgebra::Isometry3::identity(), - aabb: Aabb::empty(), - }; - dev.init(); - dev - } - - /// Gets the index of the device. - pub const fn idx(&self) -> usize { - self.idx as _ - } - - /// Gets the number of transducers of the device. - pub fn num_transducers(&self) -> usize { - self.transducers.len() - } - - /// Translates the device to the target position. - pub fn translate_to(&mut self, t: Point3) { - self.translate(t - self.transducers[0].position()); - } - - /// Rotates the device to the target rotation. - pub fn rotate_to(&mut self, r: UnitQuaternion) { - self.rotate(r * self.rotation.conjugate()); - } - - /// Translates the device. - pub fn translate(&mut self, t: Vector3) { - self.affine(t, UnitQuaternion::identity()); - } - - /// Rotates the device. - pub fn rotate(&mut self, r: UnitQuaternion) { - self.affine(Vector3::zeros(), r); - } - - /// Translates and rotates the device. - pub fn affine(&mut self, t: Vector3, r: UnitQuaternion) { - let isometry = Isometry { - translation: Translation::from(t), - rotation: r, - }; - self.transducers - .iter_mut() - .for_each(|tr| tr.affine(&isometry)); - self.rotation = r * self.rotation; - self.init(); - } - - /// Sets the sound speed of enabled devices from the temperature `t`. - /// - /// This is equivalent to `Self::set_sound_speed_from_temp_with(t, 1.4, 8.314_463, 28.9647e-3)`. - pub fn set_sound_speed_from_temp(&mut self, temp: f32) { - self.set_sound_speed_from_temp_with(temp, 1.4, 8.314_463, 28.9647e-3); - } - - /// Sets the sound speed of enabled devices from the temperature `t`, heat capacity ratio `k`, gas constant `r`, and molar mass `m` [kg/mol]. - pub fn set_sound_speed_from_temp_with(&mut self, temp: f32, k: f32, r: f32, m: f32) { - self.sound_speed = (k * r * (273.15 + temp) / m).sqrt() * METER; - } - - /// Gets the wavelength of the ultrasound. - pub fn wavelength(&self) -> f32 { - self.sound_speed / ultrasound_freq().hz() as f32 - } - - /// Gets the wavenumber of the ultrasound. - pub fn wavenumber(&self) -> f32 { - 2.0 * PI * ultrasound_freq().hz() as f32 / self.sound_speed - } - - fn get_direction(dir: Vector3, rotation: &UnitQuaternion) -> UnitVector3 { - let dir: UnitQuaternion = UnitQuaternion::from_quaternion(Quaternion::from_imag(dir)); - UnitVector3::new_normalize((rotation * dir * rotation.conjugate()).imag()) - } -} - -/// Trait for converting to [`Device`]. -pub trait IntoDevice { - /// Converts to [`Device`]. - fn into_device(self, dev_idx: u16) -> Device; -} - -impl IntoDevice for Device { - fn into_device(mut self, dev_idx: u16) -> Device { - self.idx = dev_idx; - self - } -} - -#[cfg(test)] -pub(crate) mod tests { - use rand::Rng; - - use super::*; - use crate::{ - autd3_device::AUTD3, - defined::{mm, PI}, - geometry::tests::create_device, - }; - - macro_rules! assert_approx_eq_vec3 { - ($a:expr, $b:expr) => { - approx::assert_abs_diff_eq!($a.x, $b.x, epsilon = 1e-3); - approx::assert_abs_diff_eq!($a.y, $b.y, epsilon = 1e-3); - approx::assert_abs_diff_eq!($a.z, $b.z, epsilon = 1e-3); - }; - } - - macro_rules! assert_approx_eq_quat { - ($a:expr, $b:expr) => { - approx::assert_abs_diff_eq!($a.w, $b.w, epsilon = 1e-3); - approx::assert_abs_diff_eq!($a.i, $b.i, epsilon = 1e-3); - approx::assert_abs_diff_eq!($a.j, $b.j, epsilon = 1e-3); - approx::assert_abs_diff_eq!($a.k, $b.k, epsilon = 1e-3); - }; - } - - #[rstest::rstest] - #[test] - #[case(0)] - #[case(1)] - fn idx(#[case] expect: u16) { - assert_eq!(expect, create_device(expect, 249).idx() as u16); - } - - #[rstest::rstest] - #[test] - #[case(1)] - #[case(249)] - fn num_transducers(#[case] n: u8) { - assert_eq!(n, create_device(0, n).num_transducers() as u8); - } - - #[test] - fn center() { - let device = AUTD3::new(Point3::origin()).into_device(0); - let expected = - device.iter().map(|t| t.position().coords).sum::() / device.len() as f32; - assert_approx_eq_vec3!(expected, device.center()); - } - - #[rstest::rstest] - #[test] - #[case( - Vector3::new(10., 20., 30.), - Vector3::new(10., 20., 30.), - Point3::origin(), - UnitQuaternion::identity() - )] - #[case( - Vector3::zeros(), - Vector3::new(10., 20., 30.), - Point3::new(10., 20., 30.), - UnitQuaternion::identity() - )] - #[case( - Vector3::new(20., -10., 30.), - Vector3::new(10., 20., 30.), - Point3::origin(), - UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.) - )] - #[case( - Vector3::new(30., 30., -30.), - Vector3::new(40., 50., 60.), - Point3::new(10., 20., 30.), - UnitQuaternion::from_axis_angle(&Vector3::x_axis(), PI / 2.) - )] - fn inv( - #[case] expected: Vector3, - #[case] target: Vector3, - #[case] origin: Point3, - #[case] rot: UnitQuaternion, - ) { - let device = AUTD3::new(origin).with_rotation(rot).into_device(0); - assert_approx_eq_vec3!(expected, device.inv.transform_point(&Point3::from(target))); - } - - #[test] - fn translate_to() { - let mut rng = rand::thread_rng(); - let origin = Point3::new(rng.gen(), rng.gen(), rng.gen()); - - let mut device = AUTD3::new(origin).into_device(0); - - let t = Point3::new(40., 50., 60.); - device.translate_to(t); - - AUTD3::new(t) - .into_device(0) - .iter() - .zip(device.iter()) - .for_each(|(expect, tr)| { - assert_approx_eq_vec3!(expect.position(), tr.position()); - }); - } - - #[test] - fn rotate_to() { - let mut rng = rand::thread_rng(); - let mut device = AUTD3::new(Point3::origin()) - .with_rotation( - UnitQuaternion::from_axis_angle(&Vector3::x_axis(), rng.gen()) - * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), rng.gen()) - * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), rng.gen()), - ) - .into_device(0); - - let rot = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.) - * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 0.) - * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.); - device.rotate_to(rot); - - let expect_x = Vector3::new(0., 1., 0.); - let expect_y = Vector3::new(-1., 0., 0.); - let expect_z = if cfg!(feature = "left_handed") { - Vector3::new(0., 0., -1.) // GRCOV_EXCL_LINE - } else { - Vector3::new(0., 0., 1.) - }; - assert_approx_eq_quat!(rot, device.rotation()); - assert_approx_eq_vec3!(expect_x, device.x_direction()); - assert_approx_eq_vec3!(expect_y, device.y_direction()); - assert_approx_eq_vec3!(expect_z, device.axial_direction()); - AUTD3::new(Point3::origin()) - .with_rotation(rot) - .into_device(0) - .iter() - .zip(device.iter()) - .for_each(|(expect, tr)| { - assert_approx_eq_vec3!(expect.position(), tr.position()); - }); - } - - #[test] - fn translate() { - let mut rng = rand::thread_rng(); - let origin = Point3::new(rng.gen(), rng.gen(), rng.gen()); - - let mut device = AUTD3::new(origin).into_device(0); - - let t = Vector3::new(40., 50., 60.); - device.translate(t); - AUTD3::new(origin + t) - .into_device(0) - .iter() - .zip(device.iter()) - .for_each(|(expect, tr)| { - assert_approx_eq_vec3!(expect.position(), tr.position()); - }); - } - - #[test] - fn rotate() { - let mut device = AUTD3::new(Point3::origin()).into_device(0); - - let rot = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.) - * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 0.) - * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.); - device.rotate(rot); - let expect_x = Vector3::new(0., 1., 0.); - let expect_y = Vector3::new(-1., 0., 0.); - let expect_z = if cfg!(feature = "left_handed") { - Vector3::new(0., 0., -1.) // GRCOV_EXCL_LINE - } else { - Vector3::new(0., 0., 1.) - }; - assert_approx_eq_quat!(rot, device.rotation()); - assert_approx_eq_vec3!(expect_x, device.x_direction()); - assert_approx_eq_vec3!(expect_y, device.y_direction()); - assert_approx_eq_vec3!(expect_z, device.axial_direction()); - AUTD3::new(Point3::origin()) - .with_rotation(rot) - .into_device(0) - .iter() - .zip(device.iter()) - .for_each(|(expect, tr)| { - assert_approx_eq_vec3!(expect.position(), tr.position()); - }); - } - - #[test] - fn affine() { - let mut device = AUTD3::new(Point3::origin()).into_device(0); - - let t = Vector3::new(40., 50., 60.); - let rot = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.) - * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 0.) - * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.); - device.affine(t, rot); - - let expect_x = Vector3::new(0., 1., 0.); - let expect_y = Vector3::new(-1., 0., 0.); - let expect_z = if cfg!(feature = "left_handed") { - Vector3::new(0., 0., -1.) // GRCOV_EXCL_LINE - } else { - Vector3::new(0., 0., 1.) - }; - assert_approx_eq_quat!(rot, device.rotation()); - assert_approx_eq_vec3!(expect_x, device.x_direction()); - assert_approx_eq_vec3!(expect_y, device.y_direction()); - assert_approx_eq_vec3!(expect_z, device.axial_direction()); - - AUTD3::new(Point3::from(t)) - .with_rotation(rot) - .into_device(0) - .iter() - .zip(device.iter()) - .for_each(|(expect, tr)| { - assert_approx_eq_vec3!(expect.position(), tr.position()); - }); - } - - #[rstest::rstest] - #[test] - #[case(340.29525e3, 15.)] - #[case(343.23497e3, 20.)] - #[case(349.04013e3, 30.)] - fn set_sound_speed_from_temp(#[case] expected: f32, #[case] temp: f32) { - let mut device = create_device(0, 249); - device.set_sound_speed_from_temp(temp); - approx::assert_abs_diff_eq!(expected * mm, device.sound_speed, epsilon = 1e-3); - } - - #[rstest::rstest] - #[test] - #[case(8.5, 340e3)] - #[case(10., 400e3)] - fn wavelength(#[case] expect: f32, #[case] c: f32) { - let mut device = create_device(0, 249); - device.sound_speed = c; - approx::assert_abs_diff_eq!(expect, device.wavelength()); - } - - #[rstest::rstest] - #[test] - #[case(0.739_198_27, 340e3)] - #[case(0.628_318_55, 400e3)] - fn wavenumber(#[case] expect: f32, #[case] c: f32) { - let mut device = create_device(0, 249); - device.sound_speed = c; - approx::assert_abs_diff_eq!(expect, device.wavenumber()); - } - - #[test] - fn into_device() { - let mut rng = rand::thread_rng(); - let t = Vector3::new(rng.gen(), rng.gen(), rng.gen()); - let rot = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), rng.gen()) - * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), rng.gen()) - * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), rng.gen()); - let Device { - idx: _idx, - transducers: expect_transducers, - enable: expect_enable, - sound_speed: expect_sound_speed, - rotation: expect_rotation, - center: expect_center, - x_direction: expect_x_direction, - y_direction: expect_y_direction, - axial_direction: expect_axial_direction, - inv: expect_inv, - aabb: expect_aabb, - } = AUTD3::new(Point3::from(t)) - .with_rotation(rot) - .into_device(0); - let dev = AUTD3::new(Point3::from(t)) - .with_rotation(rot) - .into_device(0) - .into_device(1); - assert_eq!(1, dev.idx()); - assert_eq!(expect_transducers, dev.transducers); - assert_eq!(expect_enable, dev.enable); - assert_eq!(expect_sound_speed, dev.sound_speed); - assert_eq!(expect_rotation, dev.rotation); - assert_eq!(expect_center, dev.center); - assert_eq!(expect_x_direction, dev.x_direction); - assert_eq!(expect_y_direction, dev.y_direction); - assert_eq!(expect_axial_direction, dev.axial_direction); - assert_eq!(expect_inv, dev.inv); - assert_eq!(expect_aabb.min, dev.aabb.min); - assert_eq!(expect_aabb.max, dev.aabb.max); - } -} diff --git a/autd3-driver/src/geometry/mod.rs b/autd3-driver/src/geometry/mod.rs deleted file mode 100644 index ca2ea011..00000000 --- a/autd3-driver/src/geometry/mod.rs +++ /dev/null @@ -1,272 +0,0 @@ -pub(crate) mod device; -mod rotation; -mod transducer; - -/// 3-dimensional column vector. -pub type Vector3 = nalgebra::Vector3; -/// 3-dimensional unit vector. -pub type UnitVector3 = nalgebra::UnitVector3; -/// 3-dimensional point. -pub type Point3 = nalgebra::Point3; -/// 4-dimensional column vector. -pub type Vector4 = nalgebra::Vector4; -/// A quaternion. -pub type Quaternion = nalgebra::Quaternion; -/// A unit quaternion. -pub type UnitQuaternion = nalgebra::UnitQuaternion; -pub(crate) type Translation = nalgebra::Translation3; -pub(crate) type Isometry = nalgebra::Isometry3; - -use autd3_derive::Builder; -use bvh::aabb::Aabb; -pub use device::*; -pub use rotation::*; -pub use transducer::*; - -use derive_more::{Deref, IntoIterator}; -use derive_new::new; - -/// Geometry of the devices. -#[derive(Deref, Builder, IntoIterator, new)] -pub struct Geometry { - #[deref] - #[into_iterator(ref)] - pub(crate) devices: Vec, - #[new(default)] - #[get(no_doc)] - version: usize, - #[get(no_doc)] - default_parallel_threshold: usize, -} - -impl Geometry { - /// Gets the number of enabled devices. - pub fn num_devices(&self) -> usize { - self.devices().count() - } - - /// Gets the number of enabled transducers. - pub fn num_transducers(&self) -> usize { - self.devices().map(|dev| dev.num_transducers()).sum() - } - - /// Gets the center of the enabled transducers. - pub fn center(&self) -> Point3 { - Point3::from( - self.devices().map(|d| d.center().coords).sum::() / self.devices.len() as f32, - ) - } - - /// Gets the iterator of enabled devices. - pub fn devices(&self) -> impl Iterator { - self.iter().filter(|dev| dev.enable) - } - - /// Gets the mutable iterator of enabled devices. - pub fn devices_mut(&mut self) -> impl Iterator { - self.iter_mut().filter(|dev| dev.enable) - } - - /// Sets the sound speed of enabled devices. - pub fn set_sound_speed(&mut self, c: f32) { - self.devices_mut().for_each(|dev| dev.sound_speed = c); - } - - /// Sets the sound speed of enabled devices from the temperature `t`. - /// - /// This is equivalent to `Self::set_sound_speed_from_temp_with(t, 1.4, 8.314_463, 28.9647e-3)`. - pub fn set_sound_speed_from_temp(&mut self, t: f32) { - self.set_sound_speed_from_temp_with(t, 1.4, 8.314_463, 28.9647e-3); - } - - /// Sets the sound speed of enabled devices from the temperature `t`, heat capacity ratio `k`, gas constant `r`, and molar mass `m` [kg/mol]. - pub fn set_sound_speed_from_temp_with(&mut self, t: f32, k: f32, r: f32, m: f32) { - self.devices_mut() - .for_each(|dev| dev.set_sound_speed_from_temp_with(t, k, r, m)); - } - - /// Axis Aligned Bounding Box of enabled devices. - pub fn aabb(&self) -> Aabb { - self.devices() - .fold(Aabb::empty(), |aabb, dev| aabb.join(dev.aabb())) - } - - #[doc(hidden)] - pub fn parallel(&self, threshold: Option) -> bool { - self.num_devices() > threshold.unwrap_or(self.default_parallel_threshold) - } -} - -impl<'a> IntoIterator for &'a mut Geometry { - type Item = &'a mut Device; - type IntoIter = std::slice::IterMut<'a, Device>; - - fn into_iter(self) -> Self::IntoIter { - self.version += 1; - self.devices.iter_mut() - } -} - -impl std::ops::DerefMut for Geometry { - fn deref_mut(&mut self) -> &mut Self::Target { - self.version += 1; - &mut self.devices - } -} - -#[cfg(test)] -pub(crate) mod tests { - use crate::{ - autd3_device::AUTD3, - defined::{deg, mm}, - }; - - use super::*; - - macro_rules! assert_approx_eq_vec3 { - ($a:expr, $b:expr) => { - approx::assert_abs_diff_eq!($a.x, $b.x, epsilon = 1e-3); - approx::assert_abs_diff_eq!($a.y, $b.y, epsilon = 1e-3); - approx::assert_abs_diff_eq!($a.z, $b.z, epsilon = 1e-3); - }; - } - - pub fn create_device(idx: u16, n: u8) -> Device { - Device::new( - idx, - UnitQuaternion::identity(), - (0..n) - .map(|i| Transducer::new(i, idx, Point3::origin())) - .collect(), - ) - } - - pub fn create_geometry(n: u16, num_trans_in_unit: u8) -> Geometry { - Geometry::new( - (0..n) - .map(|i| create_device(i, num_trans_in_unit)) - .collect(), - 4, - ) - } - - #[rstest::rstest] - #[test] - #[case(1, vec![create_device(0, 249)])] - #[case(2, vec![create_device(0, 249), create_device(0, 249)])] - fn test_num_devices(#[case] expected: usize, #[case] devices: Vec) { - let geometry = Geometry::new(devices, 4); - assert_eq!(0, geometry.version()); - assert_eq!(expected, geometry.num_devices()); - assert_eq!(0, geometry.version()); - } - - #[rstest::rstest] - #[test] - #[case(249, vec![create_device(0, 249)])] - #[case(498, vec![create_device(0, 249), create_device(0, 249)])] - fn test_num_transducers(#[case] expected: usize, #[case] devices: Vec) { - let geometry = Geometry::new(devices, 4); - assert_eq!(0, geometry.version()); - assert_eq!(expected, geometry.num_transducers()); - assert_eq!(0, geometry.version()); - } - - #[test] - fn test_center() { - let geometry = Geometry::new( - vec![ - Device::new( - 0, - UnitQuaternion::identity(), - itertools::iproduct!(0..18, 0..14) - .enumerate() - .map(|(i, (y, x))| { - Transducer::new( - i as _, - i as _, - 10.16 * Point3::new(x as f32, y as f32, 0.), - ) - }) - .collect::>(), - ), - Device::new( - 1, - UnitQuaternion::identity(), - itertools::iproduct!(0..18, 0..14) - .enumerate() - .map(|(i, (y, x))| { - Transducer::new( - i as _, - i as _, - 10.16 * Point3::new(x as f32, y as f32, 0.) - + Vector3::new(10., 20., 30.), - ) - }) - .collect::>(), - ), - ], - 4, - ); - let expect = geometry - .iter() - .map(|dev| dev.center().coords) - .sum::() - / geometry.num_devices() as f32; - assert_eq!(0, geometry.version()); - assert_approx_eq_vec3!(expect, geometry.center()); - assert_eq!(0, geometry.version()); - } - - #[rstest::rstest] - #[test] - #[case(340.29525e3, 15.)] - #[case(343.23497e3, 20.)] - #[case(349.04013e3, 30.)] - fn test_set_sound_speed_from_temp(#[case] expected: f32, #[case] temp: f32) { - let mut geometry = create_geometry(2, 1); - assert_eq!(0, geometry.version()); - geometry.set_sound_speed_from_temp(temp); - assert_eq!(1, geometry.version()); - geometry.iter().for_each(|dev| { - approx::assert_abs_diff_eq!(expected * mm, dev.sound_speed, epsilon = 1e-3); - }); - } - - #[rstest::rstest] - #[test] - #[case(3.402_952_8e5)] - #[case(3.432_35e5)] - #[case(3.490_401_6e5)] - fn test_set_sound_speed(#[case] temp: f32) { - let mut geometry = create_geometry(2, 1); - assert_eq!(0, geometry.version()); - geometry.set_sound_speed(temp * mm); - assert_eq!(1, geometry.version()); - geometry.iter().for_each(|dev| { - assert_eq!(dev.sound_speed, temp * mm); - }); - } - - #[test] - fn into_iter() { - let mut geometry = create_geometry(1, 1); - assert_eq!(0, geometry.version()); - for dev in &mut geometry { - dev.enable = true; - } - assert_eq!(1, geometry.version()); - } - - #[rstest::rstest] - #[test] - #[case(Aabb{min: Point3::origin(), max: Point3::new(172.72 * mm, 132.08 * mm, 0.)}, vec![AUTD3::new(Point3::origin()).into_device(0)])] - #[case(Aabb{min: Point3::new(10. * mm, 20. * mm, 30. * mm), max: Point3::new(182.72 * mm, 152.08 * mm, 30. * mm)}, vec![AUTD3::new(Point3::new(10. * mm, 20. * mm, 30. * mm)).into_device(0)])] - #[case(Aabb{min: Point3::new(-132.08 * mm, 0., 0.), max: Point3::new(0., 172.72 * mm, 0.)}, vec![AUTD3::new(Point3::origin()).with_rotation(EulerAngle::ZYZ(90. * deg, 0. * deg, 0. * deg)).into_device(0)])] - #[case(Aabb{min: Point3::new(-132.08 * mm, -10. * mm, 0.), max: Point3::new(172.72 * mm, 162.72 * mm, 10. * mm)}, vec![AUTD3::new(Point3::origin()).into_device(0), AUTD3::new(Point3::new(0., -10. * mm, 10. * mm)).with_rotation(EulerAngle::ZYZ(90. * deg, 0. * deg, 0. * deg)).into_device(1)])] - fn aabb(#[case] expect: Aabb, #[case] dev: Vec) { - let geometry = Geometry::new(dev, 4); - assert_approx_eq_vec3!(expect.min, geometry.aabb().min); - assert_approx_eq_vec3!(expect.max, geometry.aabb().max); - } -} diff --git a/autd3-driver/src/geometry/rotation.rs b/autd3-driver/src/geometry/rotation.rs deleted file mode 100644 index e0593c0d..00000000 --- a/autd3-driver/src/geometry/rotation.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::defined::Angle; - -use super::{UnitQuaternion, Vector3}; - -use paste::paste; - -macro_rules! make_euler_angle_intrinsic { - ($({$first:ident, $second:ident, $third:ident}),*) => { - paste! { - /// Euler angle (intrinsic) - pub enum EulerAngleIntrinsic { - $( - #[doc = stringify!($first-$second-$third)] - #[doc = "euler angle."] - [<$first:upper $second:upper $third:upper>](Angle, Angle, Angle), - )* - } - - impl From for UnitQuaternion { - fn from(angle: EulerAngleIntrinsic) -> Self { - match angle { - $( - EulerAngleIntrinsic::[<$first:upper $second:upper $third:upper>](first, second, third) => { - UnitQuaternion::from_axis_angle(&Vector3::[<$first _axis>](), first.radian()) - * UnitQuaternion::from_axis_angle(&Vector3::[<$second _axis>](), second.radian()) - * UnitQuaternion::from_axis_angle(&Vector3::[<$third _axis>](), third.radian()) - } - )* - } - } - } - } - } -} - -macro_rules! make_euler_angle_extrinsic { - ($({$first:ident, $second:ident, $third:ident}),*) => { - paste! { - /// Euler angle (extrinsic) - pub enum EulerAngleExtrinsic { - $( - #[doc = stringify!($first-$second-$third)] - #[doc = "euler angle."] - [<$first:upper $second:upper $third:upper>](Angle, Angle, Angle), - )* - } - - impl From for UnitQuaternion { - fn from(angle: EulerAngleExtrinsic) -> Self { - match angle { - $( - EulerAngleExtrinsic::[<$first:upper $second:upper $third:upper>](first, second, third) => { - UnitQuaternion::from_axis_angle(&Vector3::[<$third _axis>](), third.radian()) - * UnitQuaternion::from_axis_angle(&Vector3::[<$second _axis>](), second.radian()) - * UnitQuaternion::from_axis_angle(&Vector3::[<$first _axis>](), first.radian()) - } - )* - } - } - } - } - } -} - -make_euler_angle_intrinsic!({x, y, z}, {x, z, y}, {y, x, z}, {y, z, x}, {z, x, y}, {z, y, x}, {x, y, x}, {x, z, x}, {y, x, y}, {y, z, y}, {z, x, z}, {z, y, z}); -make_euler_angle_extrinsic!({x, y, z}, {x, z, y}, {y, x, z}, {y, z, x}, {z, x, y}, {z, y, x}, {x, y, x}, {x, z, x}, {y, x, y}, {y, z, y}, {z, x, z}, {z, y, z}); - -/// Euler angle (intrinsic) -pub type EulerAngle = EulerAngleIntrinsic; - -#[cfg(test)] -mod tests { - use super::*; - use crate::defined::{deg, rad, PI}; - - macro_rules! assert_approx_eq_quat { - ($a:expr, $b:expr) => { - approx::assert_abs_diff_eq!($a.w, $b.w, epsilon = 1e-3); - approx::assert_abs_diff_eq!($a.i, $b.i, epsilon = 1e-3); - approx::assert_abs_diff_eq!($a.j, $b.j, epsilon = 1e-3); - approx::assert_abs_diff_eq!($a.k, $b.k, epsilon = 1e-3); - }; - } - - #[rstest::rstest] - #[test] - #[case(0., 0. * deg)] - #[case(PI / 2., 90. * deg)] - #[case(0., 0. * rad)] - #[case(PI / 2., PI / 2. * rad)] - fn test_to_radians(#[case] expected: f32, #[case] angle: Angle) { - approx::assert_abs_diff_eq!(expected, angle.radian()); - } - - #[rstest::rstest] - #[test] - #[case(UnitQuaternion::from_axis_angle(&Vector3::x_axis(), PI / 2.), EulerAngle::XYZ(90. * deg, 0. * deg, 0. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngle::XYZ(0. * deg, 90. * deg, 0. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngle::XYZ(0. * deg, 0. * deg, 90. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngle::XYZ(0. * deg, 90. * deg, 90. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::x_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngle::XYZ(90. * deg, 90. * deg, 0. * deg))] - fn test_rotation_xyz(#[case] expected: UnitQuaternion, #[case] angle: EulerAngle) { - let angle: UnitQuaternion = angle.into(); - assert_approx_eq_quat!(expected, angle); - } - - #[rstest::rstest] - #[test] - #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngle::ZYZ(90. * deg, 0. * deg, 0. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngle::ZYZ(0. * deg, 90. * deg, 0. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngle::ZYZ(0. * deg, 0. * deg, 90. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngle::ZYZ(0. * deg, 90. * deg, 90. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngle::ZYZ(90. * deg, 90. * deg, 0. * deg))] - fn test_rotation_zyz(#[case] expected: UnitQuaternion, #[case] angle: EulerAngle) { - let angle: UnitQuaternion = angle.into(); - assert_approx_eq_quat!(expected, angle); - } - - #[rstest::rstest] - #[test] - #[case(UnitQuaternion::from_axis_angle(&Vector3::x_axis(), PI / 2.), EulerAngleExtrinsic::XYZ(90. * deg, 0. * deg, 0. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngleExtrinsic::XYZ(0. * deg, 90. * deg, 0. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngleExtrinsic::XYZ(0. * deg, 0. * deg, 90. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngleExtrinsic::XYZ(0. * deg, 90. * deg, 90. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::x_axis(), PI / 2.), EulerAngleExtrinsic::XYZ(90. * deg, 90. * deg, 0. * deg))] - fn test_rotation_xyz_extrinsic( - #[case] expected: UnitQuaternion, - #[case] angle: EulerAngleExtrinsic, - ) { - let angle: UnitQuaternion = angle.into(); - assert_approx_eq_quat!(expected, angle); - } - - #[rstest::rstest] - #[test] - #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngleExtrinsic::ZYZ(90. * deg, 0. * deg, 0. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngleExtrinsic::ZYZ(0. * deg, 90. * deg, 0. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngleExtrinsic::ZYZ(0. * deg, 0. * deg, 90. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.), EulerAngleExtrinsic::ZYZ(0. * deg, 90. * deg, 90. * deg))] - #[case(UnitQuaternion::from_axis_angle(&Vector3::y_axis(), PI / 2.) * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.), EulerAngleExtrinsic::ZYZ(90. * deg, 90. * deg, 0. * deg))] - fn test_rotation_zyz_extrinsic( - #[case] expected: UnitQuaternion, - #[case] angle: EulerAngleExtrinsic, - ) { - let angle: UnitQuaternion = angle.into(); - assert_approx_eq_quat!(expected, angle); - } -} diff --git a/autd3-driver/src/geometry/transducer.rs b/autd3-driver/src/geometry/transducer.rs deleted file mode 100644 index bb28e2f9..00000000 --- a/autd3-driver/src/geometry/transducer.rs +++ /dev/null @@ -1,69 +0,0 @@ -use autd3_derive::Builder; - -use super::{Isometry, Point3}; - -use derive_new::new; - -/// A ultrasound transducer. -#[derive(Clone, Debug, PartialEq, Builder, new)] -pub struct Transducer { - idx: u8, - dev_idx: u16, - #[get(ref)] - /// The position of the transducer. - position: Point3, -} - -impl Transducer { - /// Gets the local index of the transducer. - pub const fn idx(&self) -> usize { - self.idx as _ - } - - /// Gets the index of the device to which this transducer belongs. - pub const fn dev_idx(&self) -> usize { - self.dev_idx as _ - } - - pub(super) fn affine(&mut self, isometry: &Isometry) { - self.position = isometry * self.position; - } -} - -#[cfg(test)] -mod tests { - use std::f32::consts::PI; - - use crate::geometry::{Translation, UnitQuaternion, Vector3}; - - use super::*; - - macro_rules! assert_vec3_approx_eq { - ($a:expr, $b:expr) => { - approx::assert_abs_diff_eq!($a.x, $b.x, epsilon = 1e-3); - approx::assert_abs_diff_eq!($a.y, $b.y, epsilon = 1e-3); - approx::assert_abs_diff_eq!($a.z, $b.z, epsilon = 1e-3); - }; - } - - #[rstest::fixture] - fn tr() -> Transducer { - Transducer::new(0, 0, Point3::origin()) - } - - #[rstest::rstest] - #[test] - fn affine(mut tr: Transducer) { - let vector = Vector3::new(40., 50., 60.); - let rotation = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), 0.) - * UnitQuaternion::from_axis_angle(&Vector3::y_axis(), 0.) - * UnitQuaternion::from_axis_angle(&Vector3::z_axis(), PI / 2.); - tr.affine(&Isometry { - translation: Translation { vector }, - rotation, - }); - - let expect_pos = vector; - assert_vec3_approx_eq!(expect_pos, tr.position()); - } -} diff --git a/autd3-driver/src/lib.rs b/autd3-driver/src/lib.rs index 0acde69a..13e3f206 100644 --- a/autd3-driver/src/lib.rs +++ b/autd3-driver/src/lib.rs @@ -5,56 +5,19 @@ //! A base library to drive AUTD3. -/// Utilities for acoustics. -pub mod acoustics; /// AUTD3 device. pub mod autd3_device; /// [`Datagram`] implementations. /// /// [`Datagram`]: crate::datagram::Datagram pub mod datagram; -/// Common constants and types. -pub mod defined; /// Error module. pub mod error; /// Definitions for EtherCAT. pub mod ethercat; /// A module for working with firmware. pub mod firmware; -/// Geometry related modules. -pub mod geometry; -/// A interface to the device. -pub mod link; #[doc(hidden)] pub mod utils; -#[cfg(feature = "async-trait")] -pub use async_trait::async_trait; - -/// Utilities for user-defined [`Gain`] and [`Modulation`]. -/// -/// [`Gain`]: crate::datagram::Gain -/// [`Modulation`]: crate::datagram::Modulation -#[cfg_attr(docsrs, doc(cfg(feature = "derive")))] -#[cfg(feature = "derive")] -pub mod derive { - pub use crate::{ - datagram::{ - Datagram, DatagramS, Gain, GainContextGenerator, GainOperationGenerator, Modulation, - ModulationOperationGenerator, ModulationProperty, - }, - defined::DEFAULT_TIMEOUT, - error::AUTDDriverError, - firmware::{ - fpga::{ - Drive, EmitIntensity, LoopBehavior, Phase, SamplingConfig, Segment, TransitionMode, - }, - operation::GainContext, - }, - geometry::{Device, Geometry, Transducer}, - }; - pub use autd3_derive::{Builder, Gain, Modulation}; - pub use bit_vec::BitVec; - pub use std::{collections::HashMap, sync::Arc}; - pub use tracing; -} +pub use autd3_core::{defined, geometry}; diff --git a/autd3-driver/src/link/async.rs b/autd3-driver/src/link/async.rs deleted file mode 100644 index e6d2b5bd..00000000 --- a/autd3-driver/src/link/async.rs +++ /dev/null @@ -1,126 +0,0 @@ -use std::time::Duration; - -use crate::{ - error::AUTDDriverError, - firmware::cpu::{RxMessage, TxMessage}, - geometry::Geometry, -}; - -pub use internal::{AsyncLink, AsyncLinkBuilder}; - -#[cfg(feature = "async-trait")] -mod internal { - use super::*; - - /// A trait that provides the interface with the device. - #[async_trait::async_trait] - pub trait AsyncLink: Send { - /// Closes the link. - async fn close(&mut self) -> Result<(), AUTDDriverError>; - - #[doc(hidden)] - async fn update(&mut self, _geometry: &Geometry) -> Result<(), AUTDDriverError> { - Ok(()) - } - - /// Sends a message to the device. - async fn send(&mut self, tx: &[TxMessage]) -> Result; - - /// Receives a message from the device. - async fn receive(&mut self, rx: &mut [RxMessage]) -> Result; - - /// Checks if the link is open. - #[must_use] - fn is_open(&self) -> bool; - - #[doc(hidden)] - fn trace(&mut self, _: Option, _: Option) {} - } - - /// A trait to build a link. - #[async_trait::async_trait] - pub trait AsyncLinkBuilder: Send + Sync { - /// The link type. - type L: AsyncLink; - - /// Opens a link. - async fn open(self, geometry: &Geometry) -> Result; - } - - #[async_trait::async_trait] - impl AsyncLink for Box { - async fn close(&mut self) -> Result<(), AUTDDriverError> { - self.as_mut().close().await - } - - async fn update(&mut self, geometry: &Geometry) -> Result<(), AUTDDriverError> { - self.as_mut().update(geometry).await - } - - async fn send(&mut self, tx: &[TxMessage]) -> Result { - self.as_mut().send(tx).await - } - - async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { - self.as_mut().receive(rx).await - } - - fn is_open(&self) -> bool { - self.as_ref().is_open() - } - - fn trace(&mut self, timeout: Option, parallel_threshold: Option) { - self.as_mut().trace(timeout, parallel_threshold) - } - } -} - -#[cfg(not(feature = "async-trait"))] -mod internal { - use super::*; - - /// A trait that provides the interface with the device. - pub trait AsyncLink: Send { - /// Closes the link. - fn close(&mut self) -> impl std::future::Future>; - - #[doc(hidden)] - fn update( - &mut self, - _geometry: &Geometry, - ) -> impl std::future::Future> { - async { Ok(()) } - } - - /// Sends a message to the device. - fn send( - &mut self, - tx: &[TxMessage], - ) -> impl std::future::Future>; - - /// Receives a message from the device. - fn receive( - &mut self, - rx: &mut [RxMessage], - ) -> impl std::future::Future>; - - /// Checks if the link is open. - #[must_use] - fn is_open(&self) -> bool; - - #[doc(hidden)] - fn trace(&mut self, _: Option, _: Option) {} - } - - /// A trait to build a link. - pub trait AsyncLinkBuilder { - /// The link type. - type L: AsyncLink; - - /// Opens a link. - fn open( - self, - geometry: &Geometry, - ) -> impl std::future::Future>; - } -} diff --git a/autd3-driver/src/link/mod.rs b/autd3-driver/src/link/mod.rs deleted file mode 100644 index a384e709..00000000 --- a/autd3-driver/src/link/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[cfg_attr(docsrs, doc(cfg(feature = "async")))] -#[cfg(feature = "async")] -mod r#async; -mod sync; - -#[cfg(feature = "async")] -#[doc(inline)] -pub use r#async::*; -#[doc(inline)] -pub use sync::*; diff --git a/autd3-driver/src/link/sync.rs b/autd3-driver/src/link/sync.rs deleted file mode 100644 index 9fa16282..00000000 --- a/autd3-driver/src/link/sync.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::time::Duration; - -use crate::{ - derive::Geometry, - error::AUTDDriverError, - firmware::cpu::{RxMessage, TxMessage}, -}; - -/// A trait that provides the interface with the device. -pub trait Link: Send { - /// Closes the link. - fn close(&mut self) -> Result<(), AUTDDriverError>; - - #[doc(hidden)] - fn update(&mut self, _geometry: &Geometry) -> Result<(), AUTDDriverError> { - Ok(()) - } - - /// Sends a message to the device. - fn send(&mut self, tx: &[TxMessage]) -> Result; - - /// Receives a message from the device. - fn receive(&mut self, rx: &mut [RxMessage]) -> Result; - - /// Checks if the link is open. - #[must_use] - fn is_open(&self) -> bool; - - #[doc(hidden)] - fn trace(&mut self, _: Option, _: Option) {} -} - -/// A trait to build a link. -pub trait LinkBuilder: Send + Sync { - /// The link type. - type L: Link; - - /// Opens a link. - fn open(self, geometry: &Geometry) -> Result; -} - -impl Link for Box { - fn close(&mut self) -> Result<(), AUTDDriverError> { - self.as_mut().close() - } - - fn update(&mut self, geometry: &Geometry) -> Result<(), AUTDDriverError> { - self.as_mut().update(geometry) - } - - fn send(&mut self, tx: &[TxMessage]) -> Result { - self.as_mut().send(tx) - } - - fn receive(&mut self, rx: &mut [RxMessage]) -> Result { - self.as_mut().receive(rx) - } - - fn is_open(&self) -> bool { - self.as_ref().is_open() - } - - fn trace(&mut self, timeout: Option, parallel_threshold: Option) { - self.as_mut().trace(timeout, parallel_threshold) - } -} diff --git a/autd3-driver/src/utils/float.rs b/autd3-driver/src/utils/float.rs deleted file mode 100644 index 0a1be3bc..00000000 --- a/autd3-driver/src/utils/float.rs +++ /dev/null @@ -1,38 +0,0 @@ -const EPSILON: f64 = 1e-6; - -#[doc(hidden)] -pub fn is_integer(a: f64) -> bool { - 0.5 - (a.fract() - 0.5).abs() < EPSILON -} - -#[cfg(test)] -mod tests { - - #[rstest::rstest] - #[test] - #[case(true, 1.0)] - #[case(false, 1.5)] - #[case(true, 1.0 + f32::EPSILON)] - #[case(true, 1.0 - f32::EPSILON)] - #[case(false, 1.0 + 1e-3)] - #[case(false, 1.0 - 1e-3)] - fn is_integer(#[case] expected: bool, #[case] a: f32) { - assert_eq!(super::is_integer(a as f64), expected, "{}", a); - } - - #[test] - fn is_integer_rand() { - use rand::{thread_rng, Rng}; - - const N: usize = 100000; - - let mut rng = thread_rng(); - - (0..N).for_each(|_| { - let a: u32 = rng.gen_range(1..40000); - let m: u32 = rng.gen_range(512..=0xFFFFFFFF); - let b = a as f64 / m as f64; - assert!(super::is_integer(b * m as f64)); - }); - } -} diff --git a/autd3-driver/src/utils/mod.rs b/autd3-driver/src/utils/mod.rs index 2ba164f7..1227065c 100644 --- a/autd3-driver/src/utils/mod.rs +++ b/autd3-driver/src/utils/mod.rs @@ -1,2 +1 @@ -pub mod float; pub mod timer; From ca44702dbc222b6a7c1289f6107597e469c828bc Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Tue, 14 Jan 2025 16:02:35 +0900 Subject: [PATCH 07/20] update autd3-derive --- autd3-derive/Cargo.toml | 12 ++++++------ autd3-derive/src/gain.rs | 5 +++-- autd3-derive/src/lib.rs | 22 +++++++++++----------- autd3-derive/src/modulation.rs | 5 +++-- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/autd3-derive/Cargo.toml b/autd3-derive/Cargo.toml index da72023c..51c67e95 100644 --- a/autd3-derive/Cargo.toml +++ b/autd3-derive/Cargo.toml @@ -13,11 +13,11 @@ repository = { workspace = true } proc-macro = true [dependencies] -proc-macro2 = { workspace = true } -quote = { workspace = true } -regex = { workspace = true } -syn = { workspace = true, features = ["derive"] } -itertools = { workspace = true } +proc-macro2 = { workspace = true, features = ["proc-macro"] } +quote = { workspace = true, features = ["proc-macro"] } +syn = { workspace = true, features = ["clone-impls", "printing", "derive", "parsing", "proc-macro"] } +regex = { workspace = true, features = ["unicode"] } +itertools = { workspace = true, features = ["use_std"] } [dev-dependencies] -autd3-driver = { workspace = true, features = ["derive"] } +autd3-core = { workspace = true, features = ["derive", "gain", "modulation"] } diff --git a/autd3-derive/src/gain.rs b/autd3-derive/src/gain.rs index 16ae7ec6..627268da 100644 --- a/autd3-derive/src/gain.rs +++ b/autd3-derive/src/gain.rs @@ -11,9 +11,10 @@ pub(crate) fn impl_gain_macro(ast: syn::DeriveInput) -> TokenStream { let datagram = quote! { impl <#(#linetimes,)* #(#type_params,)*> DatagramS for #name #ty_generics #where_clause { - type G = GainOperationGenerator<::G>; + type G = GainOperationGenerator<::G>; + type Error = GainError; - fn operation_generator_with_segment(self, geometry: &Geometry, segment: Segment, transition: Option) -> Result { + fn operation_generator_with_segment(self, geometry: &Geometry, segment: Segment, transition: Option) -> Result { Self::G::new( self, geometry, diff --git a/autd3-derive/src/lib.rs b/autd3-derive/src/lib.rs index 83643833..77b1b559 100644 --- a/autd3-derive/src/lib.rs +++ b/autd3-derive/src/lib.rs @@ -17,9 +17,9 @@ use proc_macro::TokenStream; /// The following example shows how to define a custom [`Gain`] that generates a single focal point. /// /// ``` -/// use autd3_driver::derive::*; -/// use autd3_driver::geometry::Point3; -/// use autd3_driver::defined::rad; +/// use autd3_core::derive::*; +/// use autd3_core::geometry::Point3; +/// use autd3_core::defined::rad; /// /// #[derive(Gain, Debug)] /// pub struct FocalPoint { @@ -58,8 +58,8 @@ use proc_macro::TokenStream; /// fn init( /// self, /// _geometry: &Geometry, -/// _filter: Option<&HashMap>>, -/// ) -> Result { +/// _filter: Option<&HashMap>, +/// ) -> Result { /// Ok(self) /// } /// } @@ -82,7 +82,7 @@ pub fn gain_derive(input: TokenStream) -> TokenStream { /// If you add `#[no_change]` attribute to `config`, you can't change the value of `config` except for the constructor. /// /// ``` -/// use autd3_driver::derive::*; +/// use autd3_core::derive::*; /// /// #[derive(Modulation, Debug)] /// pub struct Burst { @@ -100,7 +100,7 @@ pub fn gain_derive(input: TokenStream) -> TokenStream { /// } /// /// impl Modulation for Burst { -/// fn calc(self) -> Result, AUTDDriverError> { +/// fn calc(self) -> Result, ModulationError> { /// Ok((0..4000) /// .map(|i| if i == 3999 { u8::MAX } else { u8::MIN }) /// .collect()) @@ -123,7 +123,7 @@ pub fn modulation_derive(input: TokenStream) -> TokenStream { /// ## getter /// /// ``` -/// use autd3_driver::derive::*; +/// use autd3_derive::Builder; /// /// #[derive(Builder)] /// struct Foo { @@ -146,7 +146,7 @@ pub fn modulation_derive(input: TokenStream) -> TokenStream { /// If you use `get(ref)` and `get(ref_mut)`, you can get the reference of the field. /// /// ``` -/// use autd3_driver::derive::*; +/// use autd3_derive::Builder; /// /// #[derive(Builder)] /// struct Foo { @@ -173,7 +173,7 @@ pub fn modulation_derive(input: TokenStream) -> TokenStream { /// ## setter /// /// ``` -/// use autd3_driver::derive::*; +/// use autd3_derive::Builder; /// /// #[derive(Builder)] /// struct Foo { @@ -198,7 +198,7 @@ pub fn modulation_derive(input: TokenStream) -> TokenStream { /// If you use `set(into)`, you can use `Into` trait for the setter. /// /// ``` -/// use autd3_driver::derive::*; +/// use autd3_derive::Builder; /// /// #[derive(Builder)] /// struct Foo { diff --git a/autd3-derive/src/modulation.rs b/autd3-derive/src/modulation.rs index c89e32f8..174432b6 100644 --- a/autd3-derive/src/modulation.rs +++ b/autd3-derive/src/modulation.rs @@ -74,9 +74,10 @@ pub(crate) fn impl_mod_macro(input: syn::DeriveInput) -> TokenStream { let (_, ty_generics, where_clause) = generics.split_for_impl(); let datagram_with_segment_transition = quote! { impl <#(#linetimes,)* #(#type_params,)* > DatagramS for #name #ty_generics #where_clause { - type G = ModulationOperationGenerator; + type G = ModulationOperationGenerator; + type Error = ModulationError; - fn operation_generator_with_segment(self, _: &Geometry, segment: Segment, transition_mode: Option) -> Result { + fn operation_generator_with_segment(self, _: &Geometry, segment: Segment, transition_mode: Option) -> Result { let config = self.sampling_config(); let loop_behavior = self.loop_behavior(); let g = self.calc()?; From 867c48c00554f93d0e944d58c8d0cfb60ea33e8c Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Tue, 14 Jan 2025 16:02:47 +0900 Subject: [PATCH 08/20] update autd3-firmware-emulator --- autd3-firmware-emulator/Cargo.toml | 5 ++-- autd3-firmware-emulator/src/cpu/emulator.rs | 2 +- .../src/fpga/emulator/memory.rs | 3 ++- .../src/fpga/emulator/mod.rs | 6 ++--- .../src/fpga/emulator/modulation.rs | 3 +-- .../src/fpga/emulator/stm/foci.rs | 5 +--- .../src/fpga/emulator/stm/gain.rs | 5 +--- .../src/fpga/emulator/stm/mod.rs | 3 +-- .../src/fpga/emulator/swapchain.rs | 9 ++++--- autd3-firmware-emulator/tests/op/clear.rs | 4 +-- autd3-firmware-emulator/tests/op/gain.rs | 7 +++--- autd3-firmware-emulator/tests/op/info.rs | 2 +- .../tests/op/modulation.rs | 4 +-- .../tests/op/reads_fpga_state.rs | 6 +++-- autd3-firmware-emulator/tests/op/silener.rs | 6 +++-- autd3-firmware-emulator/tests/op/stm/foci.rs | 6 ++--- autd3-firmware-emulator/tests/op/stm/gain.rs | 12 +++++---- autd3-firmware-emulator/tests/test.rs | 25 +++++++++++++------ 18 files changed, 62 insertions(+), 51 deletions(-) diff --git a/autd3-firmware-emulator/Cargo.toml b/autd3-firmware-emulator/Cargo.toml index 491485f7..db2f1bfa 100644 --- a/autd3-firmware-emulator/Cargo.toml +++ b/autd3-firmware-emulator/Cargo.toml @@ -10,13 +10,15 @@ license = { workspace = true } repository = { workspace = true } [dependencies] -autd3-driver = { workspace = true, features = ["derive"] } +autd3-derive = { workspace = true } +autd3-driver = { workspace = true } bitfield-struct = { workspace = true } time = { workspace = true, features = ["std"] } zerocopy = { workspace = true } [dev-dependencies] anyhow = { workspace = true } +autd3-core = { workspace = true, features = ["derive"] } time = { workspace = true, features = ["macros"] } itertools = { workspace = true } rand = { workspace = true } @@ -24,5 +26,4 @@ rstest = { workspace = true } [features] default = [] -async-trait = ["autd3-driver/async-trait"] dynamic_freq = ["autd3-driver/dynamic_freq"] diff --git a/autd3-firmware-emulator/src/cpu/emulator.rs b/autd3-firmware-emulator/src/cpu/emulator.rs index c78b4d27..9a004314 100644 --- a/autd3-firmware-emulator/src/cpu/emulator.rs +++ b/autd3-firmware-emulator/src/cpu/emulator.rs @@ -1,5 +1,5 @@ +use autd3_derive::Builder; use autd3_driver::{ - derive::Builder, ethercat::{DcSysTime, EC_OUTPUT_FRAME_SIZE}, firmware::cpu::{Header, RxMessage, TxMessage}, }; diff --git a/autd3-firmware-emulator/src/fpga/emulator/memory.rs b/autd3-firmware-emulator/src/fpga/emulator/memory.rs index d2cc17bd..f874a04b 100644 --- a/autd3-firmware-emulator/src/fpga/emulator/memory.rs +++ b/autd3-firmware-emulator/src/fpga/emulator/memory.rs @@ -5,7 +5,8 @@ use std::{ use crate::FPGAEmulator; -use autd3_driver::derive::{Builder, Segment}; +use autd3_derive::Builder; +use autd3_driver::firmware::fpga::Segment; use super::super::params::*; diff --git a/autd3-firmware-emulator/src/fpga/emulator/mod.rs b/autd3-firmware-emulator/src/fpga/emulator/mod.rs index 7f8c3dce..499f8717 100644 --- a/autd3-firmware-emulator/src/fpga/emulator/mod.rs +++ b/autd3-firmware-emulator/src/fpga/emulator/mod.rs @@ -7,10 +7,8 @@ mod silencer; mod stm; mod swapchain; -use autd3_driver::{ - derive::{Builder, Segment}, - ethercat::DcSysTime, -}; +use autd3_derive::Builder; +use autd3_driver::{ethercat::DcSysTime, firmware::fpga::Segment}; use memory::Memory; diff --git a/autd3-firmware-emulator/src/fpga/emulator/modulation.rs b/autd3-firmware-emulator/src/fpga/emulator/modulation.rs index 6468be51..84bb1e80 100644 --- a/autd3-firmware-emulator/src/fpga/emulator/modulation.rs +++ b/autd3-firmware-emulator/src/fpga/emulator/modulation.rs @@ -1,7 +1,6 @@ use autd3_driver::{ - derive::{LoopBehavior, Segment, TransitionMode}, ethercat::DcSysTime, - firmware::fpga::GPIOIn, + firmware::fpga::{GPIOIn, LoopBehavior, Segment, TransitionMode}, }; use super::{super::params::*, memory::Memory, FPGAEmulator}; diff --git a/autd3-firmware-emulator/src/fpga/emulator/stm/foci.rs b/autd3-firmware-emulator/src/fpga/emulator/stm/foci.rs index c5ac563c..610c8a2f 100644 --- a/autd3-firmware-emulator/src/fpga/emulator/stm/foci.rs +++ b/autd3-firmware-emulator/src/fpga/emulator/stm/foci.rs @@ -1,7 +1,4 @@ -use autd3_driver::{ - derive::Segment, - firmware::fpga::{Drive, EmitIntensity, Phase}, -}; +use autd3_driver::firmware::fpga::{Drive, EmitIntensity, Phase, Segment}; use super::super::{super::params::*, FPGAEmulator}; diff --git a/autd3-firmware-emulator/src/fpga/emulator/stm/gain.rs b/autd3-firmware-emulator/src/fpga/emulator/stm/gain.rs index 77af26ee..87e4f2b0 100644 --- a/autd3-firmware-emulator/src/fpga/emulator/stm/gain.rs +++ b/autd3-firmware-emulator/src/fpga/emulator/stm/gain.rs @@ -1,7 +1,4 @@ -use autd3_driver::{ - derive::Segment, - firmware::fpga::{Drive, EmitIntensity, Phase}, -}; +use autd3_driver::firmware::fpga::{Drive, EmitIntensity, Phase, Segment}; use crate::FPGAEmulator; diff --git a/autd3-firmware-emulator/src/fpga/emulator/stm/mod.rs b/autd3-firmware-emulator/src/fpga/emulator/stm/mod.rs index 18518691..1417138e 100644 --- a/autd3-firmware-emulator/src/fpga/emulator/stm/mod.rs +++ b/autd3-firmware-emulator/src/fpga/emulator/stm/mod.rs @@ -1,7 +1,6 @@ use autd3_driver::{ - derive::{LoopBehavior, Segment, TransitionMode}, ethercat::DcSysTime, - firmware::fpga::{Drive, GPIOIn}, + firmware::fpga::{Drive, GPIOIn, LoopBehavior, Segment, TransitionMode}, }; use super::{super::params::*, memory::Memory, FPGAEmulator}; diff --git a/autd3-firmware-emulator/src/fpga/emulator/swapchain.rs b/autd3-firmware-emulator/src/fpga/emulator/swapchain.rs index f5c51cf1..e180ae51 100644 --- a/autd3-firmware-emulator/src/fpga/emulator/swapchain.rs +++ b/autd3-firmware-emulator/src/fpga/emulator/swapchain.rs @@ -1,14 +1,15 @@ use std::collections::HashMap; use autd3_driver::{ - defined::Freq, - derive::{LoopBehavior, Segment, TransitionMode}, + defined::{Freq, Hz}, ethercat::DcSysTime, - firmware::fpga::FPGA_MAIN_CLK_FREQ, + firmware::fpga::{LoopBehavior, Segment, TransitionMode}, }; use super::FPGAEmulator; +const FPGA_MAIN_CLK_FREQ: u32 = 10240000; + pub(crate) struct Swapchain { sys_time: DcSysTime, pub(crate) fpga_clk_freq: Freq, @@ -38,7 +39,7 @@ impl Swapchain { pub fn new() -> Self { Self { sys_time: DcSysTime::now(), - fpga_clk_freq: FPGA_MAIN_CLK_FREQ, + fpga_clk_freq: FPGA_MAIN_CLK_FREQ * Hz, rep: 0, freq_div: [(Segment::S0, 10u16), (Segment::S1, 10u16)] .into_iter() diff --git a/autd3-firmware-emulator/tests/op/clear.rs b/autd3-firmware-emulator/tests/op/clear.rs index a0f4383f..639da518 100644 --- a/autd3-firmware-emulator/tests/op/clear.rs +++ b/autd3-firmware-emulator/tests/op/clear.rs @@ -5,10 +5,10 @@ use crate::{ op::{gain::TestGain, stm::foci::gen_random_foci}, send, }; +use autd3_core::derive::*; use autd3_driver::{ autd3_device::AUTD3, datagram::*, - derive::*, firmware::{ cpu::TxMessage, fpga::{ @@ -28,7 +28,7 @@ struct TestMod { } impl Modulation for TestMod { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { Ok(vec![u8::MIN; 100]) } } diff --git a/autd3-firmware-emulator/tests/op/gain.rs b/autd3-firmware-emulator/tests/op/gain.rs index 2e3e765b..736937c5 100644 --- a/autd3-firmware-emulator/tests/op/gain.rs +++ b/autd3-firmware-emulator/tests/op/gain.rs @@ -1,8 +1,9 @@ use std::collections::HashMap; +use autd3_core::derive::*; use autd3_driver::{ datagram::*, - derive::*, + error::AUTDDriverError, firmware::{ cpu::TxMessage, fpga::{Drive, EmitIntensity, Phase}, @@ -48,8 +49,8 @@ impl Gain for TestGain { fn init( self, _geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { Ok(self) } } diff --git a/autd3-firmware-emulator/tests/op/info.rs b/autd3-firmware-emulator/tests/op/info.rs index 2a79d3b3..ecce3afc 100644 --- a/autd3-firmware-emulator/tests/op/info.rs +++ b/autd3-firmware-emulator/tests/op/info.rs @@ -72,7 +72,7 @@ fn invalid_info_type() -> anyhow::Result<()> { cpu.send(&tx); assert_eq!( Err(AUTDDriverError::InvalidInfoType), - Result::<(), AUTDDriverError>::from(&cpu.rx()) + autd3_driver::firmware::cpu::check_firmware_err(&cpu.rx()) ); Ok(()) diff --git a/autd3-firmware-emulator/tests/op/modulation.rs b/autd3-firmware-emulator/tests/op/modulation.rs index 14d9d02f..b0231239 100644 --- a/autd3-firmware-emulator/tests/op/modulation.rs +++ b/autd3-firmware-emulator/tests/op/modulation.rs @@ -1,8 +1,8 @@ use std::{num::NonZeroU16, time::Duration}; +use autd3_core::derive::*; use autd3_driver::{ datagram::{FixedCompletionSteps, IntoDatagramWithSegment, Silencer, SwapSegment}, - derive::*, error::AUTDDriverError, ethercat::{DcSysTime, ECAT_DC_SYS_TIME_BASE}, firmware::{ @@ -31,7 +31,7 @@ pub struct TestModulation { } impl Modulation for TestModulation { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { Ok(self.buf.clone()) } } diff --git a/autd3-firmware-emulator/tests/op/reads_fpga_state.rs b/autd3-firmware-emulator/tests/op/reads_fpga_state.rs index 5dc56610..52e14956 100644 --- a/autd3-firmware-emulator/tests/op/reads_fpga_state.rs +++ b/autd3-firmware-emulator/tests/op/reads_fpga_state.rs @@ -1,7 +1,9 @@ use autd3_driver::{ datagram::*, - derive::{LoopBehavior, SamplingConfig, Segment, TransitionMode}, - firmware::{cpu::TxMessage, fpga::FPGAState}, + firmware::{ + cpu::TxMessage, + fpga::{FPGAState, LoopBehavior, SamplingConfig, Segment, TransitionMode}, + }, geometry::Point3, }; use autd3_firmware_emulator::CPUEmulator; diff --git a/autd3-firmware-emulator/tests/op/silener.rs b/autd3-firmware-emulator/tests/op/silener.rs index fe7aec6b..77e0177d 100644 --- a/autd3-firmware-emulator/tests/op/silener.rs +++ b/autd3-firmware-emulator/tests/op/silener.rs @@ -2,9 +2,11 @@ use std::num::NonZeroU16; use autd3_driver::{ datagram::*, - derive::{LoopBehavior, SamplingConfig, Segment, TransitionMode}, error::AUTDDriverError, - firmware::{cpu::TxMessage, fpga::SilencerTarget}, + firmware::{ + cpu::TxMessage, + fpga::{LoopBehavior, SamplingConfig, Segment, SilencerTarget, TransitionMode}, + }, geometry::Point3, }; use autd3_firmware_emulator::CPUEmulator; diff --git a/autd3-firmware-emulator/tests/op/stm/foci.rs b/autd3-firmware-emulator/tests/op/stm/foci.rs index 1e90c807..99c45a82 100644 --- a/autd3-firmware-emulator/tests/op/stm/foci.rs +++ b/autd3-firmware-emulator/tests/op/stm/foci.rs @@ -6,14 +6,14 @@ use autd3_driver::{ IntoDatagramWithSegment, Silencer, SwapSegment, }, defined::{mm, METER}, - derive::{LoopBehavior, SamplingConfig, Segment}, error::AUTDDriverError, ethercat::{DcSysTime, ECAT_DC_SYS_TIME_BASE}, firmware::{ cpu::TxMessage, fpga::{ - Drive, Phase, TransitionMode, FOCI_STM_BUF_SIZE_MAX, FOCI_STM_FIXED_NUM_UNIT, - SILENCER_STEPS_INTENSITY_DEFAULT, SILENCER_STEPS_PHASE_DEFAULT, + Drive, LoopBehavior, Phase, SamplingConfig, Segment, TransitionMode, + FOCI_STM_BUF_SIZE_MAX, FOCI_STM_FIXED_NUM_UNIT, SILENCER_STEPS_INTENSITY_DEFAULT, + SILENCER_STEPS_PHASE_DEFAULT, }, }, geometry::Point3, diff --git a/autd3-firmware-emulator/tests/op/stm/gain.rs b/autd3-firmware-emulator/tests/op/stm/gain.rs index 5d3ee9da..58c175df 100644 --- a/autd3-firmware-emulator/tests/op/stm/gain.rs +++ b/autd3-firmware-emulator/tests/op/stm/gain.rs @@ -1,20 +1,22 @@ use std::{collections::HashMap, num::NonZeroU16}; +use autd3_core::datagram::Datagram; use autd3_driver::{ datagram::{ ControlPoint, FixedCompletionSteps, FociSTM, GainSTM, IntoDatagramWithSegment, Silencer, SwapSegment, }, - derive::*, + error::AUTDDriverError, firmware::{ cpu::{GainSTMMode, TxMessage}, fpga::{ - Drive, EmitIntensity, GPIOIn, Phase, GAIN_STM_BUF_SIZE_MAX, - SILENCER_STEPS_INTENSITY_DEFAULT, SILENCER_STEPS_PHASE_DEFAULT, + Drive, EmitIntensity, GPIOIn, LoopBehavior, Phase, SamplingConfig, Segment, + TransitionMode, GAIN_STM_BUF_SIZE_MAX, SILENCER_STEPS_INTENSITY_DEFAULT, + SILENCER_STEPS_PHASE_DEFAULT, }, operation::OperationHandler, }, - geometry::Point3, + geometry::{Geometry, Point3}, }; use autd3_firmware_emulator::CPUEmulator; @@ -443,7 +445,7 @@ fn invalid_gain_stm_mode() -> anyhow::Result<()> { cpu.send(&tx); assert_eq!( Err(AUTDDriverError::InvalidGainSTMMode), - Result::<(), AUTDDriverError>::from(&cpu.rx()) + autd3_driver::firmware::cpu::check_firmware_err(&cpu.rx()) ); Ok(()) diff --git a/autd3-firmware-emulator/tests/test.rs b/autd3-firmware-emulator/tests/test.rs index ca069922..a11ead76 100644 --- a/autd3-firmware-emulator/tests/test.rs +++ b/autd3-firmware-emulator/tests/test.rs @@ -1,8 +1,12 @@ +use autd3_core::datagram::Operation; use autd3_driver::{ autd3_device::AUTD3, datagram::*, error::AUTDDriverError, - firmware::{cpu::TxMessage, operation::OperationHandler}, + firmware::{ + cpu::TxMessage, + operation::{OperationGenerator, OperationHandler}, + }, geometry::{Geometry, IntoDevice, Point3}, }; use autd3_firmware_emulator::{cpu::params::ERR_BIT, CPUEmulator}; @@ -19,12 +23,19 @@ pub fn create_geometry(n: usize) -> Geometry { ) } -pub fn send( +pub fn send( cpu: &mut CPUEmulator, - d: impl Datagram, + d: D, geometry: &Geometry, tx: &mut [TxMessage], -) -> Result<(), AUTDDriverError> { +) -> Result<(), AUTDDriverError> +where + D: Datagram, + AUTDDriverError: From, + D::G: OperationGenerator, + AUTDDriverError: From<<::O1 as Operation>::Error> + + From<<::O2 as Operation>::Error>, +{ let _timeout = d.timeout(); let parallel = geometry.num_devices() > d.parallel_threshold().unwrap_or(4); let generator = d.operation_generator(geometry)?; @@ -56,7 +67,7 @@ fn send_invalid_tag() { cpu.send(&tx); assert_eq!( Err(AUTDDriverError::NotSupportedTag), - Result::<(), AUTDDriverError>::from(&cpu.rx()) + autd3_driver::firmware::cpu::check_firmware_err(&cpu.rx()) ); } @@ -72,7 +83,7 @@ fn send_invalid_msg_id() { cpu.send(&tx); assert_eq!( Err(AUTDDriverError::InvalidMessageID), - Result::<(), AUTDDriverError>::from(&cpu.rx()) + autd3_driver::firmware::cpu::check_firmware_err(&cpu.rx()) ); } @@ -140,7 +151,7 @@ fn send_slot_2_err() -> anyhow::Result<()> { cpu.send(&tx); assert_eq!( Err(AUTDDriverError::NotSupportedTag), - Result::<(), AUTDDriverError>::from(&cpu.rx()) + autd3_driver::firmware::cpu::check_firmware_err(&cpu.rx()) ); Ok(()) From affbc7d367e5765791883b0bd983782feedd3ca4 Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Tue, 14 Jan 2025 16:02:58 +0900 Subject: [PATCH 09/20] update autd3-gain-holo --- autd3-gain-holo/Cargo.toml | 10 ++-- autd3-gain-holo/src/amp.rs | 2 +- autd3-gain-holo/src/backend.rs | 6 +-- autd3-gain-holo/src/backend_nalgebra.rs | 51 +++++-------------- autd3-gain-holo/src/combinatorial/greedy.rs | 43 +++++----------- autd3-gain-holo/src/constraint.rs | 2 +- autd3-gain-holo/src/error.rs | 8 +-- autd3-gain-holo/src/helper.rs | 15 +++--- autd3-gain-holo/src/lib.rs | 22 +++++++- autd3-gain-holo/src/linear_synthesis/gs.rs | 28 ++++------ autd3-gain-holo/src/linear_synthesis/gspat.rs | 23 ++++----- autd3-gain-holo/src/linear_synthesis/naive.rs | 37 +++++--------- autd3-gain-holo/src/nls/lm.rs | 26 ++++------ 13 files changed, 110 insertions(+), 163 deletions(-) diff --git a/autd3-gain-holo/Cargo.toml b/autd3-gain-holo/Cargo.toml index 4fe91d83..d74dd56d 100644 --- a/autd3-gain-holo/Cargo.toml +++ b/autd3-gain-holo/Cargo.toml @@ -10,22 +10,20 @@ license = { workspace = true } repository = { workspace = true } [dependencies] -autd3-driver = { workspace = true, features = ["derive"] } +autd3-core = { workspace = true, features = ["acoustics", "gain"] } +autd3-derive = { workspace = true } nalgebra = { workspace = true } thiserror = { workspace = true } rand = { workspace = true } -bit-vec = { workspace = true } derive_more = { workspace = true } rayon = { workspace = true } tynm = { workspace = true } derive-new = { workspace = true } zerocopy = { workspace = true } -[features] -default = [] -async-trait = ["autd3-driver/async-trait"] - [dev-dependencies] +autd3-driver = { workspace = true } anyhow = { workspace = true } rstest = { workspace = true } approx = { workspace = true } +itertools.workspace = true diff --git a/autd3-gain-holo/src/amp.rs b/autd3-gain-holo/src/amp.rs index a3767497..f13eae60 100644 --- a/autd3-gain-holo/src/amp.rs +++ b/autd3-gain-holo/src/amp.rs @@ -1,4 +1,4 @@ -use autd3_driver::defined::ABSOLUTE_THRESHOLD_OF_HEARING; +use autd3_core::defined::ABSOLUTE_THRESHOLD_OF_HEARING; use derive_more::{Display, Div, Mul}; use zerocopy::{Immutable, IntoBytes}; diff --git a/autd3-gain-holo/src/backend.rs b/autd3-gain-holo/src/backend.rs index cc44e781..b5a5bb3d 100644 --- a/autd3-gain-holo/src/backend.rs +++ b/autd3-gain-holo/src/backend.rs @@ -1,13 +1,13 @@ use std::collections::HashMap; -use autd3_driver::{ +use autd3_core::{ acoustics::directivity::Directivity, + gain::BitVec, geometry::{Geometry, Point3}, }; use nalgebra::{Dyn, VecStorage, U1}; use crate::error::HoloError; -use bit_vec::BitVec; /// Complex number pub type Complex = nalgebra::Complex; @@ -43,7 +43,7 @@ pub trait LinAlgBackend { &self, geometry: &Geometry, foci: &[Point3], - filter: Option<&HashMap>>, + filter: Option<&HashMap>, ) -> Result; fn alloc_v(&self, size: usize) -> Result; diff --git a/autd3-gain-holo/src/backend_nalgebra.rs b/autd3-gain-holo/src/backend_nalgebra.rs index fc14a179..ce407343 100644 --- a/autd3-gain-holo/src/backend_nalgebra.rs +++ b/autd3-gain-holo/src/backend_nalgebra.rs @@ -3,18 +3,16 @@ use std::{ mem::{ManuallyDrop, MaybeUninit}, }; -use bit_vec::BitVec; -use derive_new::new; -use nalgebra::{ComplexField, Dyn, Normed, VecStorage, U1}; - -use autd3_driver::{ +use autd3_core::{ acoustics::{ directivity::{Directivity, Sphere}, propagate, }, - defined::Complex, - geometry::Geometry, + gain::BitVec, + geometry::{Complex, Geometry, Point3}, }; +use derive_new::new; +use nalgebra::{ComplexField, Dyn, Normed, VecStorage, U1}; use crate::{error::HoloError, LinAlgBackend, MatrixX, MatrixXc, VectorX, VectorXc}; @@ -93,8 +91,8 @@ impl LinAlgBackend for NalgebraBackend { fn generate_propagation_matrix( &self, geometry: &Geometry, - foci: &[autd3_driver::geometry::Point3], - filter: Option<&HashMap>>, + foci: &[Point3], + filter: Option<&HashMap>, ) -> Result { use rayon::prelude::*; @@ -560,14 +558,9 @@ impl LinAlgBackend for NalgebraBackend { #[cfg(test)] mod tests { - use autd3_driver::{ - acoustics::directivity::Sphere, - autd3_device::AUTD3, - defined::PI, - geometry::{IntoDevice, Point3}, - }; + use std::f32::consts::PI; - use crate::{Amplitude, Pa, Trans}; + use crate::{tests::create_geometry, Amplitude, Pa, Trans}; use super::*; @@ -576,24 +569,6 @@ mod tests { const N: usize = 10; const EPS: f32 = 1e-3; - fn generate_geometry(size: usize) -> Geometry { - Geometry::new( - (0..size) - .flat_map(|i| { - (0..size).map(move |j| { - AUTD3::new(Point3::new( - i as f32 * AUTD3::DEVICE_WIDTH, - j as f32 * AUTD3::DEVICE_HEIGHT, - 0., - )) - .into_device((j + i * size) as _) - }) - }) - .collect(), - 4, - ) - } - fn gen_foci(n: usize) -> impl Iterator { (0..n).map(move |i| { ( @@ -1980,7 +1955,7 @@ mod tests { g }; - let geometry = generate_geometry(dev_num); + let geometry = create_geometry(dev_num, dev_num); let foci = gen_foci(foci_num).map(|(p, _)| p).collect::>(); let g = backend.generate_propagation_matrix(&geometry, &foci, None)?; @@ -2011,7 +1986,7 @@ mod tests { geometry .iter() .map(|dev| { - let mut filter = bit_vec::BitVec::new(); + let mut filter = BitVec::new(); dev.iter().for_each(|tr| { filter.push(tr.idx() > dev.num_transducers() / 2); }); @@ -2049,7 +2024,7 @@ mod tests { g }; - let geometry = generate_geometry(dev_num); + let geometry = create_geometry(dev_num, dev_num); let foci = gen_foci(foci_num).map(|(p, _)| p).collect::>(); let filter = filter(&geometry); @@ -2077,7 +2052,7 @@ mod tests { #[rstest::rstest] #[test] fn test_gen_back_prop(backend: NalgebraBackend) -> Result<(), HoloError> { - let geometry = generate_geometry(1); + let geometry = create_geometry(1, 1); let foci = gen_foci(1).map(|(p, _)| p).collect::>(); let m = geometry diff --git a/autd3-gain-holo/src/combinatorial/greedy.rs b/autd3-gain-holo/src/combinatorial/greedy.rs index 83dcc0c8..3b2cfa5c 100644 --- a/autd3-gain-holo/src/combinatorial/greedy.rs +++ b/autd3-gain-holo/src/combinatorial/greedy.rs @@ -2,19 +2,14 @@ use std::{collections::HashMap, num::NonZeroU8}; use crate::{constraint::EmissionConstraint, Amplitude, Complex}; -use autd3_driver::{ +use autd3_core::{ acoustics::{directivity::Directivity, propagate}, - datagram::GainContextGenerator, defined::PI, derive::*, - firmware::{ - fpga::{Drive, EmitIntensity, Phase}, - operation::GainContext, - }, - geometry::{Point3, Transducer, UnitVector3}, + geometry::{Point3, UnitVector3}, }; -use bit_vec::BitVec; +use autd3_derive::Builder; use derive_more::Debug; use nalgebra::ComplexField; use rand::seq::SliceRandom; @@ -86,7 +81,7 @@ pub struct ContextGenerator { impl GainContextGenerator for ContextGenerator { type Context = Context; - fn generate(&mut self, device: &autd3_driver::geometry::Device) -> Self::Context { + fn generate(&mut self, device: &Device) -> Self::Context { Context { g: self.g.remove(&device.idx()).unwrap(), } @@ -99,8 +94,8 @@ impl Gain for Greedy { fn init( self, geometry: &Geometry, - filter: Option<&HashMap>>, - ) -> Result { + filter: Option<&HashMap>, + ) -> Result { let phase_candidates = (0..self.phase_div.get()) .map(|i| Complex::new(0., 2.0 * PI * i as f32 / self.phase_div.get() as f32).exp()) .collect::>(); @@ -176,13 +171,14 @@ impl Gain for Greedy { #[cfg(test)] mod tests { + use crate::tests::create_geometry; + use super::{super::super::Pa, *}; - use autd3_driver::{acoustics::directivity::Sphere, autd3_device::AUTD3, geometry::IntoDevice}; + use autd3_core::acoustics::directivity::Sphere; #[test] fn test_greedy_all() { - let geometry: Geometry = - Geometry::new(vec![AUTD3::new(Point3::origin()).into_device(0)], 4); + let geometry = create_geometry(1, 1); let g = Greedy::::new([(Point3::origin(), 1. * Pa), (Point3::origin(), 1. * Pa)]) .with_phase_div(NonZeroU8::MIN); @@ -207,13 +203,7 @@ mod tests { #[test] fn test_greedy_all_disabled() -> anyhow::Result<()> { - let mut geometry = Geometry::new( - vec![ - AUTD3::new(Point3::origin()).into_device(0), - AUTD3::new(Point3::origin()).into_device(1), - ], - 4, - ); + let mut geometry = create_geometry(2, 1); geometry[0].enable = false; let g = Greedy::::new([(Point3::origin(), 1. * Pa), (Point3::origin(), 1. * Pa)]); @@ -233,8 +223,7 @@ mod tests { #[test] fn test_greedy_filtered() { - let geometry: Geometry = - Geometry::new(vec![AUTD3::new(Point3::origin()).into_device(0)], 4); + let geometry = create_geometry(1, 1); let g = Greedy::::new([ (Point3::new(10., 10., 100.), 5e3 * Pa), @@ -260,13 +249,7 @@ mod tests { #[test] fn test_greedy_filtered_disabled() -> anyhow::Result<()> { - let mut geometry = Geometry::new( - vec![ - AUTD3::new(Point3::origin()).into_device(0), - AUTD3::new(Point3::origin()).into_device(1), - ], - 4, - ); + let mut geometry = create_geometry(2, 1); geometry[0].enable = false; let g = Greedy::::new([(Point3::origin(), 1. * Pa), (Point3::origin(), 1. * Pa)]); diff --git a/autd3-gain-holo/src/constraint.rs b/autd3-gain-holo/src/constraint.rs index 9ab81fd3..160db75b 100644 --- a/autd3-gain-holo/src/constraint.rs +++ b/autd3-gain-holo/src/constraint.rs @@ -1,4 +1,4 @@ -use autd3_driver::firmware::fpga::EmitIntensity; +use autd3_core::gain::EmitIntensity; /// Emission constraint of transducers. #[derive(Clone, Copy, Debug, PartialEq)] diff --git a/autd3-gain-holo/src/error.rs b/autd3-gain-holo/src/error.rs index 9a1b8e19..c2b9be1e 100644 --- a/autd3-gain-holo/src/error.rs +++ b/autd3-gain-holo/src/error.rs @@ -1,4 +1,4 @@ -use autd3_driver::error::AUTDDriverError; +use autd3_core::gain::GainError; use thiserror::Error; /// A interface for error handling in autd3-gain-holo. @@ -16,9 +16,9 @@ pub enum HoloError { InvalidOperation, } -impl From for AUTDDriverError { +impl From for GainError { fn from(value: HoloError) -> Self { - AUTDDriverError::GainError(value.to_string()) + GainError::new(value.to_string()) } } @@ -49,7 +49,7 @@ mod tests { #[cfg_attr(miri, ignore)] fn from() { let err = HoloError::SolveFailed; - let err: AUTDDriverError = err.into(); + let err: GainError = err.into(); assert_eq!(format!("{}", err), "Failed to solve linear system"); } } diff --git a/autd3-gain-holo/src/helper.rs b/autd3-gain-holo/src/helper.rs index a1b4e454..a862453f 100644 --- a/autd3-gain-holo/src/helper.rs +++ b/autd3-gain-holo/src/helper.rs @@ -1,13 +1,10 @@ use std::{collections::HashMap, sync::Arc}; -use autd3_driver::{ +use autd3_core::{ defined::rad, - derive::{GainContext, GainContextGenerator, Transducer}, - error::AUTDDriverError, - firmware::fpga::{Drive, Phase}, - geometry::Geometry, + gain::{BitVec, Drive, GainContext, GainContextGenerator, GainError, Phase}, + geometry::{Device, Geometry, Transducer}, }; -use bit_vec::BitVec; use nalgebra::ComplexField; use rayon::iter::Either; @@ -99,7 +96,7 @@ pub struct HoloContextGenerator { impl GainContextGenerator for HoloContextGenerator { type Context = HoloContext; - fn generate(&mut self, device: &autd3_driver::geometry::Device) -> Self::Context { + fn generate(&mut self, device: &Device) -> Self::Context { match &mut self.map { Either::Left(map) => HoloContext { q: self.q.clone(), @@ -127,8 +124,8 @@ pub(crate) fn generate_result( >, max_coefficient: f32, constraint: EmissionConstraint, - filter: Option<&HashMap>>, -) -> Result, AUTDDriverError> + filter: Option<&HashMap>, +) -> Result, GainError> where T: IntoDrive + Copy + Send + Sync + 'static, { diff --git a/autd3-gain-holo/src/lib.rs b/autd3-gain-holo/src/lib.rs index 383c30d4..c35391cd 100644 --- a/autd3-gain-holo/src/lib.rs +++ b/autd3-gain-holo/src/lib.rs @@ -26,4 +26,24 @@ pub use linear_synthesis::*; pub use nls::*; pub use amp::{dB, kPa, Amplitude, Pa}; -pub use autd3_driver::acoustics::directivity::{Sphere, T4010A1}; +pub use autd3_core::acoustics::directivity::{Sphere, T4010A1}; + +#[cfg(test)] +pub(crate) mod tests { + use autd3_core::geometry::{Geometry, IntoDevice, Point3}; + use autd3_driver::autd3_device::AUTD3; + + pub fn create_geometry(row: usize, col: usize) -> Geometry { + Geometry::new( + (0..col) + .flat_map(|i| { + (0..row).map(move |j| { + AUTD3::new(Point3::new(i as f32 * 192., j as f32 * 151.4, 0.)) + .into_device((j + i * row) as _) + }) + }) + .collect(), + 4, + ) + } +} diff --git a/autd3-gain-holo/src/linear_synthesis/gs.rs b/autd3-gain-holo/src/linear_synthesis/gs.rs index 77f163f8..ab4ca418 100644 --- a/autd3-gain-holo/src/linear_synthesis/gs.rs +++ b/autd3-gain-holo/src/linear_synthesis/gs.rs @@ -6,10 +6,8 @@ use crate::{ Amplitude, Complex, LinAlgBackend, Trans, }; -use autd3_driver::{ - acoustics::directivity::Directivity, derive::*, firmware::fpga::EmitIntensity, geometry::Point3, -}; -use bit_vec::BitVec; +use autd3_core::{acoustics::directivity::Directivity, derive::*, geometry::Point3}; +use autd3_derive::Builder; use derive_more::Debug; use zerocopy::{FromBytes, IntoBytes}; @@ -59,8 +57,8 @@ impl> Gain for GS { fn init( self, geometry: &Geometry, - filter: Option<&HashMap>>, - ) -> Result { + filter: Option<&HashMap>, + ) -> Result { let g = self .backend .generate_propagation_matrix(geometry, &self.foci, filter)?; @@ -79,7 +77,7 @@ impl> Gain for GS { .backend .from_slice_cv(<[f32]>::ref_from_bytes(self.amps.as_bytes()).unwrap())?; let mut p = self.backend.alloc_zeros_cv(m)?; - (0..self.repeat.get()).try_for_each(|_| -> Result<(), AUTDDriverError> { + (0..self.repeat.get()).try_for_each(|_| -> Result<(), GainError> { self.backend.scaled_to_assign_cv(&q0, &mut q)?; self.backend.gemv_c( Trans::NoTrans, @@ -112,13 +110,15 @@ impl> Gain for GS { #[cfg(test)] mod tests { + use autd3_core::gain::{Drive, GainContext, GainContextGenerator}; + + use crate::tests::create_geometry; + use super::{super::super::NalgebraBackend, super::super::Pa, *}; - use autd3_driver::{autd3_device::AUTD3, firmware::fpga::Drive, geometry::IntoDevice}; #[test] fn test_gs_all() { - let geometry: Geometry = - Geometry::new(vec![AUTD3::new(Point3::origin()).into_device(0)], 4); + let geometry = create_geometry(1, 1); let backend = std::sync::Arc::new(NalgebraBackend::default()); let g = GS::new( @@ -149,13 +149,7 @@ mod tests { #[test] fn test_gs_filtered() { - let geometry: Geometry = Geometry::new( - vec![ - AUTD3::new(Point3::origin()).into_device(0), - AUTD3::new(Point3::origin()).into_device(1), - ], - 4, - ); + let geometry = create_geometry(2, 1); let backend = std::sync::Arc::new(NalgebraBackend::default()); let g = GS::new( diff --git a/autd3-gain-holo/src/linear_synthesis/gspat.rs b/autd3-gain-holo/src/linear_synthesis/gspat.rs index 9a422246..bc4193c2 100644 --- a/autd3-gain-holo/src/linear_synthesis/gspat.rs +++ b/autd3-gain-holo/src/linear_synthesis/gspat.rs @@ -6,10 +6,8 @@ use crate::{ Amplitude, Complex, LinAlgBackend, Trans, }; -use autd3_driver::{ - acoustics::directivity::Directivity, derive::*, firmware::fpga::EmitIntensity, geometry::Point3, -}; -use bit_vec::BitVec; +use autd3_core::{acoustics::directivity::Directivity, derive::*, geometry::Point3}; +use autd3_derive::Builder; use derive_more::Debug; use zerocopy::{FromBytes, IntoBytes}; @@ -59,8 +57,8 @@ impl> Gain for GSPAT { fn init( self, geometry: &Geometry, - filter: Option<&HashMap>>, - ) -> Result { + filter: Option<&HashMap>, + ) -> Result { let g = self .backend .generate_propagation_matrix(geometry, &self.foci, filter)?; @@ -97,7 +95,7 @@ impl> Gain for GSPAT { Complex::new(0., 0.), &mut gamma, )?; - (0..self.repeat.get()).try_for_each(|_| -> Result<(), AUTDDriverError> { + (0..self.repeat.get()).try_for_each(|_| -> Result<(), GainError> { self.backend.scaled_to_cv(&gamma, &s, &mut p)?; self.backend.gemv_c( Trans::NoTrans, @@ -129,13 +127,15 @@ impl> Gain for GSPAT { #[cfg(test)] mod tests { + use autd3_core::gain::{Drive, GainContext, GainContextGenerator}; + + use crate::tests::create_geometry; + use super::{super::super::NalgebraBackend, super::super::Pa, *}; - use autd3_driver::{autd3_device::AUTD3, firmware::fpga::Drive, geometry::IntoDevice}; #[test] fn test_gspat_all() { - let geometry: Geometry = - Geometry::new(vec![AUTD3::new(Point3::origin()).into_device(0)], 4); + let geometry = create_geometry(1, 1); let backend = std::sync::Arc::new(NalgebraBackend::default()); let g = GSPAT::new( @@ -166,8 +166,7 @@ mod tests { #[test] fn test_gspat_filtered() { - let geometry: Geometry = - Geometry::new(vec![AUTD3::new(Point3::origin()).into_device(0)], 4); + let geometry = create_geometry(1, 1); let backend = std::sync::Arc::new(NalgebraBackend::default()); let g = GSPAT::new( diff --git a/autd3-gain-holo/src/linear_synthesis/naive.rs b/autd3-gain-holo/src/linear_synthesis/naive.rs index 88fb59b4..52a44c45 100644 --- a/autd3-gain-holo/src/linear_synthesis/naive.rs +++ b/autd3-gain-holo/src/linear_synthesis/naive.rs @@ -6,10 +6,8 @@ use crate::{ Amplitude, Complex, LinAlgBackend, Trans, }; -use autd3_driver::{ - acoustics::directivity::Directivity, derive::*, firmware::fpga::EmitIntensity, geometry::Point3, -}; -use bit_vec::BitVec; +use autd3_core::{acoustics::directivity::Directivity, derive::*, geometry::Point3}; +use autd3_derive::Builder; use derive_more::Debug; use zerocopy::{FromBytes, IntoBytes}; @@ -52,8 +50,8 @@ impl> Gain for Naive { fn init( self, geometry: &Geometry, - filter: Option<&HashMap>>, - ) -> Result { + filter: Option<&HashMap>, + ) -> Result { let g = self .backend .generate_propagation_matrix(geometry, &self.foci, filter)?; @@ -86,13 +84,15 @@ impl> Gain for Naive { #[cfg(test)] mod tests { + use autd3_core::gain::{Drive, GainContext, GainContextGenerator}; + + use crate::tests::create_geometry; + use super::{super::super::NalgebraBackend, super::super::Pa, *}; - use autd3_driver::{autd3_device::AUTD3, firmware::fpga::Drive, geometry::IntoDevice}; #[test] fn test_naive_all() { - let geometry: Geometry = - Geometry::new(vec![AUTD3::new(Point3::origin()).into_device(0)], 4); + let geometry = create_geometry(1, 1); let backend = std::sync::Arc::new(NalgebraBackend::default()); let g = Naive::new( @@ -121,13 +121,7 @@ mod tests { #[test] fn test_naive_all_disabled() -> anyhow::Result<()> { - let mut geometry = Geometry::new( - vec![ - AUTD3::new(Point3::origin()).into_device(0), - AUTD3::new(Point3::origin()).into_device(1), - ], - 4, - ); + let mut geometry = create_geometry(2, 1); geometry[0].enable = false; let backend = std::sync::Arc::new(NalgebraBackend::default()); @@ -153,8 +147,7 @@ mod tests { #[test] fn test_naive_filtered() { - let geometry: Geometry = - Geometry::new(vec![AUTD3::new(Point3::origin()).into_device(0)], 4); + let geometry = create_geometry(1, 1); let backend = std::sync::Arc::new(NalgebraBackend::default()); let g = Naive::new( @@ -184,13 +177,7 @@ mod tests { #[test] fn test_naive_filtered_disabled() -> anyhow::Result<()> { - let mut geometry = Geometry::new( - vec![ - AUTD3::new(Point3::origin()).into_device(0), - AUTD3::new(Point3::origin()).into_device(1), - ], - 4, - ); + let mut geometry = create_geometry(2, 1); geometry[0].enable = false; let backend = std::sync::Arc::new(NalgebraBackend::default()); diff --git a/autd3-gain-holo/src/nls/lm.rs b/autd3-gain-holo/src/nls/lm.rs index 2092ff0f..f177c11f 100644 --- a/autd3-gain-holo/src/nls/lm.rs +++ b/autd3-gain-holo/src/nls/lm.rs @@ -6,10 +6,8 @@ use crate::{ Amplitude, Complex, HoloError, LinAlgBackend, Trans, }; -use autd3_driver::{ - acoustics::directivity::Directivity, derive::*, firmware::fpga::EmitIntensity, geometry::Point3, -}; -use bit_vec::BitVec; +use autd3_core::{acoustics::directivity::Directivity, derive::*, geometry::Point3}; +use autd3_derive::Builder; use derive_more::Debug; use zerocopy::{FromBytes, IntoBytes}; @@ -146,8 +144,8 @@ impl> Gain for LM { fn init( self, geometry: &Geometry, - filter: Option<&HashMap>>, - ) -> Result { + filter: Option<&HashMap>, + ) -> Result { let g = self .backend .generate_propagation_matrix(geometry, &self.foci, filter)?; @@ -287,14 +285,16 @@ impl> Gain for LM { #[cfg(test)] mod tests { + use autd3_core::gain::{Drive, GainContext, GainContextGenerator}; + + use crate::tests::create_geometry; + use super::{super::super::NalgebraBackend, super::super::Pa, *}; - use autd3_driver::{autd3_device::AUTD3, firmware::fpga::Drive, geometry::IntoDevice}; #[test] #[cfg_attr(miri, ignore)] fn test_lm_all() { - let geometry: Geometry = - Geometry::new(vec![AUTD3::new(Point3::origin()).into_device(0)], 4); + let geometry = create_geometry(1, 1); let backend = std::sync::Arc::new(NalgebraBackend::default()); let g = LM::new( @@ -334,13 +334,7 @@ mod tests { #[test] #[cfg_attr(miri, ignore)] fn test_lm_filtered() { - let geometry: Geometry = Geometry::new( - vec![ - AUTD3::new(Point3::origin()).into_device(0), - AUTD3::new(Point3::origin()).into_device(1), - ], - 4, - ); + let geometry = create_geometry(2, 1); let backend = std::sync::Arc::new(NalgebraBackend::default()); let g = LM::new( From 196b9972395b38ac95963190ecb70705ab15e25b Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Tue, 14 Jan 2025 16:03:08 +0900 Subject: [PATCH 10/20] update autd3-link-simulator --- autd3-link-simulator/Cargo.toml | 5 ++-- autd3-link-simulator/src/lib.rs | 43 ++++++++++++--------------------- 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/autd3-link-simulator/Cargo.toml b/autd3-link-simulator/Cargo.toml index 55896ab0..b6153659 100644 --- a/autd3-link-simulator/Cargo.toml +++ b/autd3-link-simulator/Cargo.toml @@ -11,7 +11,8 @@ repository = { workspace = true } [dependencies] autd3-protobuf = { workspace = true } -autd3-driver = { workspace = true, features = ["async", "derive"] } +autd3-core = { workspace = true, features = ["link", "async", "derive"] } +autd3-derive = { workspace = true } tonic = { workspace = true } tracing = { workspace = true } tokio = { workspace = true, features = ["rt-multi-thread"], optional = true } @@ -19,7 +20,7 @@ tokio = { workspace = true, features = ["rt-multi-thread"], optional = true } [features] default = [] blocking = ["tokio"] -async-trait = ["autd3-driver/async-trait", "autd3-protobuf/async-trait"] +async-trait = ["autd3-core/async-trait", "autd3-protobuf/async-trait"] [package.metadata.docs.rs] features = ["blocking"] diff --git a/autd3-link-simulator/src/lib.rs b/autd3-link-simulator/src/lib.rs index 778309bd..7d68583c 100644 --- a/autd3-link-simulator/src/lib.rs +++ b/autd3-link-simulator/src/lib.rs @@ -7,16 +7,12 @@ //! //! [`AUTD3 Simulator`]: https://github.com/shinolab/autd3-server +use autd3_core::link::{AsyncLink, AsyncLinkBuilder, LinkError, RxMessage, TxMessage}; +use autd3_derive::Builder; use autd3_protobuf::*; use std::net::SocketAddr; -use autd3_driver::{ - derive::*, - firmware::cpu::{RxMessage, TxMessage}, - link::{AsyncLink, AsyncLinkBuilder}, -}; - /// A [`AsyncLink`] for [`AUTD3 Simulator`]. /// /// [`AUTD3 Simulator`]: https://github.com/shinolab/autd3-server @@ -34,15 +30,12 @@ pub struct SimulatorBuilder { addr: SocketAddr, } -#[cfg_attr(feature = "async-trait", autd3_driver::async_trait)] +#[cfg_attr(feature = "async-trait", autd3_core::async_trait)] impl AsyncLinkBuilder for SimulatorBuilder { type L = Simulator; #[tracing::instrument(level = "debug", skip(geometry))] - async fn open( - self, - geometry: &autd3_driver::geometry::Geometry, - ) -> Result { + async fn open(self, geometry: &autd3_core::geometry::Geometry) -> Result { tracing::info!("Connecting to simulator@{}", self.addr); let conn = tonic::transport::Endpoint::new(format!("http://{}", self.addr)) .map_err(AUTDProtoBufError::from)? @@ -74,9 +67,9 @@ impl Simulator { } } -#[cfg_attr(feature = "async-trait", autd3_driver::async_trait)] +#[cfg_attr(feature = "async-trait", autd3_core::async_trait)] impl AsyncLink for Simulator { - async fn close(&mut self) -> Result<(), AUTDDriverError> { + async fn close(&mut self) -> Result<(), LinkError> { if !self.is_open { return Ok(()); } @@ -90,10 +83,7 @@ impl AsyncLink for Simulator { Ok(()) } - async fn update( - &mut self, - geometry: &autd3_driver::geometry::Geometry, - ) -> Result<(), AUTDDriverError> { + async fn update(&mut self, geometry: &autd3_core::geometry::Geometry) -> Result<(), LinkError> { if self.last_geometry_version == geometry.version() { return Ok(()); } @@ -108,7 +98,7 @@ impl AsyncLink for Simulator { Ok(()) } - async fn send(&mut self, tx: &[TxMessage]) -> Result { + async fn send(&mut self, tx: &[TxMessage]) -> Result { let res = self .client .send_data(tx.to_msg(None)) @@ -118,7 +108,7 @@ impl AsyncLink for Simulator { Ok(res.into_inner().success) } - async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { let rx_ = Vec::::from_msg( &self .client @@ -140,7 +130,7 @@ impl AsyncLink for Simulator { } #[cfg(feature = "blocking")] -use autd3_driver::link::{Link, LinkBuilder}; +use autd3_core::link::{Link, LinkBuilder}; /// A [`Link`] for [`AUTD3 Simulator`]. /// @@ -154,22 +144,19 @@ pub struct SimulatorBlocking { #[cfg(feature = "blocking")] impl Link for SimulatorBlocking { - fn close(&mut self) -> Result<(), AUTDDriverError> { + fn close(&mut self) -> Result<(), LinkError> { self.runtime.block_on(self.inner.close()) } - fn update( - &mut self, - geometry: &autd3_driver::geometry::Geometry, - ) -> Result<(), AUTDDriverError> { + fn update(&mut self, geometry: &autd3_core::geometry::Geometry) -> Result<(), LinkError> { self.runtime.block_on(self.inner.update(geometry)) } - fn send(&mut self, tx: &[TxMessage]) -> Result { + fn send(&mut self, tx: &[TxMessage]) -> Result { self.runtime.block_on(self.inner.send(tx)) } - fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + fn receive(&mut self, rx: &mut [RxMessage]) -> Result { self.runtime.block_on(self.inner.receive(rx)) } @@ -187,7 +174,7 @@ impl Link for SimulatorBlocking { impl LinkBuilder for SimulatorBuilder { type L = SimulatorBlocking; - fn open(self, geometry: &autd3_driver::geometry::Geometry) -> Result { + fn open(self, geometry: &autd3_core::geometry::Geometry) -> Result { let runtime = tokio::runtime::Builder::new_multi_thread() .enable_all() .build() From d86d38b9f6e433e2b8f01b158d8896a792a3d88e Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Tue, 14 Jan 2025 16:03:17 +0900 Subject: [PATCH 11/20] update autd3-protobuf --- autd3-protobuf/Cargo.toml | 8 ++-- autd3-protobuf/src/error.rs | 12 ++---- autd3-protobuf/src/lightweight/client.rs | 6 +-- autd3-protobuf/src/lightweight/server.rs | 14 +++---- .../src/traits/autd3/gain/bessel.rs | 10 ++--- autd3-protobuf/src/traits/autd3/gain/focus.rs | 4 +- autd3-protobuf/src/traits/autd3/gain/null.rs | 2 +- autd3-protobuf/src/traits/autd3/gain/plane.rs | 4 +- .../src/traits/autd3/gain/uniform.rs | 2 +- .../src/traits/autd3/modulation/sine/exact.rs | 10 ++--- .../autd3/modulation/sine/exact_float.rs | 10 ++--- .../traits/autd3/modulation/sine/nearest.rs | 10 ++--- .../traits/autd3/modulation/square/exact.rs | 8 ++-- .../autd3/modulation/square/exact_float.rs | 8 ++-- .../traits/autd3/modulation/square/nearest.rs | 9 ++--- .../src/traits/autd3/modulation/static.rs | 4 +- .../src/traits/driver/datagram/clear.rs | 2 +- .../src/traits/driver/datagram/force_fan.rs | 4 +- .../src/traits/driver/datagram/mod.rs | 4 +- .../driver/datagram/reads_fpga_state.rs | 4 +- .../src/traits/driver/datagram/segment.rs | 2 +- .../src/traits/driver/datagram/silencer.rs | 4 +- .../driver/datagram/stm/control_point.rs | 6 +-- .../src/traits/driver/datagram/stm/foci.rs | 4 +- .../src/traits/driver/datagram/stm/gain.rs | 6 +-- .../src/traits/driver/datagram/sync.rs | 2 +- .../src/traits/driver/defined/angle.rs | 10 ++--- .../traits/driver/firmware/cpu/datagram/rx.rs | 2 +- .../traits/driver/firmware/cpu/datagram/tx.rs | 2 +- .../driver/firmware/fpga/emit_intensity.rs | 2 +- .../driver/firmware/fpga/loop_behavior.rs | 4 +- .../src/traits/driver/firmware/fpga/phase.rs | 2 +- .../driver/firmware/fpga/sampling_config.rs | 4 +- .../driver/firmware/fpga/transition_mode.rs | 12 +++--- .../src/traits/driver/geometry/mod.rs | 40 +++++++++---------- autd3-protobuf/src/traits/holo/amp.rs | 2 +- .../src/traits/holo/combinatorial/greedy.rs | 10 ++--- autd3-protobuf/src/traits/holo/constraint.rs | 2 +- .../src/traits/holo/linear_synthesis/gs.rs | 14 +++---- .../src/traits/holo/linear_synthesis/gspat.rs | 14 +++---- .../src/traits/holo/linear_synthesis/naive.rs | 14 +++---- autd3-protobuf/src/traits/holo/nls/lm.rs | 14 +++---- autd3-protobuf/src/traits/mod.rs | 2 +- 43 files changed, 151 insertions(+), 158 deletions(-) diff --git a/autd3-protobuf/Cargo.toml b/autd3-protobuf/Cargo.toml index e9613199..dd0a3cf6 100644 --- a/autd3-protobuf/Cargo.toml +++ b/autd3-protobuf/Cargo.toml @@ -10,9 +10,11 @@ license = { workspace = true } repository = { workspace = true } [dependencies] -prost = { workspace = true } +prost = { workspace = true, features = ["derive"] } tonic = { workspace = true, features = ["channel", "codegen", "prost", "server"] } +autd3-core = { workspace = true, features = ["geometry"] } autd3-driver = { workspace = true } +autd3-derive = { workspace = true, optional = true } autd3 = { workspace = true, optional = true } autd3-gain-holo = { workspace = true, optional = true } thiserror = { workspace = true } @@ -26,8 +28,8 @@ tonic-build = { workspace = true, optional = true, features = ["prost"] } [features] default = [] tonic-build = ["dep:tonic-build"] -lightweight = ["tokio", "seq-macro", "autd3", "autd3-gain-holo", "autd3-driver/derive", "autd3-driver/lightweight", "async-trait"] -async-trait = ["autd3-driver/async-trait", "autd3/async-trait", "autd3-gain-holo/async-trait"] +lightweight = ["tokio", "seq-macro", "autd3", "autd3-derive", "autd3-gain-holo", "autd3-driver/lightweight", "async-trait"] +async-trait = ["autd3-core/async-trait", "autd3/async-trait"] [dev-dependencies] approx = { workspace = true } diff --git a/autd3-protobuf/src/error.rs b/autd3-protobuf/src/error.rs index 8dbb778c..51add840 100644 --- a/autd3-protobuf/src/error.rs +++ b/autd3-protobuf/src/error.rs @@ -1,3 +1,4 @@ +use autd3_core::link::LinkError; use thiserror::Error; #[derive(Error, Debug)] @@ -46,16 +47,9 @@ impl From> for AUTDProtoBufError { } } -impl From for autd3_driver::error::AUTDDriverError { +impl From for autd3_core::link::LinkError { fn from(e: AUTDProtoBufError) -> Self { - autd3_driver::error::AUTDDriverError::LinkError(e.to_string()) - } -} - -#[cfg(feature = "lightweight")] -impl From for autd3::error::AUTDError { - fn from(e: AUTDProtoBufError) -> Self { - Self::Driver(e.into()) + LinkError::new(e.to_string()) } } diff --git a/autd3-protobuf/src/lightweight/client.rs b/autd3-protobuf/src/lightweight/client.rs index 7d464ce6..26d7d313 100644 --- a/autd3-protobuf/src/lightweight/client.rs +++ b/autd3-protobuf/src/lightweight/client.rs @@ -1,9 +1,7 @@ use std::net::SocketAddr; -use autd3_driver::{ - derive::*, - geometry::{Device, Geometry, IntoDevice}, -}; +use autd3_core::geometry::{Device, Geometry, IntoDevice}; +use autd3_derive::Builder; use crate::{traits::*, OpenRequestLightweight}; diff --git a/autd3-protobuf/src/lightweight/server.rs b/autd3-protobuf/src/lightweight/server.rs index 41083df9..376bdfbb 100644 --- a/autd3-protobuf/src/lightweight/server.rs +++ b/autd3-protobuf/src/lightweight/server.rs @@ -2,14 +2,13 @@ use std::time::Duration; use crate::{error::*, pb::*, traits::*}; -use autd3::derive::AUTDDriverError; use autd3_driver::datagram::IntoDatagramWithSegment; use tokio::sync::RwLock; use tonic::{Request, Response, Status}; #[doc(hidden)] pub struct LightweightServer< - L: autd3_driver::link::AsyncLinkBuilder + 'static, + L: autd3_core::link::AsyncLinkBuilder + 'static, F: Fn() -> L + Send + Sync + 'static, > where L::L: Sync, @@ -29,11 +28,12 @@ impl autd3_driver::datagram::Datagram for DatagramWithTimeoutAndParallelThreshold { type G = D::G; + type Error = D::Error; fn operation_generator( self, - geometry: &autd3_driver::geometry::Geometry, - ) -> Result { + geometry: &autd3_core::geometry::Geometry, + ) -> Result { self.datagram.operation_generator(geometry) } @@ -47,7 +47,7 @@ impl autd3_driver::datagram::Datagram } } -impl L + Send + Sync + 'static> +impl L + Send + Sync + 'static> LightweightServer where L::L: Sync, @@ -156,7 +156,7 @@ where } #[tonic::async_trait] -impl L + Send + Sync + 'static> +impl L + Send + Sync + 'static> ecat_light_server::EcatLight for LightweightServer where L::L: Sync, @@ -176,7 +176,7 @@ where } let req = req.into_inner(); if let Some(ref geometry) = req.geometry { - if let Ok(geometry) = autd3_driver::geometry::Geometry::from_msg(geometry) { + if let Ok(geometry) = autd3_core::geometry::Geometry::from_msg(geometry) { #[allow(unused_mut)] let mut builder = autd3::r#async::Controller::builder(geometry.iter().map(|d| { autd3::prelude::AUTD3::new(*d[0].position()).with_rotation(*d.rotation()) diff --git a/autd3-protobuf/src/traits/autd3/gain/bessel.rs b/autd3-protobuf/src/traits/autd3/gain/bessel.rs index 641d0418..2a32ac21 100644 --- a/autd3-protobuf/src/traits/autd3/gain/bessel.rs +++ b/autd3-protobuf/src/traits/autd3/gain/bessel.rs @@ -7,7 +7,7 @@ use crate::{ impl ToMessage for autd3::gain::Bessel { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Gain(Gain { gain: Some(gain::Gain::Bessel(Bessel { @@ -27,9 +27,9 @@ impl ToMessage for autd3::gain::Bessel { impl FromMessage for autd3::gain::Bessel { fn from_msg(msg: &Bessel) -> Result { let mut g = Self::new( - autd3_driver::geometry::Point3::from_msg(&msg.pos)?, - autd3_driver::geometry::UnitVector3::from_msg(&msg.dir)?, - autd3_driver::defined::Angle::from_msg(&msg.theta)?, + autd3_core::geometry::Point3::from_msg(&msg.pos)?, + autd3_core::geometry::UnitVector3::from_msg(&msg.dir)?, + autd3_core::defined::Angle::from_msg(&msg.theta)?, ); if let Some(intensity) = msg.intensity.as_ref() { g = g.with_intensity(autd3_driver::firmware::fpga::EmitIntensity::from_msg( @@ -59,7 +59,7 @@ mod tests { let g = autd3::gain::Bessel::new( Point3::new(rng.gen(), rng.gen(), rng.gen()), - autd3_driver::geometry::UnitVector3::new_normalize(Vector3::new( + autd3_core::geometry::UnitVector3::new_normalize(Vector3::new( rng.gen(), rng.gen(), rng.gen(), diff --git a/autd3-protobuf/src/traits/autd3/gain/focus.rs b/autd3-protobuf/src/traits/autd3/gain/focus.rs index a38071e2..901434ba 100644 --- a/autd3-protobuf/src/traits/autd3/gain/focus.rs +++ b/autd3-protobuf/src/traits/autd3/gain/focus.rs @@ -7,7 +7,7 @@ use crate::{ impl ToMessage for autd3::gain::Focus { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Gain(Gain { gain: Some(gain::Gain::Focus(Focus { @@ -24,7 +24,7 @@ impl ToMessage for autd3::gain::Focus { impl FromMessage for autd3::gain::Focus { fn from_msg(msg: &Focus) -> Result { - let mut g = Self::new(autd3_driver::geometry::Point3::from_msg(&msg.pos)?); + let mut g = Self::new(autd3_core::geometry::Point3::from_msg(&msg.pos)?); if let Some(intensity) = msg.intensity.as_ref() { g = g.with_intensity(autd3_driver::firmware::fpga::EmitIntensity::from_msg( intensity, diff --git a/autd3-protobuf/src/traits/autd3/gain/null.rs b/autd3-protobuf/src/traits/autd3/gain/null.rs index e9881cf6..9cccb52b 100644 --- a/autd3-protobuf/src/traits/autd3/gain/null.rs +++ b/autd3-protobuf/src/traits/autd3/gain/null.rs @@ -7,7 +7,7 @@ use crate::{ impl ToMessage for autd3::gain::Null { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Gain(Gain { gain: Some(gain::Gain::Null(Null {})), diff --git a/autd3-protobuf/src/traits/autd3/gain/plane.rs b/autd3-protobuf/src/traits/autd3/gain/plane.rs index ad0c1b1b..78c43d44 100644 --- a/autd3-protobuf/src/traits/autd3/gain/plane.rs +++ b/autd3-protobuf/src/traits/autd3/gain/plane.rs @@ -7,7 +7,7 @@ use crate::{ impl ToMessage for autd3::gain::Plane { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Gain(Gain { gain: Some(gain::Gain::Plane(Plane { @@ -24,7 +24,7 @@ impl ToMessage for autd3::gain::Plane { impl FromMessage for autd3::gain::Plane { fn from_msg(msg: &Plane) -> Result { - let mut g = Self::new(autd3_driver::geometry::UnitVector3::from_msg(&msg.dir)?); + let mut g = Self::new(autd3_core::geometry::UnitVector3::from_msg(&msg.dir)?); if let Some(intensity) = msg.intensity.as_ref() { g = g.with_intensity(autd3_driver::firmware::fpga::EmitIntensity::from_msg( intensity, diff --git a/autd3-protobuf/src/traits/autd3/gain/uniform.rs b/autd3-protobuf/src/traits/autd3/gain/uniform.rs index fb05d2ae..091d9475 100644 --- a/autd3-protobuf/src/traits/autd3/gain/uniform.rs +++ b/autd3-protobuf/src/traits/autd3/gain/uniform.rs @@ -7,7 +7,7 @@ use crate::{ impl ToMessage for autd3::gain::Uniform { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Gain(Gain { gain: Some(gain::Gain::Uniform(Uniform { diff --git a/autd3-protobuf/src/traits/autd3/modulation/sine/exact.rs b/autd3-protobuf/src/traits/autd3/modulation/sine/exact.rs index 3519cd9c..34028666 100644 --- a/autd3-protobuf/src/traits/autd3/modulation/sine/exact.rs +++ b/autd3-protobuf/src/traits/autd3/modulation/sine/exact.rs @@ -1,4 +1,4 @@ -use autd3_driver::derive::ModulationProperty; +use autd3_core::modulation::ModulationProperty; use crate::{ pb::*, @@ -9,7 +9,7 @@ use crate::{ impl ToMessage for autd3::modulation::Sine { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Modulation(Modulation { modulation: Some(modulation::Modulation::SineExact(SineExact { @@ -31,7 +31,7 @@ impl FromMessage for autd3::modulation::Sine { fn from_msg(msg: &SineExact) -> Result { - let mut sine = autd3::modulation::Sine::new(msg.freq * autd3_driver::defined::Hz); + let mut sine = autd3::modulation::Sine::new(msg.freq * autd3_core::defined::Hz); if let Some(intensity) = msg.intensity { sine = sine.with_intensity(intensity as _); } @@ -39,7 +39,7 @@ impl FromMessage sine = sine.with_offset(offset as _); } if msg.phase.is_some() { - sine = sine.with_phase(autd3_driver::defined::Angle::from_msg(&msg.phase)?); + sine = sine.with_phase(autd3_core::defined::Angle::from_msg(&msg.phase)?); } if let Some(config) = msg.config.as_ref() { sine = sine.with_sampling_config( @@ -54,7 +54,7 @@ impl FromMessage mod tests { use super::*; use autd3::modulation::sampling_mode::ExactFreq; - use autd3_driver::defined::{rad, Hz}; + use autd3_core::defined::{rad, Hz}; use rand::Rng; #[test] diff --git a/autd3-protobuf/src/traits/autd3/modulation/sine/exact_float.rs b/autd3-protobuf/src/traits/autd3/modulation/sine/exact_float.rs index 04a24897..6547a69b 100644 --- a/autd3-protobuf/src/traits/autd3/modulation/sine/exact_float.rs +++ b/autd3-protobuf/src/traits/autd3/modulation/sine/exact_float.rs @@ -1,4 +1,4 @@ -use autd3_driver::derive::ModulationProperty; +use autd3_core::modulation::ModulationProperty; use crate::{ pb::*, @@ -9,7 +9,7 @@ use crate::{ impl ToMessage for autd3::modulation::Sine { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Modulation(Modulation { modulation: Some(modulation::Modulation::SineExactFloat(SineExactFloat { @@ -31,7 +31,7 @@ impl FromMessage for autd3::modulation::Sine { fn from_msg(msg: &SineExactFloat) -> Result { - let mut sine = autd3::modulation::Sine::new(msg.freq * autd3_driver::defined::Hz); + let mut sine = autd3::modulation::Sine::new(msg.freq * autd3_core::defined::Hz); if let Some(intensity) = msg.intensity { sine = sine.with_intensity(intensity as _); } @@ -39,7 +39,7 @@ impl FromMessage sine = sine.with_offset(offset as _); } if msg.phase.is_some() { - sine = sine.with_phase(autd3_driver::defined::Angle::from_msg(&msg.phase)?); + sine = sine.with_phase(autd3_core::defined::Angle::from_msg(&msg.phase)?); } if let Some(config) = msg.config.as_ref() { sine = sine.with_sampling_config( @@ -54,7 +54,7 @@ impl FromMessage mod tests { use super::*; use autd3::modulation::sampling_mode::ExactFreqFloat; - use autd3_driver::defined::{rad, Hz}; + use autd3_core::defined::{rad, Hz}; use rand::Rng; #[test] diff --git a/autd3-protobuf/src/traits/autd3/modulation/sine/nearest.rs b/autd3-protobuf/src/traits/autd3/modulation/sine/nearest.rs index 77e73047..aef69f03 100644 --- a/autd3-protobuf/src/traits/autd3/modulation/sine/nearest.rs +++ b/autd3-protobuf/src/traits/autd3/modulation/sine/nearest.rs @@ -1,4 +1,4 @@ -use autd3_driver::derive::ModulationProperty; +use autd3_core::modulation::ModulationProperty; use crate::{ pb::*, @@ -9,7 +9,7 @@ use crate::{ impl ToMessage for autd3::modulation::Sine { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Modulation(Modulation { modulation: Some(modulation::Modulation::SineNearest(SineNearest { @@ -31,7 +31,7 @@ impl FromMessage for autd3::modulation::Sine { fn from_msg(msg: &SineNearest) -> Result { - let mut sine = autd3::modulation::Sine::new_nearest(msg.freq * autd3_driver::defined::Hz); + let mut sine = autd3::modulation::Sine::new_nearest(msg.freq * autd3_core::defined::Hz); if let Some(intensity) = msg.intensity { sine = sine.with_intensity(intensity as _); } @@ -39,7 +39,7 @@ impl FromMessage sine = sine.with_offset(offset as _); } if msg.phase.is_some() { - sine = sine.with_phase(autd3_driver::defined::Angle::from_msg(&msg.phase)?); + sine = sine.with_phase(autd3_core::defined::Angle::from_msg(&msg.phase)?); } if let Some(config) = msg.config.as_ref() { sine = sine.with_sampling_config( @@ -54,7 +54,7 @@ impl FromMessage mod tests { use super::*; use autd3::modulation::sampling_mode::NearestFreq; - use autd3_driver::defined::{rad, Hz}; + use autd3_core::defined::{rad, Hz}; use rand::Rng; #[test] diff --git a/autd3-protobuf/src/traits/autd3/modulation/square/exact.rs b/autd3-protobuf/src/traits/autd3/modulation/square/exact.rs index 4ed208e9..22c5af27 100644 --- a/autd3-protobuf/src/traits/autd3/modulation/square/exact.rs +++ b/autd3-protobuf/src/traits/autd3/modulation/square/exact.rs @@ -1,4 +1,4 @@ -use autd3_driver::derive::ModulationProperty; +use autd3_core::modulation::ModulationProperty; use crate::{ pb::*, @@ -9,7 +9,7 @@ use crate::{ impl ToMessage for autd3::modulation::Square { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Modulation(Modulation { modulation: Some(modulation::Modulation::SquareExact(SquareExact { @@ -31,7 +31,7 @@ impl FromMessage for autd3::modulation::Square { fn from_msg(msg: &SquareExact) -> Result { - let mut square = autd3::modulation::Square::new(msg.freq * autd3_driver::defined::Hz); + let mut square = autd3::modulation::Square::new(msg.freq * autd3_core::defined::Hz); if let Some(high) = msg.high { square = square.with_high(high as _); } @@ -54,7 +54,7 @@ impl FromMessage mod tests { use super::*; use autd3::modulation::sampling_mode::ExactFreq; - use autd3_driver::defined::Hz; + use autd3_core::defined::Hz; use rand::Rng; #[test] diff --git a/autd3-protobuf/src/traits/autd3/modulation/square/exact_float.rs b/autd3-protobuf/src/traits/autd3/modulation/square/exact_float.rs index 1cfb1e0a..fc8786b4 100644 --- a/autd3-protobuf/src/traits/autd3/modulation/square/exact_float.rs +++ b/autd3-protobuf/src/traits/autd3/modulation/square/exact_float.rs @@ -1,4 +1,4 @@ -use autd3_driver::derive::ModulationProperty; +use autd3_core::modulation::ModulationProperty; use crate::{ pb::*, @@ -9,7 +9,7 @@ use crate::{ impl ToMessage for autd3::modulation::Square { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Modulation(Modulation { modulation: Some(modulation::Modulation::SquareExactFloat(SquareExactFloat { @@ -31,7 +31,7 @@ impl FromMessage for autd3::modulation::Square { fn from_msg(msg: &SquareExactFloat) -> Result { - let mut square = autd3::modulation::Square::new(msg.freq * autd3_driver::defined::Hz); + let mut square = autd3::modulation::Square::new(msg.freq * autd3_core::defined::Hz); if let Some(high) = msg.high { square = square.with_high(high as _); } @@ -54,7 +54,7 @@ impl FromMessage mod tests { use super::*; use autd3::modulation::sampling_mode::ExactFreqFloat; - use autd3_driver::defined::Hz; + use autd3_core::defined::Hz; use rand::Rng; #[test] diff --git a/autd3-protobuf/src/traits/autd3/modulation/square/nearest.rs b/autd3-protobuf/src/traits/autd3/modulation/square/nearest.rs index 6dbe2b68..18930685 100644 --- a/autd3-protobuf/src/traits/autd3/modulation/square/nearest.rs +++ b/autd3-protobuf/src/traits/autd3/modulation/square/nearest.rs @@ -1,4 +1,4 @@ -use autd3_driver::derive::ModulationProperty; +use autd3_core::modulation::ModulationProperty; use crate::{ pb::*, @@ -9,7 +9,7 @@ use crate::{ impl ToMessage for autd3::modulation::Square { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Modulation(Modulation { modulation: Some(modulation::Modulation::SquareNearest(SquareNearest { @@ -31,8 +31,7 @@ impl FromMessage for autd3::modulation::Square { fn from_msg(msg: &SquareNearest) -> Result { - let mut square = - autd3::modulation::Square::new_nearest(msg.freq * autd3_driver::defined::Hz); + let mut square = autd3::modulation::Square::new_nearest(msg.freq * autd3_core::defined::Hz); if let Some(high) = msg.high { square = square.with_high(high as _); } @@ -55,7 +54,7 @@ impl FromMessage mod tests { use super::*; use autd3::modulation::sampling_mode::NearestFreq; - use autd3_driver::defined::Hz; + use autd3_core::defined::Hz; use rand::Rng; #[test] diff --git a/autd3-protobuf/src/traits/autd3/modulation/static.rs b/autd3-protobuf/src/traits/autd3/modulation/static.rs index b745e3f4..7b154484 100644 --- a/autd3-protobuf/src/traits/autd3/modulation/static.rs +++ b/autd3-protobuf/src/traits/autd3/modulation/static.rs @@ -1,4 +1,4 @@ -use autd3_driver::derive::ModulationProperty; +use autd3_core::modulation::ModulationProperty; use crate::{ pb::*, @@ -9,7 +9,7 @@ use crate::{ impl ToMessage for autd3::modulation::Static { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Modulation(Modulation { modulation: Some(modulation::Modulation::Static(Static { diff --git a/autd3-protobuf/src/traits/driver/datagram/clear.rs b/autd3-protobuf/src/traits/driver/datagram/clear.rs index ac669f5e..40fb28bc 100644 --- a/autd3-protobuf/src/traits/driver/datagram/clear.rs +++ b/autd3-protobuf/src/traits/driver/datagram/clear.rs @@ -7,7 +7,7 @@ use crate::{ impl ToMessage for autd3_driver::datagram::Clear { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Clear(Clear {})), parallel_threshold: None, diff --git a/autd3-protobuf/src/traits/driver/datagram/force_fan.rs b/autd3-protobuf/src/traits/driver/datagram/force_fan.rs index cf5eda5c..11ad404e 100644 --- a/autd3-protobuf/src/traits/driver/datagram/force_fan.rs +++ b/autd3-protobuf/src/traits/driver/datagram/force_fan.rs @@ -1,4 +1,4 @@ -use autd3_driver::geometry::Device; +use autd3_core::geometry::Device; use crate::{ pb::*, @@ -9,7 +9,7 @@ use crate::{ impl bool> ToMessage for autd3_driver::datagram::ForceFan { type Message = Datagram; - fn to_msg(&self, geometry: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, geometry: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::ForceFan(ForceFan { value: geometry diff --git a/autd3-protobuf/src/traits/driver/datagram/mod.rs b/autd3-protobuf/src/traits/driver/datagram/mod.rs index 8a1ff5c0..34cfaec0 100644 --- a/autd3-protobuf/src/traits/driver/datagram/mod.rs +++ b/autd3-protobuf/src/traits/driver/datagram/mod.rs @@ -12,11 +12,11 @@ use crate::{pb::*, traits::ToMessage}; impl ToMessage for autd3_driver::datagram::DatagramWithSegment where - T: autd3_driver::datagram::DatagramS + ToMessage, + T: autd3_core::datagram::DatagramS + ToMessage, { type Message = Datagram; - fn to_msg(&self, geometry: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, geometry: Option<&autd3_core::geometry::Geometry>) -> Self::Message { let datagram = ::to_msg(self.deref(), geometry); match datagram.datagram { diff --git a/autd3-protobuf/src/traits/driver/datagram/reads_fpga_state.rs b/autd3-protobuf/src/traits/driver/datagram/reads_fpga_state.rs index 213bd8f8..3cd0a809 100644 --- a/autd3-protobuf/src/traits/driver/datagram/reads_fpga_state.rs +++ b/autd3-protobuf/src/traits/driver/datagram/reads_fpga_state.rs @@ -1,4 +1,4 @@ -use autd3_driver::geometry::Device; +use autd3_core::geometry::Device; use crate::{ pb::*, @@ -9,7 +9,7 @@ use crate::{ impl bool> ToMessage for autd3_driver::datagram::ReadsFPGAState { type Message = Datagram; - fn to_msg(&self, geometry: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, geometry: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::ReadsFpgaState(ReadsFpgaState { value: geometry diff --git a/autd3-protobuf/src/traits/driver/datagram/segment.rs b/autd3-protobuf/src/traits/driver/datagram/segment.rs index a4d0e53f..be61f373 100644 --- a/autd3-protobuf/src/traits/driver/datagram/segment.rs +++ b/autd3-protobuf/src/traits/driver/datagram/segment.rs @@ -7,7 +7,7 @@ use crate::{ impl ToMessage for autd3_driver::datagram::SwapSegment { type Message = SwapSegment; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { inner: Some(match self { autd3_driver::datagram::SwapSegment::Gain(segment, transition) => { diff --git a/autd3-protobuf/src/traits/driver/datagram/silencer.rs b/autd3-protobuf/src/traits/driver/datagram/silencer.rs index 56091895..9d8053ac 100644 --- a/autd3-protobuf/src/traits/driver/datagram/silencer.rs +++ b/autd3-protobuf/src/traits/driver/datagram/silencer.rs @@ -19,7 +19,7 @@ fn silencer_target_to(v: i32) -> Result { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Silencer(Silencer { config: Some(silencer::Config::FixedUpdateRate(SilencerFixedUpdateRate { @@ -55,7 +55,7 @@ impl FromMessage impl ToMessage for autd3_driver::datagram::Silencer { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Silencer(Silencer { config: Some(silencer::Config::FixedCompletionTime( diff --git a/autd3-protobuf/src/traits/driver/datagram/stm/control_point.rs b/autd3-protobuf/src/traits/driver/datagram/stm/control_point.rs index d596f7d2..64570a49 100644 --- a/autd3-protobuf/src/traits/driver/datagram/stm/control_point.rs +++ b/autd3-protobuf/src/traits/driver/datagram/stm/control_point.rs @@ -7,7 +7,7 @@ use crate::{ impl ToMessage for autd3_driver::datagram::ControlPoint { type Message = ControlPoint; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { pos: Some(self.point().to_msg(None)), offset: Some(self.phase_offset().to_msg(None)), @@ -18,7 +18,7 @@ impl ToMessage for autd3_driver::datagram::ControlPoint { impl FromMessage for autd3_driver::datagram::ControlPoint { fn from_msg(msg: &ControlPoint) -> Result { let mut p = autd3_driver::datagram::ControlPoint::from( - autd3_driver::geometry::Point3::from_msg(&msg.pos)?, + autd3_core::geometry::Point3::from_msg(&msg.pos)?, ); if let Some(offset) = msg.offset.as_ref() { p = p.with_phase_offset(autd3_driver::firmware::fpga::Phase::from_msg(offset)?); @@ -30,7 +30,7 @@ impl FromMessage for autd3_driver::datagram::ControlPoint { impl ToMessage for autd3_driver::datagram::ControlPoints { type Message = ControlPoints; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { points: self.iter().map(|p| p.to_msg(None)).collect(), intensity: Some(self.intensity().to_msg(None)), diff --git a/autd3-protobuf/src/traits/driver/datagram/stm/foci.rs b/autd3-protobuf/src/traits/driver/datagram/stm/foci.rs index f6942de3..6293c7d9 100644 --- a/autd3-protobuf/src/traits/driver/datagram/stm/foci.rs +++ b/autd3-protobuf/src/traits/driver/datagram/stm/foci.rs @@ -1,4 +1,4 @@ -use autd3_driver::derive::SamplingConfig; +use autd3_driver::firmware::fpga::SamplingConfig; use crate::{pb::*, traits::*}; @@ -7,7 +7,7 @@ seq_macro::seq!(N in 1..=8 { impl ToMessage for autd3_driver::datagram::FociSTM>> { type Message = FociStm~N; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { props: Some(FociStmProps { config: Some(self.sampling_config().to_msg(None)), diff --git a/autd3-protobuf/src/traits/driver/datagram/stm/gain.rs b/autd3-protobuf/src/traits/driver/datagram/stm/gain.rs index 0c45ab93..d5fae3ee 100644 --- a/autd3-protobuf/src/traits/driver/datagram/stm/gain.rs +++ b/autd3-protobuf/src/traits/driver/datagram/stm/gain.rs @@ -1,4 +1,4 @@ -use autd3_driver::derive::SamplingConfig; +use autd3_driver::firmware::fpga::SamplingConfig; use crate::{ pb::*, @@ -8,11 +8,11 @@ use crate::{ impl ToMessage for autd3_driver::datagram::GainSTM> where - G: autd3_driver::datagram::Gain + ToMessage, + G: autd3_core::gain::Gain + ToMessage, { type Message = GainStm; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { config: Some(self.sampling_config().to_msg(None)), loop_behavior: Some(self.loop_behavior().to_msg(None)), diff --git a/autd3-protobuf/src/traits/driver/datagram/sync.rs b/autd3-protobuf/src/traits/driver/datagram/sync.rs index 69d8ca21..cd74a740 100644 --- a/autd3-protobuf/src/traits/driver/datagram/sync.rs +++ b/autd3-protobuf/src/traits/driver/datagram/sync.rs @@ -7,7 +7,7 @@ use crate::{ impl ToMessage for autd3_driver::datagram::Synchronize { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Synchronize(Synchronize {})), parallel_threshold: None, diff --git a/autd3-protobuf/src/traits/driver/defined/angle.rs b/autd3-protobuf/src/traits/driver/defined/angle.rs index f05a614c..46129990 100644 --- a/autd3-protobuf/src/traits/driver/defined/angle.rs +++ b/autd3-protobuf/src/traits/driver/defined/angle.rs @@ -4,19 +4,19 @@ use crate::{ AUTDProtoBufError, }; -impl ToMessage for autd3_driver::defined::Angle { +impl ToMessage for autd3_core::defined::Angle { type Message = Angle; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { rad: self.radian() as _, } } } -impl FromMessage> for autd3_driver::defined::Angle { +impl FromMessage> for autd3_core::defined::Angle { fn from_msg(msg: &Option) -> Result { - msg.map(|msg| msg.rad * autd3_driver::defined::rad) + msg.map(|msg| msg.rad * autd3_core::defined::rad) .ok_or(AUTDProtoBufError::DataParseError) } } @@ -24,7 +24,7 @@ impl FromMessage> for autd3_driver::defined::Angle { #[cfg(test)] mod tests { use super::*; - use autd3_driver::defined::{rad, Angle}; + use autd3_core::defined::{rad, Angle}; use rand::Rng; #[test] diff --git a/autd3-protobuf/src/traits/driver/firmware/cpu/datagram/rx.rs b/autd3-protobuf/src/traits/driver/firmware/cpu/datagram/rx.rs index 4a784165..8ff2c1ae 100644 --- a/autd3-protobuf/src/traits/driver/firmware/cpu/datagram/rx.rs +++ b/autd3-protobuf/src/traits/driver/firmware/cpu/datagram/rx.rs @@ -9,7 +9,7 @@ use zerocopy::{FromBytes, IntoBytes}; impl ToMessage for Vec { type Message = RxMessage; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { data: self.as_bytes().to_vec(), } diff --git a/autd3-protobuf/src/traits/driver/firmware/cpu/datagram/tx.rs b/autd3-protobuf/src/traits/driver/firmware/cpu/datagram/tx.rs index dd95df10..24decd40 100644 --- a/autd3-protobuf/src/traits/driver/firmware/cpu/datagram/tx.rs +++ b/autd3-protobuf/src/traits/driver/firmware/cpu/datagram/tx.rs @@ -9,7 +9,7 @@ use zerocopy::{FromZeros, IntoBytes}; impl ToMessage for &[autd3_driver::firmware::cpu::TxMessage] { type Message = TxRawData; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { data: self.as_bytes().to_vec(), n: self.len() as _, diff --git a/autd3-protobuf/src/traits/driver/firmware/fpga/emit_intensity.rs b/autd3-protobuf/src/traits/driver/firmware/fpga/emit_intensity.rs index 73500c5f..d188b8c9 100644 --- a/autd3-protobuf/src/traits/driver/firmware/fpga/emit_intensity.rs +++ b/autd3-protobuf/src/traits/driver/firmware/fpga/emit_intensity.rs @@ -7,7 +7,7 @@ use crate::{ impl ToMessage for autd3_driver::firmware::fpga::EmitIntensity { type Message = EmitIntensity; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { value: self.value() as _, } diff --git a/autd3-protobuf/src/traits/driver/firmware/fpga/loop_behavior.rs b/autd3-protobuf/src/traits/driver/firmware/fpga/loop_behavior.rs index 3ff55d36..43c8fdf7 100644 --- a/autd3-protobuf/src/traits/driver/firmware/fpga/loop_behavior.rs +++ b/autd3-protobuf/src/traits/driver/firmware/fpga/loop_behavior.rs @@ -7,7 +7,7 @@ use crate::{ impl ToMessage for autd3_driver::firmware::fpga::LoopBehavior { type Message = LoopBehavior; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { rep: self.rep() as _, } @@ -26,7 +26,7 @@ impl FromMessage for autd3_driver::firmware::fpga::LoopBehavior { #[cfg(test)] mod tests { use super::*; - use autd3_driver::derive::LoopBehavior; + use autd3_driver::firmware::fpga::LoopBehavior; use rand::Rng; #[test] diff --git a/autd3-protobuf/src/traits/driver/firmware/fpga/phase.rs b/autd3-protobuf/src/traits/driver/firmware/fpga/phase.rs index 4362fc11..af523681 100644 --- a/autd3-protobuf/src/traits/driver/firmware/fpga/phase.rs +++ b/autd3-protobuf/src/traits/driver/firmware/fpga/phase.rs @@ -7,7 +7,7 @@ use crate::{ impl ToMessage for autd3_driver::firmware::fpga::Phase { type Message = Phase; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { value: self.value() as _, } diff --git a/autd3-protobuf/src/traits/driver/firmware/fpga/sampling_config.rs b/autd3-protobuf/src/traits/driver/firmware/fpga/sampling_config.rs index 380d59c8..c7fb7a19 100644 --- a/autd3-protobuf/src/traits/driver/firmware/fpga/sampling_config.rs +++ b/autd3-protobuf/src/traits/driver/firmware/fpga/sampling_config.rs @@ -7,7 +7,7 @@ use crate::{ impl ToMessage for autd3_driver::firmware::fpga::SamplingConfig { type Message = SamplingConfig; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { div: self.division() as _, } @@ -23,7 +23,7 @@ impl FromMessage for autd3_driver::firmware::fpga::SamplingConfi #[cfg(test)] mod tests { use super::*; - use autd3_driver::derive::SamplingConfig; + use autd3_driver::firmware::fpga::SamplingConfig; use rand::Rng; #[test] diff --git a/autd3-protobuf/src/traits/driver/firmware/fpga/transition_mode.rs b/autd3-protobuf/src/traits/driver/firmware/fpga/transition_mode.rs index 95613982..fc47c5e5 100644 --- a/autd3-protobuf/src/traits/driver/firmware/fpga/transition_mode.rs +++ b/autd3-protobuf/src/traits/driver/firmware/fpga/transition_mode.rs @@ -9,24 +9,24 @@ use crate::{ impl ToMessage for autd3_driver::firmware::fpga::TransitionMode { type Message = TransitionMode; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { mode: Some(match *self { - autd3::derive::TransitionMode::SyncIdx => { + autd3_driver::firmware::fpga::TransitionMode::SyncIdx => { transition_mode::Mode::SyncIdx(TransitionModeSyncIdx {}) } - autd3::derive::TransitionMode::SysTime(value) => { + autd3_driver::firmware::fpga::TransitionMode::SysTime(value) => { transition_mode::Mode::SysTime(TransitionModeSysTime { value: value.sys_time(), }) } - autd3::derive::TransitionMode::GPIO(value) => { + autd3_driver::firmware::fpga::TransitionMode::GPIO(value) => { transition_mode::Mode::Gpio(TransitionModeGpio { value: value as _ }) } - autd3::derive::TransitionMode::Ext => { + autd3_driver::firmware::fpga::TransitionMode::Ext => { transition_mode::Mode::Ext(TransitionModeExt {}) } - autd3::derive::TransitionMode::Immediate => { + autd3_driver::firmware::fpga::TransitionMode::Immediate => { transition_mode::Mode::Immediate(TransitionModeImmediate {}) } _ => unimplemented!(), diff --git a/autd3-protobuf/src/traits/driver/geometry/mod.rs b/autd3-protobuf/src/traits/driver/geometry/mod.rs index fe7f9476..9cb77980 100644 --- a/autd3-protobuf/src/traits/driver/geometry/mod.rs +++ b/autd3-protobuf/src/traits/driver/geometry/mod.rs @@ -1,4 +1,4 @@ -use autd3_driver::geometry::IntoDevice; +use autd3_core::geometry::IntoDevice; use crate::{ pb::*, @@ -6,10 +6,10 @@ use crate::{ AUTDProtoBufError, }; -impl ToMessage for autd3_driver::geometry::UnitVector3 { +impl ToMessage for autd3_core::geometry::UnitVector3 { type Message = UnitVector3; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { x: self.x as _, y: self.y as _, @@ -18,10 +18,10 @@ impl ToMessage for autd3_driver::geometry::UnitVector3 { } } -impl ToMessage for autd3_driver::geometry::Point3 { +impl ToMessage for autd3_core::geometry::Point3 { type Message = Point3; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { x: self.x as _, y: self.y as _, @@ -30,10 +30,10 @@ impl ToMessage for autd3_driver::geometry::Point3 { } } -impl ToMessage for autd3_driver::geometry::Quaternion { +impl ToMessage for autd3_core::geometry::Quaternion { type Message = Quaternion; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { w: self.w as _, x: self.coords.x as _, @@ -43,10 +43,10 @@ impl ToMessage for autd3_driver::geometry::Quaternion { } } -impl ToMessage for autd3_driver::geometry::Geometry { +impl ToMessage for autd3_core::geometry::Geometry { type Message = Geometry; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { devices: self .iter() @@ -61,32 +61,32 @@ impl ToMessage for autd3_driver::geometry::Geometry { } } -impl FromMessage> for autd3_driver::geometry::UnitVector3 { +impl FromMessage> for autd3_core::geometry::UnitVector3 { fn from_msg(msg: &Option) -> Result { msg.as_ref() .map(|msg| { - autd3_driver::geometry::UnitVector3::new_unchecked( - autd3_driver::geometry::Vector3::new(msg.x as _, msg.y as _, msg.z as _), + autd3_core::geometry::UnitVector3::new_unchecked( + autd3_core::geometry::Vector3::new(msg.x as _, msg.y as _, msg.z as _), ) }) .ok_or(AUTDProtoBufError::DataParseError) } } -impl FromMessage> for autd3_driver::geometry::Point3 { +impl FromMessage> for autd3_core::geometry::Point3 { fn from_msg(msg: &Option) -> Result { msg.as_ref() - .map(|msg| autd3_driver::geometry::Point3::new(msg.x as _, msg.y as _, msg.z as _)) + .map(|msg| autd3_core::geometry::Point3::new(msg.x as _, msg.y as _, msg.z as _)) .ok_or(AUTDProtoBufError::DataParseError) } } -impl FromMessage> for autd3_driver::geometry::UnitQuaternion { +impl FromMessage> for autd3_core::geometry::UnitQuaternion { fn from_msg(msg: &Option) -> Result { msg.as_ref() .map(|msg| { - autd3_driver::geometry::UnitQuaternion::from_quaternion( - autd3_driver::geometry::Quaternion::new( + autd3_core::geometry::UnitQuaternion::from_quaternion( + autd3_core::geometry::Quaternion::new( msg.w as _, msg.x as _, msg.y as _, msg.z as _, ), ) @@ -95,14 +95,14 @@ impl FromMessage> for autd3_driver::geometry::UnitQuaternion } } -impl FromMessage for autd3_driver::geometry::Geometry { +impl FromMessage for autd3_core::geometry::Geometry { fn from_msg(msg: &Geometry) -> Result { msg.devices .iter() .enumerate() .map(|(i, dev_msg)| { - let pos = autd3_driver::geometry::Point3::from_msg(&dev_msg.pos)?; - let rot = autd3_driver::geometry::UnitQuaternion::from_msg(&dev_msg.rot)?; + let pos = autd3_core::geometry::Point3::from_msg(&dev_msg.pos)?; + let rot = autd3_core::geometry::UnitQuaternion::from_msg(&dev_msg.rot)?; let mut dev = autd3_driver::autd3_device::AUTD3::new(pos) .with_rotation(rot) .into_device(i as _); diff --git a/autd3-protobuf/src/traits/holo/amp.rs b/autd3-protobuf/src/traits/holo/amp.rs index 805da63a..53b015b3 100644 --- a/autd3-protobuf/src/traits/holo/amp.rs +++ b/autd3-protobuf/src/traits/holo/amp.rs @@ -7,7 +7,7 @@ use crate::{ impl ToMessage for autd3_gain_holo::Amplitude { type Message = Amplitude; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { value: self.pascal() as _, } diff --git a/autd3-protobuf/src/traits/holo/combinatorial/greedy.rs b/autd3-protobuf/src/traits/holo/combinatorial/greedy.rs index 19eb8593..840b037f 100644 --- a/autd3-protobuf/src/traits/holo/combinatorial/greedy.rs +++ b/autd3-protobuf/src/traits/holo/combinatorial/greedy.rs @@ -7,10 +7,10 @@ use crate::{ AUTDProtoBufError, }; -impl ToMessage for autd3_gain_holo::Greedy { +impl ToMessage for autd3_gain_holo::Greedy { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Gain(Gain { gain: Some(gain::Gain::Greedy(Greedy { @@ -25,14 +25,14 @@ impl ToMessage for autd3_gain_holo::Greedy for autd3_gain_holo::Greedy { +impl FromMessage for autd3_gain_holo::Greedy { fn from_msg(msg: &Greedy) -> Result { let mut g = Self::new( msg.holo .iter() .map(|h| { Ok(( - autd3_driver::geometry::Point3::from_msg(&h.pos)?, + autd3_core::geometry::Point3::from_msg(&h.pos)?, autd3_gain_holo::Amplitude::from_msg(&h.amp)?, )) }) @@ -53,7 +53,7 @@ impl FromMessage for autd3_gain_holo::Greedy) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { match self { autd3_gain_holo::EmissionConstraint::Normalize => Self::Message { constraint: Some(emission_constraint::Constraint::Normalize( diff --git a/autd3-protobuf/src/traits/holo/linear_synthesis/gs.rs b/autd3-protobuf/src/traits/holo/linear_synthesis/gs.rs index 51c1e9a9..ce0a9faf 100644 --- a/autd3-protobuf/src/traits/holo/linear_synthesis/gs.rs +++ b/autd3-protobuf/src/traits/holo/linear_synthesis/gs.rs @@ -11,13 +11,13 @@ use crate::{ impl ToMessage for autd3_gain_holo::GS< - autd3_driver::acoustics::directivity::Sphere, - NalgebraBackend, + autd3_core::acoustics::directivity::Sphere, + NalgebraBackend, > { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Gain(Gain { gain: Some(gain::Gain::Gs(Gs { @@ -34,8 +34,8 @@ impl ToMessage impl FromMessage for autd3_gain_holo::GS< - autd3_driver::acoustics::directivity::Sphere, - NalgebraBackend, + autd3_core::acoustics::directivity::Sphere, + NalgebraBackend, > { fn from_msg(msg: &Gs) -> Result { @@ -45,7 +45,7 @@ impl FromMessage .iter() .map(|h| { Ok(( - autd3_driver::geometry::Point3::from_msg(&h.pos)?, + autd3_core::geometry::Point3::from_msg(&h.pos)?, autd3_gain_holo::Amplitude::from_msg(&h.amp)?, )) }) @@ -66,7 +66,7 @@ impl FromMessage #[cfg(test)] mod tests { use super::*; - use autd3_driver::geometry::Point3; + use autd3_core::geometry::Point3; use rand::Rng; #[test] diff --git a/autd3-protobuf/src/traits/holo/linear_synthesis/gspat.rs b/autd3-protobuf/src/traits/holo/linear_synthesis/gspat.rs index 41780687..767257f1 100644 --- a/autd3-protobuf/src/traits/holo/linear_synthesis/gspat.rs +++ b/autd3-protobuf/src/traits/holo/linear_synthesis/gspat.rs @@ -11,13 +11,13 @@ use crate::{ impl ToMessage for autd3_gain_holo::GSPAT< - autd3_driver::acoustics::directivity::Sphere, - NalgebraBackend, + autd3_core::acoustics::directivity::Sphere, + NalgebraBackend, > { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Gain(Gain { gain: Some(gain::Gain::Gspat(Gspat { @@ -34,8 +34,8 @@ impl ToMessage impl FromMessage for autd3_gain_holo::GSPAT< - autd3_driver::acoustics::directivity::Sphere, - NalgebraBackend, + autd3_core::acoustics::directivity::Sphere, + NalgebraBackend, > { fn from_msg(msg: &Gspat) -> Result { @@ -45,7 +45,7 @@ impl FromMessage .iter() .map(|h| { Ok(( - autd3_driver::geometry::Point3::from_msg(&h.pos)?, + autd3_core::geometry::Point3::from_msg(&h.pos)?, autd3_gain_holo::Amplitude::from_msg(&h.amp)?, )) }) @@ -66,7 +66,7 @@ impl FromMessage #[cfg(test)] mod tests { use super::*; - use autd3_driver::geometry::Point3; + use autd3_core::geometry::Point3; use rand::Rng; #[test] diff --git a/autd3-protobuf/src/traits/holo/linear_synthesis/naive.rs b/autd3-protobuf/src/traits/holo/linear_synthesis/naive.rs index 409892b5..7aad0180 100644 --- a/autd3-protobuf/src/traits/holo/linear_synthesis/naive.rs +++ b/autd3-protobuf/src/traits/holo/linear_synthesis/naive.rs @@ -9,13 +9,13 @@ use crate::{ impl ToMessage for autd3_gain_holo::Naive< - autd3_driver::acoustics::directivity::Sphere, - NalgebraBackend, + autd3_core::acoustics::directivity::Sphere, + NalgebraBackend, > { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Gain(Gain { gain: Some(gain::Gain::Naive(Naive { @@ -31,8 +31,8 @@ impl ToMessage impl FromMessage for autd3_gain_holo::Naive< - autd3_driver::acoustics::directivity::Sphere, - NalgebraBackend, + autd3_core::acoustics::directivity::Sphere, + NalgebraBackend, > { fn from_msg(msg: &Naive) -> Result { @@ -42,7 +42,7 @@ impl FromMessage .iter() .map(|h| { Ok(( - autd3_driver::geometry::Point3::from_msg(&h.pos)?, + autd3_core::geometry::Point3::from_msg(&h.pos)?, autd3_gain_holo::Amplitude::from_msg(&h.amp)?, )) }) @@ -58,7 +58,7 @@ impl FromMessage #[cfg(test)] mod tests { use super::*; - use autd3_driver::geometry::Point3; + use autd3_core::geometry::Point3; use rand::Rng; #[test] diff --git a/autd3-protobuf/src/traits/holo/nls/lm.rs b/autd3-protobuf/src/traits/holo/nls/lm.rs index 8549fdde..3d0ddfe6 100644 --- a/autd3-protobuf/src/traits/holo/nls/lm.rs +++ b/autd3-protobuf/src/traits/holo/nls/lm.rs @@ -11,13 +11,13 @@ use crate::{ impl ToMessage for autd3_gain_holo::LM< - autd3_driver::acoustics::directivity::Sphere, - NalgebraBackend, + autd3_core::acoustics::directivity::Sphere, + NalgebraBackend, > { type Message = Datagram; - fn to_msg(&self, _: Option<&autd3_driver::geometry::Geometry>) -> Self::Message { + fn to_msg(&self, _: Option<&autd3_core::geometry::Geometry>) -> Self::Message { Self::Message { datagram: Some(datagram::Datagram::Gain(Gain { gain: Some(gain::Gain::Lm(Lm { @@ -38,8 +38,8 @@ impl ToMessage impl FromMessage for autd3_gain_holo::LM< - autd3_driver::acoustics::directivity::Sphere, - NalgebraBackend, + autd3_core::acoustics::directivity::Sphere, + NalgebraBackend, > { fn from_msg(msg: &Lm) -> Result { @@ -49,7 +49,7 @@ impl FromMessage .iter() .map(|h| { Ok(( - autd3_driver::geometry::Point3::from_msg(&h.pos)?, + autd3_core::geometry::Point3::from_msg(&h.pos)?, autd3_gain_holo::Amplitude::from_msg(&h.amp)?, )) }) @@ -80,7 +80,7 @@ impl FromMessage #[cfg(test)] mod tests { use super::*; - use autd3_driver::geometry::Point3; + use autd3_core::geometry::Point3; use rand::Rng; #[test] diff --git a/autd3-protobuf/src/traits/mod.rs b/autd3-protobuf/src/traits/mod.rs index b512f47e..3855bd95 100644 --- a/autd3-protobuf/src/traits/mod.rs +++ b/autd3-protobuf/src/traits/mod.rs @@ -9,7 +9,7 @@ mod holo; pub trait ToMessage { type Message: prost::Message; - fn to_msg(&self, geometry: Option<&autd3_driver::geometry::Geometry>) -> Self::Message; + fn to_msg(&self, geometry: Option<&autd3_core::geometry::Geometry>) -> Self::Message; } pub trait FromMessage From b24c7ec05389b6a1e5fea7526d137c313e0d15f9 Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Tue, 14 Jan 2025 16:03:30 +0900 Subject: [PATCH 12/20] update autd3-modulation-audio-file --- autd3-modulation-audio-file/Cargo.toml | 6 +++--- autd3-modulation-audio-file/src/csv.rs | 15 ++++++++------- autd3-modulation-audio-file/src/error.rs | 8 ++++---- autd3-modulation-audio-file/src/rawpcm.rs | 16 ++++++++-------- autd3-modulation-audio-file/src/wav.rs | 14 +++++++------- 5 files changed, 30 insertions(+), 29 deletions(-) diff --git a/autd3-modulation-audio-file/Cargo.toml b/autd3-modulation-audio-file/Cargo.toml index abfe356b..cd61e743 100644 --- a/autd3-modulation-audio-file/Cargo.toml +++ b/autd3-modulation-audio-file/Cargo.toml @@ -11,15 +11,15 @@ repository = { workspace = true } [dependencies] hound = { workspace = true } -autd3 = { workspace = true } -autd3-driver = { workspace = true } +autd3-core = { workspace = true, features = ["derive", "modulation", "resampler"] } +autd3-derive = { workspace = true } thiserror = { workspace = true } csv = { workspace = true } tracing = { workspace = true, features = ["attributes"] } [features] default = [] -async-trait = ["autd3-driver/async-trait"] +async-trait = ["autd3-core/async-trait"] [dev-dependencies] anyhow = { workspace = true } diff --git a/autd3-modulation-audio-file/src/csv.rs b/autd3-modulation-audio-file/src/csv.rs index fb88c5f6..8ce5aee4 100644 --- a/autd3-modulation-audio-file/src/csv.rs +++ b/autd3-modulation-audio-file/src/csv.rs @@ -1,5 +1,5 @@ -use autd3::modulation::resampler::Resampler; -use autd3_driver::{defined::Freq, derive::*}; +use autd3_core::{defined::Freq, derive::*, resampler::Resampler}; +use autd3_derive::Builder; use std::{ fs::File, @@ -42,8 +42,7 @@ impl Csv { /// # Examples /// /// ``` - /// use autd3::prelude::*; - /// use autd3::modulation::resampler::SincInterpolation; + /// use autd3_core::{resampler::SincInterpolation, defined::kHz}; /// use autd3_modulation_audio_file::Csv; /// /// let path = "path/to/file.csv"; @@ -98,15 +97,17 @@ impl Csv { } impl Modulation for Csv { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { Ok(self.read_buf()?) } } #[cfg(test)] mod tests { - use autd3::{modulation::resampler::SincInterpolation, prelude::kHz}; - use autd3_driver::defined::{Freq, Hz}; + use autd3_core::{ + defined::{kHz, Freq, Hz}, + resampler::SincInterpolation, + }; use super::*; use std::io::Write; diff --git a/autd3-modulation-audio-file/src/error.rs b/autd3-modulation-audio-file/src/error.rs index edcba70f..418bfa3c 100644 --- a/autd3-modulation-audio-file/src/error.rs +++ b/autd3-modulation-audio-file/src/error.rs @@ -1,6 +1,6 @@ use std::num::ParseIntError; -use autd3_driver::error::AUTDDriverError; +use autd3_core::{derive::ModulationError, modulation::SamplingConfigError}; use thiserror::Error; #[derive(Error, Debug)] @@ -15,13 +15,13 @@ pub enum AudioFileError { #[error("{0}")] Csv(#[from] csv::Error), #[error("{0}")] - AUTDDriverError(#[from] AUTDDriverError), + SamplingConfig(#[from] SamplingConfigError), } // GRCOV_EXCL_START -impl From for AUTDDriverError { +impl From for ModulationError { fn from(value: AudioFileError) -> Self { - AUTDDriverError::ModulationError(value.to_string()) + ModulationError::new(value.to_string()) } } // GRCOV_EXCL_STOP diff --git a/autd3-modulation-audio-file/src/rawpcm.rs b/autd3-modulation-audio-file/src/rawpcm.rs index 41b9dc81..93d9ebf3 100644 --- a/autd3-modulation-audio-file/src/rawpcm.rs +++ b/autd3-modulation-audio-file/src/rawpcm.rs @@ -1,5 +1,4 @@ -use autd3::modulation::resampler::Resampler; -use autd3_driver::{defined::Freq, derive::*}; +use autd3_core::{defined::Freq, derive::*, resampler::Resampler}; use std::{ fs::File, @@ -38,8 +37,7 @@ impl RawPCM { /// # Examples /// /// ``` - /// use autd3::prelude::*; - /// use autd3::modulation::resampler::SincInterpolation; + /// use autd3_core::{resampler::SincInterpolation, defined::kHz}; /// use autd3_modulation_audio_file::RawPCM; /// /// let path = "path/to/file.dat"; @@ -76,15 +74,17 @@ impl RawPCM { } impl Modulation for RawPCM { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { Ok(self.read_buf()?) } } #[cfg(test)] mod tests { - use autd3::{modulation::resampler::SincInterpolation, prelude::kHz}; - use autd3_driver::defined::{Freq, Hz}; + use autd3_core::{ + defined::{kHz, Freq, Hz}, + resampler::SincInterpolation, + }; use super::*; use std::io::Write; @@ -99,7 +99,7 @@ mod tests { #[test] #[case(Ok(vec![0xFF, 0x7F, 0x00]), vec![0xFF, 0x7F, 0x00], 4000 * Hz)] fn new( - #[case] expect: Result, AUTDDriverError>, + #[case] expect: Result, ModulationError>, #[case] data: Vec, #[case] sample_rate: Freq, ) -> anyhow::Result<()> { diff --git a/autd3-modulation-audio-file/src/wav.rs b/autd3-modulation-audio-file/src/wav.rs index 65546abd..467ae0cd 100644 --- a/autd3-modulation-audio-file/src/wav.rs +++ b/autd3-modulation-audio-file/src/wav.rs @@ -1,5 +1,4 @@ -use autd3::modulation::resampler::Resampler; -use autd3_driver::{defined::Hz, derive::*}; +use autd3_core::{defined::Hz, derive::*, resampler::Resampler}; use hound::SampleFormat; use std::path::{Path, PathBuf}; @@ -35,8 +34,7 @@ impl Wav { /// # Examples /// /// ``` - /// use autd3::prelude::*; - /// use autd3::modulation::resampler::SincInterpolation; + /// use autd3_core::{resampler::SincInterpolation, defined::kHz}; /// use autd3_modulation_audio_file::Wav; /// /// let path = "path/to/file.wav"; @@ -108,15 +106,17 @@ impl Wav { } impl Modulation for Wav { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { Ok(self.read_buf()?) } } #[cfg(test)] mod tests { - use autd3::{modulation::resampler::SincInterpolation, prelude::kHz}; - use autd3_driver::defined::Freq; + use autd3_core::{ + defined::{kHz, Freq}, + resampler::SincInterpolation, + }; use super::*; From c7b6836116e42a822eb952f45416f29f225d6aba Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Tue, 14 Jan 2025 16:03:39 +0900 Subject: [PATCH 13/20] update autd3 --- autd3/Cargo.toml | 8 +- autd3/src/async/controller/builder.rs | 4 +- autd3/src/async/controller/group.rs | 19 ++- autd3/src/async/controller/mod.rs | 27 +++- autd3/src/async/controller/timer/mod.rs | 43 ++++-- autd3/src/controller/builder.rs | 4 +- autd3/src/controller/group.rs | 21 +-- autd3/src/controller/mod.rs | 29 ++-- autd3/src/controller/timer/mod.rs | 41 +++-- autd3/src/datagram/gain/bessel.rs | 7 +- autd3/src/datagram/gain/cache.rs | 23 ++- autd3/src/datagram/gain/custom.rs | 6 +- autd3/src/datagram/gain/focus.rs | 7 +- autd3/src/datagram/gain/group.rs | 28 ++-- autd3/src/datagram/gain/null.rs | 7 +- autd3/src/datagram/gain/plane.rs | 7 +- autd3/src/datagram/gain/uniform.rs | 8 +- autd3/src/datagram/modulation/cache.rs | 9 +- autd3/src/datagram/modulation/custom.rs | 10 +- autd3/src/datagram/modulation/fir.rs | 4 +- autd3/src/datagram/modulation/fourier.rs | 27 ++-- autd3/src/datagram/modulation/mod.rs | 2 - .../datagram/modulation/radiation_pressure.rs | 4 +- .../src/datagram/modulation/resampler/mod.rs | 25 --- .../src/datagram/modulation/resampler/sinc.rs | 146 ------------------ .../modulation/resampler/window/blackman.rs | 21 --- .../modulation/resampler/window/mod.rs | 13 -- .../resampler/window/rectangular.rs | 20 --- .../src/datagram/modulation/sampling_mode.rs | 29 ++-- autd3/src/datagram/modulation/sine.rs | 41 ++--- autd3/src/datagram/modulation/square.rs | 29 ++-- autd3/src/datagram/modulation/static.rs | 5 +- autd3/src/datagram/stm/circle.rs | 29 ++-- autd3/src/datagram/stm/line.rs | 20 ++- autd3/src/error.rs | 7 + autd3/src/lib.rs | 2 +- autd3/src/link/audit.rs | 35 +++-- autd3/src/link/nop.rs | 29 ++-- autd3/src/prelude.rs | 6 +- autd3/tests/async/link/audit.rs | 13 +- autd3/tests/async/link/nop.rs | 2 +- autd3/tests/sync/link/audit.rs | 13 +- autd3/tests/sync/link/nop.rs | 3 +- 43 files changed, 348 insertions(+), 485 deletions(-) delete mode 100644 autd3/src/datagram/modulation/resampler/mod.rs delete mode 100644 autd3/src/datagram/modulation/resampler/sinc.rs delete mode 100644 autd3/src/datagram/modulation/resampler/window/blackman.rs delete mode 100644 autd3/src/datagram/modulation/resampler/window/mod.rs delete mode 100644 autd3/src/datagram/modulation/resampler/window/rectangular.rs diff --git a/autd3/Cargo.toml b/autd3/Cargo.toml index 999683e7..a31ad4bc 100644 --- a/autd3/Cargo.toml +++ b/autd3/Cargo.toml @@ -11,7 +11,9 @@ repository = { workspace = true } [dependencies] autd3-firmware-emulator = { workspace = true } -autd3-driver = { workspace = true, features = ["derive"] } +autd3-core = { workspace = true, features = ["link", "derive", "gain", "modulation", "resampler"] } +autd3-driver = { workspace = true } +autd3-derive = { workspace = true } num = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["rt-multi-thread", "time"], optional = true } @@ -30,8 +32,8 @@ windows = { workspace = true, features = ["Win32_Security"] } [features] default = [] -async = ["tokio", "autd3-driver/async"] -async-trait = ["async", "autd3-driver/async-trait"] +async = ["tokio", "autd3-core/async"] +async-trait = ["async", "autd3-core/async-trait"] dynamic_freq = ["autd3-driver/dynamic_freq", "autd3-firmware-emulator/dynamic_freq"] [dev-dependencies] diff --git a/autd3/src/async/controller/builder.rs b/autd3/src/async/controller/builder.rs index 38f9046c..a40e6e81 100644 --- a/autd3/src/async/controller/builder.rs +++ b/autd3/src/async/controller/builder.rs @@ -1,10 +1,10 @@ use std::time::Duration; +use autd3_core::{defined::DEFAULT_TIMEOUT, link::AsyncLinkBuilder}; +use autd3_derive::Builder; use autd3_driver::{ - derive::*, firmware::cpu::{RxMessage, TxMessage}, geometry::{Device, Geometry, IntoDevice}, - link::AsyncLinkBuilder, }; use derive_more::Debug; diff --git a/autd3/src/async/controller/group.rs b/autd3/src/async/controller/group.rs index c527604c..420c3f3b 100644 --- a/autd3/src/async/controller/group.rs +++ b/autd3/src/async/controller/group.rs @@ -1,14 +1,15 @@ use std::{fmt::Debug, hash::Hash, time::Duration}; +use autd3_core::link::AsyncLink; use autd3_driver::{ datagram::Datagram, error::AUTDDriverError, - firmware::operation::{Operation, OperationGenerator}, + firmware::operation::{BoxedOperation, Operation, OperationGenerator}, geometry::Device, }; use itertools::Itertools; -use super::{AsyncLink, Controller}; +use super::Controller; use tracing; @@ -20,7 +21,7 @@ pub struct Group<'a, K: PartialEq + Debug, L: AsyncLink> { pub(crate) done: Vec, pub(crate) timeout: Option, pub(crate) parallel_threshold: Option, - pub(crate) operations: Vec, Box)>>, + pub(crate) operations: Vec>, } impl<'a, K: PartialEq + Debug, L: AsyncLink> Group<'a, K, L> { @@ -47,8 +48,12 @@ impl<'a, K: PartialEq + Debug, L: AsyncLink> Group<'a, K, L> { #[tracing::instrument(level = "debug", skip(self))] pub fn set(self, key: K, data: D) -> Result where + AUTDDriverError: From, + D::G: OperationGenerator, <::G as OperationGenerator>::O1: 'static, <::G as OperationGenerator>::O2: 'static, + AUTDDriverError: From<<::O1 as Operation>::Error> + + From<<::O2 as Operation>::Error>, { let Self { keys, @@ -106,7 +111,7 @@ impl<'a, K: PartialEq + Debug, L: AsyncLink> Group<'a, K, L> { *done = true; tracing::debug!("Generate operation for device {}", dev.idx()); let (op1, op2) = generator.generate(dev); - *op = Some((Box::new(op1) as Box<_>, Box::new(op2) as Box<_>)); + *op = Some((BoxedOperation::new(op1), BoxedOperation::new(op2))); Ok(()) })?; @@ -202,10 +207,10 @@ impl Controller { mod tests { use std::sync::Mutex; + use autd3_core::derive::*; use autd3_driver::{ datagram::{GainSTM, SwapSegment}, defined::Hz, - derive::*, error::AUTDDriverError, firmware::fpga::{Drive, EmitIntensity, Phase}, }; @@ -372,8 +377,8 @@ mod tests { fn init( self, geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { geometry.iter().for_each(|dev| { self.test.lock().unwrap()[dev.idx()] = dev.enable; }); diff --git a/autd3/src/async/controller/mod.rs b/autd3/src/async/controller/mod.rs index c213f5cf..3665d32d 100644 --- a/autd3/src/async/controller/mod.rs +++ b/autd3/src/async/controller/mod.rs @@ -7,18 +7,18 @@ use crate::{error::AUTDError, gain::Null, prelude::Static}; use std::time::Duration; +use autd3_core::link::AsyncLink; +use autd3_derive::Builder; use autd3_driver::{ datagram::{Clear, Datagram, ForceFan, IntoDatagramWithTimeout, Silencer, Synchronize}, - derive::Builder, error::AUTDDriverError, firmware::{ cpu::{check_if_msg_is_processed, RxMessage, TxMessage}, fpga::FPGAState, - operation::{FirmwareVersionType, OperationHandler}, + operation::{FirmwareVersionType, Operation, OperationGenerator, OperationHandler}, version::FirmwareVersion, }, geometry::{Device, Geometry}, - link::AsyncLink, }; use timer::Timer; @@ -55,7 +55,13 @@ impl Controller { /// /// The calculation of each [`Datagram`] is executed in parallel for each device if the number of enabled devices is greater than the [`Datagram::parallel_threshold`]. #[tracing::instrument(level = "debug", skip(self))] - pub async fn send(&mut self, s: impl Datagram) -> Result<(), AUTDDriverError> { + pub async fn send(&mut self, s: D) -> Result<(), AUTDDriverError> + where + AUTDDriverError: From, + D::G: OperationGenerator, + AUTDDriverError: From<<::O1 as Operation>::Error> + + From<<::O2 as Operation>::Error>, + { let timeout = s.timeout(); let parallel_threshold = s.parallel_threshold(); self.link.trace(timeout, parallel_threshold); @@ -110,7 +116,7 @@ impl Controller { self.send(Silencer::default().with_strict_mode(false)).await, self.send((Static::new(), Null::default())).await, self.send(Clear::new()).await, - self.link.close().await, + Ok(self.link.close().await?), ] .into_iter() .try_fold((), |_, x| x) @@ -189,7 +195,7 @@ impl Controller { )); } if self.link.receive(&mut self.rx_buf).await? { - Ok(self.rx_buf.iter().map(Option::from).collect()) + Ok(self.rx_buf.iter().map(FPGAState::from_rx).collect()) } else { Err(AUTDError::ReadFPGAStateFailed) } @@ -275,11 +281,16 @@ impl Drop for Controller { #[cfg(test)] mod tests { + use autd3_core::{ + datagram::Segment, + derive::Modulation, + gain::{EmitIntensity, Gain, GainContext, GainContextGenerator}, + link::LinkError, + }; use autd3_driver::{ autd3_device::AUTD3, datagram::{GainSTM, ReadsFPGAState}, defined::{mm, Hz}, - derive::{EmitIntensity, Gain, GainContext, GainContextGenerator, Modulation, Segment}, geometry::Point3, }; @@ -414,7 +425,7 @@ mod tests { let mut autd = create_controller(1).await?; autd.link_mut().break_down(); assert_eq!( - Err(AUTDDriverError::LinkError("broken".to_owned())), + Err(AUTDDriverError::Link(LinkError::new("broken".to_owned()))), autd.close().await ); } diff --git a/autd3/src/async/controller/timer/mod.rs b/autd3/src/async/controller/timer/mod.rs index 9787667b..a8960535 100644 --- a/autd3/src/async/controller/timer/mod.rs +++ b/autd3/src/async/controller/timer/mod.rs @@ -9,14 +9,14 @@ pub use sleep::{AsyncSleeper, SpinSleeper, StdSleeper}; use std::time::{Duration, Instant}; +use autd3_core::{derive::*, link::AsyncLink}; +use autd3_derive::Builder; use autd3_driver::{ - derive::{Builder, Geometry}, error::AUTDDriverError, firmware::{ cpu::{check_if_msg_is_processed, RxMessage, TxMessage}, operation::{Operation, OperationHandler}, }, - link::AsyncLink, }; use itertools::Itertools; @@ -61,16 +61,21 @@ pub struct Timer { } impl Timer { - pub(crate) async fn send( + pub(crate) async fn send( &self, geometry: &Geometry, tx: &mut [TxMessage], rx: &mut [RxMessage], link: &mut impl AsyncLink, - operations: Vec<(impl Operation, impl Operation)>, + operations: Vec<(O1, O2)>, timeout: Option, parallel_threshold: Option, - ) -> Result<(), AUTDDriverError> { + ) -> Result<(), AUTDDriverError> + where + O1: Operation, + O2: Operation, + AUTDDriverError: From + From, + { let timeout = timeout.unwrap_or(self.default_timeout); let parallel = geometry.parallel(parallel_threshold); tracing::debug!("timeout: {:?}, parallel: {:?}", timeout, parallel); @@ -104,17 +109,22 @@ impl Timer { } } - async fn _send( + async fn _send( &self, sleeper: &S, geometry: &Geometry, tx: &mut [TxMessage], rx: &mut [RxMessage], link: &mut impl AsyncLink, - mut operations: Vec<(impl Operation, impl Operation)>, + mut operations: Vec<(O1, O2)>, timeout: Duration, parallel: bool, - ) -> Result<(), AUTDDriverError> { + ) -> Result<(), AUTDDriverError> + where + O1: Operation, + O2: Operation, + AUTDDriverError: From + From, + { link.update(geometry).await?; // We prioritize average behavior for the transmission timing. That is, not the interval from the previous transmission, but ensuring that T/`send_interval` transmissions are performed in a sufficiently long time T. @@ -181,7 +191,9 @@ impl Timer { sleeper.sleep_until(receive_timing).await; } rx.iter() - .try_fold((), |_, r| Result::<(), AUTDDriverError>::from(r)) + .try_fold((), |_, r| { + autd3_driver::firmware::cpu::check_firmware_err(r) + }) .and_then(|e| { if timeout == Duration::ZERO { Ok(()) @@ -195,6 +207,7 @@ impl Timer { #[cfg(test)] mod tests { + use autd3_core::link::LinkError; use zerocopy::FromZeros; use super::*; @@ -206,21 +219,21 @@ mod tests { pub down: bool, } - #[cfg_attr(feature = "async-trait", autd3_driver::async_trait)] + #[cfg_attr(feature = "async-trait", autd3_core::async_trait)] impl AsyncLink for MockLink { - async fn close(&mut self) -> Result<(), AUTDDriverError> { + async fn close(&mut self) -> Result<(), LinkError> { self.is_open = false; Ok(()) } - async fn send(&mut self, _: &[TxMessage]) -> Result { + async fn send(&mut self, _: &[TxMessage]) -> Result { self.send_cnt += 1; Ok(!self.down) } - async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { if self.recv_cnt > 10 { - return Err(AUTDDriverError::LinkError("too many".to_owned())); + return Err(LinkError::new("too many".to_owned())); } self.recv_cnt += 1; @@ -378,7 +391,7 @@ mod tests { link.recv_cnt = 0; tx[0].header_mut().msg_id = 20; assert_eq!( - Err(AUTDDriverError::LinkError("too many".to_owned())), + Err(AUTDDriverError::Link(LinkError::new("too many".to_owned()))), timer .wait_msg_processed(&sleeper, &tx, &mut rx, &mut link, Duration::from_secs(10)) .await diff --git a/autd3/src/controller/builder.rs b/autd3/src/controller/builder.rs index d4d93bb2..99c580be 100644 --- a/autd3/src/controller/builder.rs +++ b/autd3/src/controller/builder.rs @@ -1,10 +1,10 @@ use std::time::Duration; +use autd3_core::{derive::DEFAULT_TIMEOUT, link::LinkBuilder}; +use autd3_derive::Builder; use autd3_driver::{ - derive::*, firmware::cpu::{RxMessage, TxMessage}, geometry::{Device, Geometry, IntoDevice}, - link::LinkBuilder, }; use derive_more::Debug; diff --git a/autd3/src/controller/group.rs b/autd3/src/controller/group.rs index c09b005b..bf597918 100644 --- a/autd3/src/controller/group.rs +++ b/autd3/src/controller/group.rs @@ -1,16 +1,15 @@ use std::{fmt::Debug, hash::Hash, time::Duration}; +use autd3_core::link::Link; use autd3_driver::{ datagram::Datagram, error::AUTDDriverError, - firmware::operation::{Operation, OperationGenerator}, + firmware::operation::{BoxedOperation, Operation, OperationGenerator}, geometry::Device, }; use itertools::Itertools; -use super::{Controller, Link}; - -use tracing; +use super::Controller; /// A struct for grouping devices and sending different data to each group. See also [`Controller::group`]. #[allow(clippy::type_complexity)] @@ -20,7 +19,7 @@ pub struct Group<'a, K: PartialEq + Debug, L: Link> { pub(crate) done: Vec, pub(crate) timeout: Option, pub(crate) parallel_threshold: Option, - pub(crate) operations: Vec, Box)>>, + pub(crate) operations: Vec>, } impl<'a, K: PartialEq + Debug, L: Link> Group<'a, K, L> { @@ -47,8 +46,12 @@ impl<'a, K: PartialEq + Debug, L: Link> Group<'a, K, L> { #[tracing::instrument(level = "debug", skip(self))] pub fn set(self, key: K, data: D) -> Result where + AUTDDriverError: From, + D::G: OperationGenerator, <::G as OperationGenerator>::O1: 'static, <::G as OperationGenerator>::O2: 'static, + AUTDDriverError: From<<::O1 as Operation>::Error> + + From<<::O2 as Operation>::Error>, { let Self { keys, @@ -106,7 +109,7 @@ impl<'a, K: PartialEq + Debug, L: Link> Group<'a, K, L> { *done = true; tracing::debug!("Generate operation for device {}", dev.idx()); let (op1, op2) = generator.generate(dev); - *op = Some((Box::new(op1) as Box<_>, Box::new(op2) as Box<_>)); + *op = Some((BoxedOperation::new(op1), BoxedOperation::new(op2))); Ok(()) })?; @@ -199,10 +202,10 @@ impl Controller { mod tests { use std::sync::Mutex; + use autd3_core::derive::*; use autd3_driver::{ datagram::{GainSTM, SwapSegment}, defined::Hz, - derive::*, error::AUTDDriverError, firmware::fpga::{Drive, EmitIntensity, Phase}, }; @@ -364,8 +367,8 @@ mod tests { fn init( self, geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { geometry.iter().for_each(|dev| { self.test.lock().unwrap()[dev.idx()] = dev.enable; }); diff --git a/autd3/src/controller/mod.rs b/autd3/src/controller/mod.rs index aa553935..2a69b22b 100644 --- a/autd3/src/controller/mod.rs +++ b/autd3/src/controller/mod.rs @@ -7,18 +7,18 @@ use crate::{error::AUTDError, gain::Null, prelude::Static}; use std::time::Duration; +use autd3_core::link::Link; +use autd3_derive::Builder; use autd3_driver::{ datagram::{Clear, Datagram, ForceFan, IntoDatagramWithTimeout, Silencer, Synchronize}, - derive::Builder, error::AUTDDriverError, firmware::{ cpu::{check_if_msg_is_processed, RxMessage, TxMessage}, fpga::FPGAState, - operation::{FirmwareVersionType, OperationHandler}, + operation::{FirmwareVersionType, Operation, OperationGenerator, OperationHandler}, version::FirmwareVersion, }, geometry::{Device, Geometry}, - link::Link, }; use timer::Timer; @@ -55,7 +55,13 @@ impl Controller { /// /// The calculation of each [`Datagram`] is executed in parallel for each device if the number of enabled devices is greater than the [`Datagram::parallel_threshold`]. #[tracing::instrument(level = "debug", skip(self))] - pub fn send(&mut self, s: impl Datagram) -> Result<(), AUTDDriverError> { + pub fn send(&mut self, s: D) -> Result<(), AUTDDriverError> + where + AUTDDriverError: From, + D::G: OperationGenerator, + AUTDDriverError: From<<::O1 as Operation>::Error> + + From<<::O2 as Operation>::Error>, + { let timeout = s.timeout(); let parallel_threshold = s.parallel_threshold(); self.link.trace(timeout, parallel_threshold); @@ -104,7 +110,7 @@ impl Controller { self.send(Silencer::default().with_strict_mode(false)), self.send((Static::new(), Null::default())), self.send(Clear::new()), - self.link.close(), + Ok(self.link.close()?), ] .into_iter() .try_fold((), |_, x| x) @@ -182,7 +188,7 @@ impl Controller { )); } if self.link.receive(&mut self.rx_buf)? { - Ok(self.rx_buf.iter().map(Option::from).collect()) + Ok(self.rx_buf.iter().map(FPGAState::from_rx).collect()) } else { Err(AUTDError::ReadFPGAStateFailed) } @@ -259,12 +265,11 @@ impl Drop for Controller { #[cfg(test)] mod tests { - use autd3_driver::{ - autd3_device::AUTD3, - defined::Hz, - derive::{Gain, GainContext, GainContextGenerator, Segment}, - geometry::Point3, + use autd3_core::{ + gain::{Gain, GainContext, GainContextGenerator}, + link::LinkError, }; + use autd3_driver::{autd3_device::AUTD3, defined::Hz, geometry::Point3}; use crate::{controller::timer::*, link::Audit, prelude::*}; @@ -389,7 +394,7 @@ mod tests { let mut autd = create_controller(1)?; autd.link_mut().break_down(); assert_eq!( - Err(AUTDDriverError::LinkError("broken".to_owned())), + Err(AUTDDriverError::Link(LinkError::new("broken".to_owned()))), autd.close() ); } diff --git a/autd3/src/controller/timer/mod.rs b/autd3/src/controller/timer/mod.rs index 50107590..5b187473 100644 --- a/autd3/src/controller/timer/mod.rs +++ b/autd3/src/controller/timer/mod.rs @@ -2,6 +2,8 @@ pub(crate) mod sleep; +use autd3_core::{geometry::Geometry, link::Link}; +use autd3_derive::Builder; use sleep::Sleeper; #[cfg(target_os = "windows")] pub use sleep::WaitableSleeper; @@ -10,13 +12,11 @@ pub use sleep::{SpinSleeper, StdSleeper}; use std::time::{Duration, Instant}; use autd3_driver::{ - derive::{Builder, Geometry}, error::AUTDDriverError, firmware::{ cpu::{check_if_msg_is_processed, RxMessage, TxMessage}, operation::{Operation, OperationHandler}, }, - link::Link, }; use itertools::Itertools; @@ -59,16 +59,21 @@ pub struct Timer { } impl Timer { - pub(crate) fn send( + pub(crate) fn send( &self, geometry: &Geometry, tx: &mut [TxMessage], rx: &mut [RxMessage], link: &mut impl Link, - operations: Vec<(impl Operation, impl Operation)>, + operations: Vec<(O1, O2)>, timeout: Option, parallel_threshold: Option, - ) -> Result<(), AUTDDriverError> { + ) -> Result<(), AUTDDriverError> + where + O1: Operation, + O2: Operation, + AUTDDriverError: From + From, + { let timeout = timeout.unwrap_or(self.default_timeout); let parallel = geometry.parallel(parallel_threshold); tracing::debug!("timeout: {:?}, parallel: {:?}", timeout, parallel); @@ -87,17 +92,22 @@ impl Timer { } } - fn _send( + fn _send( &self, sleeper: &S, geometry: &Geometry, tx: &mut [TxMessage], rx: &mut [RxMessage], link: &mut impl Link, - mut operations: Vec<(impl Operation, impl Operation)>, + mut operations: Vec<(O1, O2)>, timeout: Duration, parallel: bool, - ) -> Result<(), AUTDDriverError> { + ) -> Result<(), AUTDDriverError> + where + O1: Operation, + O2: Operation, + AUTDDriverError: From + From, + { link.update(geometry)?; // We prioritize average behavior for the transmission timing. That is, not the interval from the previous transmission, but ensuring that T/`send_interval` transmissions are performed in a sufficiently long time T. @@ -163,7 +173,9 @@ impl Timer { sleeper.sleep_until(receive_timing); } rx.iter() - .try_fold((), |_, r| Result::<(), AUTDDriverError>::from(r)) + .try_fold((), |_, r| { + autd3_driver::firmware::cpu::check_firmware_err(r) + }) .and_then(|e| { if timeout == Duration::ZERO { Ok(()) @@ -177,6 +189,7 @@ impl Timer { #[cfg(test)] mod tests { + use autd3_core::link::LinkError; use zerocopy::FromZeros; #[cfg(target_os = "windows")] @@ -193,19 +206,19 @@ mod tests { } impl Link for MockLink { - fn close(&mut self) -> Result<(), AUTDDriverError> { + fn close(&mut self) -> Result<(), LinkError> { self.is_open = false; Ok(()) } - fn send(&mut self, _: &[TxMessage]) -> Result { + fn send(&mut self, _: &[TxMessage]) -> Result { self.send_cnt += 1; Ok(!self.down) } - fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + fn receive(&mut self, rx: &mut [RxMessage]) -> Result { if self.recv_cnt > 10 { - return Err(AUTDDriverError::LinkError("too many".to_owned())); + return Err(LinkError::new("too many".to_owned())); } self.recv_cnt += 1; @@ -342,7 +355,7 @@ mod tests { link.recv_cnt = 0; tx[0].header_mut().msg_id = 20; assert_eq!( - Err(AUTDDriverError::LinkError("too many".to_owned())), + Err(AUTDDriverError::Link(LinkError::new("too many".to_owned()))), timer.wait_msg_processed(&sleeper, &tx, &mut rx, &mut link, Duration::from_secs(10)) ); } diff --git a/autd3/src/datagram/gain/bessel.rs b/autd3/src/datagram/gain/bessel.rs index dd3df0f1..2bd6b68e 100644 --- a/autd3/src/datagram/gain/bessel.rs +++ b/autd3/src/datagram/gain/bessel.rs @@ -1,6 +1,7 @@ +use autd3_core::derive::*; +use autd3_derive::Builder; use autd3_driver::{ defined::{rad, Angle}, - derive::*, firmware::fpga::{EmitIntensity, Phase}, geometry::{Point3, UnitQuaternion, UnitVector3, Vector3}, }; @@ -82,8 +83,8 @@ impl Gain for Bessel { fn init( self, _geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { Ok(self) } } diff --git a/autd3/src/datagram/gain/cache.rs b/autd3/src/datagram/gain/cache.rs index bc2f7f59..03b5cb46 100644 --- a/autd3/src/datagram/gain/cache.rs +++ b/autd3/src/datagram/gain/cache.rs @@ -1,7 +1,6 @@ -pub use autd3_driver::derive::Gain; -pub use autd3_driver::{ - derive::*, - error::AUTDDriverError, +use autd3_core::derive::*; +use autd3_derive::Builder; +use autd3_driver::{ firmware::fpga::{Drive, Segment}, geometry::Geometry, }; @@ -52,7 +51,7 @@ impl Cache { /// # Errors /// /// Returns [`AUTDDriverError::GainError`] if you initialize with some devices disabled and then reinitialize after enabling the devices. - pub fn init(&self, geometry: &Geometry) -> Result<(), AUTDDriverError> { + pub fn init(&self, geometry: &Geometry) -> Result<(), GainError> { if let Some(gain) = self.gain.take() { let mut f = gain.init(geometry, None)?; geometry @@ -73,7 +72,7 @@ impl Cache { .devices() .any(|dev| !self.cache.borrow().contains_key(&dev.idx())) { - return Err(AUTDDriverError::GainError( + return Err(GainError::new( "Cache is initialized with different geometry".to_string(), )); } @@ -113,8 +112,8 @@ impl Gain for Cache { fn init( self, geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { Cache::init(&self, geometry)?; Ok(self) } @@ -150,7 +149,7 @@ mod tests { let cf = gc.generate(dev); dev.iter().try_for_each(|tr| { assert_eq!(gf.calc(tr), cf.calc(tr)); - Result::<(), AUTDDriverError>::Ok(()) + Result::<(), GainError>::Ok(()) }) })?; Ok(()) @@ -168,7 +167,7 @@ mod tests { geometry[1].enable = false; assert_eq!( - Some(AUTDDriverError::GainError( + Some(GainError::new( "Cache is initialized with different geometry".to_string() )), cache.init(&geometry, None).err() @@ -204,8 +203,8 @@ mod tests { fn init( self, _geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { self.calc_cnt.fetch_add(1, Ordering::Relaxed); Ok(self) } diff --git a/autd3/src/datagram/gain/custom.rs b/autd3/src/datagram/gain/custom.rs index b36b6ebd..9b38748f 100644 --- a/autd3/src/datagram/gain/custom.rs +++ b/autd3/src/datagram/gain/custom.rs @@ -1,5 +1,5 @@ +use autd3_core::derive::*; use autd3_driver::{ - derive::*, firmware::fpga::Drive, geometry::{Device, Transducer}, }; @@ -69,8 +69,8 @@ impl< fn init( self, _geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { Ok(self) } } diff --git a/autd3/src/datagram/gain/focus.rs b/autd3/src/datagram/gain/focus.rs index 3e7755e0..6c49d489 100644 --- a/autd3/src/datagram/gain/focus.rs +++ b/autd3/src/datagram/gain/focus.rs @@ -1,6 +1,7 @@ +use autd3_core::derive::*; +use autd3_derive::Builder; use autd3_driver::{ defined::rad, - derive::*, firmware::fpga::{EmitIntensity, Phase}, geometry::Point3, }; @@ -61,8 +62,8 @@ impl Gain for Focus { fn init( self, _geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { Ok(self) } } diff --git a/autd3/src/datagram/gain/group.rs b/autd3/src/datagram/gain/group.rs index bbee41ec..d327b5fd 100644 --- a/autd3/src/datagram/gain/group.rs +++ b/autd3/src/datagram/gain/group.rs @@ -1,6 +1,7 @@ +use autd3_core::derive::*; +use autd3_derive::Builder; use autd3_driver::{ datagram::{BoxedGain, IntoBoxedGain}, - derive::*, error::AUTDDriverError, firmware::fpga::Drive, geometry::{Device, Transducer}, @@ -68,8 +69,8 @@ where Ok(self) } - fn get_filters(&self, geometry: &Geometry) -> HashMap>> { - let mut filters: HashMap>> = HashMap::new(); + fn get_filters(&self, geometry: &Geometry) -> HashMap> { + let mut filters: HashMap> = HashMap::new(); geometry.devices().for_each(|dev| { dev.iter().for_each(|tr| { if let Some(key) = (self.f)(dev)(tr) { @@ -134,8 +135,8 @@ where fn init( self, geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { let mut filters = self.get_filters(geometry); let mut g = geometry @@ -153,7 +154,7 @@ where .map(|(k, g)| { let filter = filters .remove(&k) - .ok_or(AUTDDriverError::UnkownKey(format!("{:?}", k)))?; + .ok_or(GainError::new(format!("Unknown group key({:?})", k)))?; let mut g = g.init(geometry, Some(&filter))?; Ok(( k, @@ -163,19 +164,20 @@ where .collect::>(), )) }) - .collect::, AUTDDriverError>>()?; + .collect::, GainError>>()?; if !filters.is_empty() { - return Err(AUTDDriverError::UnusedKey( + return Err(GainError::new(format!( + "Unused group keys: {}", filters.keys().map(|k| format!("{:?}", k)).join(", "), - )); + ))); } let f = &self.f; if geometry.parallel(None) { gain_map .par_iter() - .try_for_each(|(k, c)| -> Result<(), AUTDDriverError> { + .try_for_each(|(k, c)| -> Result<(), GainError> { geometry.devices().zip(c.iter()).for_each(|(dev, c)| { let f = (f)(dev); let r = g[&dev.idx()].as_ptr() as *mut Drive; @@ -190,7 +192,7 @@ where } else { gain_map .iter() - .try_for_each(|(k, c)| -> Result<(), AUTDDriverError> { + .try_for_each(|(k, c)| -> Result<(), GainError> { geometry.devices().zip(c.iter()).for_each(|(dev, c)| { let f = (f)(dev); let r = g.get_mut(&dev.idx()).unwrap(); @@ -371,7 +373,7 @@ mod tests { .set("test2", Null::new())?; assert_eq!( - Some(AUTDDriverError::UnkownKey("\"test2\"".to_owned())), + Some(GainError::new("Unknown group key(\"test2\")".to_owned())), gain.init(&geometry, None).err() ); @@ -405,7 +407,7 @@ mod tests { .set(1, Null::new())?; assert_eq!( - Some(AUTDDriverError::UnusedKey("0".to_owned())), + Some(GainError::new("Unused group keys: 0".to_owned())), gain.init(&geometry, None).err() ); diff --git a/autd3/src/datagram/gain/null.rs b/autd3/src/datagram/gain/null.rs index abebb414..17fe51ba 100644 --- a/autd3/src/datagram/gain/null.rs +++ b/autd3/src/datagram/gain/null.rs @@ -1,4 +1,5 @@ -use autd3_driver::{derive::*, firmware::fpga::Drive}; +use autd3_core::derive::*; +use autd3_driver::firmware::fpga::Drive; use derive_new::new; /// [`Gain`] that output nothing @@ -25,8 +26,8 @@ impl Gain for Null { fn init( self, _geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { Ok(self) } } diff --git a/autd3/src/datagram/gain/plane.rs b/autd3/src/datagram/gain/plane.rs index bca0e6c7..be8b8d53 100644 --- a/autd3/src/datagram/gain/plane.rs +++ b/autd3/src/datagram/gain/plane.rs @@ -1,6 +1,7 @@ +use autd3_core::derive::*; +use autd3_derive::Builder; use autd3_driver::{ defined::rad, - derive::*, firmware::fpga::{EmitIntensity, Phase}, geometry::UnitVector3, }; @@ -61,8 +62,8 @@ impl Gain for Plane { fn init( self, _geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { Ok(self) } } diff --git a/autd3/src/datagram/gain/uniform.rs b/autd3/src/datagram/gain/uniform.rs index 9109c7d3..86b6eba2 100644 --- a/autd3/src/datagram/gain/uniform.rs +++ b/autd3/src/datagram/gain/uniform.rs @@ -1,4 +1,6 @@ -use autd3_driver::{derive::*, firmware::fpga::Drive}; +use autd3_core::derive::*; +use autd3_derive::Builder; +use autd3_driver::firmware::fpga::Drive; use derive_new::new; /// [`Gain`] that output uniform phase and intensity @@ -30,8 +32,8 @@ impl Gain for Uniform { fn init( self, _geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { Ok(self) } } diff --git a/autd3/src/datagram/modulation/cache.rs b/autd3/src/datagram/modulation/cache.rs index f034c1b9..918b68e4 100644 --- a/autd3/src/datagram/modulation/cache.rs +++ b/autd3/src/datagram/modulation/cache.rs @@ -1,4 +1,5 @@ -use crate::derive::*; +use autd3_core::derive::*; +use autd3_derive::Builder; use std::{cell::RefCell, rc::Rc}; @@ -51,7 +52,7 @@ impl Cache { } /// Initialize cache. - pub fn init(&self) -> Result<(), AUTDDriverError> { + pub fn init(&self) -> Result<(), ModulationError> { if let Some(m) = self.m.take() { tracing::debug!("Initializing cache"); *self.cache.borrow_mut() = m.calc()?; @@ -66,7 +67,7 @@ impl Cache { } impl Modulation for Cache { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { self.init()?; let buffer = self.cache().clone(); Ok(buffer) @@ -107,7 +108,7 @@ mod tests { } impl Modulation for TestCacheModulation { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { self.calc_cnt.fetch_add(1, Ordering::Relaxed); Ok(vec![0x00, 0x00]) } diff --git a/autd3/src/datagram/modulation/custom.rs b/autd3/src/datagram/modulation/custom.rs index 923de06c..92f05ae7 100644 --- a/autd3/src/datagram/modulation/custom.rs +++ b/autd3/src/datagram/modulation/custom.rs @@ -1,8 +1,6 @@ use std::borrow::Borrow; -use autd3_driver::{defined::Freq, derive::*}; - -use super::resampler::Resampler; +use autd3_core::{defined::Freq, derive::*, resampler::Resampler}; ///[`Modulation`] to use arbitrary modulation data #[derive(Modulation, Clone, PartialEq, Debug)] @@ -42,7 +40,7 @@ impl Custom { /// ``` /// use autd3::prelude::*; /// use autd3::modulation::Custom; - /// use autd3::modulation::resampler::SincInterpolation; + /// use autd3::core::resampler::SincInterpolation; /// /// Custom::new_with_resample(&[0x00, 0xFF], 2.0 * kHz, 4 * kHz, SincInterpolation::default()); /// ``` @@ -68,7 +66,7 @@ impl Custom { } impl Modulation for Custom { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { Ok(self.buffer.clone()) } } @@ -78,7 +76,7 @@ mod tests { use autd3_driver::defined::kHz; use rand::Rng; - use crate::modulation::resampler::SincInterpolation; + use autd3_core::resampler::SincInterpolation; use super::*; diff --git a/autd3/src/datagram/modulation/fir.rs b/autd3/src/datagram/modulation/fir.rs index ab933aa8..1226aed0 100644 --- a/autd3/src/datagram/modulation/fir.rs +++ b/autd3/src/datagram/modulation/fir.rs @@ -1,6 +1,6 @@ use std::borrow::Borrow; -use crate::derive::*; +use autd3_core::derive::*; /// [`Modulation`] that applies FIR filter to the original [`Modulation`]. #[derive(Modulation, Debug)] @@ -36,7 +36,7 @@ impl IntoFir for M { } impl Modulation for Fir { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { let src = self.m.calc()?; let src_len = src.len() as isize; let filter_len = self.filter.len() as isize; diff --git a/autd3/src/datagram/modulation/fourier.rs b/autd3/src/datagram/modulation/fourier.rs index 9869e8b7..be4bebe6 100644 --- a/autd3/src/datagram/modulation/fourier.rs +++ b/autd3/src/datagram/modulation/fourier.rs @@ -1,6 +1,7 @@ use super::{sampling_mode::SamplingMode, sine::Sine}; -use autd3_driver::derive::*; +use autd3_core::derive::*; +use autd3_derive::Builder; use derive_more::Deref; use num::integer::lcm; @@ -31,7 +32,7 @@ pub struct Fourier { impl Fourier { /// Create a new [`Fourier`] modulation. - pub fn new(componens: impl IntoIterator>) -> Result { + pub fn new(componens: impl IntoIterator>) -> Result { let components = componens .into_iter() .map(|s| s.with_clamp(false)) @@ -39,7 +40,7 @@ impl Fourier { tracing::trace!("Fourier components: {:?}", components); let config = components .first() - .ok_or(AUTDDriverError::ModulationError( + .ok_or(ModulationError::new( "Components must not be empty".to_string(), ))? .sampling_config(); @@ -48,7 +49,7 @@ impl Fourier { .skip(1) .any(|c| c.sampling_config() != config) { - return Err(AUTDDriverError::ModulationError( + return Err(ModulationError::new( "All components must have the same sampling configuration".to_string(), )); } @@ -64,12 +65,12 @@ impl Fourier { } impl Modulation for Fourier { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { let buffers = self .components .iter() .map(|c| Ok(c.calc_raw()?.collect::>())) - .collect::, AUTDDriverError>>()?; + .collect::, ModulationError>>()?; let scale = self.scale_factor.unwrap_or(1. / buffers.len() as f32); let res = vec![0f32; buffers.iter().fold(1, |acc, x| lcm(acc, x.len()))]; buffers @@ -88,7 +89,7 @@ impl Modulation for Fourier { } else if self.clamp { Ok(v.clamp(u8::MIN as _, u8::MAX as _) as _) } else { - Err(AUTDDriverError::ModulationError(format!( + Err(ModulationError::new(format!( "Fourier modulation value ({}) is out of range [{}, {}]", v, u8::MIN, @@ -96,7 +97,7 @@ impl Modulation for Fourier { )))? } }) - .collect::, AUTDDriverError>>() + .collect::, ModulationError>>() } } @@ -157,7 +158,7 @@ mod tests { #[test] fn mismatch_sampling_config() -> anyhow::Result<()> { assert_eq!( - Err(AUTDDriverError::ModulationError( + Err(ModulationError::new( "All components must have the same sampling configuration".to_string() )), Fourier::new([ @@ -171,7 +172,7 @@ mod tests { #[test] fn empty_components() { assert_eq!( - Err(AUTDDriverError::ModulationError( + Err(ModulationError::new( "Components must not be empty".to_string() )), Fourier::::new(vec![]) @@ -180,7 +181,7 @@ mod tests { #[rstest::rstest] #[case( - Err(AUTDDriverError::ModulationError("Fourier modulation value (-1) is out of range [0, 255]".to_owned())), + Err(ModulationError::new("Fourier modulation value (-1) is out of range [0, 255]".to_owned())), 0x00, false, None @@ -192,14 +193,14 @@ mod tests { None )] #[case( - Err(AUTDDriverError::ModulationError("Fourier modulation value (510) is out of range [0, 255]".to_owned())), + Err(ModulationError::new("Fourier modulation value (510) is out of range [0, 255]".to_owned())), 0xFF, false, Some(2.) )] #[test] fn out_of_range( - #[case] expect: Result, AUTDDriverError>, + #[case] expect: Result, ModulationError>, #[case] offset: u8, #[case] clamp: bool, #[case] scale: Option, diff --git a/autd3/src/datagram/modulation/mod.rs b/autd3/src/datagram/modulation/mod.rs index d1882ee4..c5639ec2 100644 --- a/autd3/src/datagram/modulation/mod.rs +++ b/autd3/src/datagram/modulation/mod.rs @@ -3,8 +3,6 @@ mod custom; mod fir; mod fourier; mod radiation_pressure; -/// Resampler module. -pub mod resampler; /// Sampling mode module. pub mod sampling_mode; mod sine; diff --git a/autd3/src/datagram/modulation/radiation_pressure.rs b/autd3/src/datagram/modulation/radiation_pressure.rs index 6c5cc467..66fb5f31 100644 --- a/autd3/src/datagram/modulation/radiation_pressure.rs +++ b/autd3/src/datagram/modulation/radiation_pressure.rs @@ -1,4 +1,4 @@ -use crate::derive::*; +use autd3_core::derive::*; /// [`Modulation`] for appling modulation to the radiation pressure instead of the acoustic pressure. #[derive(Modulation, Debug)] @@ -32,7 +32,7 @@ impl IntoRadiationPressure for M { } impl Modulation for RadiationPressure { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { let src = self.m.calc()?; Ok(src .iter() diff --git a/autd3/src/datagram/modulation/resampler/mod.rs b/autd3/src/datagram/modulation/resampler/mod.rs deleted file mode 100644 index 8d540fc5..00000000 --- a/autd3/src/datagram/modulation/resampler/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -mod sinc; -mod window; - -use autd3_driver::{defined::Freq, derive::SamplingConfig}; -pub use sinc::SincInterpolation; -pub use window::*; - -/// Resampler trait. -pub trait Resampler: std::fmt::Debug + Send + Sync { - /// Upsample the buffer. - fn upsample(&self, buffer: &[u8], ratio: f64) -> Vec; - /// Downsample the buffer. - fn downsample(&self, buffer: &[u8], ratio: f64) -> Vec; - /// Resample the buffer. - fn resample(&self, buffer: &[u8], source: Freq, target: SamplingConfig) -> Vec { - let src_fs = source.hz().abs() as f64; - let target_fs = target.freq().hz().abs() as f64; - let ratio = target_fs / src_fs; - if ratio > 1.0 { - self.upsample(buffer, ratio) - } else { - self.downsample(buffer, ratio) - } - } -} diff --git a/autd3/src/datagram/modulation/resampler/sinc.rs b/autd3/src/datagram/modulation/resampler/sinc.rs deleted file mode 100644 index a2b0d66b..00000000 --- a/autd3/src/datagram/modulation/resampler/sinc.rs +++ /dev/null @@ -1,146 +0,0 @@ -use std::{f64::consts::PI, num::NonZeroUsize}; - -use autd3_driver::utils::float::is_integer; - -use super::{window::InterpolationWindow, Blackman, Resampler}; - -/// Sinc interpolation resampler. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct SincInterpolation { - /// Window function. - pub window: T, -} - -impl Default for SincInterpolation { - fn default() -> Self { - Self { - window: Blackman { - size: NonZeroUsize::new(32).unwrap(), - }, - } - } -} - -#[inline] -fn sinc(x: f64) -> f64 { - if x == 0.0 { - 1.0 - } else { - (x * PI).sin() / (x * PI) - } -} - -#[inline] -fn modf(lhs: f64) -> (isize, f64) { - let int = lhs.floor() as isize; - let frac = lhs - int as f64; - (int, frac) -} - -impl Resampler for SincInterpolation { - fn upsample(&self, buffer: &[u8], ratio: f64) -> Vec { - let source_len = buffer.len(); - let window_size = self.window.window_size(); - let target_size = source_len as f64 * ratio; - // GRCOV_EXCL_START - if !is_integer(target_size) { - tracing::warn!( - "The target size ({}) is not an integer, ceiling to {}.", - target_size, - target_size.ceil() - ); - } - // GRCOV_EXCL_STOP - (0..target_size.ceil() as usize) - .map(|m| { - let (n, frac) = modf(m as f64 / ratio); - (0..window_size) - .map(|k| { - let kk = k as isize - window_size as isize / 2; - let idx = ((n + kk).rem_euclid(source_len as isize)) as usize; - buffer[idx] as f64 * sinc(kk as f64 - frac) * self.window.value(k) - }) - .sum::() - .round() as u8 - }) - .collect() - } - - fn downsample(&self, buffer: &[u8], ratio: f64) -> Vec { - let source_len = buffer.len(); - let window_size = self.window.window_size(); - let target_size = source_len as f64 * ratio; - // GRCOV_EXCL_START - if !is_integer(target_size) { - tracing::warn!( - "The target size ({}) is not an integer, ceiling to {}.", - target_size, - target_size.ceil() - ); - } - // GRCOV_EXCL_STOP - (0..target_size.ceil() as usize) - .map(|m| { - let (n, frac) = modf(m as f64 / ratio); - (0..window_size) - .map(|k| { - let kk = k as isize - window_size as isize / 2; - let idx = ((n + kk).rem_euclid(source_len as isize)) as usize; - ratio - * (buffer[idx] as f64 - * sinc((kk as f64 - frac) * ratio) - * self.window.value(k)) - }) - .sum::() - .round() as u8 - }) - .collect() - } -} - -#[cfg(test)] -mod tests { - use crate::modulation::resampler::{Blackman, Rectangular}; - - use super::*; - - #[rstest::rstest] - #[test] - #[case(vec![127, 217, 255, 223, 127, 42, 0, 37], vec![127, 255, 127, 0], 2.0, Rectangular { size: NonZeroUsize::new(32).unwrap() })] - #[case(vec![127, 217, 255, 217, 127, 37, 0, 37], vec![127, 255, 127, 0], 2.0, Rectangular { size: NonZeroUsize::new(4096).unwrap() })] - #[case(vec![127, 130, 127, 130], vec![127, 127], 2.0, Rectangular { size: NonZeroUsize::new(32).unwrap() })] - #[case(vec![127, 127, 127, 127], vec![127, 127], 2.0, Rectangular { size: NonZeroUsize::new(4096).unwrap() })] - #[case(vec![127, 217, 255, 217, 127, 37, 0, 37], vec![127, 255, 127, 0], 2.0, Blackman { size: NonZeroUsize::new(32).unwrap() })] - #[case(vec![127, 217, 255, 217, 127, 37, 0, 37], vec![127, 255, 127, 0], 2.0, Blackman { size: NonZeroUsize::new(4096).unwrap() })] - #[case(vec![127, 126, 127, 126], vec![127, 127], 2.0, Blackman { size: NonZeroUsize::new(32).unwrap() })] - #[case(vec![127, 127, 127, 127], vec![127, 127], 2.0, Blackman { size: NonZeroUsize::new(4096).unwrap() })] - fn upsample( - #[case] expect: Vec, - #[case] buffer: Vec, - #[case] ratio: f64, - #[case] window: impl InterpolationWindow, - ) { - let resampler = SincInterpolation { window }; - assert_eq!(expect, resampler.upsample(&buffer, ratio)); - } - - #[rstest::rstest] - #[test] - #[case(vec![124, 249, 124, 1], vec![127, 217, 255, 217, 127, 37, 0, 37], 0.5, Rectangular { size: NonZeroUsize::new(32).unwrap() })] - #[case(vec![127, 255, 127, 0], vec![127, 217, 255, 217, 127, 37, 0, 37], 0.5, Rectangular { size: NonZeroUsize::new(4096).unwrap() })] - #[case(vec![124, 124], vec![127, 127, 127, 127], 0.5, Rectangular { size: NonZeroUsize::new(32).unwrap() })] - #[case(vec![127, 127], vec![127, 127, 127, 127], 0.5, Rectangular { size: NonZeroUsize::new(4096).unwrap() })] - #[case(vec![127, 255, 127, 0], vec![127, 217, 255, 217, 127, 37, 0, 37], 0.5, Blackman { size: NonZeroUsize::new(32).unwrap() })] - #[case(vec![127, 255, 127, 0], vec![127, 217, 255, 217, 127, 37, 0, 37], 0.5, Blackman { size: NonZeroUsize::new(4096).unwrap() })] - #[case(vec![127, 127], vec![127, 127, 127, 127], 0.5, Blackman { size: NonZeroUsize::new(32).unwrap() })] - #[case(vec![127, 127], vec![127, 127, 127, 127], 0.5, Blackman { size: NonZeroUsize::new(4096).unwrap() })] - fn downsample( - #[case] expect: Vec, - #[case] buffer: Vec, - #[case] ratio: f64, - #[case] window: impl InterpolationWindow, - ) { - let resampler = SincInterpolation { window }; - assert_eq!(expect, resampler.downsample(&buffer, ratio)); - } -} diff --git a/autd3/src/datagram/modulation/resampler/window/blackman.rs b/autd3/src/datagram/modulation/resampler/window/blackman.rs deleted file mode 100644 index de96781a..00000000 --- a/autd3/src/datagram/modulation/resampler/window/blackman.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::{f64::consts::PI, num::NonZeroUsize}; - -use super::InterpolationWindow; - -/// Blackman window. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct Blackman { - /// Window size. - pub size: NonZeroUsize, -} - -impl InterpolationWindow for Blackman { - fn value(&self, idx: usize) -> f64 { - let x = idx as f64 / self.window_size() as f64; - 0.42 - 0.5 * (2.0 * PI * x).cos() + 0.08 * (4.0 * PI * x).cos() - } - - fn window_size(&self) -> usize { - self.size.get() - } -} diff --git a/autd3/src/datagram/modulation/resampler/window/mod.rs b/autd3/src/datagram/modulation/resampler/window/mod.rs deleted file mode 100644 index 4c9bb7ae..00000000 --- a/autd3/src/datagram/modulation/resampler/window/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -mod blackman; -mod rectangular; - -pub use blackman::Blackman; -pub use rectangular::Rectangular; - -/// Interpolation window trait. -pub trait InterpolationWindow: std::fmt::Debug + Clone + Copy + PartialEq + Send + Sync { - /// Get the window size. - fn window_size(&self) -> usize; - /// Get the value of the window at given index. - fn value(&self, idx: usize) -> f64; -} diff --git a/autd3/src/datagram/modulation/resampler/window/rectangular.rs b/autd3/src/datagram/modulation/resampler/window/rectangular.rs deleted file mode 100644 index 9d61ff27..00000000 --- a/autd3/src/datagram/modulation/resampler/window/rectangular.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::num::NonZeroUsize; - -use super::InterpolationWindow; - -/// Rectangular window. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct Rectangular { - /// Window size. - pub size: NonZeroUsize, -} - -impl InterpolationWindow for Rectangular { - fn value(&self, _idx: usize) -> f64 { - 1.0 - } - - fn window_size(&self) -> usize { - self.size.get() - } -} diff --git a/autd3/src/datagram/modulation/sampling_mode.rs b/autd3/src/datagram/modulation/sampling_mode.rs index 1bd9e3e9..eed56be3 100644 --- a/autd3/src/datagram/modulation/sampling_mode.rs +++ b/autd3/src/datagram/modulation/sampling_mode.rs @@ -1,9 +1,10 @@ +use autd3_core::{ + derive::{ModulationError, SamplingConfig}, + utils::float::is_integer, +}; use autd3_driver::{ defined::{ultrasound_freq, Freq, Hz}, - derive::SamplingConfig, - error::AUTDDriverError, firmware::fpga::MOD_BUF_SIZE_MAX, - utils::float::is_integer, }; use num::integer::gcd; use std::fmt::Debug; @@ -20,7 +21,7 @@ pub trait SamplingMode: Clone + Sync + Debug { fn validate( freq: Self::T, sampling_config: SamplingConfig, - ) -> Result<(u64, u64), AUTDDriverError>; + ) -> Result<(u64, u64), ModulationError>; } /// Exact frequency sampling mode with integer number. @@ -32,16 +33,16 @@ impl SamplingMode for ExactFreq { fn validate( freq: Freq, sampling_config: SamplingConfig, - ) -> Result<(u64, u64), AUTDDriverError> { + ) -> Result<(u64, u64), ModulationError> { if freq.hz() as f32 >= sampling_config.freq().hz() / 2. { - return Err(AUTDDriverError::ModulationError(format!( + return Err(ModulationError::new(format!( "Frequency ({:?}) is equal to or greater than the Nyquist frequency ({:?})", freq, sampling_config.freq() / 2. ))); } if freq.hz() == 0 { - return Err(AUTDDriverError::ModulationError( + return Err(ModulationError::new( "Frequency must not be zero. If intentional, Use `Static` instead.".to_string(), )); } @@ -63,20 +64,20 @@ impl SamplingMode for ExactFreqFloat { fn validate( freq: Freq, sampling_config: SamplingConfig, - ) -> Result<(u64, u64), AUTDDriverError> { + ) -> Result<(u64, u64), ModulationError> { if freq.hz() < 0. || freq.hz().is_nan() { - return Err(AUTDDriverError::ModulationError(format!( + return Err(ModulationError::new(format!( "Frequency ({:?}) must be valid positive value", freq ))); } if freq.hz() == 0. { - return Err(AUTDDriverError::ModulationError( + return Err(ModulationError::new( "Frequency must not be zero. If intentional, Use `Static` instead.".to_string(), )); } if freq.hz() >= sampling_config.freq().hz() / 2. { - return Err(AUTDDriverError::ModulationError(format!( + return Err(ModulationError::new(format!( "Frequency ({:?}) is equal to or greater than the Nyquist frequency ({:?})", freq, sampling_config.freq() / 2. @@ -96,7 +97,7 @@ impl SamplingMode for ExactFreqFloat { let k = fnd / fs; return Ok((n as _, k as _)); } - Err(AUTDDriverError::ModulationError(format!( + Err(ModulationError::new(format!( "Frequency ({:?}) cannot be output with the sampling config ({:?}).", freq, sampling_config ))) @@ -118,10 +119,10 @@ impl SamplingMode for NearestFreq { fn validate( freq: Freq, sampling_config: SamplingConfig, - ) -> Result<(u64, u64), AUTDDriverError> { + ) -> Result<(u64, u64), ModulationError> { let freq = Self::freq(freq, sampling_config); if freq.hz().is_nan() { - return Err(AUTDDriverError::ModulationError(format!( + return Err(ModulationError::new(format!( "Frequency ({:?}) must be valid value", freq ))); diff --git a/autd3/src/datagram/modulation/sine.rs b/autd3/src/datagram/modulation/sine.rs index b1d584c0..3994e815 100644 --- a/autd3/src/datagram/modulation/sine.rs +++ b/autd3/src/datagram/modulation/sine.rs @@ -1,7 +1,10 @@ -use autd3_driver::{ - defined::{Angle, Freq, PI}, +use std::f32::consts::PI; + +use autd3_core::{ + defined::{Angle, Freq}, derive::*, }; +use autd3_derive::Builder; use super::sampling_mode::{ExactFreq, NearestFreq, SamplingMode, SamplingModeInference}; @@ -80,7 +83,7 @@ impl Sine { } impl Sine { - pub(super) fn calc_raw(&self) -> Result, AUTDDriverError> { + pub(super) fn calc_raw(&self) -> Result, ModulationError> { let (n, rep) = S::validate(self.freq, self.config)?; let intensity = self.intensity; let offset = self.offset; @@ -93,7 +96,7 @@ impl Sine { } impl Modulation for Sine { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { self.calc_raw()? .map(|v| v.floor() as i16) .map(|v| { @@ -102,7 +105,7 @@ impl Modulation for Sine { } else if self.clamp { Ok(v.clamp(u8::MIN as _, u8::MAX as _) as _) } else { - Err(AUTDDriverError::ModulationError(format!( + Err(ModulationError::new(format!( "Sine modulation value ({}) is out of range [{}, {}]", v, u8::MIN, @@ -110,7 +113,7 @@ impl Modulation for Sine { )))? } }) - .collect::, AUTDDriverError>>() + .collect::, ModulationError>>() } } @@ -149,39 +152,39 @@ mod tests { 781.25*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency (150.01 Hz) cannot be output with the sampling config (SamplingConfig { division: 10 }).".to_owned())), + Err(ModulationError::new("Frequency (150.01 Hz) cannot be output with the sampling config (SamplingConfig { division: 10 }).".to_owned())), 150.01*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency (2000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), + Err(ModulationError::new("Frequency (2000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), 2000.*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency (2000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), + Err(ModulationError::new("Frequency (2000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), 2000*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency (4000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), + Err(ModulationError::new("Frequency (4000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), 4000.*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency (4000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), + Err(ModulationError::new("Frequency (4000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), 4000*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency (-0.1 Hz) must be valid positive value".to_owned())), + Err(ModulationError::new("Frequency (-0.1 Hz) must be valid positive value".to_owned())), -0.1*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency must not be zero. If intentional, Use `Static` instead.".to_owned())), + Err(ModulationError::new("Frequency must not be zero. If intentional, Use `Static` instead.".to_owned())), 0*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency must not be zero. If intentional, Use `Static` instead.".to_owned())), + Err(ModulationError::new("Frequency must not be zero. If intentional, Use `Static` instead.".to_owned())), 0.*Hz )] fn new( - #[case] expect: Result, AUTDDriverError>, + #[case] expect: Result, ModulationError>, #[case] freq: impl SamplingModeInference, ) { let m = Sine::new(freq); @@ -206,10 +209,10 @@ mod tests { 200.*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency (NaN Hz) must be valid value".to_owned())), + Err(ModulationError::new("Frequency (NaN Hz) must be valid value".to_owned())), f32::NAN * Hz )] - fn new_nearest(#[case] expect: Result, AUTDDriverError>, #[case] freq: Freq) { + fn new_nearest(#[case] expect: Result, ModulationError>, #[case] freq: Freq) { let m = Sine::new_nearest(freq); if !freq.hz().is_nan() { assert_eq!(freq, m.freq()); @@ -238,7 +241,7 @@ mod tests { #[rstest::rstest] #[case( - Err(AUTDDriverError::ModulationError("Sine modulation value (-1) is out of range [0, 255]".to_owned())), + Err(ModulationError::new("Sine modulation value (-1) is out of range [0, 255]".to_owned())), 0x00, false )] @@ -249,7 +252,7 @@ mod tests { )] #[test] fn out_of_range( - #[case] expect: Result, AUTDDriverError>, + #[case] expect: Result, ModulationError>, #[case] offset: u8, #[case] clamp: bool, ) { diff --git a/autd3/src/datagram/modulation/square.rs b/autd3/src/datagram/modulation/square.rs index 861a6057..8f9b8fd0 100644 --- a/autd3/src/datagram/modulation/square.rs +++ b/autd3/src/datagram/modulation/square.rs @@ -1,4 +1,5 @@ -use autd3_driver::{defined::Freq, derive::*}; +use autd3_core::{defined::Freq, derive::*}; +use autd3_derive::Builder; use super::sampling_mode::{ExactFreq, NearestFreq, SamplingMode, SamplingModeInference}; @@ -69,9 +70,9 @@ impl Square { } impl Modulation for Square { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { if !(0.0..=1.0).contains(&self.duty) { - return Err(AUTDDriverError::ModulationError( + return Err(ModulationError::new( "duty must be in range from 0 to 1".to_string(), )); } @@ -138,35 +139,35 @@ mod tests { 781.25*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency (150.01 Hz) cannot be output with the sampling config (SamplingConfig { division: 10 }).".to_owned())), + Err(ModulationError::new("Frequency (150.01 Hz) cannot be output with the sampling config (SamplingConfig { division: 10 }).".to_owned())), 150.01*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency (2000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), + Err(ModulationError::new("Frequency (2000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), 2000.*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency (2000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), + Err(ModulationError::new("Frequency (2000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), 2000*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency (4000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), + Err(ModulationError::new("Frequency (4000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), 4000.*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency (4000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), + Err(ModulationError::new("Frequency (4000 Hz) is equal to or greater than the Nyquist frequency (2000 Hz)".to_owned())), 4000*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency must not be zero. If intentional, Use `Static` instead.".to_owned())), + Err(ModulationError::new("Frequency must not be zero. If intentional, Use `Static` instead.".to_owned())), 0*Hz )] #[case( - Err(AUTDDriverError::ModulationError("Frequency must not be zero. If intentional, Use `Static` instead.".to_owned())), + Err(ModulationError::new("Frequency must not be zero. If intentional, Use `Static` instead.".to_owned())), 0.*Hz )] fn with_freq_float_exact( - #[case] expect: Result, AUTDDriverError>, + #[case] expect: Result, ModulationError>, #[case] freq: impl SamplingModeInference, ) { let m = Square::new(freq); @@ -191,7 +192,7 @@ mod tests { Ok(vec![255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), 200.*Hz )] - fn new_nearest(#[case] expect: Result, AUTDDriverError>, #[case] freq: Freq) { + fn new_nearest(#[case] expect: Result, ModulationError>, #[case] freq: Freq) { let m = Square::new_nearest(freq); assert_eq!(freq, m.freq()); assert_eq!(u8::MIN, m.low()); @@ -236,14 +237,14 @@ mod tests { #[test] fn duty_out_of_range() { assert_eq!( - Some(AUTDDriverError::ModulationError( + Some(ModulationError::new( "duty must be in range from 0 to 1".to_string() )), Square::new(150. * Hz).with_duty(-0.1).calc().err() ); assert_eq!( - Some(AUTDDriverError::ModulationError( + Some(ModulationError::new( "duty must be in range from 0 to 1".to_string() )), Square::new(150. * Hz).with_duty(1.1).calc().err() diff --git a/autd3/src/datagram/modulation/static.rs b/autd3/src/datagram/modulation/static.rs index f2710259..16234fc4 100644 --- a/autd3/src/datagram/modulation/static.rs +++ b/autd3/src/datagram/modulation/static.rs @@ -1,4 +1,5 @@ -use autd3_driver::derive::*; +use autd3_core::derive::*; +use autd3_derive::Builder; /// [`Modulation`] for no modulation #[derive(Modulation, Clone, Debug, PartialEq, Builder)] @@ -28,7 +29,7 @@ impl Static { } impl Modulation for Static { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { let intensity = self.intensity; Ok(vec![intensity; 2]) } diff --git a/autd3/src/datagram/stm/circle.rs b/autd3/src/datagram/stm/circle.rs index d8e8865b..5c2bac00 100644 --- a/autd3/src/datagram/stm/circle.rs +++ b/autd3/src/datagram/stm/circle.rs @@ -1,12 +1,16 @@ -use std::f32::consts::PI; +use std::{collections::HashMap, f32::consts::PI}; +use autd3_core::{ + derive::{Device, Geometry}, + gain::{BitVec, EmitIntensity, GainError, Phase}, +}; use autd3_driver::{ datagram::{ ControlPoint, ControlPoints, FociSTMContext, FociSTMContextGenerator, FociSTMGenerator, GainSTMContext, GainSTMContextGenerator, GainSTMGenerator, IntoFociSTMGenerator, IntoGainSTMGenerator, }, - derive::{EmitIntensity, Phase}, + error::AUTDDriverError, geometry::{Point3, UnitVector3, Vector3}, }; @@ -89,7 +93,7 @@ impl GainSTMContext for CircleSTMContext { impl FociSTMContextGenerator<1> for Circle { type Context = CircleSTMContext; - fn generate(&mut self, device: &autd3_driver::derive::Device) -> Self::Context { + fn generate(&mut self, device: &Device) -> Self::Context { let v = if self.n.dot(&Vector3::z()).abs() < 0.9 { Vector3::z() } else { @@ -114,7 +118,7 @@ impl GainSTMContextGenerator for Circle { type Gain = Focus; type Context = CircleSTMContext; - fn generate(&mut self, device: &autd3_driver::derive::Device) -> Self::Context { + fn generate(&mut self, device: &Device) -> Self::Context { FociSTMContextGenerator::<1>::generate(self, device) } } @@ -123,7 +127,7 @@ impl FociSTMGenerator<1> for Circle { type T = Self; // GRCOV_EXCL_START - fn init(self) -> Result { + fn init(self) -> Result { Ok(self) } // GRCOV_EXCL_STOP @@ -139,9 +143,9 @@ impl GainSTMGenerator for Circle { // GRCOV_EXCL_START fn init( self, - _geometry: &autd3_driver::derive::Geometry, - _filter: Option<&std::collections::HashMap>>, - ) -> Result { + _geometry: &Geometry, + _filter: Option<&HashMap>, + ) -> Result { Ok(self) } // GRCOV_EXCL_STOP @@ -171,7 +175,12 @@ impl IntoGainSTMGenerator for Circle { mod tests { use std::ops::DerefMut; - use autd3_driver::{datagram::FociSTM, defined::mm}; + use autd3_core::modulation::SamplingConfig; + use autd3_driver::{ + datagram::{FociSTM, GainSTM}, + defined::mm, + geometry::IntoDevice, + }; use crate::assert_near_vector3; @@ -210,8 +219,6 @@ mod tests { )] #[test] fn circle(#[case] expect: Vec, #[case] n: UnitVector3) { - use autd3_driver::{datagram::GainSTM, derive::SamplingConfig, geometry::IntoDevice}; - let circle = Circle { center: Point3::origin(), radius: 30.0 * mm, diff --git a/autd3/src/datagram/stm/line.rs b/autd3/src/datagram/stm/line.rs index 4e57d3cf..37df6313 100644 --- a/autd3/src/datagram/stm/line.rs +++ b/autd3/src/datagram/stm/line.rs @@ -1,10 +1,14 @@ +use autd3_core::{ + derive::{Device, Geometry}, + gain::{EmitIntensity, GainError, Phase}, +}; use autd3_driver::{ datagram::{ ControlPoint, ControlPoints, FociSTMContext, FociSTMContextGenerator, FociSTMGenerator, GainSTMContext, GainSTMContextGenerator, GainSTMGenerator, IntoFociSTMGenerator, IntoGainSTMGenerator, }, - derive::{EmitIntensity, Phase}, + error::AUTDDriverError, geometry::{Point3, Vector3}, }; @@ -82,7 +86,7 @@ impl GainSTMContext for LineSTMContext { impl FociSTMContextGenerator<1> for Line { type Context = LineSTMContext; - fn generate(&mut self, device: &autd3_driver::derive::Device) -> Self::Context { + fn generate(&mut self, device: &Device) -> Self::Context { Self::Context { start: self.start, dir: self.end - self.start, @@ -98,7 +102,7 @@ impl GainSTMContextGenerator for Line { type Gain = Focus; type Context = LineSTMContext; - fn generate(&mut self, device: &autd3_driver::derive::Device) -> Self::Context { + fn generate(&mut self, device: &Device) -> Self::Context { FociSTMContextGenerator::<1>::generate(self, device) } } @@ -107,7 +111,7 @@ impl FociSTMGenerator<1> for Line { type T = Self; // GRCOV_EXCL_START - fn init(self) -> Result { + fn init(self) -> Result { Ok(self) } // GRCOV_EXCL_STOP @@ -123,9 +127,9 @@ impl GainSTMGenerator for Line { // GRCOV_EXCL_START fn init( self, - _geometry: &autd3_driver::derive::Geometry, - _filter: Option<&std::collections::HashMap>>, - ) -> Result { + _geometry: &Geometry, + _filter: Option<&std::collections::HashMap>, + ) -> Result { Ok(self) } // GRCOV_EXCL_STOP @@ -155,10 +159,10 @@ impl IntoGainSTMGenerator for Line { mod tests { use std::ops::DerefMut; + use autd3_core::modulation::SamplingConfig; use autd3_driver::{ datagram::{FociSTM, GainSTM}, defined::mm, - derive::SamplingConfig, }; use crate::assert_near_vector3; diff --git a/autd3/src/error.rs b/autd3/src/error.rs index 705c246f..0a779640 100644 --- a/autd3/src/error.rs +++ b/autd3/src/error.rs @@ -1,3 +1,4 @@ +use autd3_core::link::LinkError; use autd3_driver::error::AUTDDriverError; use thiserror::Error; @@ -15,3 +16,9 @@ pub enum AUTDError { #[error("{0}")] Driver(#[from] AUTDDriverError), } + +impl From for AUTDError { + fn from(e: LinkError) -> Self { + AUTDError::Driver(AUTDDriverError::Link(e)) + } +} \ No newline at end of file diff --git a/autd3/src/lib.rs b/autd3/src/lib.rs index cecfd30f..442dee54 100644 --- a/autd3/src/lib.rs +++ b/autd3/src/lib.rs @@ -32,8 +32,8 @@ pub mod prelude; #[cfg(feature = "async")] pub mod r#async; +pub use autd3_core as core; pub use autd3_driver as driver; -pub use autd3_driver::derive; pub use datagram::gain; pub use datagram::modulation; diff --git a/autd3/src/link/audit.rs b/autd3/src/link/audit.rs index 82841909..c7467c0c 100644 --- a/autd3/src/link/audit.rs +++ b/autd3/src/link/audit.rs @@ -1,8 +1,9 @@ -use autd3_driver::{ - derive::*, - firmware::cpu::{RxMessage, TxMessage}, - link::{Link, LinkBuilder}, +use autd3_core::{ + geometry::Geometry, + link::{Link, LinkBuilder, LinkError}, }; +use autd3_derive::Builder; +use autd3_driver::firmware::cpu::{RxMessage, TxMessage}; use autd3_firmware_emulator::CPUEmulator; use derive_more::{Deref, DerefMut}; @@ -44,7 +45,7 @@ pub struct AuditBuilder { impl LinkBuilder for AuditBuilder { type L = Audit; - fn open(self, geometry: &autd3_driver::geometry::Geometry) -> Result { + fn open(self, geometry: &Geometry) -> Result { Ok(Audit { is_open: true, cpus: geometry @@ -112,14 +113,14 @@ impl Audit { } impl Link for Audit { - fn close(&mut self) -> Result<(), AUTDDriverError> { + fn close(&mut self) -> Result<(), LinkError> { self.is_open = false; Ok(()) } - fn send(&mut self, tx: &[TxMessage]) -> Result { + fn send(&mut self, tx: &[TxMessage]) -> Result { if self.broken { - return Err(AUTDDriverError::LinkError("broken".to_owned())); + return Err(LinkError::new("broken".to_owned())); } if self.down { @@ -133,9 +134,9 @@ impl Link for Audit { Ok(true) } - fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + fn receive(&mut self, rx: &mut [RxMessage]) -> Result { if self.broken { - return Err(AUTDDriverError::LinkError("broken".to_owned())); + return Err(LinkError::new("broken".to_owned())); } if self.down { @@ -161,32 +162,32 @@ impl Link for Audit { } #[cfg(feature = "async")] -use autd3_driver::link::{AsyncLink, AsyncLinkBuilder}; +use autd3_core::link::{AsyncLink, AsyncLinkBuilder}; #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] -#[cfg_attr(feature = "async-trait", autd3_driver::async_trait)] +#[cfg_attr(feature = "async-trait", autd3_core::async_trait)] impl AsyncLinkBuilder for AuditBuilder { type L = Audit; - async fn open(self, geometry: &Geometry) -> Result { + async fn open(self, geometry: &Geometry) -> Result { ::open(self, geometry) } } #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] -#[cfg_attr(feature = "async-trait", autd3_driver::async_trait)] +#[cfg_attr(feature = "async-trait", autd3_core::async_trait)] impl AsyncLink for Audit { - async fn close(&mut self) -> Result<(), AUTDDriverError> { + async fn close(&mut self) -> Result<(), LinkError> { ::close(self) } - async fn send(&mut self, tx: &[TxMessage]) -> Result { + async fn send(&mut self, tx: &[TxMessage]) -> Result { ::send(self, tx) } - async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { ::receive(self, rx) } diff --git a/autd3/src/link/nop.rs b/autd3/src/link/nop.rs index 95bf71f2..b2d28427 100644 --- a/autd3/src/link/nop.rs +++ b/autd3/src/link/nop.rs @@ -1,8 +1,9 @@ -use autd3_driver::{ +use autd3_core::{ derive::*, - firmware::cpu::{RxMessage, TxMessage}, - link::{Link, LinkBuilder}, + link::{Link, LinkBuilder, LinkError}, }; +use autd3_derive::Builder; +use autd3_driver::firmware::cpu::{RxMessage, TxMessage}; use autd3_firmware_emulator::CPUEmulator; /// A [`Link`] that does nothing. @@ -20,7 +21,7 @@ pub struct NopBuilder {} impl LinkBuilder for NopBuilder { type L = Nop; - fn open(self, geometry: &Geometry) -> Result { + fn open(self, geometry: &Geometry) -> Result { Ok(Nop { is_open: true, cpus: geometry @@ -33,12 +34,12 @@ impl LinkBuilder for NopBuilder { } impl Link for Nop { - fn close(&mut self) -> Result<(), AUTDDriverError> { + fn close(&mut self) -> Result<(), LinkError> { self.is_open = false; Ok(()) } - fn send(&mut self, tx: &[TxMessage]) -> Result { + fn send(&mut self, tx: &[TxMessage]) -> Result { self.cpus.iter_mut().for_each(|cpu| { cpu.send(tx); }); @@ -46,7 +47,7 @@ impl Link for Nop { Ok(true) } - fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + fn receive(&mut self, rx: &mut [RxMessage]) -> Result { self.cpus.iter_mut().for_each(|cpu| { cpu.update(); rx[cpu.idx()] = cpu.rx(); @@ -61,32 +62,32 @@ impl Link for Nop { } #[cfg(feature = "async")] -use autd3_driver::link::{AsyncLink, AsyncLinkBuilder}; +use autd3_core::link::{AsyncLink, AsyncLinkBuilder}; #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] -#[cfg_attr(feature = "async-trait", autd3_driver::async_trait)] +#[cfg_attr(feature = "async-trait", autd3_core::async_trait)] impl AsyncLinkBuilder for NopBuilder { type L = Nop; - async fn open(self, geometry: &Geometry) -> Result { + async fn open(self, geometry: &Geometry) -> Result { ::open(self, geometry) } } #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] -#[cfg_attr(feature = "async-trait", autd3_driver::async_trait)] +#[cfg_attr(feature = "async-trait", autd3_core::async_trait)] impl AsyncLink for Nop { - async fn close(&mut self) -> Result<(), AUTDDriverError> { + async fn close(&mut self) -> Result<(), LinkError> { ::close(self) } - async fn send(&mut self, tx: &[TxMessage]) -> Result { + async fn send(&mut self, tx: &[TxMessage]) -> Result { ::send(self, tx) } - async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { + async fn receive(&mut self, rx: &mut [RxMessage]) -> Result { ::receive(self, rx) } diff --git a/autd3/src/prelude.rs b/autd3/src/prelude.rs index 13dcc96b..e1782420 100644 --- a/autd3/src/prelude.rs +++ b/autd3/src/prelude.rs @@ -9,13 +9,15 @@ pub use crate::{ link::Nop, }; +pub use autd3_core::modulation::{Modulation, ModulationProperty}; + pub use autd3_driver::{ autd3_device::AUTD3, datagram::{ Clear, ControlPoint, ControlPoints, DebugSettings, FixedUpdateRate, FociSTM, ForceFan, GainSTM, IntoDatagramWithParallelThreshold, IntoDatagramWithSegment, - IntoDatagramWithTimeout, Modulation, ModulationProperty, PhaseCorrection, - PulseWidthEncoder, ReadsFPGAState, Silencer, SwapSegment, + IntoDatagramWithTimeout, PhaseCorrection, PulseWidthEncoder, ReadsFPGAState, Silencer, + SwapSegment, }, defined::{deg, kHz, mm, rad, ultrasound_freq, Hz, PI}, error::AUTDDriverError, diff --git a/autd3/tests/async/link/audit.rs b/autd3/tests/async/link/audit.rs index 03ca40a2..2bdff796 100644 --- a/autd3/tests/async/link/audit.rs +++ b/autd3/tests/async/link/audit.rs @@ -1,6 +1,7 @@ use std::time::Duration; use autd3::{link::Audit, prelude::*, r#async::Controller}; +use autd3_core::link::LinkError; use autd3_driver::firmware::{cpu::RxMessage, fpga::FPGAState}; #[tokio::test] @@ -35,13 +36,13 @@ async fn audit_test() -> anyhow::Result<()> { assert!(autd.send(ReadsFPGAState::new(|_| true)).await.is_ok()); autd.link_mut()[0].update(); assert_eq!( - vec![Option::::from(&RxMessage::new(0x88, 0x00))], + vec![FPGAState::from_rx(&RxMessage::new(0x88, 0x00))], autd.fpga_state().await? ); autd.link_mut()[0].fpga_mut().assert_thermal_sensor(); autd.link_mut()[0].update(); assert_eq!( - vec![Option::::from(&RxMessage::new(0x89, 0x00))], + vec![FPGAState::from_rx(&RxMessage::new(0x89, 0x00))], autd.fpga_state().await? ); } @@ -57,13 +58,13 @@ async fn audit_test() -> anyhow::Result<()> { assert!(autd.send(Static::new()).await.is_ok()); autd.link_mut().break_down(); assert_eq!( - Err(AUTDDriverError::LinkError("broken".to_string())), + Err(AUTDDriverError::Link(LinkError::new("broken".to_string()))), autd.send(Static::new()).await ); assert_eq!( - Err(AUTDError::Driver(AUTDDriverError::LinkError( + Err(AUTDError::Driver(AUTDDriverError::Link(LinkError::new( "broken".to_string() - ))), + )))), autd.fpga_state().await ); autd.link_mut().repair(); @@ -71,7 +72,7 @@ async fn audit_test() -> anyhow::Result<()> { } { - use autd3_driver::link::AsyncLink; + use autd3_core::link::AsyncLink; assert!(autd.link_mut().close().await.is_ok()); assert_eq!( Err(AUTDDriverError::LinkClosed), diff --git a/autd3/tests/async/link/nop.rs b/autd3/tests/async/link/nop.rs index 67c3ec1d..1b378cf4 100644 --- a/autd3/tests/async/link/nop.rs +++ b/autd3/tests/async/link/nop.rs @@ -1,4 +1,4 @@ -use autd3::{driver::link::AsyncLink, prelude::*, r#async::Controller}; +use autd3::{core::link::AsyncLink, prelude::*, r#async::Controller}; #[tokio::test] async fn nop_test() -> anyhow::Result<()> { diff --git a/autd3/tests/sync/link/audit.rs b/autd3/tests/sync/link/audit.rs index 995e6e54..a7413ced 100644 --- a/autd3/tests/sync/link/audit.rs +++ b/autd3/tests/sync/link/audit.rs @@ -1,6 +1,7 @@ use std::time::Duration; use autd3::{link::Audit, prelude::*}; +use autd3_core::link::LinkError; use autd3_driver::firmware::{cpu::RxMessage, fpga::FPGAState}; #[test] @@ -32,13 +33,13 @@ fn audit_test() -> anyhow::Result<()> { assert!(autd.send(ReadsFPGAState::new(|_| true)).is_ok()); autd.link_mut()[0].update(); assert_eq!( - vec![Option::::from(&RxMessage::new(0x88, 0x00))], + vec![FPGAState::from_rx(&RxMessage::new(0x88, 0x00))], autd.fpga_state()? ); autd.link_mut()[0].fpga_mut().assert_thermal_sensor(); autd.link_mut()[0].update(); assert_eq!( - vec![Option::::from(&RxMessage::new(0x89, 0x00))], + vec![FPGAState::from_rx(&RxMessage::new(0x89, 0x00))], autd.fpga_state()? ); } @@ -54,13 +55,13 @@ fn audit_test() -> anyhow::Result<()> { assert!(autd.send(Static::new()).is_ok()); autd.link_mut().break_down(); assert_eq!( - Err(AUTDDriverError::LinkError("broken".to_string())), + Err(AUTDDriverError::Link(LinkError::new("broken".to_string()))), autd.send(Static::new()) ); assert_eq!( - Err(AUTDError::Driver(AUTDDriverError::LinkError( + Err(AUTDError::Driver(AUTDDriverError::Link(LinkError::new( "broken".to_string() - ))), + )))), autd.fpga_state() ); autd.link_mut().repair(); @@ -68,7 +69,7 @@ fn audit_test() -> anyhow::Result<()> { } { - use autd3_driver::link::Link; + use autd3_core::link::Link; assert!(autd.link_mut().close().is_ok()); assert_eq!(Err(AUTDDriverError::LinkClosed), autd.send(Static::new())); assert_eq!( diff --git a/autd3/tests/sync/link/nop.rs b/autd3/tests/sync/link/nop.rs index 55975742..265de82f 100644 --- a/autd3/tests/sync/link/nop.rs +++ b/autd3/tests/sync/link/nop.rs @@ -1,4 +1,5 @@ -use autd3::{driver::link::Link, prelude::*}; +use autd3::prelude::*; +use autd3_core::link::Link; #[test] fn nop_test() -> anyhow::Result<()> { From 8ecdec08d1a4eaa2391c3e74eeeb0d277b09b462 Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Tue, 14 Jan 2025 16:03:46 +0900 Subject: [PATCH 14/20] update examples --- examples/Cargo.toml | 7 +++---- examples/src/tests/audio_file.rs | 2 +- examples/src/tests/bessel.rs | 2 +- examples/src/tests/custom.rs | 2 +- examples/src/tests/fir.rs | 2 +- examples/src/tests/flag.rs | 2 +- examples/src/tests/focus.rs | 2 +- examples/src/tests/group.rs | 2 +- examples/src/tests/holo.rs | 6 ++---- examples/src/tests/plane.rs | 2 +- examples/src/tests/stm.rs | 2 +- examples/src/tests/test_runner.rs | 2 +- examples/src/tests/user_defined_gain_modulation.rs | 12 +++++++----- 13 files changed, 22 insertions(+), 23 deletions(-) diff --git a/examples/Cargo.toml b/examples/Cargo.toml index d73c98f4..4a3fc5a7 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -53,7 +53,7 @@ autd3-protobuf = { workspace = true, optional = true } color-print = { workspace = true } tokio = { workspace = true, features = ["macros", "rt-multi-thread"], optional = true } tonic = { workspace = true, optional = true } -tracing-subscriber = { workspace = true } +tracing-subscriber = { workspace = true, features = ["fmt"] } tracing = { workspace = true } [features] @@ -62,8 +62,7 @@ async = ["autd3/async", "tokio"] simulator = ["autd3-link-simulator"] twincat = ["autd3-link-twincat/local"] remote_twincat = ["autd3-link-twincat/remote"] -lightweight = ["autd3-protobuf/lightweight", "autd3-driver/async-trait", "autd3-driver/lightweight"] -lightweight-server = ["autd3-protobuf/lightweight", "autd3-link-twincat/local", "autd3-link-twincat/async-trait", "autd3-driver/async-trait", "autd3-driver/lightweight", "tonic", "tokio/signal"] +lightweight = ["autd3-protobuf/lightweight", "autd3-protobuf/async-trait", "autd3-driver/lightweight"] +lightweight-server = ["autd3-protobuf/lightweight", "autd3-link-twincat/local", "autd3-link-twincat/async-trait", "autd3-protobuf/async-trait", "autd3-driver/lightweight", "tonic", "tokio/signal"] all = ["async", "twincat", "remote_twincat", "simulator"] -unity = ["autd3-driver/use_meter", "autd3-driver/left_handed"] dynamic_freq = ["autd3-driver/dynamic_freq"] diff --git a/examples/src/tests/audio_file.rs b/examples/src/tests/audio_file.rs index c0bd2b45..bd61222f 100644 --- a/examples/src/tests/audio_file.rs +++ b/examples/src/tests/audio_file.rs @@ -1,4 +1,4 @@ -use autd3::{driver::link::Link, prelude::*}; +use autd3::{core::link::Link, prelude::*}; pub fn audio_file(autd: &mut Controller) -> anyhow::Result { autd.send(Silencer::default())?; diff --git a/examples/src/tests/bessel.rs b/examples/src/tests/bessel.rs index 3dd501a2..6f3f18a7 100644 --- a/examples/src/tests/bessel.rs +++ b/examples/src/tests/bessel.rs @@ -1,4 +1,4 @@ -use autd3::{driver::link::Link, prelude::*}; +use autd3::{core::link::Link, prelude::*}; pub fn bessel(autd: &mut Controller) -> anyhow::Result { autd.send(Silencer::default())?; diff --git a/examples/src/tests/custom.rs b/examples/src/tests/custom.rs index c0ad3511..3bf7243b 100644 --- a/examples/src/tests/custom.rs +++ b/examples/src/tests/custom.rs @@ -1,4 +1,4 @@ -use autd3::{driver::link::Link, prelude::*}; +use autd3::{core::link::Link, prelude::*}; pub fn custom(autd: &mut Controller) -> anyhow::Result { autd.send(Silencer::disable())?; diff --git a/examples/src/tests/fir.rs b/examples/src/tests/fir.rs index b6c068bd..92c790da 100644 --- a/examples/src/tests/fir.rs +++ b/examples/src/tests/fir.rs @@ -1,4 +1,4 @@ -use autd3::{driver::link::Link, prelude::*}; +use autd3::{core::link::Link, prelude::*}; pub fn fir(autd: &mut Controller) -> anyhow::Result { autd.send(Silencer::disable())?; diff --git a/examples/src/tests/flag.rs b/examples/src/tests/flag.rs index 49fd1d2c..8a73101a 100644 --- a/examples/src/tests/flag.rs +++ b/examples/src/tests/flag.rs @@ -1,4 +1,4 @@ -use autd3::{driver::link::Link, prelude::*}; +use autd3::{core::link::Link, prelude::*}; pub fn flag(autd: &mut Controller) -> anyhow::Result { autd.send(ReadsFPGAState::new(|_dev| true))?; diff --git a/examples/src/tests/focus.rs b/examples/src/tests/focus.rs index 8a01224a..0d43bc6a 100644 --- a/examples/src/tests/focus.rs +++ b/examples/src/tests/focus.rs @@ -1,4 +1,4 @@ -use autd3::{driver::link::Link, prelude::*}; +use autd3::{core::link::Link, prelude::*}; pub fn focus(autd: &mut Controller) -> anyhow::Result { autd.send(Silencer::default())?; diff --git a/examples/src/tests/group.rs b/examples/src/tests/group.rs index 6c07d443..9880899c 100644 --- a/examples/src/tests/group.rs +++ b/examples/src/tests/group.rs @@ -1,4 +1,4 @@ -use autd3::{driver::link::Link, prelude::*}; +use autd3::{core::link::Link, prelude::*}; pub fn group_by_device(autd: &mut Controller) -> anyhow::Result { let center = autd.center() + Vector3::new(0., 0., 150.0 * mm); diff --git a/examples/src/tests/holo.rs b/examples/src/tests/holo.rs index ca75eabe..11bd1935 100644 --- a/examples/src/tests/holo.rs +++ b/examples/src/tests/holo.rs @@ -1,8 +1,6 @@ use autd3::{ - driver::{ - datagram::{BoxedGain, IntoBoxedGain}, - link::Link, - }, + core::link::Link, + driver::datagram::{BoxedGain, IntoBoxedGain}, prelude::*, }; use autd3_gain_holo::*; diff --git a/examples/src/tests/plane.rs b/examples/src/tests/plane.rs index 151f01a7..1b4a7bbd 100644 --- a/examples/src/tests/plane.rs +++ b/examples/src/tests/plane.rs @@ -1,4 +1,4 @@ -use autd3::{driver::link::Link, prelude::*}; +use autd3::{core::link::Link, prelude::*}; pub fn plane(autd: &mut Controller) -> anyhow::Result { autd.send(Silencer::default())?; diff --git a/examples/src/tests/stm.rs b/examples/src/tests/stm.rs index c992511c..49ac65b7 100644 --- a/examples/src/tests/stm.rs +++ b/examples/src/tests/stm.rs @@ -1,4 +1,4 @@ -use autd3::{driver::link::Link, prelude::*}; +use autd3::{core::link::Link, prelude::*}; pub fn foci_stm(autd: &mut Controller) -> anyhow::Result { autd.send(Silencer::disable())?; diff --git a/examples/src/tests/test_runner.rs b/examples/src/tests/test_runner.rs index 5625edc8..6873d45a 100644 --- a/examples/src/tests/test_runner.rs +++ b/examples/src/tests/test_runner.rs @@ -1,6 +1,6 @@ use std::io::{self, Write}; -use autd3::{driver::link::Link, prelude::*}; +use autd3::{core::link::Link, prelude::*}; use super::{ audio_file::*, bessel::*, custom::*, fir::*, flag::*, focus::*, group::*, holo::*, plane::*, diff --git a/examples/src/tests/user_defined_gain_modulation.rs b/examples/src/tests/user_defined_gain_modulation.rs index 6bcab514..3beb2b39 100644 --- a/examples/src/tests/user_defined_gain_modulation.rs +++ b/examples/src/tests/user_defined_gain_modulation.rs @@ -1,5 +1,7 @@ -use autd3::{driver::link::Link, prelude::*}; -use autd3_driver::derive::*; +use autd3::{ + core::{derive::*, link::Link}, + prelude::*, +}; #[derive(Gain, Clone, Copy, Debug)] pub struct MyUniform {} @@ -30,8 +32,8 @@ impl Gain for MyUniform { fn init( self, _geometry: &Geometry, - _filter: Option<&HashMap>>, - ) -> Result { + _filter: Option<&HashMap>, + ) -> Result { Ok(self) } } @@ -52,7 +54,7 @@ impl Burst { } impl Modulation for Burst { - fn calc(self) -> Result, AUTDDriverError> { + fn calc(self) -> Result, ModulationError> { Ok((0..4000) .map(|i| if i == 3999 { u8::MAX } else { u8::MIN }) .collect()) From d945e2aeca820eaf7075566654e94bcd0c0d5852 Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Tue, 14 Jan 2025 16:03:56 +0900 Subject: [PATCH 15/20] update github workflows --- .github/workflows/build.yml | 1 + .github/workflows/pr.yml | 1 + .github/workflows/release.yml | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f7ebb184..b9035df8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -86,6 +86,7 @@ jobs: - uses: dtolnay/install@cargo-docs-rs - run: | cargo +nightly docs-rs -pautd3 + cargo +nightly docs-rs -pautd3-core cargo +nightly docs-rs -pautd3-derive cargo +nightly docs-rs -pautd3-driver cargo +nightly docs-rs -pautd3-firmware-emulator diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 8cedf212..a6cea9a7 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -151,6 +151,7 @@ jobs: - uses: dtolnay/install@cargo-docs-rs - run: | cargo +nightly docs-rs -pautd3 + cargo +nightly docs-rs -pautd3-core cargo +nightly docs-rs -pautd3-derive cargo +nightly docs-rs -pautd3-driver cargo +nightly docs-rs -pautd3-firmware-emulator diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9401c7fe..c773c940 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,6 +20,10 @@ jobs: with: feature-group: default-features - run: | + cd autd3-core + cargo publish --no-verify --token ${{ secrets.CRATEIO_TOKEN }} + cd .. && sleep 15 + cd autd3-driver cargo publish --no-verify --token ${{ secrets.CRATEIO_TOKEN }} cd .. && sleep 15 From 0067fada9c699c56ac44a7f77188f0a5a140983a Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Tue, 14 Jan 2025 16:04:08 +0900 Subject: [PATCH 16/20] update deps --- Cargo.toml | 62 +++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 129abbb4..ab83f19f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,57 +25,57 @@ repository = "https://github.com/shinolab/autd3-rs" keywords = ["autd"] [workspace.dependencies] +zerocopy = { version = "0.8.14", default-features = false } anyhow = { version = "1.0.95", default-features = false } -async-trait = "0.1.85" -bitfield-struct = "0.10.0" -bitflags = "2.7.0" +async-trait = { version = "0.1.85", default-features = false } +bitfield-struct = { version = "0.10.0", default-features = false } +bitflags = { version = "2.7.0", default-features = false } bit-vec = { version = "0.8.0", default-features = false } bvh = { version = "0.10.0", default-features = false } -cc = "1.2.7" -color-print = "0.3.7" +cc = { version = "1.2.9", default-features = false } +color-print = { version = "0.3.7", default-features = false } criterion = { version = "0.5.1", default-features = false } derive-new = { version = "0.7.0", default-features = false } -glob = "0.3.2" -hound = "3.5.1" -itertools = { version = "0.14.0", features = ["use_alloc"], default-features = false } -libloading = "0.8.6" +glob = { version = "0.3.2", default-features = false } +hound = { version = "3.5.1", default-features = false } +itertools = { version = "0.14.0", default-features = false } +libloading = { version = "0.8.6", default-features = false } nalgebra = { version = "0.33.2", default-features = false } num = { version = "0.4.3", default-features = false } -paste = "1.0.15" -proc-macro2 = { version = "1.0.89", default-features = false } -prost = { version = "0.13.4", features = ["derive"], default-features = false } +paste = { version = "1.0.15", default-features = false } +proc-macro2 = { version = "1.0.93", default-features = false } +prost = { version = "0.13.4", default-features = false } quote = { version = "1.0.38", default-features = false } rand = { version = "0.8.5", default-features = false } rstest = { version = "0.24.0", default-features = false } serde = { version = "1.0.217", default-features = false } -serde_json = "1.0.135" +serde_json = { version = "1.0.135", default-features = false } syn = { version = "2.0.96", default-features = false } -tempfile = "3.15.0" +tempfile = { version = "3.15.0", default-features = false } thiserror = { version = "2.0.10", default-features = false } time = { version = "0.3.37", default-features = false } -tokio = "1.43.0" -tokio-test = "0.4.4" +tokio = { version = "1.43.0", default-features = false } +tokio-test = { version = "0.4.4", default-features = false } tonic = { version = "0.12.3", default-features = false } tonic-build = { version = "0.12.3", default-features = false } -rayon = "1.10.0" +rayon = { version = "1.10.0", default-features = false } derive_more = { version = "1.0.0", default-features = false } -tracing-subscriber = { version = "0.3.19", features = ["fmt"], default-features = false } +tracing-subscriber = { version = "0.3.19", default-features = false } tracing = { version = "0.1.41", default-features = false } -regex = "1.11.1" -tynm = "0.1.10" +regex = { version = "1.11.1", default-features = false } +tynm = { version = "0.1.10", default-features = false } approx = { version = "0.5.1", default-features = false } -csv = "1.3.1" -seq-macro = "0.3.5" -spin_sleep = "1.3.0" +csv = { version = "1.3.1", default-features = false } +seq-macro = { version = "0.3.5", default-features = false } +spin_sleep = { version = "1.3.0", default-features = false } windows = { version = "0.59.0", default-features = false } -zerocopy = { version = "0.8.14", features = ["derive"] } -autd3 = { path = "./autd3", version = "29.0.0-rc.14" } +autd3 = { path = "./autd3", version = "29.0.0-rc.14", default-features = false } autd3-core = { path = "./autd3-core", version = "29.0.0-rc.14", default-features = false } autd3-driver = { path = "./autd3-driver", version = "29.0.0-rc.14", default-features = false } -autd3-derive = { path = "./autd3-derive", version = "29.0.0-rc.14" } -autd3-gain-holo = { path = "./autd3-gain-holo", version = "29.0.0-rc.14" } -autd3-link-simulator = { path = "./autd3-link-simulator", version = "29.0.0-rc.14" } +autd3-derive = { path = "./autd3-derive", version = "29.0.0-rc.14", default-features = false } +autd3-gain-holo = { path = "./autd3-gain-holo", version = "29.0.0-rc.14", default-features = false } +autd3-link-simulator = { path = "./autd3-link-simulator", version = "29.0.0-rc.14", default-features = false } autd3-link-twincat = { path = "./autd3-link-twincat", version = "29.0.0-rc.14", default-features = false } -autd3-modulation-audio-file = { path = "./autd3-modulation-audio-file", version = "29.0.0-rc.14" } -autd3-firmware-emulator = { path = "./autd3-firmware-emulator", version = "29.0.0-rc.14" } -autd3-protobuf = { path = "./autd3-protobuf", version = "29.0.0-rc.14" } +autd3-modulation-audio-file = { path = "./autd3-modulation-audio-file", version = "29.0.0-rc.14", default-features = false } +autd3-firmware-emulator = { path = "./autd3-firmware-emulator", version = "29.0.0-rc.14", default-features = false } +autd3-protobuf = { path = "./autd3-protobuf", version = "29.0.0-rc.14", default-features = false } From 63537f141d587c498568b4121545064233cefb44 Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Tue, 14 Jan 2025 16:09:09 +0900 Subject: [PATCH 17/20] excl infallible error test from cov --- autd3-driver/src/error.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/autd3-driver/src/error.rs b/autd3-driver/src/error.rs index 6d84f027..c83fcabc 100644 --- a/autd3-driver/src/error.rs +++ b/autd3-driver/src/error.rs @@ -160,6 +160,7 @@ impl AUTDDriverError { } } +// GRCOV_EXCL_START impl From for AUTDDriverError { fn from(_: Infallible) -> Self { unreachable!() @@ -179,6 +180,7 @@ where } } } +// GRCOV_EXCL_STOP #[cfg(test)] mod tests { From 7e50586102f4e5437d6dedc5d4fd72b0c06e4fe2 Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Wed, 15 Jan 2025 10:09:41 +0900 Subject: [PATCH 18/20] update doc --- autd3-core/src/datagram/operation.rs | 1 + autd3-core/src/datagram/transition_mode.rs | 3 +++ autd3-core/src/datagram/tuple.rs | 2 ++ autd3-core/src/gain/error.rs | 1 + autd3-core/src/gain/mod.rs | 4 ++-- autd3-core/src/geometry/mod.rs | 2 ++ autd3-core/src/lib.rs | 15 +++++++++++++-- autd3-core/src/link/error.rs | 1 + autd3-core/src/modulation/error.rs | 2 ++ autd3-core/src/modulation/loop_behavior.rs | 6 +----- autd3-derive/src/lib.rs | 4 ++-- autd3-driver/src/datagram/phase_corr.rs | 2 ++ autd3-driver/src/datagram/silencer.rs | 4 ++-- autd3-driver/src/datagram/stm/foci/mod.rs | 2 +- autd3-driver/src/datagram/stm/gain/mod.rs | 6 +++++- autd3-driver/src/firmware/fpga/mod.rs | 4 ++-- autd3-driver/src/firmware/operation/segment.rs | 4 ++-- autd3-gain-holo/src/lib.rs | 2 +- autd3/src/datagram/gain/cache.rs | 2 +- autd3/src/datagram/mod.rs | 4 ++-- autd3/src/lib.rs | 6 +++--- 21 files changed, 51 insertions(+), 26 deletions(-) diff --git a/autd3-core/src/datagram/operation.rs b/autd3-core/src/datagram/operation.rs index dbb7365a..acd93933 100644 --- a/autd3-core/src/datagram/operation.rs +++ b/autd3-core/src/datagram/operation.rs @@ -9,6 +9,7 @@ pub trait Operation: Send + Sync { fn is_done(&self) -> bool; } +#[doc(hidden)] pub struct NullOp; impl Operation for NullOp { diff --git a/autd3-core/src/datagram/transition_mode.rs b/autd3-core/src/datagram/transition_mode.rs index 98f07018..03ead03b 100644 --- a/autd3-core/src/datagram/transition_mode.rs +++ b/autd3-core/src/datagram/transition_mode.rs @@ -6,6 +6,7 @@ pub(crate) const TRANSITION_MODE_SYNC_IDX: u8 = 0x00; pub(crate) const TRANSITION_MODE_SYS_TIME: u8 = 0x01; pub(crate) const TRANSITION_MODE_GPIO: u8 = 0x02; pub(crate) const TRANSITION_MODE_EXT: u8 = 0xF0; +#[doc(hidden)] pub const TRANSITION_MODE_NONE: u8 = 0xFE; pub(crate) const TRANSITION_MODE_IMMEDIATE: u8 = 0xFF; @@ -26,6 +27,7 @@ pub enum TransitionMode { } impl TransitionMode { + #[doc(hidden)] pub const fn mode(&self) -> u8 { match self { TransitionMode::SyncIdx => TRANSITION_MODE_SYNC_IDX, @@ -36,6 +38,7 @@ impl TransitionMode { } } + #[doc(hidden)] pub const fn value(&self) -> u64 { match self { TransitionMode::SyncIdx | TransitionMode::Ext | TransitionMode::Immediate => 0, diff --git a/autd3-core/src/datagram/tuple.rs b/autd3-core/src/datagram/tuple.rs index 5088df31..3d86f11f 100644 --- a/autd3-core/src/datagram/tuple.rs +++ b/autd3-core/src/datagram/tuple.rs @@ -6,12 +6,14 @@ use crate::geometry::Geometry; use super::Datagram; +#[doc(hidden)] pub struct CombinedOperationGenerator { pub o1: O1, pub o2: O2, } #[derive(Error, Debug, PartialEq)] +#[doc(hidden)] pub enum CombinedError where E1: std::error::Error, diff --git a/autd3-core/src/gain/error.rs b/autd3-core/src/gain/error.rs index be59f968..bef7eff8 100644 --- a/autd3-core/src/gain/error.rs +++ b/autd3-core/src/gain/error.rs @@ -4,6 +4,7 @@ use thiserror::Error; #[derive(new, Error, Debug, Display, PartialEq, Clone)] #[display("{}", msg)] +/// An error occurred during gain calculation. pub struct GainError { msg: String, } diff --git a/autd3-core/src/gain/mod.rs b/autd3-core/src/gain/mod.rs index 4008692a..6a0b02e8 100644 --- a/autd3-core/src/gain/mod.rs +++ b/autd3-core/src/gain/mod.rs @@ -5,6 +5,7 @@ mod phase; use std::collections::HashMap; +/// A bit vector type. pub type BitVec = bit_vec::BitVec; pub use drive::Drive; @@ -19,7 +20,7 @@ use crate::{ /// A trait to calculate the phase and intensity for [`Gain`]. /// -/// [`Gain`]: crate::datagram::Gain +/// [`Gain`]: crate::gain::Gain pub trait GainContext: Send + Sync { /// Calculates the phase and intensity for the transducer. fn calc(&self, tr: &Transducer) -> Drive; @@ -81,4 +82,3 @@ impl GainOperationGenerator { }) } } - diff --git a/autd3-core/src/geometry/mod.rs b/autd3-core/src/geometry/mod.rs index 0bc01440..6ea77368 100644 --- a/autd3-core/src/geometry/mod.rs +++ b/autd3-core/src/geometry/mod.rs @@ -14,7 +14,9 @@ pub type Point3 = nalgebra::Point3; pub type Quaternion = nalgebra::Quaternion; /// A unit quaternion. pub type UnitQuaternion = nalgebra::UnitQuaternion; +/// A 3-dimensional translation. pub type Translation = nalgebra::Translation3; +/// A 3-dimensional isometry. pub type Isometry = nalgebra::Isometry3; pub use bvh::aabb::Aabb; diff --git a/autd3-core/src/lib.rs b/autd3-core/src/lib.rs index ca586565..6dcb65d3 100644 --- a/autd3-core/src/lib.rs +++ b/autd3-core/src/lib.rs @@ -1,14 +1,24 @@ +#![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(missing_docs)] +#![warn(rustdoc::missing_crate_level_docs)] +#![warn(rustdoc::unescaped_backticks)] + +//! Core traits and types for AUTD3. + #[cfg(feature = "acoustics")] /// Utilities for acoustics. pub mod acoustics; #[cfg(feature = "datagram")] +/// Core traits for Datagram. pub mod datagram; #[cfg(feature = "defined")] /// Common constants and types. pub mod defined; #[cfg(feature = "ethercat")] +/// Definitions for EtherCAT. pub mod ethercat; #[cfg(feature = "gain")] +/// Core traits for Gain. pub mod gain; #[cfg(feature = "geometry")] /// Geometry related modules. @@ -17,6 +27,7 @@ pub mod geometry; /// A interface to the device. pub mod link; #[cfg(feature = "modulation")] +/// Core traits for Modulation. pub mod modulation; #[cfg(feature = "resampler")] /// Resampler module. @@ -30,8 +41,8 @@ pub use async_trait::async_trait; /// Utilities for user-defined [`Gain`] and [`Modulation`]. /// -/// [`Gain`]: crate::datagram::Gain -/// [`Modulation`]: crate::datagram::Modulation +/// [`Gain`]: crate::gain::Gain +/// [`Modulation`]: crate::modulation::Modulation #[cfg_attr(docsrs, doc(cfg(feature = "derive")))] #[cfg(feature = "derive")] pub mod derive { diff --git a/autd3-core/src/link/error.rs b/autd3-core/src/link/error.rs index 918af19c..7637a006 100644 --- a/autd3-core/src/link/error.rs +++ b/autd3-core/src/link/error.rs @@ -4,6 +4,7 @@ use thiserror::Error; #[derive(new, Error, Debug, Display, PartialEq, Clone)] #[display("{}", msg)] +/// An error produced by the link. pub struct LinkError { msg: String, } diff --git a/autd3-core/src/modulation/error.rs b/autd3-core/src/modulation/error.rs index 88e6b430..11231d07 100644 --- a/autd3-core/src/modulation/error.rs +++ b/autd3-core/src/modulation/error.rs @@ -8,11 +8,13 @@ use crate::defined::Freq; #[derive(new, Error, Debug, Display, PartialEq, Clone)] #[display("{}", msg)] +/// An error occurred during modulation calculation. pub struct ModulationError { msg: String, } #[derive(Error, Debug, PartialEq, Clone)] +/// An error produced by the sampling configuration. pub enum SamplingConfigError { /// Invalid sampling division. #[error("Sampling division ({0}) must not be zero")] diff --git a/autd3-core/src/modulation/loop_behavior.rs b/autd3-core/src/modulation/loop_behavior.rs index 180e1cd4..c87065b7 100644 --- a/autd3-core/src/modulation/loop_behavior.rs +++ b/autd3-core/src/modulation/loop_behavior.rs @@ -1,11 +1,7 @@ use autd3_derive::Builder; use derive_more::Debug; -/// The behavior of the loop for [`Modulation`], [`FociSTM`], and [`GainSTM`]. -/// -/// [`Modulation`]: crate::datagram::Modulation -/// [`FociSTM`]: crate::datagram::FociSTM -/// [`GainSTM`]: crate::datagram::GainSTM +/// The behavior of the loop. #[derive(Clone, Copy, PartialEq, Eq, Debug, Builder)] #[debug("{}", match self.rep { 0xFFFF => "Infinite".to_string(), 0 => "Once".to_string(), i => format!("Finite({})", i + 1) })] #[repr(C)] diff --git a/autd3-derive/src/lib.rs b/autd3-derive/src/lib.rs index 77b1b559..1543508c 100644 --- a/autd3-derive/src/lib.rs +++ b/autd3-derive/src/lib.rs @@ -65,7 +65,7 @@ use proc_macro::TokenStream; /// } /// ``` /// -/// [`Gain`]: https://docs.rs/autd3-driver/latest/autd3_driver/datagram/trait.Gain.html +/// [`Gain`]: https://docs.rs/autd3-core/latest/autd3_core/gain/trait.Gain.html #[proc_macro_derive(Gain)] pub fn gain_derive(input: TokenStream) -> TokenStream { let ast = syn::parse(input).unwrap(); @@ -108,7 +108,7 @@ pub fn gain_derive(input: TokenStream) -> TokenStream { /// } /// ``` /// -/// [`Modulation`]: https://docs.rs/autd3-driver/latest/autd3_driver/datagram/trait.Modulation.html +/// [`Modulation`]: https://docs.rs/autd3-core/latest/autd3_core/gain/trait.Modulation.html #[proc_macro_derive(Modulation, attributes(no_change))] pub fn modulation_derive(input: TokenStream) -> TokenStream { let ast = syn::parse(input).unwrap(); diff --git a/autd3-driver/src/datagram/phase_corr.rs b/autd3-driver/src/datagram/phase_corr.rs index 3f8e6782..6f0e7687 100644 --- a/autd3-driver/src/datagram/phase_corr.rs +++ b/autd3-driver/src/datagram/phase_corr.rs @@ -21,6 +21,8 @@ use derive_new::new; /// # use autd3_driver::firmware::fpga::Phase; /// PhaseCorrection::new(|_dev| |_tr| Phase::PI); /// ``` +/// +/// [`Gain`]: autd3_core::gain::Gain #[derive(Builder, Debug, new)] pub struct PhaseCorrection Phase, F: Fn(&Device) -> FT> { #[debug(ignore)] diff --git a/autd3-driver/src/datagram/silencer.rs b/autd3-driver/src/datagram/silencer.rs index 44ef77f0..3e57018f 100644 --- a/autd3-driver/src/datagram/silencer.rs +++ b/autd3-driver/src/datagram/silencer.rs @@ -110,7 +110,7 @@ impl Silencer { /// /// If the strict mode is enabled, an error is returned if the phase/intensity change of [`Modulation`], [`FociSTM`] or [`GainSTM`] cannot be completed within the time specified by the silencer. /// - /// [`Modulation`]: crate::datagram::Modulation + /// [`Modulation`]: autd3_core::modulation::Modulation /// [`FociSTM`]: crate::datagram::FociSTM /// [`GainSTM`]: crate::datagram::GainSTM pub const fn strict_mode(&self) -> bool { @@ -146,7 +146,7 @@ impl Silencer { /// /// If the strict mode is enabled, an error is returned if the phase/intensity change of [`Modulation`], [`FociSTM`] or [`GainSTM`] cannot be completed within the time specified by the silencer. /// - /// [`Modulation`]: crate::datagram::Modulation + /// [`Modulation`]: autd3_core::modulation::Modulation /// [`FociSTM`]: crate::datagram::FociSTM /// [`GainSTM`]: crate::datagram::GainSTM pub const fn strict_mode(&self) -> bool { diff --git a/autd3-driver/src/datagram/stm/foci/mod.rs b/autd3-driver/src/datagram/stm/foci/mod.rs index 80f8e3b1..76ac6d6d 100644 --- a/autd3-driver/src/datagram/stm/foci/mod.rs +++ b/autd3-driver/src/datagram/stm/foci/mod.rs @@ -69,7 +69,7 @@ impl> FociSTM { /// /// # Errors /// - /// Returns [`AUTDDriverError::SamplingFreqOutOfRangeF`], [`AUTDDriverError::SamplingFreqInvalidF`], or [`AUTDDriverError::STMPeriodInvalid`] if the frequency or period cannot be set strictly. + /// Returns [`AUTDDriverError::SamplingConfig`] or [`AUTDDriverError::STMPeriodInvalid`] if the frequency or period cannot be set strictly. pub fn new( config: impl Into, iter: impl IntoFociSTMGenerator, diff --git a/autd3-driver/src/datagram/stm/gain/mod.rs b/autd3-driver/src/datagram/stm/gain/mod.rs index 4b810bda..88583dc6 100644 --- a/autd3-driver/src/datagram/stm/gain/mod.rs +++ b/autd3-driver/src/datagram/stm/gain/mod.rs @@ -27,6 +27,8 @@ pub trait GainSTMContextGenerator { /// The element type of the gain sequence. type Gain: GainContextGenerator; /// [`GainSTMContext`] that generates the sequence of [`Gain`]. + /// + /// [`Gain`]: autd3_core::gain::Gain type Context: GainSTMContext::Context>; /// generates the context. @@ -59,6 +61,8 @@ pub trait IntoGainSTMGenerator { } /// [`Datagram`] to produce STM by [`Gain`]. +/// +/// [`Gain`]: autd3_core::gain::Gain #[derive(Builder, Clone, Debug, Deref, DerefMut)] pub struct GainSTM { #[deref] @@ -92,7 +96,7 @@ impl GainSTM { /// /// # Errors /// - /// Returns [`AUTDDriverError::SamplingFreqOutOfRangeF`], [`AUTDDriverError::SamplingFreqInvalidF`], or [`AUTDDriverError::STMPeriodInvalid`] if the frequency or period cannot be set strictly. + /// Returns [`AUTDDriverError::SamplingConfig`] or [`AUTDDriverError::STMPeriodInvalid`] if the frequency or period cannot be set strictly. pub fn new>( config: impl Into, iter: T, diff --git a/autd3-driver/src/firmware/fpga/mod.rs b/autd3-driver/src/firmware/fpga/mod.rs index 8ab17627..b6191c58 100644 --- a/autd3-driver/src/firmware/fpga/mod.rs +++ b/autd3-driver/src/firmware/fpga/mod.rs @@ -40,11 +40,11 @@ pub const SILENCER_STEPS_PHASE_DEFAULT: u16 = 40; /// The minimum buffer size of [`Modulation`]. /// -/// [`Modulation`]: crate::datagram::Modulation +/// [`Modulation`]: autd3_core::modulation::Modulation pub const MOD_BUF_SIZE_MIN: usize = 2; /// The maximum buffer size of [`Modulation`]. /// -/// [`Modulation`]: crate::datagram::Modulation +/// [`Modulation`]: autd3_core::modulation::Modulation pub const MOD_BUF_SIZE_MAX: usize = 32768; /// The minimum buffer size of [`FociSTM`] and [`GainSTM`]. diff --git a/autd3-driver/src/firmware/operation/segment.rs b/autd3-driver/src/firmware/operation/segment.rs index f9208526..9deed684 100644 --- a/autd3-driver/src/firmware/operation/segment.rs +++ b/autd3-driver/src/firmware/operation/segment.rs @@ -21,11 +21,11 @@ use zerocopy::{Immutable, IntoBytes}; pub enum SwapSegment { /// Change the [`Gain`] segment. /// - /// [`Gain`]: crate::datagram::Gain + /// [`Gain`]: autd3_core::gain::Gain Gain(Segment, TransitionMode), /// Change the [`Modulation`] segment. /// - /// [`Modulation`]: crate::datagram::Modulation + /// [`Modulation`]: autd3_core::modulation::Modulation Modulation(Segment, TransitionMode), /// Change the [`FociSTM`] segment. /// diff --git a/autd3-gain-holo/src/lib.rs b/autd3-gain-holo/src/lib.rs index c35391cd..b7512f1b 100644 --- a/autd3-gain-holo/src/lib.rs +++ b/autd3-gain-holo/src/lib.rs @@ -5,7 +5,7 @@ //! This crate provides [`Gain`] that produces multiple focal points. //! -//! [`Gain`]: autd3_driver::datagram::Gain +//! [`Gain`]: autd3_core::gain::Gain mod amp; mod backend; diff --git a/autd3/src/datagram/gain/cache.rs b/autd3/src/datagram/gain/cache.rs index 03b5cb46..2e4e9614 100644 --- a/autd3/src/datagram/gain/cache.rs +++ b/autd3/src/datagram/gain/cache.rs @@ -50,7 +50,7 @@ impl Cache { /// /// # Errors /// - /// Returns [`AUTDDriverError::GainError`] if you initialize with some devices disabled and then reinitialize after enabling the devices. + /// Returns [`GainError`] if you initialize with some devices disabled and then reinitialize after enabling the devices. pub fn init(&self, geometry: &Geometry) -> Result<(), GainError> { if let Some(gain) = self.gain.take() { let mut f = gain.init(geometry, None)?; diff --git a/autd3/src/datagram/mod.rs b/autd3/src/datagram/mod.rs index 4bb6c1fe..6e235917 100644 --- a/autd3/src/datagram/mod.rs +++ b/autd3/src/datagram/mod.rs @@ -1,11 +1,11 @@ /// Primitive [`Gain`] /// -/// [`Gain`]: autd3_driver::datagram::Gain +/// [`Gain`]: autd3_core::gain::Gain pub mod gain; /// Primitive [`Modulation`] /// -/// [`Modulation`]: autd3_driver::datagram::Modulation +/// [`Modulation`]: autd3_core::modulation::Modulation pub mod modulation; /// Utilities for [`GainSTM`] and [`FociSTM`] diff --git a/autd3/src/lib.rs b/autd3/src/lib.rs index 442dee54..5e29b33b 100644 --- a/autd3/src/lib.rs +++ b/autd3/src/lib.rs @@ -13,8 +13,8 @@ pub mod controller; /// Primitive [`Gain`], [`Modulation`] and utilities for [`GainSTM`] and [`FociSTM`]. /// -/// [`Gain`]: autd3_driver::datagram::Gain -/// [`Modulation`]: autd3_driver::datagram::Modulation +/// [`Gain`]: autd3_core::gain::Gain +/// [`Modulation`]: autd3_core::modulation::Modulation /// [`GainSTM`]: autd3_driver::datagram::GainSTM /// [`FociSTM`]: autd3_driver::datagram::FociSTM pub mod datagram; @@ -22,7 +22,7 @@ pub mod datagram; pub mod error; /// Primitive [`Link`]. /// -/// [`Link`]: autd3_driver::link::Link +/// [`Link`]: autd3_core::link::Link pub mod link; /// Prelude module. pub mod prelude; From 4a0b9f7a6ffd85916679da137e14da919bb91636 Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Wed, 15 Jan 2025 10:19:29 +0900 Subject: [PATCH 19/20] update autd3-core doc --- autd3-core/Cargo.toml | 4 ++++ autd3-core/src/lib.rs | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/autd3-core/Cargo.toml b/autd3-core/Cargo.toml index 16ae93ea..86520d04 100644 --- a/autd3-core/Cargo.toml +++ b/autd3-core/Cargo.toml @@ -60,3 +60,7 @@ modulation = ["autd3-derive", "utils", "defined", "datagram", "derive_more", "de resampler = ["modulation", "defined", "tracing", "utils"] use_meter = [] utils = [] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/autd3-core/src/lib.rs b/autd3-core/src/lib.rs index 6dcb65d3..974b3f54 100644 --- a/autd3-core/src/lib.rs +++ b/autd3-core/src/lib.rs @@ -5,37 +5,48 @@ //! Core traits and types for AUTD3. +#[cfg_attr(docsrs, doc(cfg(feature = "acoustics")))] #[cfg(feature = "acoustics")] /// Utilities for acoustics. pub mod acoustics; +#[cfg_attr(docsrs, doc(cfg(feature = "datagram")))] #[cfg(feature = "datagram")] /// Core traits for Datagram. pub mod datagram; +#[cfg_attr(docsrs, doc(cfg(feature = "defined")))] #[cfg(feature = "defined")] /// Common constants and types. pub mod defined; +#[cfg_attr(docsrs, doc(cfg(feature = "ethercat")))] #[cfg(feature = "ethercat")] /// Definitions for EtherCAT. pub mod ethercat; +#[cfg_attr(docsrs, doc(cfg(feature = "gain")))] #[cfg(feature = "gain")] /// Core traits for Gain. pub mod gain; +#[cfg_attr(docsrs, doc(cfg(feature = "geometry")))] #[cfg(feature = "geometry")] /// Geometry related modules. pub mod geometry; +#[cfg_attr(docsrs, doc(cfg(feature = "link")))] #[cfg(feature = "link")] /// A interface to the device. pub mod link; +#[cfg_attr(docsrs, doc(cfg(feature = "modulation")))] #[cfg(feature = "modulation")] /// Core traits for Modulation. pub mod modulation; +#[cfg_attr(docsrs, doc(cfg(feature = "resampler")))] #[cfg(feature = "resampler")] /// Resampler module. pub mod resampler; +#[cfg_attr(docsrs, doc(cfg(feature = "utils")))] #[cfg(feature = "utils")] #[doc(hidden)] pub mod utils; +#[cfg_attr(docsrs, doc(cfg(feature = "async-trait")))] #[cfg(feature = "async-trait")] pub use async_trait::async_trait; From 9a9f45466e7199f09b381c9e9dfc2d1d80a8ffa1 Mon Sep 17 00:00:00 2001 From: shun suzuki Date: Wed, 15 Jan 2025 10:19:42 +0900 Subject: [PATCH 20/20] fix autd3-gain-holo deps --- autd3-gain-holo/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autd3-gain-holo/Cargo.toml b/autd3-gain-holo/Cargo.toml index d74dd56d..12d6d9e8 100644 --- a/autd3-gain-holo/Cargo.toml +++ b/autd3-gain-holo/Cargo.toml @@ -10,7 +10,7 @@ license = { workspace = true } repository = { workspace = true } [dependencies] -autd3-core = { workspace = true, features = ["acoustics", "gain"] } +autd3-core = { workspace = true, features = ["acoustics", "derive", "gain"] } autd3-derive = { workspace = true } nalgebra = { workspace = true } thiserror = { workspace = true }