PatternStm
PatternStm provides an STM feature that switches arbitrary sound fields (the phase and intensity of each transducer) by a hardware timer.
Unlike FociStm, it can handle arbitrary Patterns, but the number of frames to transfer is large (i.e. the latency is high).
PatternStm::new(freq, &patterns, intensities, option);PatternStm(freq, patterns, intensities, option)new PatternStm(freq, patterns, intensities, option);| Parameter | Type | Description |
|---|---|---|
freq |
impl Into<StmConfig> |
Playback rate (Freq / Duration / SamplingConfig) |
phases |
&[Vec<Vec<Phase>>] |
Phases of each sample (at least 2 samples) |
intensities |
impl Into<StmIntensity> |
Intensities for every sample (a uniform value, one buffer shared by all indices, or one buffer per index) |
option |
PatternStmOption |
Options such as transfer mode, loop, and transition |
Passing a Freq / Duration to freq represents “the frequency/period for one full cycle over all samples”.
A sampling frequency can also be specified.
A Duration that is not divisible by the number of samples is an error (use Nearest to round it).
intensities accepts the following three types.
Intensity: to use the same value for every device, every transducer and every index&[Vec<Intensity>]: to use values that differ per transducer but are shared by all indices&[Vec<Vec<Intensity>>]: to use a different value for every index
PatternStmOption
Section titled “PatternStmOption”PatternStmOption { bank, mode, loop_behavior, transition_mode, ..Default::default()}PatternStmOption( bank, mode, loop_behavior, transition_mode,)new PatternStmOption{ Bank = bank, Mode = mode, LoopBehavior = loopBehavior, TransitionMode = transitionMode,}| Field | Type | Default | Description |
|---|---|---|---|
bank |
PatternBank |
PatternBank::B0 |
Bank to write to / play back |
mode |
PatternStmMode |
PatternStmMode::PhaseIntensityFull |
Transfer format |
loop_behavior |
LoopBehavior |
LoopBehavior::Infinite |
Loop count |
transition_mode |
TransitionMode |
TransitionMode::Immediate |
Transition timing on bank switching |
For the behavior of loop_behavior and transition_mode, see playback control.
PatternStmMode
Section titled “PatternStmMode”Selects the transfer format of each Pattern. The phase-only and low-resolution formats can reduce the number of frames to transfer.
| Mode | Content |
|---|---|
PhaseIntensityFull |
Transfer phase and amplitude as-is (default) |
PhaseFull |
Transfer phase and a single uniform amplitude |
PhaseHalf |
Transfer 4-bit phase and a single uniform amplitude |
Equivalent Low-Level Commands
Section titled “Equivalent Low-Level Commands”PatternStm internally expands into the three commands WritePatternBuffer (each index) + ConfigPattern + ChangePatternBank.
The following is equivalent to PatternStm::new(freq, &phases, intensities, option).
When PatternStmMode is PhaseFull / PhaseHalf, WritePatternCompressed is used instead of WritePatternBuffer (intensities must then be a uniform Intensity; anything else is an error).
for (index, phases) in patterns.iter().enumerate() { WritePatternBuffer::new(option.bank, index, phases, intensities);}ConfigPattern { bank: option.bank, config: StmConfig::new(freq).into_sampling_config(patterns.len()), size: patterns.len(), loop_behavior: option.loop_behavior,};ChangePatternBank { bank: option.bank, transition_mode: option.transition_mode,};for index, phases in enumerate(patterns): WritePatternBuffer( bank=option.bank, index=index, phases=phases, intensities=intensities, )ConfigPattern( bank=option.bank, config=StmConfig(freq).into_sampling_config(len(patterns)), size=len(patterns), loop_behavior=option.loop_behavior,)ChangePatternBank( bank=option.bank, transition_mode=option.transition_mode,)for (var index = 0; index < patterns.Length; index++){ new WritePatternBuffer( bank: option.Bank, index: (ushort)index, phases: patterns[index], intensities: intensities );}new ConfigPattern( bank: option.Bank, config: new StmConfig(freq).IntoSamplingConfig(patterns.Length), size: (uint)patterns.Length, loopBehavior: option.LoopBehavior);new ChangePatternBank( bank: option.Bank, transitionMode: option.TransitionMode);Example
Section titled “Example”use autd3_rs::commands::{PatternStm, PatternStmMode, PatternStmOption};use autd3_rs::geometry::{Autd3, Geometry, offset};use autd3_rs::units::{Hz, m, mm, s};use autd3_rs::value::{Intensity, LoopBehavior, PatternBank, TransitionMode};use autd3_rs::{Client, ClientConfig};use autd3_rs_link_nop::Nop;use autd3_rs_pattern::{focus, wavelength};
let geometry = Geometry::new(vec![Autd3::default()]);let client = Client::open(&geometry, Nop, ClientConfig::default()).await?;
let center = geometry.center() + offset(0.0 * mm, 0.0 * mm, 150.0 * mm);let wavelength = wavelength(340.0 * m / s);let patterns = (0..200) .map(|i| { let theta = 2.0 * std::f32::consts::PI * i as f32 / 200.0; let target = center + offset(30.0 * theta.cos() * mm, 30.0 * theta.sin() * mm, 0.0 * mm); let mut buffer = geometry.phase_buffer(); focus( &geometry, target, wavelength, &mut buffer, ); buffer }) .collect::<Vec<_>>();let mut builder = client.datagram_builder();builder.push(PatternStm::new( 1.0 * Hz, &patterns, Intensity::MAX, PatternStmOption { bank: PatternBank::B0, mode: PatternStmMode::PhaseIntensityFull, loop_behavior: LoopBehavior::Infinite, transition_mode: TransitionMode::Immediate, ..Default::default() },));let frames = builder.build()?;for frame in &frames { client.send_checked(frame).await?;}
client.close().await?;import asyncioimport math
import numpy as np
from autd3 import Client, ClientConfigfrom autd3.commands import PatternStm, PatternStmMode, PatternStmOptionfrom autd3.geometry import Autd3, Geometryfrom autd3.units import Hz, m, sfrom autd3.value import Intensity, LoopBehavior, PatternBank, TransitionModefrom autd3_link_nop import Nopfrom autd3_pattern import focusfrom autd3_pattern import wavelength as calc_wavelength
async def main() -> None: geometry = Geometry([Autd3([0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0])]) async with await Client.open(geometry, Nop(), ClientConfig()) as client: center = geometry.center() + np.array([0.0, 0.0, 150.0]) wavelength = calc_wavelength(340 * m / s) patterns = [] for i in range(200): theta = 2.0 * math.pi * i / 200 target = center + np.array([30.0 * math.cos(theta), 30.0 * math.sin(theta), 0.0]) buffer = geometry.phase_buffer() focus( geometry, target, wavelength, buffer, ) patterns.append(buffer)
builder = client.datagram_builder() builder.push( PatternStm( 1.0 * Hz, patterns, Intensity.MAX, PatternStmOption( bank=PatternBank.B0, mode=PatternStmMode.PhaseIntensityFull, loop_behavior=LoopBehavior.Infinite, transition_mode=TransitionMode.Immediate, ), ) ) frames = builder.build() for frame in frames: await client.send_checked(frame)
asyncio.run(main())using System;using System.Numerics;using System.Threading.Tasks;using AUTD3;using AUTD3.Link;using Nop = AUTD3.Link.Nop;using static AUTD3.Units;
var geometry = new Geometry(new[] { new Autd3(Vector3.Zero) });await using var client = await Client.OpenAsync(geometry, new Nop(), new ClientConfig());
var center = geometry.Center + new Vector3(0.0f, 0.0f, 150.0f);var wavelength = Pattern.Wavelength(340.0f * m / s);var patterns = new PhaseBuffer[200];for (var i = 0; i < 200; i++){ var theta = 2.0f * MathF.PI * i / 200.0f; var target = center + new Vector3(30.0f * MathF.Cos(theta), 30.0f * MathF.Sin(theta), 0.0f); var buffer = geometry.PhaseBuffer(); Pattern.Focus( geometry, target, wavelength, buffer ); patterns[i] = buffer;}
var builder = client.DatagramBuilder();builder.Push(new PatternStm( 1.0f * Hz, patterns, Intensity.Max, new PatternStmOption { Bank = PatternBank.B0, Mode = PatternStmMode.PhaseIntensityFull, LoopBehavior = LoopBehavior.Infinite, TransitionMode = TransitionMode.Immediate, }));var frames = builder.Build();foreach (var frame in frames){ await client.SendCheckedAsync(frame);}