Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for dynamic_freq firmware #188

Merged
merged 6 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ jobs:
- os: ubuntu-latest
features: "lightweight async-trait"
lint: true
- os: ubuntu-latest
features: "dynamic_freq"
lint: true
steps:
- uses: actions/checkout@v4
with:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ jobs:
- os: ubuntu-latest
features: "lightweight async-trait"
lint: true
- os: ubuntu-latest
features: "dynamic_freq"
lint: true
steps:
- uses: actions/checkout@v4
with:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Update firmware to v10.0.1
- phase correction bram and pulse width encoder table reset to default in clear op
- support for `dynamic_freq` version
- Remove `Deref<Target = Link>` and `DerefMut` for `Controller`
- Impl `Deref<Target = Geometry>` and `DerefMut` for `Controller` instead
- Make `Transducer::new` public
Expand Down
20 changes: 10 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ members = [
resolver = "2"

[workspace.package]
version = "29.0.0-rc.12"
version = "29.0.0-rc.13"
authors = ["shun suzuki <suzuki@hapis.k.u-tokyo.ac.jp>"]
edition = "2021"
license = "MIT"
Expand Down Expand Up @@ -68,12 +68,12 @@ seq-macro = "0.3.5"
spin_sleep = "1.2.1"
windows = { version = "0.58.0", default-features = false }
zerocopy = { version = "0.8.10", features = ["derive"] }
autd3 = { path = "./autd3", version = "29.0.0-rc.12" }
autd3-driver = { path = "./autd3-driver", version = "29.0.0-rc.12", default-features = false }
autd3-derive = { path = "./autd3-derive", version = "29.0.0-rc.12" }
autd3-gain-holo = { path = "./autd3-gain-holo", version = "29.0.0-rc.12" }
autd3-link-simulator = { path = "./autd3-link-simulator", version = "29.0.0-rc.12" }
autd3-link-twincat = { path = "./autd3-link-twincat", version = "29.0.0-rc.12", default-features = false }
autd3-modulation-audio-file = { path = "./autd3-modulation-audio-file", version = "29.0.0-rc.12" }
autd3-firmware-emulator = { path = "./autd3-firmware-emulator", version = "29.0.0-rc.12" }
autd3-protobuf = { path = "./autd3-protobuf", version = "29.0.0-rc.12" }
autd3 = { path = "./autd3", version = "29.0.0-rc.13" }
autd3-driver = { path = "./autd3-driver", version = "29.0.0-rc.13", default-features = false }
autd3-derive = { path = "./autd3-derive", version = "29.0.0-rc.13" }
autd3-gain-holo = { path = "./autd3-gain-holo", version = "29.0.0-rc.13" }
autd3-link-simulator = { path = "./autd3-link-simulator", version = "29.0.0-rc.13" }
autd3-link-twincat = { path = "./autd3-link-twincat", version = "29.0.0-rc.13", default-features = false }
autd3-modulation-audio-file = { path = "./autd3-modulation-audio-file", version = "29.0.0-rc.13" }
autd3-firmware-emulator = { path = "./autd3-firmware-emulator", version = "29.0.0-rc.13" }
autd3-protobuf = { path = "./autd3-protobuf", version = "29.0.0-rc.13" }
3 changes: 2 additions & 1 deletion autd3-driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ use_meter = []
left_handed = []
serde = ["dep:serde"]
derive = []
dynamic_freq = []

[lib]
bench = false
Expand All @@ -58,5 +59,5 @@ path = "benches/gain.rs"
harness = false

[package.metadata.docs.rs]
all-features = true
features = ["lightweight", "async-trait", "serde", "derive"]
rustdoc-args = ["--cfg", "docsrs"]
32 changes: 32 additions & 0 deletions autd3-driver/src/datagram/clock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::{defined::ultrasound_freq, firmware::operation::ConfigureClockOp};

use crate::datagram::*;

#[derive(Default, Debug)]
#[doc(hidden)]
pub struct ConfigureFPGAClock {}

impl ConfigureFPGAClock {
pub const fn new() -> Self {
Self {}
}
}

pub struct ConfigureClockOpGenerator {}

impl OperationGenerator for ConfigureClockOpGenerator {
type O1 = ConfigureClockOp;
type O2 = NullOp;

fn generate(&mut self, _: &Device) -> (Self::O1, Self::O2) {
(Self::O1::new(ultrasound_freq()), Self::O2::new())
}
}

impl Datagram for ConfigureFPGAClock {
type G = ConfigureClockOpGenerator;

fn operation_generator(self, _: &Geometry) -> Result<Self::G, AUTDDriverError> {
Ok(ConfigureClockOpGenerator {})
}
}
8 changes: 7 additions & 1 deletion autd3-driver/src/datagram/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
mod clear;
#[cfg(feature = "dynamic_freq")]
mod clock;
mod cpu_gpio_out;
mod debug;
mod force_fan;
Expand All @@ -23,6 +25,8 @@ pub use super::firmware::operation::SwapSegment;
#[doc(inline)]
pub use super::firmware::operation::{ControlPoint, ControlPoints};
pub use clear::Clear;
#[cfg(feature = "dynamic_freq")]
pub use clock::ConfigureFPGAClock;
#[doc(hidden)]
pub use cpu_gpio_out::{CpuGPIO, CpuGPIOPort};
pub use debug::DebugSettings;
Expand All @@ -37,7 +41,9 @@ pub use modulation::{
pub use phase_corr::PhaseCorrection;
pub use pulse_width_encoder::PulseWidthEncoder;
pub use reads_fpga_state::ReadsFPGAState;
pub use silencer::{FixedCompletionTime, FixedUpdateRate, HasSamplingConfig, Silencer};
#[cfg(not(feature = "dynamic_freq"))]
pub use silencer::FixedCompletionTime;
pub use silencer::{FixedCompletionSteps, FixedUpdateRate, HasSamplingConfig, Silencer};
pub use stm::{
FociSTM, FociSTMContext, FociSTMContextGenerator, FociSTMGenerator, GainSTM, GainSTMContext,
GainSTMContextGenerator, GainSTMGenerator, IntoFociSTMGenerator, IntoGainSTMGenerator,
Expand Down
170 changes: 141 additions & 29 deletions autd3-driver/src/datagram/silencer.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use std::{num::NonZeroU16, time::Duration};
use std::num::NonZeroU16;

use autd3_derive::Builder;

use crate::{
defined::ULTRASOUND_PERIOD,
error::AUTDDriverError,
firmware::{
fpga::{
Expand All @@ -30,20 +29,36 @@ pub trait HasSamplingConfig {
pub trait SilencerConfig: std::fmt::Debug + Clone + Copy {}
impl SilencerConfig for () {}

#[cfg(not(feature = "dynamic_freq"))]
/// To configure the silencer by the completion time.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FixedCompletionTime {
/// The completion time of the intensity change. The value must be multiple of [`ULTRASOUND_PERIOD`].
/// The completion time of the intensity change. The value must be multiple of the ultrasound period.
///
/// The larger this value, the more the noise is suppressed.
pub intensity: Duration,
/// The completion time of the phase change. The value must be multiple of [`ULTRASOUND_PERIOD`].
pub intensity: std::time::Duration,
/// The completion time of the phase change. The value must be multiple of the ultrasound period.
///
/// The larger this value, the more the noise is suppressed.
pub phase: Duration,
pub phase: std::time::Duration,
}
#[cfg(not(feature = "dynamic_freq"))]
impl SilencerConfig for FixedCompletionTime {}

/// To configure the silencer by the completion steps.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FixedCompletionSteps {
/// The completion steps of the intensity change.
///
/// The larger this value, the more the noise is suppressed.
pub intensity: NonZeroU16,
/// The completion time of the phase change.
///
/// The larger this value, the more the noise is suppressed.
pub phase: NonZeroU16,
}
impl SilencerConfig for FixedCompletionSteps {}

/// To configure the silencer by the update rate.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FixedUpdateRate {
Expand Down Expand Up @@ -72,15 +87,6 @@ pub struct Silencer<T: SilencerConfig> {
}

impl Silencer<()> {
/// The default completion time of the intensity change.
pub const DEFAULT_COMPLETION_TIME_INTENSITY: Duration = Duration::from_micros(
ULTRASOUND_PERIOD.as_micros() as u64 * SILENCER_STEPS_INTENSITY_DEFAULT as u64,
);
/// The default completion time of the phase change.
pub const DEFAULT_COMPLETION_TIME_PHASE: Duration = Duration::from_micros(
ULTRASOUND_PERIOD.as_micros() as u64 * SILENCER_STEPS_PHASE_DEFAULT as u64,
);

/// Creates a [`Silencer`].
pub const fn new<T: SilencerConfig>(config: T) -> Silencer<T> {
Silencer {
Expand All @@ -91,14 +97,15 @@ impl Silencer<()> {
}

/// Creates a [`Silencer`] to disable the silencer.
pub const fn disable() -> Silencer<FixedCompletionTime> {
Silencer::new(FixedCompletionTime {
intensity: ULTRASOUND_PERIOD,
phase: ULTRASOUND_PERIOD,
pub const fn disable() -> Silencer<FixedCompletionSteps> {
Silencer::new(FixedCompletionSteps {
intensity: NonZeroU16::MIN,
phase: NonZeroU16::MIN,
})
}
}

#[cfg(not(feature = "dynamic_freq"))]
impl Silencer<FixedCompletionTime> {
/// Whether the strict mode is enabled. The default is `true`.
///
Expand All @@ -124,16 +131,52 @@ impl Silencer<FixedCompletionTime> {
if !self.strict_mode {
return true;
}
self.config.intensity <= target.intensity().map_or(Duration::MAX, |c| c.period())
&& self.config.phase <= target.phase().map_or(Duration::MAX, |c| c.period())
self.config.intensity
<= target
.intensity()
.map_or(std::time::Duration::MAX, |c| c.period())
&& self.config.phase
<= target
.phase()
.map_or(std::time::Duration::MAX, |c| c.period())
}
}

impl Silencer<FixedCompletionSteps> {
/// Whether the strict mode is enabled. The default is `true`.
///
/// 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
/// [`FociSTM`]: crate::datagram::FociSTM
/// [`GainSTM`]: crate::datagram::GainSTM
pub const fn strict_mode(&self) -> bool {
self.strict_mode
}

/// Sets the [`strict_mode`].
///
/// [`strict_mode`]: Self::strict_mode
pub const fn with_strict_mode(mut self, strict_mode: bool) -> Self {
self.strict_mode = strict_mode;
self
}

/// Validate whether it is safe to use this [`Silencer`] with `target`.
pub fn is_valid<T: HasSamplingConfig>(&self, target: &T) -> bool {
if !self.strict_mode {
return true;
}
self.config.intensity.get() <= target.intensity().map_or(u16::MAX, |c| c.division())
&& self.config.phase.get() <= target.phase().map_or(u16::MAX, |c| c.division())
}
}

impl Default for Silencer<FixedCompletionTime> {
impl Default for Silencer<FixedCompletionSteps> {
fn default() -> Self {
Silencer::new(FixedCompletionTime {
intensity: Silencer::DEFAULT_COMPLETION_TIME_INTENSITY,
phase: Silencer::DEFAULT_COMPLETION_TIME_PHASE,
Silencer::new(FixedCompletionSteps {
intensity: NonZeroU16::new(SILENCER_STEPS_INTENSITY_DEFAULT).unwrap(),
phase: NonZeroU16::new(SILENCER_STEPS_PHASE_DEFAULT).unwrap(),
})
}
}
Expand All @@ -156,7 +199,25 @@ impl OperationGenerator for SilencerOpGenerator<FixedUpdateRate> {
}
}

#[cfg(not(feature = "dynamic_freq"))]
impl OperationGenerator for SilencerOpGenerator<FixedCompletionTime> {
type O1 = crate::firmware::operation::SilencerFixedCompletionTimeOp;
type O2 = NullOp;

fn generate(&mut self, _: &Device) -> (Self::O1, Self::O2) {
(
Self::O1::new(
self.config.intensity,
self.config.phase,
self.strict_mode,
self.target,
),
Self::O2::new(),
)
}
}

impl OperationGenerator for SilencerOpGenerator<FixedCompletionSteps> {
type O1 = SilencerFixedCompletionStepsOp;
type O2 = NullOp;

Expand Down Expand Up @@ -201,8 +262,8 @@ mod tests {
#[test]
fn disable() {
let s = Silencer::disable();
assert_eq!(ULTRASOUND_PERIOD, s.config().intensity);
assert_eq!(ULTRASOUND_PERIOD, s.config().phase);
assert_eq!(1, s.config().intensity.get());
assert_eq!(1, s.config().phase.get());
assert!(s.strict_mode());
assert_eq!(SilencerTarget::Intensity, s.target());
}
Expand All @@ -218,17 +279,66 @@ mod tests {
assert_eq!(SilencerTarget::Intensity, s.target());
}

#[cfg(not(feature = "dynamic_freq"))]
#[test]
fn from_completion_time() {
use std::time::Duration;

let s = Silencer::new(FixedCompletionTime {
intensity: Duration::from_secs(1),
phase: Duration::from_secs(1),
});
assert_eq!(Duration::from_secs(1), s.config().intensity);
assert_eq!(Duration::from_secs(1), s.config().phase);
assert_eq!(SilencerTarget::Intensity, s.target());
assert!(s.strict_mode());
}

#[test]
fn from_completion_steps() {
let s = Silencer::new(FixedCompletionSteps {
intensity: NonZeroU16::new(2).unwrap(),
phase: NonZeroU16::new(3).unwrap(),
});
assert_eq!(2, s.config().intensity.get());
assert_eq!(3, s.config().phase.get());
assert_eq!(SilencerTarget::Intensity, s.target());
assert!(s.strict_mode());
}

#[rstest::rstest]
#[test]
#[case(true, 10, 10, true, FociSTM::new(SamplingConfig::new(10).unwrap(), [Point3::origin()]).unwrap())]
#[case(false, 11, 10, true, FociSTM::new(SamplingConfig::new(10).unwrap(), [Point3::origin()]).unwrap())]
#[case(false, 10, 11, true, FociSTM::new(SamplingConfig::new(10).unwrap(), [Point3::origin()]).unwrap())]
#[case(true, 11, 10, false, FociSTM::new(SamplingConfig::new(10).unwrap(), [Point3::origin()]).unwrap())]
#[case(true, 10, 11, false, FociSTM::new(SamplingConfig::new(10).unwrap(), [Point3::origin()]).unwrap())]
#[case(true, 10, 10, true, GainSTM::new(SamplingConfig::new(10).unwrap(), [TestGain{ data: Default::default() }]).unwrap())]
#[case(false, 11, 10, true, GainSTM::new(SamplingConfig::new(10).unwrap(), [TestGain{ data: Default::default() }]).unwrap())]
#[case(false, 10, 11, true, GainSTM::new(SamplingConfig::new(10).unwrap(), [TestGain{ data: Default::default() }]).unwrap())]
#[case(true, 11, 10, false, GainSTM::new(SamplingConfig::new(10).unwrap(), [TestGain{ data: Default::default() }]).unwrap())]
#[case(true, 10, 11, false, GainSTM::new(SamplingConfig::new(10).unwrap(), [TestGain{ data: Default::default() }]).unwrap())]
#[case(true, 10, 10, true, TestModulation { config: SamplingConfig::new(10).unwrap(), loop_behavior: LoopBehavior::infinite() })]
#[case(false, 11, 10, true, TestModulation { config: SamplingConfig::new(10).unwrap(), loop_behavior: LoopBehavior::infinite() })]
#[case(true, 10, 11, true, TestModulation { config: SamplingConfig::new(10).unwrap(), loop_behavior: LoopBehavior::infinite() })]
#[case(true, 11, 10, false, TestModulation { config: SamplingConfig::new(10).unwrap(), loop_behavior: LoopBehavior::infinite() })]
#[case(true, 10, 11, false, TestModulation { config: SamplingConfig::new(10).unwrap(), loop_behavior: LoopBehavior::infinite() })]
fn fixed_completion_steps_is_valid(
#[case] expect: bool,
#[case] intensity: u16,
#[case] phase: u16,
#[case] strict: bool,
#[case] target: impl HasSamplingConfig,
) {
let s = Silencer::new(FixedCompletionSteps {
intensity: NonZeroU16::new(intensity).unwrap(),
phase: NonZeroU16::new(phase).unwrap(),
})
.with_strict_mode(strict);
assert_eq!(expect, s.is_valid(&target));
}

#[cfg(not(feature = "dynamic_freq"))]
#[rstest::rstest]
#[test]
#[case(true, 10, 10, true, FociSTM::new(SamplingConfig::new(10).unwrap(), [Point3::origin()]).unwrap())]
Expand All @@ -253,9 +363,11 @@ mod tests {
#[case] strict: bool,
#[case] target: impl HasSamplingConfig,
) {
use crate::defined::ultrasound_period;

let s = Silencer::new(FixedCompletionTime {
intensity: intensity * ULTRASOUND_PERIOD,
phase: phase * ULTRASOUND_PERIOD,
intensity: intensity * ultrasound_period(),
phase: phase * ultrasound_period(),
})
.with_strict_mode(strict);
assert_eq!(expect, s.is_valid(&target));
Expand Down
Loading
Loading