# InstroDAQ

## class InstroDAQ( name: str, driver: DAQDriverBase, publishers: list[Publisher] | None = None, **kwargs, )[source]

Bases: [`Instrument`](https://dev.nominal.io/instro/ref/library/instro.lib.instrument.Instrument.md#instro.lib.instrument.Instrument "instro.lib.instrument.Instrument")

Initialize an InstroDAQ.

Parameters:
:   - **name**

      Channel-name prefix for published data.
    - **driver**

      Concrete DAQ driver; owns its own transport:

      ```
      daq = InstroDAQ(
          "myDAQ",
          driver=Keysight34980A("USB0::0x0957::0x0507::MY44001757::INSTR"),
      )
      ```
    - **publishers**

      Publishers that receive emitted Measurement/Command data.
    - ****kwargs**

      Default tags applied to every emitted Measurement/Command.
      Pass `dataset_rid="<rid>"` to auto-create a NominalCorePublisher
      (uses the on-disk ‘default’ Nominal credential).

Attributes

| [`DEFAULT_SW_SAMPLE_RATE`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.DEFAULT_SW_SAMPLE_RATE "instro.daq.InstroDAQ.DEFAULT_SW_SAMPLE_RATE") |  |
| --- | --- |
| [`ai_channels`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.ai_channels "instro.daq.InstroDAQ.ai_channels") | Frozen snapshot of configured AI channels, keyed by alias. |
| [`ai_hw_timing_config`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.ai_hw_timing_config "instro.daq.InstroDAQ.ai_hw_timing_config") |  |
| [`ao_channels`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.ao_channels "instro.daq.InstroDAQ.ao_channels") | Frozen snapshot of configured AO channels, keyed by alias. |
| [`ao_hw_timing_config`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.ao_hw_timing_config "instro.daq.InstroDAQ.ao_hw_timing_config") |  |
| [`background_interval`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.background_interval "instro.daq.InstroDAQ.background_interval") | Daemon loop period (s); 0 unless `configure_ai_sw_sample_rate()` set a software-timed rate. |
| [`channel_names`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.channel_names "instro.daq.InstroDAQ.channel_names") | Return an insertion-ordered snapshot of all published channel names. |
| [`channels`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.channels "instro.daq.InstroDAQ.channels") | Frozen snapshot of all configured AI/AO/DI/DO channels (excludes relays). |
| [`di_channels`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.di_channels "instro.daq.InstroDAQ.di_channels") | Frozen snapshot of configured DI channels, keyed by alias. |
| [`di_hw_timing_config`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.di_hw_timing_config "instro.daq.InstroDAQ.di_hw_timing_config") |  |
| [`do_channels`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.do_channels "instro.daq.InstroDAQ.do_channels") | Frozen snapshot of configured DO channels, keyed by alias. |
| [`do_hw_timing_config`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.do_hw_timing_config "instro.daq.InstroDAQ.do_hw_timing_config") |  |
| [`driver`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.driver "instro.daq.InstroDAQ.driver") | The underlying vendor driver. |
| [`is_hw_timing_configured`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.is_hw_timing_configured "instro.daq.InstroDAQ.is_hw_timing_configured") | True once `configure_ai_hw_sample_rate()` has programmed the AI sample clock. |
| [`is_sw_timing_configured`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.is_sw_timing_configured "instro.daq.InstroDAQ.is_sw_timing_configured") | True once `configure_ai_sw_sample_rate()` has set a software-timed polling rate. |
| [`relay_channels`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.relay_channels "instro.daq.InstroDAQ.relay_channels") | Frozen snapshot of configured relay channels, keyed by alias. |

Methods

| [`add_background_daemon_function`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.add_background_daemon_function "instro.daq.InstroDAQ.add_background_daemon_function") | Append `method` to the daemon's call list. |
| --- | --- |
| [`add_publisher`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.add_publisher "instro.daq.InstroDAQ.add_publisher") | Register a publisher to receive this instrument's Measurement/Command data. |
| [`close`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.close "instro.daq.InstroDAQ.close") | Run full teardown unconditionally: daemon, then publishers, then the driver, which owns its idempotency. |
| [`close_relay`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.close_relay "instro.daq.InstroDAQ.close_relay") | Close relay `channel` (alias) — connects the circuit. |
| [`configure_ai_hw_sample_rate`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.configure_ai_hw_sample_rate "instro.daq.InstroDAQ.configure_ai_hw_sample_rate") | Configure the hardware sample clock for AI channels. |
| [`configure_ai_sample_rate`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.configure_ai_sample_rate "instro.daq.InstroDAQ.configure_ai_sample_rate") | Deprecated: use `configure_ai_hw_sample_rate()` instead. |
| [`configure_ai_sw_sample_rate`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.configure_ai_sw_sample_rate "instro.daq.InstroDAQ.configure_ai_sw_sample_rate") | Configure the software-timed polling rate for AI channels. |
| [`configure_analog_channel`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.configure_analog_channel "instro.daq.InstroDAQ.configure_analog_channel") | Deprecated: use `configure_voltage_input()` or `configure_voltage_output()` instead. |
| [`configure_current_input`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.configure_current_input "instro.daq.InstroDAQ.configure_current_input") | Configure an analog current input channel. |
| [`configure_current_output`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.configure_current_output "instro.daq.InstroDAQ.configure_current_output") | Configure an analog current output channel. |
| [`configure_digital_input`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.configure_digital_input "instro.daq.InstroDAQ.configure_digital_input") | Configure a digital input line channel. |
| [`configure_digital_line`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.configure_digital_line "instro.daq.InstroDAQ.configure_digital_line") | Deprecated: use `configure_digital_input()` or `configure_digital_output()` instead. |
| [`configure_digital_output`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.configure_digital_output "instro.daq.InstroDAQ.configure_digital_output") | Configure a digital output line channel. |
| [`configure_digital_port`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.configure_digital_port "instro.daq.InstroDAQ.configure_digital_port") | Configure a digital port channel. |
| [`configure_relay_channel`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.configure_relay_channel "instro.daq.InstroDAQ.configure_relay_channel") | Configure a relay channel (`physical_channel` e.g. `"3101"` = slot 3 / channel 101). |
| [`configure_thermocouple_input`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.configure_thermocouple_input "instro.daq.InstroDAQ.configure_thermocouple_input") | Configure a thermocouple input channel. |
| [`configure_voltage_input`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.configure_voltage_input "instro.daq.InstroDAQ.configure_voltage_input") | Configure an analog voltage input channel. |
| [`configure_voltage_output`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.configure_voltage_output "instro.daq.InstroDAQ.configure_voltage_output") | Configure an analog voltage output channel. |
| [`define_background_daemon`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.define_background_daemon "instro.daq.InstroDAQ.define_background_daemon") | Replace all daemon functions with a single `method` (called with the given args). |
| [`get_actual_sample_rate`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.get_actual_sample_rate "instro.daq.InstroDAQ.get_actual_sample_rate") | Hardware's actual sample rate after `start()`; `None` if unsupported or not started. |
| [`get_channel`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.get_channel "instro.daq.InstroDAQ.get_channel") | Return the most recent `length` samples for `channel_name` from the in-memory buffer. |
| [`get_points_in_buffer`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.get_points_in_buffer "instro.daq.InstroDAQ.get_points_in_buffer") | Publish the current DAQ buffer depth on channel `{name}.buffer`. |
| [`get_single_channel_value`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.get_single_channel_value "instro.daq.InstroDAQ.get_single_channel_value") | Return the most recent sample for `channel_name` from the in-memory buffer. |
| [`open`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.open "instro.daq.InstroDAQ.open") | Open the underlying driver. |
| [`open_relay`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.open_relay "instro.daq.InstroDAQ.open_relay") | Open relay `channel` (alias) — disconnects the circuit. |
| [`publish`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.publish "instro.daq.InstroDAQ.publish") | Fan `data` out to every configured publisher; `kwargs` pass through. |
| [`read`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.read "instro.daq.InstroDAQ.read") | Read one AI/DI channel by alias; returns its Measurement. |
| [`read_analog`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.read_analog "instro.daq.InstroDAQ.read_analog") | Dispatch a hardware-timed buffer fetch or a software-timed conversion based on configuration. |
| [`read_batch`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.read_batch "instro.daq.InstroDAQ.read_batch") | Read AI/DI channels by alias (`None` = all inputs); returns `{alias: Measurement}`, analog via `read_analog` (or the daemon buffer while it runs), digital per line/port. |
| [`read_digital_line`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.read_digital_line "instro.daq.InstroDAQ.read_digital_line") | Read DI line `channel` (alias). |
| [`read_digital_port`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.read_digital_port "instro.daq.InstroDAQ.read_digital_port") | Read DI port `channel` (alias). |
| [`start`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.start "instro.daq.InstroDAQ.start") | Start acquisition: hardware-timed, or the software-timed daemon when SW timing is configured. |
| [`stop`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.stop "instro.daq.InstroDAQ.stop") | Stop hardware acquisition and the background daemon; tolerant teardown when not open. |
| [`write`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.write "instro.daq.InstroDAQ.write") | Write `value` to one AO/DO channel by alias; returns its Command. |
| [`write_analog_value`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.write_analog_value "instro.daq.InstroDAQ.write_analog_value") | Write `value` (volts) to AO `channel` (alias). |
| [`write_batch`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.write_batch "instro.daq.InstroDAQ.write_batch") | Write `values[i]` to output `channels[i]` (alias); `continue_on_failed_write` logs and skips failed writes instead of raising. |
| [`write_digital_line`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.write_digital_line "instro.daq.InstroDAQ.write_digital_line") | Write 0/1 to DO line `channel` (alias). |
| [`write_digital_port`](https://dev.nominal.io/instro/ref/instruments/instro.daq.InstroDAQ.md#instro.daq.InstroDAQ.write_digital_port "instro.daq.InstroDAQ.write_digital_port") | Write `data` to DO port `channel` (alias). |

### DEFAULT_SW_SAMPLE_RATE: ClassVar[float] = 1.0

### property driver: DAQDriverBase

The underlying vendor driver. Source of truth for all channel/timing state.

### property channels: tuple[DAQChannel, ...]

Frozen snapshot of all configured AI/AO/DI/DO channels (excludes relays).

### property ai_channels: Mapping[str, AnalogChannel | AnalogVoltageChannel | AnalogCurrentChannel | AnalogThermocoupleChannel]

Frozen snapshot of configured AI channels, keyed by alias.

### property ao_channels: Mapping[str, AnalogChannel | AnalogVoltageChannel | AnalogCurrentChannel | AnalogThermocoupleChannel]

Frozen snapshot of configured AO channels, keyed by alias.

### property di_channels: Mapping[str, DigitalChannel]

Frozen snapshot of configured DI channels, keyed by alias.

### property do_channels: Mapping[str, DigitalChannel]

Frozen snapshot of configured DO channels, keyed by alias.

### property relay_channels: Mapping[str, RelayChannel]

Frozen snapshot of configured relay channels, keyed by alias.

### property ai_hw_timing_config: HWTimingConfig | None

### property ao_hw_timing_config: HWTimingConfig | None

### property di_hw_timing_config: HWTimingConfig | None

### property do_hw_timing_config: HWTimingConfig | None

### property is_hw_timing_configured: bool

True once `configure_ai_hw_sample_rate()` has programmed the AI sample clock.

### property is_sw_timing_configured: bool

True once `configure_ai_sw_sample_rate()` has set a software-timed polling rate.

### property background_interval: float

Daemon loop period (s); 0 unless `configure_ai_sw_sample_rate()` set a software-timed rate.

### open()[source]

Open the underlying driver.

### close()[source]

Run full teardown unconditionally: daemon, then publishers, then the driver, which owns its idempotency.

### start(background: bool = True, **kwargs)[source]

Start acquisition: hardware-timed, or the software-timed daemon when SW timing is configured.

With no AI timing configured, `background=True` falls back to software timing at
1 Hz.

Parameters:
:   - **background**

      When True (default), spin the daemon thread to continuously
      fetch the buffer. When False, begin hardware acquisition only and
      fetch the buffer yourself by calling `read_analog()`. Software-timed
      acquisition requires True — the daemon is what does the timing — so False
      logs an error and starts nothing.
    - ****kwargs**

      passed to the driver’s `start()`. `channel_type` (NI only) selects
      which DAQmx task to start; drivers ignore keys they don’t use.

### stop(**kwargs)[source]

Stop hardware acquisition and the background daemon; tolerant teardown when not open.

### configure_voltage_input( physical_channel: str, *, alias: str | None = None, range_min: float = -10.0, range_max: float = 10.0, scaler: Scaler | None = None, terminal_config: str | TerminalConfig | None = None, )[source]

Configure an analog voltage input channel.

Parameters:
:   - **physical_channel**

      Vendor-specific channel id (e.g. `"ai0"` or `"Dev1/ai0"`).
    - **alias**

      Friendly name; defaults to `physical_channel`.
    - **range_min**

      Lower voltage range (volts).
    - **range_max**

      Upper voltage range (volts).
    - **scaler**

      Optional `Scaler` applied to AI samples after read.
    - **terminal_config**

      Terminal wiring (RSE / NRSE / DIFF) for the channel.

### configure_voltage_output( physical_channel: str, *, alias: str | None = None, range_min: float = -10.0, range_max: float = 10.0, scaler: Scaler | None = None, )[source]

Configure an analog voltage output channel.

Parameters:
:   - **physical_channel**

      Vendor-specific channel id (e.g. `"ao0"` or `"Dev1/ao0"`).
    - **alias**

      Friendly name; defaults to `physical_channel`.
    - **range_min**

      Lower voltage range (volts).
    - **range_max**

      Upper voltage range (volts).
    - **scaler**

      Optional `Scaler` for the channel.

### configure_current_input( physical_channel: str, *, alias: str | None = None, range_min: float = 0.0, range_max: float = 0.02, scaler: Scaler | None = None, )[source]

Configure an analog current input channel.

Parameters:
:   - **physical_channel**

      Vendor-specific channel id (e.g. `"ai0"` or `"Dev1/ai0"`).
    - **alias**

      Friendly name; defaults to `physical_channel`.
    - **range_min**

      Lower current range (amps).
    - **range_max**

      Upper current range (amps).
    - **scaler**

      Optional `Scaler` applied to AI samples after read.

### configure_current_output( physical_channel: str, *, alias: str | None = None, range_min: float = 0.0, range_max: float = 0.02, scaler: Scaler | None = None, )[source]

Configure an analog current output channel.

Parameters:
:   - **physical_channel**

      Vendor-specific channel id (e.g. `"ao0"` or `"Dev1/ao0"`).
    - **alias**

      Friendly name; defaults to `physical_channel`.
    - **range_min**

      Lower current range (amps).
    - **range_max**

      Upper current range (amps).
    - **scaler**

      Optional `Scaler` for the channel.

### configure_thermocouple_input( physical_channel: str, tc_type: str | TC_TYPE, *, unit: str | TC_UNIT, alias: str | None = None, range_min: float = 0.0, range_max: float = 100.0, scaler: Scaler | None = None, cjc_source: str | CJCSource = CJCSource.INTERNAL, cjc_temp: float | None = None, cjc_channel: str | None = None, tc_input_scaler: Scaler | None = None, )[source]

Configure a thermocouple input channel.

Parameters:
:   - **physical_channel**

      Vendor-specific channel id (e.g. `"ai0"` or `"Dev1/ai0"`).
    - **tc_type**

      Thermocouple type — one of B, E, J, K, N, R, S, T.
    - **unit**

      Temperature unit for `range_min`/`range_max`, `cjc_temp`, and returned readings.
    - **alias**

      Friendly name; defaults to `physical_channel`.
    - **range_min**

      Lower temperature range (in `unit`).
    - **range_max**

      Upper temperature range (in `unit`).
    - **scaler**

      Optional `Scaler` applied to AI samples after read.
    - **cjc_source**

      Cold-junction compensation source (internal / constant / channel).
    - **cjc_temp**

      Cold-junction temperature when `cjc_source` is `CONSTANT`, expressed in `unit`.
    - **cjc_channel**

      Channel supplying cold-junction temperature when `cjc_source` is `CHANNEL`.
    - **tc_input_scaler**

      Volts-domain scaler applied before temperature conversion if amplifier was used (LabJack only).

### configure_digital_input( physical_channel: str, *, logic: str | Logic = Logic.HIGH, logic_level: float | None = None, alias: str | None = None, )[source]

Configure a digital input line channel.

Parameters:
:   - **physical_channel**

      Vendor-specific line id (e.g. `"port0/line3"` on NI, `"FIO0"` on LabJack).
    - **logic**

      Active-`HIGH` or active-`LOW`.
    - **logic_level**

      Voltage threshold (volts); the driver default is used when `None`.
    - **alias**

      Friendly name; defaults to `physical_channel`.

### configure_digital_output( physical_channel: str, *, logic: str | Logic = Logic.HIGH, logic_level: float | None = None, alias: str | None = None, )[source]

Configure a digital output line channel.

Parameters:
:   - **physical_channel**

      Vendor-specific line id (e.g. `"port0/line3"` on NI, `"FIO0"` on LabJack).
    - **logic**

      Active-`HIGH` or active-`LOW`.
    - **logic_level**

      Voltage threshold (volts); the driver default is used when `None`.
    - **alias**

      Friendly name; defaults to `physical_channel`.

### configure_digital_port( direction: Direction, physical_channel: str, logic: Logic, port_width: DigitalPortWidth, logic_level: float | None = None, alias: str | None = None, )[source]

Configure a digital port channel.

Parameters:
:   - **direction**

      `INPUT` or `OUTPUT`.
    - **physical_channel**

      Vendor-specific port id (e.g. `"port0"` on NI, `"5101"` on Keysight, `"AUXPORT0"` on MCC).
    - **logic**

      Active-`HIGH` or active-`LOW`.
    - **port_width**

      Port width in bits (8/16/32/64).
    - **logic_level**

      Voltage threshold (volts); the driver default is used when `None`.
    - **alias**

      Friendly name; defaults to `physical_channel`.

### configure_relay_channel( physical_channel: str, alias: str | None = None, )[source]

Configure a relay channel (`physical_channel` e.g. `"3101"` = slot 3 / channel 101).

### configure_analog_channel( direction: Direction, physical_channel: str, alias: str | None = None, range_min: float = -10.0, range_max: float = 10.0, scaler: Scaler | None = None, terminal_config: TerminalConfig | None = None, )[source]

Deprecated: use `configure_voltage_input()` or `configure_voltage_output()` instead.

Parameters:
:   - **direction**

      `INPUT` or `OUTPUT`.
    - **physical_channel**

      Vendor-specific channel id (e.g. `"ai0"` or `"Dev1/ai0"`).
    - **alias**

      Friendly name; defaults to `physical_channel`.
    - **range_min**

      Lower voltage range (volts).
    - **range_max**

      Upper voltage range (volts).
    - **scaler**

      Optional `Scaler` applied to AI samples after read.
    - **terminal_config**

      Terminal wiring (RSE / NRSE / DIFF) for the channel.

### configure_digital_line( direction: Direction, physical_channel: str, logic: Logic, logic_level: float | None = None, alias: str | None = None, )[source]

Deprecated: use `configure_digital_input()` or `configure_digital_output()` instead.

Parameters:
:   - **direction**

      `INPUT` or `OUTPUT`.
    - **physical_channel**

      Vendor-specific line id (e.g. `"port0/line3"` on NI, `"5101/3"` on Keysight, `"FIO0"` on LabJack).
    - **logic**

      Active-`HIGH` or active-`LOW`.
    - **logic_level**

      Voltage threshold (volts); the driver default is used when `None`.
    - **alias**

      Friendly name; defaults to `physical_channel`.

### configure_ai_hw_sample_rate( sample_rate: float, samples_per_channel: int | None = None, )[source]

Configure the hardware sample clock for AI channels.

Parameters:
:   - **sample_rate**

      Sample rate (Hz). Applies to all AI channels.
    - **samples_per_channel**

      Samples per channel per `read_analog()` call;
      defaults to 10 % of `sample_rate` (e.g. 100 at 1 kHz).

### configure_ai_sw_sample_rate(sample_rate: float)[source]

Configure the software-timed polling rate for AI channels.

Parameters:
:   **sample_rate**

    Rate (Hz) at which the background daemon polls AI channels.

### get_actual_sample_rate() → float | None[source]

Hardware’s actual sample rate after `start()`; `None` if unsupported or not started.

### configure_ai_sample_rate( sample_rate: float, samples_per_channel: int | None = None, **kwargs, )[source]

Deprecated: use `configure_ai_hw_sample_rate()` instead.

Parameters:
:   - **sample_rate**

      Sample rate (Hz). Applies to all AI channels.
    - **samples_per_channel**

      Samples per channel per `read_analog()` call;
      defaults to 10 % of `sample_rate` (e.g. 100 at 1 kHz).

### read(channel: str, **kwargs) → Measurement[source]

Read one AI/DI channel by alias; returns its Measurement.

### read_batch( channels: list[str] | None = None, **kwargs, ) → dict[str, Measurement][source]

Read AI/DI channels by alias (`None` = all inputs); returns `{alias: Measurement}`, analog via `read_analog` (or the daemon buffer while it runs), digital per line/port.

### read_analog( **kwargs, ) → Measurement | list[Measurement][source]

Dispatch a hardware-timed buffer fetch or a software-timed conversion based on configuration.

Each branch publishes its own Measurements; this dispatcher does not.
Either timing mode with the background daemon running raises — the daemon owns the reads.
Returns a single Measurement when channels share a timebase, otherwise one Measurement per timebase cluster.

### read_digital_line( channel: str, **kwargs, ) → Measurement[source]

Read DI line `channel` (alias). Raises `KeyError` if `channel` isn’t configured.

### read_digital_port( channel: str, **kwargs, ) → Measurement[source]

Read DI port `channel` (alias). Raises `KeyError` if `channel` isn’t configured.

### get_points_in_buffer( **kwargs, ) → Measurement[source]

Publish the current DAQ buffer depth on channel `{name}.buffer`.

### write( channel: str, value: float | int | bool, **kwargs, ) → Command[source]

Write `value` to one AO/DO channel by alias; returns its Command.

### write_batch( channels: list[str], values: list[float | int | bool], continue_on_failed_write: bool = False, **kwargs, ) → list[Command][source]

Write `values[i]` to output `channels[i]` (alias); `continue_on_failed_write` logs and skips failed writes instead of raising.

### add_background_daemon_function( method: Callable, *args, **kwargs, )

Append `method` to the daemon’s call list. Use `define_background_daemon` to replace instead.

### add_publisher( publisher: Publisher, )

Register a publisher to receive this instrument’s Measurement/Command data.

### property channel_names: tuple[str, ...]

Return an insertion-ordered snapshot of all published channel names.

Names are complete after one full daemon iteration: {name}.loop_time publishes
every iteration, so call get_channel(“loop_time”, wait_for_new_samples=True)
then read channel_names to ensure the complete set has arrived.

Raises:
:   [**RuntimeError**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError "(in Python v3.15)") – No background buffer; `start()` was not called.

### define_background_daemon( method: Callable, *args, **kwargs, )

Replace all daemon functions with a single `method` (called with the given args).

### get_channel( channel_name: str, length: int = 1, wait_for_new_samples: bool = False, timeout: float = 10.0, ) → Measurement

Return the most recent `length` samples for `channel_name` from the in-memory buffer.

If the channel does not exist yet, or no sample is available, the code will always block until `timeout` expires.

Parameters:
:   - **channel_name**

      Name of the channel to retrieve.
    - **length**

      Number of trailing samples to return.
    - **wait_for_new_samples**

      Block until at least `length` new values arrive.
    - **timeout**

      Seconds to wait when insufficient data exists or `wait_for_new_samples=True`.

Raises:
:   - [**RuntimeError**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError "(in Python v3.15)") – No background buffer; `start()` was not called.
    - [**ChannelNotFoundError**](https://dev.nominal.io/instro/ref/library/instro.lib.publishers.channel_buffer.ChannelNotFoundError.md#instro.lib.publishers.channel_buffer.ChannelNotFoundError "instro.lib.publishers.channel_buffer.ChannelNotFoundError") – channel had no values and no data appeared before `timeout`.
      `wait_for_new_samples=True` and channel did not appear within `timeout`.
    - [**ChannelValueTimeoutError**](https://dev.nominal.io/instro/ref/library/instro.lib.publishers.channel_buffer.ChannelValueTimeoutError.md#instro.lib.publishers.channel_buffer.ChannelValueTimeoutError "instro.lib.publishers.channel_buffer.ChannelValueTimeoutError") – `wait_for_new_samples=True` and values did not arrive within `timeout`.

### get_single_channel_value( channel_name: str, ) → float | str | None

Return the most recent sample for `channel_name` from the in-memory buffer.

This will not wait, if data is not available then `None` is returned.

Parameters:
:   **channel_name**

    Name of the channel to retrieve.

Raises:
:   [**RuntimeError**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError "(in Python v3.15)") – No background buffer; `start()` was not called.

### publish( data: Measurement | Command, **kwargs, )

Fan `data` out to every configured publisher; `kwargs` pass through.

### write_analog_value( channel: str, value: float, **kwargs, ) → Command[source]

Write `value` (volts) to AO `channel` (alias). Raises `KeyError` if `channel` isn’t configured.

### write_digital_line( channel: str, data: int, **kwargs, ) → Command[source]

Write 0/1 to DO line `channel` (alias). Raises `KeyError` if `channel` isn’t configured.

### write_digital_port( channel: str, data: int, **kwargs, ) → Command[source]

Write `data` to DO port `channel` (alias). Raises `KeyError` if `channel` isn’t configured.

### close_relay( channel: str, **kwargs, ) → Command[source]

Close relay `channel` (alias) — connects the circuit.

### open_relay( channel: str, **kwargs, ) → Command[source]

Open relay `channel` (alias) — disconnects the circuit.
