Skip to content
This is the development version of the documentation. It may change before the next release. See 0.6.x for the latest release.

Send Modes

The Frames obtained from DatagramBuilder::build may be split into multiple Frames. There are two ways to send a Frame, with a trade-off between reliability and throughput.

Below, the two send methods are compared using an example that moves the focal point along a circle. The focal positions to move to are prepared in advance as targets.

// Prepare 1000 focus points along a circle 150 mm above the array center.
let center = geometry.center() + offset(0.0 * mm, 0.0 * mm, 150.0 * mm);
let targets: Vec<Point3<f32>> = (0..NUM_POINTS)
.map(|i| {
let theta = 2.0 * PI * i as f32 / NUM_POINTS as f32;
center
+ offset(
RADIUS_MM * theta.cos() * mm,
RADIUS_MM * theta.sin() * mm,
0.0 * mm,
)
})
.collect();

A method that waits for a response after each frame before sending the next. It uses send_checked. Because it proceeds to the next frame only after confirming that each update has been reliably applied, it is reliable, but throughput is low because a round-trip latency is incurred on every frame.

let mut patterns = geometry.pattern_buffer();
for &target in targets {
autd3_rs_pattern::focus(
geometry,
target,
wavelength,
&autd3_rs_pattern::FocusOption::default(),
&mut patterns,
);
let mut builder = client.datagram_builder();
builder.push(Pattern::new(&patterns));
for frame in &builder.build()? {
client.send_checked(frame).await?;
}
}

A method that sends one frame after another without waiting for responses, and collects the responses together later. send returns a ResponseFuture at the point the frame is enqueued, and awaiting that ResponseFuture yields the device’s response (Response). Response can be validated for device errors with check. The maximum size of the send queue is MAX_INFLIGHT (127). ResponseFutures are held in FIFO order, and when the queue is full the oldest response is collected. Because it does not wait for a response on every frame, throughput is high.

let mut patterns = geometry.pattern_buffer();
let mut pending: VecDeque<ResponseFuture> = VecDeque::with_capacity(MAX_INFLIGHT);
for &target in targets {
autd3_rs_pattern::focus(
geometry,
target,
wavelength,
&autd3_rs_pattern::FocusOption::default(),
&mut patterns,
);
let mut builder = client.datagram_builder();
builder.push(Pattern::new(&patterns));
for frame in &builder.build()? {
if pending.len() >= MAX_INFLIGHT {
pending.pop_front().expect("non-empty").await?.check()?;
}
pending.push_back(client.send(frame).await?);
}
}
// Drain the remaining responses.
while let Some(fut) = pending.pop_front() {
fut.await?.check()?;
}

For commands that must be applied reliably one at a time, such as initialization and configuration, use stop-and-wait. For situations where throughput matters, such as updating the focal point at high frequency, use streaming. For details on send / send_checked, refer to Client.