Controller

This section introduces the APIs available in the Controller.

fpga_state

Retrieve the state of the FPGA. Before using this, you need to enable state retrieval with ReadsFPGAState.

use autd3::prelude::*;
#[allow(unused_variables)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut autd = Controller::open([AUTD3::default()], autd3::link::Nop::new())?;
autd.send(ReadsFPGAState::new(|_dev| true))?;

let info = autd.fpga_state()?;
Ok(())
}
#include<autd3.hpp>
#include<autd3/link/nop.hpp>
int main() {
using namespace autd3;
auto autd =
Controller::open({AUTD3{}}, link::Nop{});
autd.send(ReadsFPGAState([](const auto&) { return true; }));

const auto info = autd.fpga_state();
return 0; }
using AUTD3Sharp;
using AUTD3Sharp.Link;
using AUTD3Sharp.Utils;
using var autd = Controller.Open([new AUTD3()], new Nop());
autd.Send(new ReadsFPGAState(_ => true));

var info = autd.FPGAState();
from pyautd3 import Controller, AUTD3, ReadsFPGAState
from pyautd3.link.nop import Nop
autd = Controller.open([AUTD3()], Nop())
autd.send(ReadsFPGAState(lambda _: True))

info = autd.fpga_state()

The argument of the ReadsFPGAState constructor is Fn(&Device) -> bool, which specifies whether to enable state retrieval for each device.

fpga_state returns None for devices that are not enabled.

The following information can currently be obtained as the state of the FPGA:

  • is_thermal_assert: Whether the temperature sensor for fan control is asserted
  • current_mod_segment: Current Modulation Segment
  • current_stm_segment: Current FociSTM/GainSTM Segment
  • current_gain_segment: Current Gain Segment
  • is_gain_mode: Whether Gain is currently being used
  • is_stm_mode: Whether FociSTM/GainSTM is currently being used

send

Send data to the device.

Data can be sent either individually or two at a time.

sender

You can specify settings for sending via sender.

use autd3::prelude::*;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut autd = Controller::open([AUTD3::default()], autd3::link::Nop::new())?;
let mut sender = autd.sender(
    SenderOption {
        send_interval: Duration::from_millis(1),
        receive_interval: Duration::from_millis(1),
        timeout: None,
        parallel: ParallelMode::Auto,
        strict: true
    },
    FixedSchedule::default(),
);
let d = Null {};
sender.send(d)?;
Ok(())
}
#include<chrono>
#include<autd3.hpp>
#include<autd3/link/nop.hpp>
int main() {
using namespace autd3;
auto autd = Controller::open({AUTD3{}}, link::Nop{});
auto sender =
    autd.sender(SenderOption{.send_interval = std::chrono::milliseconds(1),
                             .receive_interval = std::chrono::milliseconds(1),
                             .timeout = std::nullopt,
                             .parallel = ParallelMode::Auto,
                             .strict = true},
                FixedSchedule{});
const Null d;
sender.send(d);
return 0; }
using AUTD3Sharp;
using AUTD3Sharp.Link;
using AUTD3Sharp.Gain;
using AUTD3Sharp.Utils;
using var autd = Controller.Open([new AUTD3()], new Nop());
var sender = autd.Sender(
    new SenderOption
    {
        SendInterval = Duration.FromMillis(1),
        ReceiveInterval = Duration.FromMillis(1),
        Timeout = null,
        Parallel = ParallelMode.Auto,
        Strict = true,
    },
    new FixedSchedule()
);
var d = new Null();
sender.Send(d);
from pyautd3 import AUTD3, Controller, Duration, Null, SenderOption, FixedSchedule, ParallelMode
from pyautd3.link.nop import Nop
autd = Controller.open([AUTD3()], Nop())
sender = autd.sender(
    SenderOption(
        send_interval=Duration.from_millis(1),
        receive_interval=Duration.from_millis(1),
        timeout=None,
        parallel=ParallelMode.Auto,
        strict=True
    ),
    FixedSchedule(),
)
d = Null()
sender.send(d)

Here,

  • send_interval: Send interval
  • receive_interval: Receive interval
  • timeout: Timeout duration. See About Ack check for details.
  • parallel: Parallel computation mode. See About Parallel Computation for details.
  • strict: Whether to strictly check the data to be sent. See About Ack check for details.

and the default values are as above.

The second argument is a structure to adjust send/receive intervals, and you can choose from the following:

  • FixedSchedule: Starts the next send at the specified interval from the start time of the previous send, regardless of the time taken for the send process.
  • FixedDelay: Starts the next send after the specified interval from the completion of the send process.

これらの構造体は, どのようにスレッドをスリープさせるかを指定する以下の構造体を持つ. These structures have the following structures that specify how to put the thread to sleep:

Note that Controller::send and Controller::group_send are equivalent to Sender::send and Sender::group_send with the Controller::default_sender_option (which is configurable) and default SpinSleeper.

About ack check

If the timeout value is

  • greater than 0, the send function waits until the sent data is processed by the device or the specified timeout duration elapses. If it cannot confirm that the sent data was processed by the device, it returns an error.
  • 0, the send function does not check whether the sent data was processed by the device.

If you want to ensure that the data is sent, it is recommended to set this to an appropriate value.

If timeout is not specified in SenderOption, the default value () is used.

When sending multiple data at once, the maximum timeout value of each data is used.

About Parallel Computation

Internal calculations for each data can be executed in parallel on a per-device basis.

Specifying ParallelMode::On enables parallel computation, and ParallelMode::Off disables it.

In the case of ParallelMode::Auto, parallel computation is enabled if the number of devices exceeds the parallel computation threshold value for each data as shown below.

Parallel Computation Threshold Value
Clear/GPIOOutputs/
ForceFan/PhaseCorrection/
ReadsFPGAState/SwapSegment/
Silencer/Synchronize/
FociSTM (less than 4000 foci)/
Modulation
18446744073709551615
PulseWidthEncoder/
FociSTM (4000 foci or more)/
/GainSTM/Gain
4

inspect (available only in Rust)

The calculations for Gain, Modulation, GainSTM, and FociSTM are lazy for parallelization and to minimize memory allocation, and the calculation results are constructed directly within the frame. Therefore, it is not possible to directly check these calculation results before sending.

By using Controller::inspect, you can check these calculation results without sending.

use autd3::prelude::*;
#[allow(unused_variables)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let autd = Controller::open([AUTD3::default()], autd3::link::Nop::new())?;
let r = autd.inspect(Null {})?;
dbg!(&r[0]); // result of device 0
// &r[0] = Some(
//     GainInspectionResult {
//         name: "Null",
//         data: [
//             Drive {
//                 phase: 0x00,
//                 intensity: 0x00,
//             },
//             ︙
//             Drive {
//                 phase: 0x00,
//                 intensity: 0x00,
//             },
//         ],
//         segment: S0,
//         transition_mode: None,
//     },
// )

Ok(())
}