diff --git a/tests/component/_analog_utils.py b/tests/component/_analog_utils.py new file mode 100644 index 000000000..4dec858f5 --- /dev/null +++ b/tests/component/_analog_utils.py @@ -0,0 +1,75 @@ +"""Shared utilities for analog component tests.""" + +from __future__ import annotations + +import pytest + + +# Simulated DAQ voltage data is a noisy sinewave within the range of the minimum and maximum values +# of the virtual channel. We can leverage this behavior to validate we get the correct data from +# the Python bindings. +def _get_voltage_offset_for_chan(chan_index: int) -> float: + return float(chan_index + 1) + + +def _get_voltage_setpoint_for_chan(chan_index: int) -> float: + return float(chan_index + 1) + + +def _get_current_setpoint_for_chan(chan_index: int) -> float: + return float(chan_index + 1) + + +def _get_expected_voltage_for_chan(chan_index: int) -> float: + return float(chan_index + 1) + + +def _volts_to_codes(volts: float, max_code: int = 32767, max_voltage: float = 10.0) -> int: + return int(volts * max_code / max_voltage) + + +def _pwr_volts_to_codes(volts: float, codes_per_volt: int = 4096) -> int: + return int(volts * codes_per_volt) + + +def _pwr_current_to_codes(current: float, codes_per_amp: int = 8192) -> int: + return int(current * codes_per_amp) + + +def _get_voltage_code_setpoint_for_chan(chan_index: int) -> int: + return _pwr_volts_to_codes(_get_voltage_setpoint_for_chan(chan_index)) + + +def _get_current_code_setpoint_for_chan(chan_index: int) -> int: + return _pwr_current_to_codes(_get_current_setpoint_for_chan(chan_index)) + + +# Note: Since we only use positive voltages, this works fine for both signed and unsigned reads. +def _get_voltage_code_offset_for_chan(chan_index: int) -> int: + voltage_limits = _get_voltage_offset_for_chan(chan_index) + return _volts_to_codes(voltage_limits) + + +def _assert_equal_2d(data: list[list[float]], expected: list[list[float]], abs: float) -> None: + assert len(data) == len(expected) + for i in range(len(data)): + assert data[i] == pytest.approx(expected[i], abs=abs) + + +# NOTE: We use simulated signals for AI validation, so we can be fairly strict here. +AI_VOLTAGE_EPSILON = 1e-3 + +# NOTE: We must use real signals for AO validation, but we aren't validating hardware accuracy here. +# This should be wide enough tolerance to allow for uncalibrated boards while still ensuring we are +# correctly configuring hardware. +AO_VOLTAGE_EPSILON = 1e-2 + +# NOTE: You can't scale from volts to codes correctly without knowing the internal calibration +# constants. The internal reference has a healthy amount of overrange to ensure we can calibrate to +# device specifications. I've used 10.1 volts above to approximate that, but 100mv of accuracy is +# also fine since the expected output of each channel value will be 1 volt apart. +RAW_VOLTAGE_EPSILON = 1e-1 + +VOLTAGE_CODE_EPSILON = round(_volts_to_codes(AI_VOLTAGE_EPSILON)) +POWER_EPSILON = 1e-3 +POWER_BINARY_EPSILON = 1 diff --git a/tests/component/_digital_utils.py b/tests/component/_digital_utils.py new file mode 100644 index 000000000..ad5f6e5ec --- /dev/null +++ b/tests/component/_digital_utils.py @@ -0,0 +1,127 @@ +"""Shared utilities for digital component tests.""" + +from __future__ import annotations + +from typing import Callable, TypeVar + +import numpy +from nitypes.waveform import DigitalWaveform + +import nidaqmx + +_D = TypeVar("_D", bound=numpy.generic) + + +def _start_di_task(task: nidaqmx.Task) -> None: + # Don't reserve the lines, so we can read what DO is writing. + task.di_channels.all.di_tristate = False + task.start() + + +def _start_do_task(task: nidaqmx.Task, is_port: bool = False, num_chans: int = 1) -> None: + # We'll be doing on-demand, so start the task and drive all lines low + task.start() + if is_port: + if num_chans == 8: + task.write(0) + else: + task.write([0] * num_chans) + else: + if num_chans == 1: + task.write(False) + else: + task.write([False] * num_chans) + + +def _get_num_di_lines_in_task(task: nidaqmx.Task) -> int: + return sum([chan.di_num_lines for chan in task.channels]) + + +def _get_num_do_lines_in_task(task: nidaqmx.Task) -> int: + return sum([chan.do_num_lines for chan in task.channels]) + + +def _get_digital_data_for_sample(num_lines: int, sample_number: int) -> int: + result = 0 + # Simulated digital signals "count" from 0 in binary within each group of 8 lines. + for _ in range((num_lines + 7) // 8): + result = (result << 8) | sample_number + + line_mask = (2**num_lines) - 1 + return result & line_mask + + +def _get_expected_data_for_line(num_samples: int, line_number: int) -> list[int]: + data = [] + # Simulated digital signals "count" from 0 in binary within each group of 8 lines. + # Each line represents a bit in the binary representation of the sample number. + # - line 0 represents bit 0 (LSB) - alternates every sample: 0,1,0,1,0,1,0,1... + # - line 1 represents bit 1 - alternates every 2 samples: 0,0,1,1,0,0,1,1... + # - line 2 represents bit 2 - alternates every 4 samples: 0,0,0,0,1,1,1,1... + line_number %= 8 + for sample_num in range(num_samples): + bit_value = (sample_num >> line_number) & 1 + data.append(bit_value) + return data + + +def _get_digital_data(num_lines: int, num_samples: int) -> list[int]: + return [ + _get_digital_data_for_sample(num_lines, sample_number) + for sample_number in range(num_samples) + ] + + +def _get_expected_digital_port_data_port_major( + task: nidaqmx.Task, num_samples: int +) -> list[list[int]]: + return [_get_digital_data(chan.di_num_lines, num_samples) for chan in task.channels] + + +def _get_expected_digital_port_data_sample_major( + task: nidaqmx.Task, num_samples: int +) -> list[list[int]]: + result = _get_expected_digital_port_data_port_major(task, num_samples) + return numpy.transpose(result).tolist() + + +def _get_digital_port_data_for_sample(task: nidaqmx.Task, sample_number: int) -> list[int]: + return [ + _get_digital_data_for_sample(chan.do_num_lines, sample_number) for chan in task.channels + ] + + +def _get_digital_port_data_port_major(task: nidaqmx.Task, num_samples: int) -> list[list[int]]: + return [_get_digital_data(chan.do_num_lines, num_samples) for chan in task.channels] + + +def _get_digital_port_data_sample_major(task: nidaqmx.Task, num_samples: int) -> list[list[int]]: + result = _get_digital_port_data_port_major(task, num_samples) + return numpy.transpose(result).tolist() + + +def _bool_array_to_int(bool_array: numpy.typing.NDArray[numpy.bool_]) -> int: + result = 0 + # Simulated data is little-endian + for bit in bool_array[::-1]: + result = (result << 1) | int(bit) + return result + + +def _int_to_bool_array(num_lines: int, input: int) -> numpy.typing.NDArray[numpy.bool_]: + result = numpy.full(num_lines, True, dtype=numpy.bool_) + for bit in range(num_lines): + result[bit] = (input & (1 << bit)) != 0 + return result + + +def _get_waveform_data(waveform: DigitalWaveform) -> list[int]: + assert isinstance(waveform, DigitalWaveform) + return [_bool_array_to_int(sample) for sample in waveform.data] + + +def _read_and_copy( + read_func: Callable[[numpy.typing.NDArray[_D]], None], array: numpy.typing.NDArray[_D] +) -> numpy.typing.NDArray[_D]: + read_func(array) + return array.copy() diff --git a/tests/component/_utils.py b/tests/component/_utils.py new file mode 100644 index 000000000..4814b7e86 --- /dev/null +++ b/tests/component/_utils.py @@ -0,0 +1,13 @@ +"""Shared utilities for component tests.""" + +from __future__ import annotations + +from datetime import timezone + +from hightime import datetime as ht_datetime + + +def _is_timestamp_close_to_now(timestamp: ht_datetime, tolerance_seconds: float = 1.0) -> bool: + current_time = ht_datetime.now(timezone.utc) + time_diff = abs((timestamp - current_time).total_seconds()) + return time_diff <= tolerance_seconds diff --git a/tests/component/conftest.py b/tests/component/conftest.py index 7c53572e3..55136b611 100644 --- a/tests/component/conftest.py +++ b/tests/component/conftest.py @@ -1,148 +1,24 @@ -"""Shared fixtures and utilities for component tests.""" +"""Shared fixtures for component tests.""" from __future__ import annotations -from datetime import timezone -from typing import Callable, TypeVar +from typing import Callable -import numpy import pytest -from hightime import datetime as ht_datetime -from nitypes.waveform import DigitalWaveform import nidaqmx import nidaqmx.system from nidaqmx.constants import AcquisitionType, LineGrouping from nidaqmx.utils import flatten_channel_string - - -# Simulated DAQ voltage data is a noisy sinewave within the range of the minimum and maximum values -# of the virtual channel. We can leverage this behavior to validate we get the correct data from -# the Python bindings. -def _get_voltage_offset_for_chan(chan_index: int) -> float: - return float(chan_index + 1) - - -def _get_voltage_setpoint_for_chan(chan_index: int) -> float: - return float(chan_index + 1) - - -def _get_current_setpoint_for_chan(chan_index: int) -> float: - return float(chan_index + 1) - - -def _volts_to_codes(volts: float, max_code: int = 32767, max_voltage: float = 10.0) -> int: - return int(volts * max_code / max_voltage) - - -def _pwr_volts_to_codes(volts: float, codes_per_volt: int = 4096) -> int: - return int(volts * codes_per_volt) - - -def _pwr_current_to_codes(current: float, codes_per_amp: int = 8192) -> int: - return int(current * codes_per_amp) - - -def _get_voltage_code_setpoint_for_chan(chan_index: int) -> int: - return _pwr_volts_to_codes(_get_voltage_setpoint_for_chan(chan_index)) - - -def _get_current_code_setpoint_for_chan(chan_index: int) -> int: - return _pwr_current_to_codes(_get_current_setpoint_for_chan(chan_index)) - - -# Note: Since we only use positive voltages, this works fine for both signed and unsigned reads. -def _get_voltage_code_offset_for_chan(chan_index: int) -> int: - voltage_limits = _get_voltage_offset_for_chan(chan_index) - return _volts_to_codes(voltage_limits) - - -def _get_num_lines_in_task(task: nidaqmx.Task) -> int: - return sum([chan.di_num_lines for chan in task.channels]) - - -def _get_expected_digital_data_for_sample(num_lines: int, sample_number: int) -> int: - result = 0 - # Simulated digital signals "count" from 0 in binary within each group of 8 lines. - for _ in range((num_lines + 7) // 8): - result = (result << 8) | sample_number - - line_mask = (2**num_lines) - 1 - return result & line_mask - - -def _get_expected_data_for_line(num_samples: int, line_number: int) -> list[int]: - data = [] - # Simulated digital signals "count" from 0 in binary within each group of 8 lines. - # Each line represents a bit in the binary representation of the sample number. - # - line 0 represents bit 0 (LSB) - alternates every sample: 0,1,0,1,0,1,0,1... - # - line 1 represents bit 1 - alternates every 2 samples: 0,0,1,1,0,0,1,1... - # - line 2 represents bit 2 - alternates every 4 samples: 0,0,0,0,1,1,1,1... - line_number %= 8 - for sample_num in range(num_samples): - bit_value = (sample_num >> line_number) & 1 - data.append(bit_value) - return data - - -def _get_expected_digital_data(num_lines: int, num_samples: int) -> list[int]: - return [ - _get_expected_digital_data_for_sample(num_lines, sample_number) - for sample_number in range(num_samples) - ] - - -def _get_expected_digital_port_data_port_major( - task: nidaqmx.Task, num_samples: int -) -> list[list[int]]: - return [_get_expected_digital_data(chan.di_num_lines, num_samples) for chan in task.channels] - - -def _get_expected_digital_port_data_sample_major( - task: nidaqmx.Task, num_samples: int -) -> list[list[int]]: - result = _get_expected_digital_port_data_port_major(task, num_samples) - return numpy.transpose(result).tolist() - - -def _bool_array_to_int(bool_array: numpy.typing.NDArray[numpy.bool_]) -> int: - result = 0 - # Simulated data is little-endian - for bit in bool_array[::-1]: - result = (result << 1) | int(bit) - return result - - -def _get_waveform_data(waveform: DigitalWaveform) -> list[int]: - assert isinstance(waveform, DigitalWaveform) - return [_bool_array_to_int(sample) for sample in waveform.data] - - -def _read_and_copy( - read_func: Callable[[numpy.typing.NDArray[_D]], None], array: numpy.typing.NDArray[_D] -) -> numpy.typing.NDArray[_D]: - read_func(array) - return array.copy() - - -def _is_timestamp_close_to_now(timestamp: ht_datetime, tolerance_seconds: float = 1.0) -> bool: - current_time = ht_datetime.now(timezone.utc) - time_diff = abs((timestamp - current_time).total_seconds()) - return time_diff <= tolerance_seconds - - -def _assert_equal_2d(data: list[list[float]], expected: list[list[float]], abs: float) -> None: - assert len(data) == len(expected) - for i in range(len(data)): - assert data[i] == pytest.approx(expected[i], abs=abs) - - -_D = TypeVar("_D", bound=numpy.generic) - -VOLTAGE_EPSILON = 1e-3 -VOLTAGE_CODE_EPSILON = round(_volts_to_codes(VOLTAGE_EPSILON)) -POWER_EPSILON = 1e-3 -POWER_BINARY_EPSILON = 1 +from tests.component._analog_utils import ( + AI_VOLTAGE_EPSILON, + AO_VOLTAGE_EPSILON, + _get_current_setpoint_for_chan, + _get_expected_voltage_for_chan, + _get_voltage_offset_for_chan, + _get_voltage_setpoint_for_chan, +) +from tests.component._digital_utils import _start_di_task, _start_do_task @pytest.fixture @@ -154,7 +30,7 @@ def ai_single_channel_task( task.ai_channels.add_ai_voltage_chan( sim_6363_device.ai_physical_chans[0].name, min_val=offset, - max_val=offset + VOLTAGE_EPSILON, + max_val=offset + AI_VOLTAGE_EPSILON, ) return task @@ -179,7 +55,7 @@ def ai_single_channel_task_with_high_rate( task.ai_channels.add_ai_voltage_chan( sim_charge_device.ai_physical_chans[0].name, min_val=offset, - max_val=offset + VOLTAGE_EPSILON, + max_val=offset + AI_VOLTAGE_EPSILON, ) task.timing.cfg_samp_clk_timing( rate=10_000_000, sample_mode=AcquisitionType.FINITE, samps_per_chan=50 @@ -198,7 +74,7 @@ def ai_multi_channel_task( sim_6363_device.ai_physical_chans[chan_index].name, min_val=offset, # min and max must be different, so add a small epsilon - max_val=offset + VOLTAGE_EPSILON, + max_val=offset + AI_VOLTAGE_EPSILON, ) # forcing the maximum range for binary read scaling to be predictable chan.ai_rng_high = 10 @@ -525,3 +401,321 @@ def di_multi_channel_timing_task( ) task.timing.cfg_samp_clk_timing(1000.0, sample_mode=AcquisitionType.FINITE, samps_per_chan=50) return task + + +@pytest.fixture +def ao_single_channel_task( + generate_task: Callable[[], nidaqmx.Task], + real_x_series_multiplexed_device: nidaqmx.system.Device, +) -> nidaqmx.Task: + """Configure a single-channel AO task.""" + task = generate_task() + chan_index = 0 + offset = _get_expected_voltage_for_chan(chan_index) + chan = task.ao_channels.add_ao_voltage_chan( + real_x_series_multiplexed_device.ao_physical_chans[chan_index].name, + min_val=0.0, + max_val=offset + AO_VOLTAGE_EPSILON, + ) + # forcing the maximum range for binary read scaling to be predictable + chan.ao_dac_rng_high = 10 + chan.ao_dac_rng_low = -10 + + # we'll be doing simple on-demand, so start the task now + task.start() + + # set the output to a known initial value + task.write(0.0) + + return task + + +@pytest.fixture +def ai_single_channel_loopback_task( + generate_task: Callable[[], nidaqmx.Task], + real_x_series_multiplexed_device: nidaqmx.system.Device, +) -> nidaqmx.Task: + """Configure a single-channel AI loopback task.""" + task = generate_task() + chan_index = 0 + task.ai_channels.add_ai_voltage_chan( + f"{real_x_series_multiplexed_device.name}/_ao{chan_index}_vs_aognd", + min_val=-10, + max_val=10, + ) + + # we'll be doing simple on-demand, so start the task now + task.start() + + return task + + +@pytest.fixture +def ao_multi_channel_task( + generate_task: Callable[[], nidaqmx.Task], + real_x_series_multiplexed_device: nidaqmx.system.Device, +) -> nidaqmx.Task: + """Configure a multi-channel AO task.""" + task = generate_task() + num_chans = 2 + for chan_index in range(num_chans): + offset = _get_expected_voltage_for_chan(chan_index) + chan = task.ao_channels.add_ao_voltage_chan( + real_x_series_multiplexed_device.ao_physical_chans[chan_index].name, + min_val=0.0, + max_val=offset + AO_VOLTAGE_EPSILON, + ) + # forcing the maximum range for binary read scaling to be predictable + chan.ao_dac_rng_high = 10 + chan.ao_dac_rng_low = -10 + + # we'll be doing simple on-demand, so start the task now + task.start() + + # set the output to a known initial value + task.write([0.0] * num_chans) + + return task + + +@pytest.fixture +def ai_multi_channel_loopback_task( + generate_task: Callable[[], nidaqmx.Task], + real_x_series_multiplexed_device: nidaqmx.system.Device, +) -> nidaqmx.Task: + """Configure a multi-channel AI loopback task.""" + task = generate_task() + num_chans = 2 + for chan_index in range(num_chans): + task.ai_channels.add_ai_voltage_chan( + f"{real_x_series_multiplexed_device.name}/_ao{chan_index}_vs_aognd", + min_val=-10, + max_val=10, + ) + + # we'll be doing simple on-demand, so start the task now + task.start() + + return task + + +@pytest.fixture +def do_single_line_task( + generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device +) -> nidaqmx.Task: + """Configure a single-line DO task.""" + task = generate_task() + task.do_channels.add_do_chan( + real_x_series_device.do_lines[0].name, line_grouping=LineGrouping.CHAN_FOR_ALL_LINES + ) + _start_do_task(task) + return task + + +@pytest.fixture +def do_single_channel_multi_line_task( + generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device +) -> nidaqmx.Task: + """Configure a single-channel DO task.""" + task = generate_task() + chan = task.do_channels.add_do_chan( + flatten_channel_string(real_x_series_device.do_lines.channel_names[:8]), + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + _start_do_task(task, num_chans=chan.do_num_lines) + return task + + +@pytest.fixture +def do_multi_channel_multi_line_task( + generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device +) -> nidaqmx.Task: + """Configure a multi-channel DO task.""" + task = generate_task() + task.do_channels.add_do_chan( + flatten_channel_string(real_x_series_device.do_lines.channel_names[:8]), + line_grouping=LineGrouping.CHAN_PER_LINE, + ) + _start_do_task(task, num_chans=task.number_of_channels) + return task + + +@pytest.fixture +def do_port0_task( + generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device +) -> nidaqmx.Task: + """Configure a single-channel DO task.""" + task = generate_task() + # X Series port 0 has either 32 or 8 lines. The former can only be used with 32-bit writes. The + # latter can be used with any sized port write. + task.do_channels.add_do_chan( + real_x_series_device.do_ports[0].name, + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + _start_do_task(task, is_port=True) + return task + + +@pytest.fixture +def do_port1_task( + generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device +) -> nidaqmx.Task: + """Configure a single-channel DO task.""" + task = generate_task() + # X Series port 1 has 8 lines, and can be used with any sized port write. + task.do_channels.add_do_chan( + real_x_series_device.do_ports[1].name, + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + _start_do_task(task, is_port=True) + return task + + +@pytest.fixture +def do_multi_channel_port_task( + generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device +) -> nidaqmx.Task: + """Configure a multi-channel DO task.""" + task = generate_task() + # X Series port 1 has 8 lines, and can be used with any sized port write + task.do_channels.add_do_chan( + real_x_series_device.do_ports[1].name, + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + # X Series port 2 has 8 lines, and can be used with any sized port write + task.do_channels.add_do_chan( + real_x_series_device.do_ports[2].name, + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + _start_do_task(task, is_port=True, num_chans=task.number_of_channels) + return task + + +@pytest.fixture +def di_single_line_loopback_task( + generate_task: Callable[[], nidaqmx.Task], + real_x_series_device: nidaqmx.system.Device, +) -> nidaqmx.Task: + """Configure a single-line DI loopback task.""" + task = generate_task() + task.di_channels.add_di_chan( + real_x_series_device.di_lines[0].name, line_grouping=LineGrouping.CHAN_FOR_ALL_LINES + ) + _start_di_task(task) + return task + + +@pytest.fixture +def di_multi_line_loopback_task( + generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device +) -> nidaqmx.Task: + """Configure a multi-line DI loopback task.""" + task = generate_task() + task.di_channels.add_di_chan( + flatten_channel_string(real_x_series_device.di_lines.channel_names[:8]), + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + _start_di_task(task) + return task + + +@pytest.fixture +def di_port0_loopback_task( + generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device +) -> nidaqmx.Task: + """Configure a single-channel DI loopback task.""" + task = generate_task() + task.di_channels.add_di_chan( + real_x_series_device.di_ports[0].name, + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + _start_di_task(task) + return task + + +@pytest.fixture +def di_port0_loopback_task_32dio( + generate_task: Callable[[], nidaqmx.Task], real_x_series_device_32dio: nidaqmx.system.Device +) -> nidaqmx.Task: + """Configure a single-channel DI loopback task.""" + task = generate_task() + task.di_channels.add_di_chan( + real_x_series_device_32dio.di_ports[0].name, + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + _start_di_task(task) + return task + + +@pytest.fixture +def di_port1_loopback_task( + generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device +) -> nidaqmx.Task: + """Configure a single-channel DI loopback task.""" + task = generate_task() + task.di_channels.add_di_chan( + real_x_series_device.di_ports[1].name, + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + _start_di_task(task) + return task + + +@pytest.fixture +def di_port1_loopback_task_32dio( + generate_task: Callable[[], nidaqmx.Task], real_x_series_device_32dio: nidaqmx.system.Device +) -> nidaqmx.Task: + """Configure a single-channel DI loopback task.""" + task = generate_task() + task.di_channels.add_di_chan( + real_x_series_device_32dio.di_ports[1].name, + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + _start_di_task(task) + return task + + +@pytest.fixture +def di_port2_loopback_task( + generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device +) -> nidaqmx.Task: + """Configure a single-channel DI loopback task.""" + task = generate_task() + task.di_channels.add_di_chan( + real_x_series_device.di_ports[2].name, + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + _start_di_task(task) + return task + + +@pytest.fixture +def di_port2_loopback_task_32dio( + generate_task: Callable[[], nidaqmx.Task], real_x_series_device_32dio: nidaqmx.system.Device +) -> nidaqmx.Task: + """Configure a single-channel DI loopback task.""" + task = generate_task() + task.di_channels.add_di_chan( + real_x_series_device_32dio.di_ports[2].name, + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + _start_di_task(task) + return task + + +@pytest.fixture +def di_multi_channel_port_loopback_task( + generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device +) -> nidaqmx.Task: + """Configure a multi-channel DI loopback task.""" + task = generate_task() + task.di_channels.add_di_chan( + real_x_series_device.di_ports[1].name, + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + task.di_channels.add_di_chan( + real_x_series_device.di_ports[2].name, + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + _start_di_task(task) + return task diff --git a/tests/component/stream_readers/test_analog_multi_channel_reader.py b/tests/component/stream_readers/test_analog_multi_channel_reader.py index 4c6c1c067..8017d45fa 100644 --- a/tests/component/stream_readers/test_analog_multi_channel_reader.py +++ b/tests/component/stream_readers/test_analog_multi_channel_reader.py @@ -10,15 +10,16 @@ from nitypes.waveform import AnalogWaveform, SampleIntervalMode import nidaqmx +import nidaqmx.system from nidaqmx._feature_toggles import WAVEFORM_SUPPORT, FeatureNotSupportedError from nidaqmx.constants import AcquisitionType, ReallocationPolicy, WaveformAttributeMode from nidaqmx.error_codes import DAQmxErrors from nidaqmx.stream_readers import AnalogMultiChannelReader, DaqError -from tests.component.conftest import ( - VOLTAGE_EPSILON, +from tests.component._analog_utils import ( + AI_VOLTAGE_EPSILON, _get_voltage_offset_for_chan, - _is_timestamp_close_to_now, ) +from tests.component._utils import _is_timestamp_close_to_now def test___analog_multi_channel_reader___read_one_sample___returns_valid_samples( @@ -31,7 +32,7 @@ def test___analog_multi_channel_reader___read_one_sample___returns_valid_samples reader.read_one_sample(data) expected = [_get_voltage_offset_for_chan(chan_index) for chan_index in range(num_channels)] - assert data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) def test___analog_multi_channel_reader___read_one_sample_with_wrong_dtype___raises_error_with_correct_dtype( @@ -59,7 +60,7 @@ def test___analog_multi_channel_reader___read_many_sample___returns_valid_sample assert samples_read == samples_to_read expected_vals = [_get_voltage_offset_for_chan(chan_index) for chan_index in range(num_channels)] - assert data == pytest.approx(expected_vals, abs=VOLTAGE_EPSILON) + assert data == pytest.approx(expected_vals, abs=AI_VOLTAGE_EPSILON) def test___analog_multi_channel_reader___read_many_sample_with_wrong_dtype___raises_error_with_correct_dtype( @@ -111,7 +112,7 @@ def test___analog_multi_channel_reader___read_waveforms___returns_valid_waveform for chan_index, waveform in enumerate(waveforms): assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(chan_index) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert isinstance(waveform.timing.timestamp, ht_datetime) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) @@ -139,7 +140,7 @@ def test___analog_multi_channel_reader___read_waveforms_no_args___returns_valid_ for chan_index, waveform in enumerate(waveforms): assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(chan_index) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert isinstance(waveform.timing.timestamp, ht_datetime) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) @@ -172,7 +173,7 @@ def test___analog_multi_channel_reader___read_waveforms_in_place___populates_val for chan_index, waveform in enumerate(waveforms): assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(chan_index) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert isinstance(waveform.timing.timestamp, ht_datetime) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) @@ -224,7 +225,7 @@ def test___analog_multi_channel_reader___read_into_undersized_waveforms___return for chan_index, waveform in enumerate(waveforms): assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(chan_index) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert isinstance(waveform.timing.timestamp, ht_datetime) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) @@ -244,12 +245,12 @@ def _make_multi_channel_reader(chan_a_index, chan_b_index, samps_per_chan): task.ai_channels.add_ai_voltage_chan( sim_6363_device.ai_physical_chans[chan_a_index].name, min_val=chan_a_index, - max_val=chan_a_index + VOLTAGE_EPSILON, + max_val=chan_a_index + AI_VOLTAGE_EPSILON, ) task.ai_channels.add_ai_voltage_chan( sim_6363_device.ai_physical_chans[chan_b_index].name, min_val=chan_b_index, - max_val=chan_b_index + VOLTAGE_EPSILON, + max_val=chan_b_index + AI_VOLTAGE_EPSILON, ) task.timing.cfg_samp_clk_timing( 1000.0, sample_mode=AcquisitionType.FINITE, samps_per_chan=samps_per_chan @@ -266,26 +267,26 @@ def _make_multi_channel_reader(chan_a_index, chan_b_index, samps_per_chan): reader0.read_waveforms(waveforms, 5) assert waveforms[0].sample_count == 5 - assert waveforms[0].scaled_data == pytest.approx(0, abs=VOLTAGE_EPSILON) + assert waveforms[0].scaled_data == pytest.approx(0, abs=AI_VOLTAGE_EPSILON) assert waveforms[0].channel_name == f"{sim_6363_device.name}/ai0" assert waveforms[1].sample_count == 5 - assert waveforms[1].scaled_data == pytest.approx(1, abs=VOLTAGE_EPSILON) + assert waveforms[1].scaled_data == pytest.approx(1, abs=AI_VOLTAGE_EPSILON) assert waveforms[1].channel_name == f"{sim_6363_device.name}/ai1" reader1.read_waveforms(waveforms, 10) assert waveforms[0].sample_count == 10 - assert waveforms[0].scaled_data == pytest.approx(2, abs=VOLTAGE_EPSILON) + assert waveforms[0].scaled_data == pytest.approx(2, abs=AI_VOLTAGE_EPSILON) assert waveforms[0].channel_name == f"{sim_6363_device.name}/ai2" assert waveforms[1].sample_count == 10 - assert waveforms[1].scaled_data == pytest.approx(3, abs=VOLTAGE_EPSILON) + assert waveforms[1].scaled_data == pytest.approx(3, abs=AI_VOLTAGE_EPSILON) assert waveforms[1].channel_name == f"{sim_6363_device.name}/ai3" reader2.read_waveforms(waveforms, 15) assert waveforms[0].sample_count == 15 - assert waveforms[0].scaled_data == pytest.approx(4, abs=VOLTAGE_EPSILON) + assert waveforms[0].scaled_data == pytest.approx(4, abs=AI_VOLTAGE_EPSILON) assert waveforms[0].channel_name == f"{sim_6363_device.name}/ai4" assert waveforms[1].sample_count == 15 - assert waveforms[1].scaled_data == pytest.approx(5, abs=VOLTAGE_EPSILON) + assert waveforms[1].scaled_data == pytest.approx(5, abs=AI_VOLTAGE_EPSILON) assert waveforms[1].channel_name == f"{sim_6363_device.name}/ai5" @@ -327,7 +328,7 @@ def test___analog_multi_channel_reader_with_timing_flag___read_waveforms___only_ for chan_index, waveform in enumerate(waveforms): assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(chan_index) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert isinstance(waveform.timing.timestamp, ht_datetime) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval_mode == SampleIntervalMode.REGULAR @@ -357,7 +358,7 @@ def test___analog_multi_channel_reader_with_extended_properties_flag___read_wave for chan_index, waveform in enumerate(waveforms): assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(chan_index) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert waveform.timing.sample_interval_mode == SampleIntervalMode.NONE assert ( waveform.channel_name == ai_multi_channel_task_with_timing.ai_channels[chan_index].name @@ -388,7 +389,7 @@ def test___analog_multi_channel_reader_with_both_flags___read_waveforms___includ for chan_index, waveform in enumerate(waveforms): assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(chan_index) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert isinstance(waveform.timing.timestamp, ht_datetime) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval_mode == SampleIntervalMode.REGULAR @@ -420,7 +421,7 @@ def test___analog_multi_channel_reader_with_none_flag___read_waveforms___minimal for chan_index, waveform in enumerate(waveforms): assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(chan_index) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert waveform.timing.sample_interval_mode == SampleIntervalMode.NONE assert waveform.channel_name == "" assert waveform.units == "" diff --git a/tests/component/stream_readers/test_analog_single_channel_reader.py b/tests/component/stream_readers/test_analog_single_channel_reader.py index 82dd5f7ac..6387dcd13 100644 --- a/tests/component/stream_readers/test_analog_single_channel_reader.py +++ b/tests/component/stream_readers/test_analog_single_channel_reader.py @@ -15,11 +15,11 @@ from nidaqmx.constants import AcquisitionType, ReallocationPolicy, WaveformAttributeMode from nidaqmx.error_codes import DAQmxErrors from nidaqmx.stream_readers import AnalogSingleChannelReader, DaqError -from tests.component.conftest import ( - VOLTAGE_EPSILON, +from tests.component._analog_utils import ( + AI_VOLTAGE_EPSILON, _get_voltage_offset_for_chan, - _is_timestamp_close_to_now, ) +from tests.component._utils import _is_timestamp_close_to_now def test___analog_single_channel_reader___read_one_sample___returns_valid_samples( @@ -30,7 +30,7 @@ def test___analog_single_channel_reader___read_one_sample___returns_valid_sample data = reader.read_one_sample() expected = _get_voltage_offset_for_chan(0) - assert data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) def test___analog_single_channel_reader___read_many_sample___returns_valid_samples( @@ -44,7 +44,7 @@ def test___analog_single_channel_reader___read_many_sample___returns_valid_sampl assert samples_read == samples_to_read expected = _get_voltage_offset_for_chan(0) - assert data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) def test___analog_single_channel_reader___read_many_sample_with_wrong_dtype___raises_error_with_correct_dtype( @@ -88,7 +88,7 @@ def test___analog_single_channel_reader___read_waveform___returns_valid_waveform assert samples_read == samples_to_read assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(0) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert isinstance(waveform.timing.timestamp, ht_datetime) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) @@ -109,7 +109,7 @@ def test___analog_single_channel_reader___read_waveform_no_args___returns_valid_ assert samples_read == 50 assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(0) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert isinstance(waveform.timing.timestamp, ht_datetime) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) @@ -131,7 +131,7 @@ def test___analog_single_channel_reader___read_waveform_in_place___populates_val assert samples_read == samples_to_read assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(0) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert isinstance(waveform.timing.timestamp, ht_datetime) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) @@ -149,7 +149,7 @@ def _make_single_channel_reader(chan_index, offset, rate): task.ai_channels.add_ai_voltage_chan( sim_6363_device.ai_physical_chans[chan_index].name, min_val=offset, - max_val=offset + VOLTAGE_EPSILON, + max_val=offset + AI_VOLTAGE_EPSILON, ) task.timing.cfg_samp_clk_timing(rate, sample_mode=AcquisitionType.FINITE, samps_per_chan=10) return AnalogSingleChannelReader(task.in_stream) @@ -160,13 +160,13 @@ def _make_single_channel_reader(chan_index, offset, rate): reader0.read_waveform(waveform, 10) timestamp1 = waveform.timing.timestamp - assert waveform.scaled_data == pytest.approx(0, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(0, abs=AI_VOLTAGE_EPSILON) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) assert waveform.channel_name == f"{sim_6363_device.name}/ai0" reader1.read_waveform(waveform, 10) timestamp2 = waveform.timing.timestamp - assert waveform.scaled_data == pytest.approx(1, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(1, abs=AI_VOLTAGE_EPSILON) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 2000) assert waveform.channel_name == f"{sim_6363_device.name}/ai1" @@ -201,7 +201,7 @@ def test___analog_single_channel_reader___read_into_undersized_waveform___return assert samples_read == samples_to_read assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(0) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert isinstance(waveform.timing.timestamp, ht_datetime) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) @@ -219,7 +219,7 @@ def _make_single_channel_reader(chan_index, offset, samps_per_chan): task.ai_channels.add_ai_voltage_chan( sim_6363_device.ai_physical_chans[chan_index].name, min_val=offset, - max_val=offset + VOLTAGE_EPSILON, + max_val=offset + AI_VOLTAGE_EPSILON, ) task.timing.cfg_samp_clk_timing( 1000.0, sample_mode=AcquisitionType.FINITE, samps_per_chan=samps_per_chan @@ -233,17 +233,17 @@ def _make_single_channel_reader(chan_index, offset, samps_per_chan): reader0.read_waveform(waveform, 5) assert waveform.sample_count == 5 - assert waveform.scaled_data == pytest.approx(0, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(0, abs=AI_VOLTAGE_EPSILON) assert waveform.channel_name == f"{sim_6363_device.name}/ai0" reader1.read_waveform(waveform, 10) assert waveform.sample_count == 10 - assert waveform.scaled_data == pytest.approx(1, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(1, abs=AI_VOLTAGE_EPSILON) assert waveform.channel_name == f"{sim_6363_device.name}/ai1" reader2.read_waveform(waveform, 15) assert waveform.sample_count == 15 - assert waveform.scaled_data == pytest.approx(2, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(2, abs=AI_VOLTAGE_EPSILON) assert waveform.channel_name == f"{sim_6363_device.name}/ai2" @@ -260,7 +260,7 @@ def test___analog_single_channel_reader___read_waveform_high_sample_rate___retur assert samples_read == samples_to_read assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(0) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert isinstance(waveform.timing.timestamp, ht_datetime) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 10_000_000) @@ -285,7 +285,7 @@ def test___analog_single_channel_reader_with_timing_flag___read_waveform___only_ assert isinstance(waveform, AnalogWaveform) assert waveform.sample_count == samples_to_read expected = _get_voltage_offset_for_chan(0) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert isinstance(waveform.timing.timestamp, ht_datetime) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval_mode == SampleIntervalMode.REGULAR @@ -310,7 +310,7 @@ def test___analog_single_channel_reader_with_extended_properties_flag___read_wav assert isinstance(waveform, AnalogWaveform) assert waveform.sample_count == samples_to_read expected = _get_voltage_offset_for_chan(0) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert waveform.timing.sample_interval_mode == SampleIntervalMode.NONE assert waveform.channel_name == ai_single_channel_task_with_timing.ai_channels[0].name assert waveform.units == "Volts" @@ -334,7 +334,7 @@ def test___analog_single_channel_reader_with_both_flags___read_waveform___includ assert isinstance(waveform, AnalogWaveform) assert waveform.sample_count == samples_to_read expected = _get_voltage_offset_for_chan(0) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert isinstance(waveform.timing.timestamp, ht_datetime) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval_mode == SampleIntervalMode.REGULAR @@ -359,7 +359,7 @@ def test___analog_single_channel_reader_with_none_flag___read_waveform___minimal assert isinstance(waveform, AnalogWaveform) assert waveform.sample_count == samples_to_read expected = _get_voltage_offset_for_chan(0) - assert waveform.scaled_data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.scaled_data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) assert waveform.timing.sample_interval_mode == SampleIntervalMode.NONE assert waveform.channel_name == "" assert waveform.units == "" diff --git a/tests/component/stream_readers/test_analog_unscaled_reader.py b/tests/component/stream_readers/test_analog_unscaled_reader.py index c0d0c21c2..5d90405b4 100644 --- a/tests/component/stream_readers/test_analog_unscaled_reader.py +++ b/tests/component/stream_readers/test_analog_unscaled_reader.py @@ -8,7 +8,7 @@ import nidaqmx from nidaqmx.stream_readers import AnalogUnscaledReader -from tests.component.conftest import ( +from tests.component._analog_utils import ( VOLTAGE_CODE_EPSILON, _get_voltage_code_offset_for_chan, ) diff --git a/tests/component/stream_readers/test_digital_multi_channel_reader.py b/tests/component/stream_readers/test_digital_multi_channel_reader.py index 74903290e..588799296 100644 --- a/tests/component/stream_readers/test_digital_multi_channel_reader.py +++ b/tests/component/stream_readers/test_digital_multi_channel_reader.py @@ -21,30 +21,30 @@ from nidaqmx.error_codes import DAQmxErrors from nidaqmx.stream_readers import DaqError, DigitalMultiChannelReader from nidaqmx.utils import flatten_channel_string -from tests.component.conftest import ( +from tests.component._digital_utils import ( _bool_array_to_int, _get_expected_data_for_line, - _get_expected_digital_data, + _get_digital_data, _get_expected_digital_port_data_port_major, _get_expected_digital_port_data_sample_major, - _get_num_lines_in_task, + _get_num_di_lines_in_task, _get_waveform_data, - _is_timestamp_close_to_now, _read_and_copy, ) +from tests.component._utils import _is_timestamp_close_to_now def test___digital_multi_channel_reader___read_one_sample_one_line___returns_valid_samples( di_single_line_task: nidaqmx.Task, ) -> None: reader = DigitalMultiChannelReader(di_single_line_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_line_task) + num_lines = _get_num_di_lines_in_task(di_single_line_task) samples_to_read = 256 sample = numpy.full(num_lines, False, dtype=numpy.bool_) data = [_read_and_copy(reader.read_one_sample_one_line, sample) for _ in range(samples_to_read)] - assert [_bool_array_to_int(sample) for sample in data] == _get_expected_digital_data( + assert [_bool_array_to_int(sample) for sample in data] == _get_digital_data( num_lines, samples_to_read ) @@ -53,7 +53,7 @@ def test___digital_multi_channel_reader___read_one_sample_one_line_with_wrong_dt di_single_line_task: nidaqmx.Task, ) -> None: reader = DigitalMultiChannelReader(di_single_line_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_line_task) + num_lines = _get_num_di_lines_in_task(di_single_line_task) data = numpy.full(num_lines, math.inf, dtype=numpy.float64) with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: @@ -74,7 +74,7 @@ def test___digital_multi_channel_reader___read_one_sample_multi_line___returns_v _read_and_copy(reader.read_one_sample_multi_line, sample) for _ in range(samples_to_read) ] - assert [_bool_array_to_int(sample[:, 0]) for sample in data] == _get_expected_digital_data( + assert [_bool_array_to_int(sample[:, 0]) for sample in data] == _get_digital_data( num_channels, samples_to_read ) @@ -333,7 +333,7 @@ def test___digital_multi_channel_multi_line_reader___read_waveforms___returns_va ) -> None: reader = DigitalMultiChannelReader(di_multi_chan_multi_line_timing_task.in_stream) num_channels = di_multi_chan_multi_line_timing_task.number_of_channels - num_lines = _get_num_lines_in_task(di_multi_chan_multi_line_timing_task) + num_lines = _get_num_di_lines_in_task(di_multi_chan_multi_line_timing_task) samples_to_read = 10 waveforms = [DigitalWaveform(samples_to_read) for _ in range(num_channels)] @@ -359,7 +359,7 @@ def test___digital_multi_channel_different_lines_reader___read_waveforms___retur ) -> None: reader = DigitalMultiChannelReader(di_multi_chan_diff_lines_timing_task.in_stream) num_channels = di_multi_chan_diff_lines_timing_task.number_of_channels - num_lines = _get_num_lines_in_task(di_multi_chan_diff_lines_timing_task) + num_lines = _get_num_di_lines_in_task(di_multi_chan_diff_lines_timing_task) samples_to_read = 10 waveforms = [ DigitalWaveform(samples_to_read, 1), @@ -411,7 +411,7 @@ def test___digital_multi_channel_lines_and_port_reader___read_waveforms___return ) -> None: reader = DigitalMultiChannelReader(di_multi_chan_lines_and_port_task.in_stream) num_channels = di_multi_chan_lines_and_port_task.number_of_channels - num_lines = _get_num_lines_in_task(di_multi_chan_lines_and_port_task) + num_lines = _get_num_di_lines_in_task(di_multi_chan_lines_and_port_task) samples_to_read = 10 waveforms = [ DigitalWaveform(samples_to_read, 1), @@ -497,7 +497,7 @@ def test___digital_multi_channel_multi_line_reader___read_waveforms_no_args___re ) -> None: reader = DigitalMultiChannelReader(di_multi_chan_multi_line_timing_task.in_stream) num_channels = di_multi_chan_multi_line_timing_task.number_of_channels - num_lines = _get_num_lines_in_task(di_multi_chan_multi_line_timing_task) + num_lines = _get_num_di_lines_in_task(di_multi_chan_multi_line_timing_task) waveforms = [DigitalWaveform(50) for _ in range(num_channels)] samples_read = reader.read_waveforms(waveforms) @@ -521,7 +521,7 @@ def test___digital_multi_channel_multi_line_reader___read_waveforms_in_place___p ) -> None: reader = DigitalMultiChannelReader(di_multi_chan_multi_line_timing_task.in_stream) num_channels = di_multi_chan_multi_line_timing_task.number_of_channels - num_lines = _get_num_lines_in_task(di_multi_chan_multi_line_timing_task) + num_lines = _get_num_di_lines_in_task(di_multi_chan_multi_line_timing_task) samples_to_read = 10 waveforms = [DigitalWaveform(samples_to_read) for _ in range(num_channels)] @@ -601,7 +601,7 @@ def test___digital_multi_channel_multi_line_reader___read_into_undersized_wavefo ) -> None: reader = DigitalMultiChannelReader(di_multi_chan_multi_line_timing_task.in_stream) num_channels = di_multi_chan_multi_line_timing_task.number_of_channels - num_lines = _get_num_lines_in_task(di_multi_chan_multi_line_timing_task) + num_lines = _get_num_di_lines_in_task(di_multi_chan_multi_line_timing_task) samples_to_read = 10 waveforms = [DigitalWaveform(samples_to_read - 1) for _ in range(num_channels)] @@ -696,7 +696,7 @@ def test___digital_multi_channel_multi_line_reader_with_timing_flag___read_wavef in_stream.waveform_attribute_mode = WaveformAttributeMode.TIMING reader = DigitalMultiChannelReader(in_stream) num_channels = di_multi_chan_multi_line_timing_task.number_of_channels - num_lines = _get_num_lines_in_task(di_multi_chan_multi_line_timing_task) + num_lines = _get_num_di_lines_in_task(di_multi_chan_multi_line_timing_task) samples_to_read = 10 waveforms = [DigitalWaveform(samples_to_read) for _ in range(num_channels)] @@ -724,7 +724,7 @@ def test___digital_multi_channel_multi_line_reader_with_extended_properties_flag in_stream.waveform_attribute_mode = WaveformAttributeMode.EXTENDED_PROPERTIES reader = DigitalMultiChannelReader(in_stream) num_channels = di_multi_chan_multi_line_timing_task.number_of_channels - num_lines = _get_num_lines_in_task(di_multi_chan_multi_line_timing_task) + num_lines = _get_num_di_lines_in_task(di_multi_chan_multi_line_timing_task) samples_to_read = 10 waveforms = [DigitalWaveform(samples_to_read) for _ in range(num_channels)] @@ -752,7 +752,7 @@ def test___digital_multi_channel_multi_line_reader_with_both_flags___read_wavefo ) reader = DigitalMultiChannelReader(in_stream) num_channels = di_multi_chan_multi_line_timing_task.number_of_channels - num_lines = _get_num_lines_in_task(di_multi_chan_multi_line_timing_task) + num_lines = _get_num_di_lines_in_task(di_multi_chan_multi_line_timing_task) samples_to_read = 10 waveforms = [DigitalWaveform(samples_to_read) for _ in range(num_channels)] @@ -780,7 +780,7 @@ def test___digital_multi_channel_multi_line_reader_with_none_flag___read_wavefor in_stream.waveform_attribute_mode = WaveformAttributeMode.NONE reader = DigitalMultiChannelReader(in_stream) num_channels = di_multi_chan_multi_line_timing_task.number_of_channels - num_lines = _get_num_lines_in_task(di_multi_chan_multi_line_timing_task) + num_lines = _get_num_di_lines_in_task(di_multi_chan_multi_line_timing_task) samples_to_read = 10 waveforms = [DigitalWaveform(samples_to_read) for _ in range(num_channels)] diff --git a/tests/component/stream_readers/test_digital_single_channel_reader.py b/tests/component/stream_readers/test_digital_single_channel_reader.py index 17745bd1a..0b103629d 100644 --- a/tests/component/stream_readers/test_digital_single_channel_reader.py +++ b/tests/component/stream_readers/test_digital_single_channel_reader.py @@ -21,34 +21,34 @@ from nidaqmx.error_codes import DAQmxErrors from nidaqmx.stream_readers import DaqError, DigitalSingleChannelReader from nidaqmx.utils import flatten_channel_string -from tests.component.conftest import ( +from tests.component._digital_utils import ( _bool_array_to_int, _get_expected_data_for_line, - _get_expected_digital_data, - _get_num_lines_in_task, + _get_digital_data, + _get_num_di_lines_in_task, _get_waveform_data, - _is_timestamp_close_to_now, _read_and_copy, ) +from tests.component._utils import _is_timestamp_close_to_now def test___digital_single_channel_reader___read_one_sample_one_line___returns_valid_samples( di_single_line_task: nidaqmx.Task, ) -> None: reader = DigitalSingleChannelReader(di_single_line_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_line_task) + num_lines = _get_num_di_lines_in_task(di_single_line_task) samples_to_read = 256 data = [reader.read_one_sample_one_line() for _ in range(samples_to_read)] - assert data == _get_expected_digital_data(num_lines, samples_to_read) + assert data == _get_digital_data(num_lines, samples_to_read) def test___digital_single_channel_reader___read_one_sample_multi_line___returns_valid_samples( di_single_channel_multi_line_task: nidaqmx.Task, ) -> None: reader = DigitalSingleChannelReader(di_single_channel_multi_line_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_channel_multi_line_task) + num_lines = _get_num_di_lines_in_task(di_single_channel_multi_line_task) samples_to_read = 256 sample = numpy.full(num_lines, False, dtype=numpy.bool_) @@ -56,7 +56,7 @@ def test___digital_single_channel_reader___read_one_sample_multi_line___returns_ _read_and_copy(reader.read_one_sample_multi_line, sample) for _ in range(samples_to_read) ] - assert [_bool_array_to_int(sample) for sample in data] == _get_expected_digital_data( + assert [_bool_array_to_int(sample) for sample in data] == _get_digital_data( num_lines, samples_to_read ) @@ -65,7 +65,7 @@ def test___digital_single_channel_reader___read_one_sample_multi_line_with_wrong di_single_channel_multi_line_task: nidaqmx.Task, ) -> None: reader = DigitalSingleChannelReader(di_single_channel_multi_line_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_channel_multi_line_task) + num_lines = _get_num_di_lines_in_task(di_single_channel_multi_line_task) data = numpy.full(num_lines, math.inf, dtype=numpy.float64) with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: @@ -78,43 +78,43 @@ def test___digital_single_channel_reader___read_one_sample_port_byte___returns_v di_single_channel_port_byte_task: nidaqmx.Task, ) -> None: reader = DigitalSingleChannelReader(di_single_channel_port_byte_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_channel_port_byte_task) + num_lines = _get_num_di_lines_in_task(di_single_channel_port_byte_task) samples_to_read = 256 data = [reader.read_one_sample_port_byte() for _ in range(samples_to_read)] - assert data == _get_expected_digital_data(num_lines, samples_to_read) + assert data == _get_digital_data(num_lines, samples_to_read) def test___digital_single_channel_reader___read_one_sample_port_uint16___returns_valid_samples( di_single_channel_port_uint16_task: nidaqmx.Task, ) -> None: reader = DigitalSingleChannelReader(di_single_channel_port_uint16_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_channel_port_uint16_task) + num_lines = _get_num_di_lines_in_task(di_single_channel_port_uint16_task) samples_to_read = 256 data = [reader.read_one_sample_port_uint16() for _ in range(samples_to_read)] - assert data == _get_expected_digital_data(num_lines, samples_to_read) + assert data == _get_digital_data(num_lines, samples_to_read) def test___digital_single_channel_reader___read_one_sample_port_uint32___returns_valid_samples( di_single_channel_port_uint32_task: nidaqmx.Task, ) -> None: reader = DigitalSingleChannelReader(di_single_channel_port_uint32_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_channel_port_uint32_task) + num_lines = _get_num_di_lines_in_task(di_single_channel_port_uint32_task) samples_to_read = 256 data = [reader.read_one_sample_port_uint32() for _ in range(samples_to_read)] - assert data == _get_expected_digital_data(num_lines, samples_to_read) + assert data == _get_digital_data(num_lines, samples_to_read) def test___digital_single_channel_reader___read_many_sample_port_byte___returns_valid_samples( di_single_channel_port_byte_task: nidaqmx.Task, ) -> None: reader = DigitalSingleChannelReader(di_single_channel_port_byte_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_channel_port_byte_task) + num_lines = _get_num_di_lines_in_task(di_single_channel_port_byte_task) samples_to_read = 256 data = numpy.full(samples_to_read, numpy.iinfo(numpy.uint8).min, dtype=numpy.uint8) @@ -123,7 +123,7 @@ def test___digital_single_channel_reader___read_many_sample_port_byte___returns_ ) assert samples_read == samples_to_read - assert data.tolist() == _get_expected_digital_data(num_lines, samples_to_read) + assert data.tolist() == _get_digital_data(num_lines, samples_to_read) def test___digital_single_channel_reader___read_many_sample_port_byte_with_wrong_dtype___raises_error_with_correct_dtype( @@ -143,7 +143,7 @@ def test___digital_single_channel_reader___read_many_sample_port_uint16___return di_single_channel_port_uint16_task: nidaqmx.Task, ) -> None: reader = DigitalSingleChannelReader(di_single_channel_port_uint16_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_channel_port_uint16_task) + num_lines = _get_num_di_lines_in_task(di_single_channel_port_uint16_task) samples_to_read = 256 data = numpy.full(samples_to_read, numpy.iinfo(numpy.uint16).min, dtype=numpy.uint16) @@ -152,7 +152,7 @@ def test___digital_single_channel_reader___read_many_sample_port_uint16___return ) assert samples_read == samples_to_read - assert data.tolist() == _get_expected_digital_data(num_lines, samples_to_read) + assert data.tolist() == _get_digital_data(num_lines, samples_to_read) def test___digital_single_channel_reader___read_many_sample_port_uint16_with_wrong_dtype___raises_error_with_correct_dtype( @@ -172,7 +172,7 @@ def test___digital_single_channel_reader___read_many_sample_port_uint32___return di_single_channel_port_uint32_task: nidaqmx.Task, ) -> None: reader = DigitalSingleChannelReader(di_single_channel_port_uint32_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_channel_port_uint32_task) + num_lines = _get_num_di_lines_in_task(di_single_channel_port_uint32_task) samples_to_read = 256 data = numpy.full(samples_to_read, numpy.iinfo(numpy.uint32).min, dtype=numpy.uint32) @@ -181,7 +181,7 @@ def test___digital_single_channel_reader___read_many_sample_port_uint32___return ) assert samples_read == samples_to_read - assert data.tolist() == _get_expected_digital_data(num_lines, samples_to_read) + assert data.tolist() == _get_digital_data(num_lines, samples_to_read) def test___digital_single_channel_reader___read_many_sample_port_uint32_with_wrong_dtype___raises_error_with_correct_dtype( @@ -223,7 +223,7 @@ def test___digital_single_line_reader___read_waveform___returns_valid_waveform( samples_read = reader.read_waveform(waveform, samples_to_read) assert samples_read == samples_to_read - assert _get_waveform_data(waveform) == _get_expected_digital_data(1, samples_to_read) + assert _get_waveform_data(waveform) == _get_digital_data(1, samples_to_read) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) assert waveform.timing.sample_interval_mode == SampleIntervalMode.REGULAR @@ -236,13 +236,13 @@ def test___digital_single_channel_multi_line_reader___read_waveform___returns_va ) -> None: reader = DigitalSingleChannelReader(di_single_channel_multi_line_timing_task.in_stream) samples_to_read = 10 - num_lines = _get_num_lines_in_task(di_single_channel_multi_line_timing_task) + num_lines = _get_num_di_lines_in_task(di_single_channel_multi_line_timing_task) waveform = DigitalWaveform(samples_to_read, num_lines) samples_read = reader.read_waveform(waveform, samples_to_read) assert samples_read == samples_to_read - assert _get_waveform_data(waveform) == _get_expected_digital_data(num_lines, samples_to_read) + assert _get_waveform_data(waveform) == _get_digital_data(num_lines, samples_to_read) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) assert waveform.timing.sample_interval_mode == SampleIntervalMode.REGULAR @@ -259,7 +259,7 @@ def test___digital_single_line_reader___read_waveform_no_args___returns_valid_wa samples_read = reader.read_waveform(waveform) assert samples_read == 50 - assert _get_waveform_data(waveform) == _get_expected_digital_data(1, 50) + assert _get_waveform_data(waveform) == _get_digital_data(1, 50) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) assert waveform.timing.sample_interval_mode == SampleIntervalMode.REGULAR @@ -271,13 +271,13 @@ def test___digital_single_channel_multi_line_reader___read_waveform_no_args___re di_single_channel_multi_line_timing_task: nidaqmx.Task, ) -> None: reader = DigitalSingleChannelReader(di_single_channel_multi_line_timing_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_channel_multi_line_timing_task) + num_lines = _get_num_di_lines_in_task(di_single_channel_multi_line_timing_task) waveform = DigitalWaveform(50, num_lines) samples_read = reader.read_waveform(waveform) assert samples_read == 50 - assert _get_waveform_data(waveform) == _get_expected_digital_data(num_lines, 50) + assert _get_waveform_data(waveform) == _get_digital_data(num_lines, 50) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) assert waveform.timing.sample_interval_mode == SampleIntervalMode.REGULAR @@ -294,7 +294,7 @@ def test___digital_single_line_reader___read_waveform_in_place___returns_valid_w samples_read = reader.read_waveform(waveform) assert samples_read == 50 - assert _get_waveform_data(waveform) == _get_expected_digital_data(1, 50) + assert _get_waveform_data(waveform) == _get_digital_data(1, 50) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) assert waveform.channel_name == di_single_line_timing_task.di_channels[0].name @@ -305,13 +305,13 @@ def test___digital_single_channel_multi_line_reader___read_waveform_in_place___r di_single_channel_multi_line_timing_task: nidaqmx.Task, ) -> None: reader = DigitalSingleChannelReader(di_single_channel_multi_line_timing_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_channel_multi_line_timing_task) + num_lines = _get_num_di_lines_in_task(di_single_channel_multi_line_timing_task) waveform = DigitalWaveform(sample_count=50, signal_count=8) samples_read = reader.read_waveform(waveform) assert samples_read == 50 - assert _get_waveform_data(waveform) == _get_expected_digital_data(num_lines, 50) + assert _get_waveform_data(waveform) == _get_digital_data(num_lines, 50) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) assert waveform.channel_name == di_single_channel_multi_line_timing_task.di_channels[0].name @@ -414,7 +414,7 @@ def test___digital_single_line_reader___read_into_undersized_waveform___returns_ samples_read = reader.read_waveform(waveform, samples_to_read) assert samples_read == samples_to_read - assert _get_waveform_data(waveform) == _get_expected_digital_data(1, samples_to_read) + assert _get_waveform_data(waveform) == _get_digital_data(1, samples_to_read) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) assert waveform.timing.sample_interval_mode == SampleIntervalMode.REGULAR @@ -467,7 +467,7 @@ def test___digital_single_line_reader___read_waveform_high_sample_rate___returns samples_read = reader.read_waveform(waveform, samples_to_read) assert samples_read == samples_to_read - assert _get_waveform_data(waveform) == _get_expected_digital_data(1, 50) + assert _get_waveform_data(waveform) == _get_digital_data(1, 50) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 10_000_000) assert waveform.sample_count == samples_to_read @@ -486,7 +486,7 @@ def test___digital_single_line_reader_with_timing_flag___read_waveform___only_in samples_read = reader.read_waveform(waveform) assert samples_read == 50 - assert _get_waveform_data(waveform) == _get_expected_digital_data(1, 50) + assert _get_waveform_data(waveform) == _get_digital_data(1, 50) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval_mode == SampleIntervalMode.REGULAR assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) @@ -505,7 +505,7 @@ def test___digital_single_line_reader_with_extended_properties_flag___read_wavef samples_read = reader.read_waveform(waveform) assert samples_read == 50 - assert _get_waveform_data(waveform) == _get_expected_digital_data(1, 50) + assert _get_waveform_data(waveform) == _get_digital_data(1, 50) assert waveform.timing.sample_interval_mode == SampleIntervalMode.NONE assert waveform.channel_name == di_single_line_timing_task.di_channels[0].name @@ -524,7 +524,7 @@ def test___digital_single_line_reader_with_both_flags___read_waveform___includes samples_read = reader.read_waveform(waveform) assert samples_read == 50 - assert _get_waveform_data(waveform) == _get_expected_digital_data(1, 50) + assert _get_waveform_data(waveform) == _get_digital_data(1, 50) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval_mode == SampleIntervalMode.REGULAR assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) @@ -543,7 +543,7 @@ def test___digital_single_line_reader_with_none_flag___read_waveform___minimal_w samples_read = reader.read_waveform(waveform) assert samples_read == 50 - assert _get_waveform_data(waveform) == _get_expected_digital_data(1, 50) + assert _get_waveform_data(waveform) == _get_digital_data(1, 50) assert waveform.timing.sample_interval_mode == SampleIntervalMode.NONE assert waveform.channel_name == "" @@ -557,14 +557,14 @@ def test___digital_single_channel_port_uint32_reader___read_waveform___returns_v di_single_channel_port_uint32_timing_task: nidaqmx.Task, ) -> None: reader = DigitalSingleChannelReader(di_single_channel_port_uint32_timing_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_channel_port_uint32_timing_task) + num_lines = _get_num_di_lines_in_task(di_single_channel_port_uint32_timing_task) samples_to_read = 10 waveform = DigitalWaveform(samples_to_read, num_lines) samples_read = reader.read_waveform(waveform, samples_to_read) assert samples_read == 50 - assert _get_waveform_data(waveform) == _get_expected_digital_data(num_lines, samples_to_read) + assert _get_waveform_data(waveform) == _get_digital_data(num_lines, samples_to_read) assert _is_timestamp_close_to_now(waveform.timing.timestamp) assert waveform.timing.sample_interval == ht_timedelta(seconds=1 / 1000) assert waveform.timing.sample_interval_mode == SampleIntervalMode.REGULAR @@ -577,7 +577,7 @@ def test___digital_single_channel_lines_and_port___read_waveform___returns_valid sim_6363_device: nidaqmx.system.Device, ) -> None: reader = DigitalSingleChannelReader(di_single_chan_lines_and_port_task.in_stream) - num_lines = _get_num_lines_in_task(di_single_chan_lines_and_port_task) + num_lines = _get_num_di_lines_in_task(di_single_chan_lines_and_port_task) samples_to_read = 10 waveform = DigitalWaveform(samples_to_read, num_lines) diff --git a/tests/component/stream_readers/test_power_readers_ai.py b/tests/component/stream_readers/test_power_readers_ai.py index be5090a69..9642b2e53 100644 --- a/tests/component/stream_readers/test_power_readers_ai.py +++ b/tests/component/stream_readers/test_power_readers_ai.py @@ -13,7 +13,7 @@ PowerMultiChannelReader, PowerSingleChannelReader, ) -from tests.component.conftest import ( +from tests.component._analog_utils import ( POWER_BINARY_EPSILON, POWER_EPSILON, _get_current_code_setpoint_for_chan, diff --git a/tests/component/stream_writers/test_analog_multi_channel_writer.py b/tests/component/stream_writers/test_analog_multi_channel_writer.py new file mode 100644 index 000000000..2a70b46f8 --- /dev/null +++ b/tests/component/stream_writers/test_analog_multi_channel_writer.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +import ctypes + +import numpy +import pytest + +import nidaqmx +from nidaqmx.stream_writers import AnalogMultiChannelWriter +from tests.component._analog_utils import ( + _get_expected_voltage_for_chan, + AO_VOLTAGE_EPSILON, +) + + +def test___analog_multi_channel_writer___write_one_sample___updates_output( + ao_multi_channel_task: nidaqmx.Task, + ai_multi_channel_loopback_task: nidaqmx.Task, +) -> None: + writer = AnalogMultiChannelWriter(ao_multi_channel_task.out_stream) + num_channels = ao_multi_channel_task.number_of_channels + expected = [_get_expected_voltage_for_chan(chan_index) for chan_index in range(num_channels)] + data = numpy.asarray(expected, dtype=numpy.float64) + + writer.write_one_sample(data) + + assert ai_multi_channel_loopback_task.read() == pytest.approx(expected, abs=AO_VOLTAGE_EPSILON) + + +def test___analog_multi_channel_writer___write_one_sample_with_wrong_dtype___raises_error_with_correct_dtype( + ao_multi_channel_task: nidaqmx.Task, +) -> None: + writer = AnalogMultiChannelWriter(ao_multi_channel_task.out_stream) + num_channels = ao_multi_channel_task.number_of_channels + data = numpy.full(num_channels, 0.0, dtype=numpy.float32) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + writer.write_one_sample(data) + + assert "float64" in exc_info.value.args[0] + + +def test___analog_multi_channel_writer___write_many_sample___updates_output( + ao_multi_channel_task: nidaqmx.Task, + ai_multi_channel_loopback_task: nidaqmx.Task, +) -> None: + writer = AnalogMultiChannelWriter(ao_multi_channel_task.out_stream) + num_channels = ao_multi_channel_task.number_of_channels + samples_to_write = 10 + expected = [_get_expected_voltage_for_chan(chan_index) for chan_index in range(num_channels)] + # sweep up to the expected values, the only one we'll validate + data = numpy.ascontiguousarray( + numpy.transpose( + numpy.linspace( + [0.0] * num_channels, + expected, + num=samples_to_write, + dtype=numpy.float64, + ) + ) + ) + + samples_written = writer.write_many_sample(data) + + assert samples_written == samples_to_write + assert ai_multi_channel_loopback_task.read() == pytest.approx(expected, abs=AO_VOLTAGE_EPSILON) + + +def test___analog_multi_channel_writer___write_many_sample_with_wrong_dtype___raises_error_with_correct_dtype( + ao_multi_channel_task: nidaqmx.Task, +) -> None: + writer = AnalogMultiChannelWriter(ao_multi_channel_task.out_stream) + num_channels = ao_multi_channel_task.number_of_channels + samples_to_write = 10 + data = numpy.full((num_channels, samples_to_write), 0.0, dtype=numpy.float32) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + _ = writer.write_many_sample(data) + + assert "float64" in exc_info.value.args[0] diff --git a/tests/component/stream_writers/test_analog_single_channel_writer.py b/tests/component/stream_writers/test_analog_single_channel_writer.py new file mode 100644 index 000000000..51b363a70 --- /dev/null +++ b/tests/component/stream_writers/test_analog_single_channel_writer.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +import ctypes + +import numpy +import pytest + +import nidaqmx +from nidaqmx.stream_writers import AnalogSingleChannelWriter +from tests.component._analog_utils import ( + _get_expected_voltage_for_chan, + AO_VOLTAGE_EPSILON, +) + + +def test___analog_single_channel_writer___write_one_sample___updates_output( + ao_single_channel_task: nidaqmx.Task, + ai_single_channel_loopback_task: nidaqmx.Task, +) -> None: + writer = AnalogSingleChannelWriter(ao_single_channel_task.out_stream) + expected = _get_expected_voltage_for_chan(0) + + writer.write_one_sample(expected) + + assert ai_single_channel_loopback_task.read() == pytest.approx(expected, abs=AO_VOLTAGE_EPSILON) + + +def test___analog_single_channel_writer___write_many_sample___updates_output( + ao_single_channel_task: nidaqmx.Task, + ai_single_channel_loopback_task: nidaqmx.Task, +) -> None: + writer = AnalogSingleChannelWriter(ao_single_channel_task.out_stream) + samples_to_write = 10 + expected = _get_expected_voltage_for_chan(0) + # sweep up to the expected value, the only one we'll validate + data = numpy.linspace(0.0, expected, num=samples_to_write, dtype=numpy.float64) + + samples_written = writer.write_many_sample(data) + + assert samples_written == samples_to_write + assert ai_single_channel_loopback_task.read() == pytest.approx(expected, abs=AO_VOLTAGE_EPSILON) + + +def test___analog_single_channel_writer___write_many_sample_with_wrong_dtype___raises_error_with_correct_dtype( + ao_single_channel_task: nidaqmx.Task, +) -> None: + writer = AnalogSingleChannelWriter(ao_single_channel_task.out_stream) + samples_to_write = 10 + expected = _get_expected_voltage_for_chan(0) + data = numpy.full(samples_to_write, expected, dtype=numpy.float32) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + _ = writer.write_many_sample(data) + + assert "float64" in exc_info.value.args[0] diff --git a/tests/component/stream_writers/test_analog_unscaled_writer.py b/tests/component/stream_writers/test_analog_unscaled_writer.py new file mode 100644 index 000000000..c677e4f95 --- /dev/null +++ b/tests/component/stream_writers/test_analog_unscaled_writer.py @@ -0,0 +1,174 @@ +from __future__ import annotations + +import ctypes + +import numpy +import pytest + +import nidaqmx +from nidaqmx.stream_writers import AnalogUnscaledWriter +from tests.component._analog_utils import ( + RAW_VOLTAGE_EPSILON, + _get_expected_voltage_for_chan, + _volts_to_codes, +) + + +def test___analog_unscaled_writer___write_int16___updates_output( + ao_multi_channel_task: nidaqmx.Task, + ai_multi_channel_loopback_task: nidaqmx.Task, +) -> None: + writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) + num_channels = ao_multi_channel_task.number_of_channels + samples_to_write = 10 + expected = [_get_expected_voltage_for_chan(chan_index) for chan_index in range(num_channels)] + # sweep up to the expected values, the only one we'll validate + data = numpy.ascontiguousarray( + numpy.transpose( + numpy.linspace( + [0] * num_channels, + [_volts_to_codes(v) for v in expected], + num=samples_to_write, + dtype=numpy.int16, + ) + ) + ) + + samples_written = writer.write_int16(data) + + assert samples_written == samples_to_write + assert ai_multi_channel_loopback_task.read() == pytest.approx(expected, abs=RAW_VOLTAGE_EPSILON) + + +def test___analog_unscaled_writer___write_int16_with_wrong_dtype___raises_error_with_correct_dtype( + ao_multi_channel_task: nidaqmx.Task, +) -> None: + writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) + num_channels = ao_multi_channel_task.number_of_channels + samples_to_write = 10 + data = numpy.full((num_channels, samples_to_write), 0.0, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + _ = writer.write_int16(data) + + assert "int16" in exc_info.value.args[0] + + +def test___analog_unscaled_writer___write_int32___updates_output( + ao_multi_channel_task: nidaqmx.Task, + ai_multi_channel_loopback_task: nidaqmx.Task, +) -> None: + writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) + num_channels = ao_multi_channel_task.number_of_channels + samples_to_write = 10 + expected = [_get_expected_voltage_for_chan(chan_index) for chan_index in range(num_channels)] + # sweep up to the expected values, the only one we'll validate + data = numpy.ascontiguousarray( + numpy.transpose( + numpy.linspace( + [0] * num_channels, + [_volts_to_codes(v) for v in expected], + num=samples_to_write, + dtype=numpy.int32, + ) + ) + ) + + samples_written = writer.write_int32(data) + + assert samples_written == samples_to_write + assert ai_multi_channel_loopback_task.read() == pytest.approx(expected, abs=RAW_VOLTAGE_EPSILON) + + +def test___analog_unscaled_writer___write_int32_with_wrong_dtype___raises_error_with_correct_dtype( + ao_multi_channel_task: nidaqmx.Task, +) -> None: + writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) + num_channels = ao_multi_channel_task.number_of_channels + samples_to_write = 10 + data = numpy.full((num_channels, samples_to_write), 0.0, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + _ = writer.write_int32(data) + + assert "int32" in exc_info.value.args[0] + + +def test___analog_unscaled_writer___write_uint16___updates_output( + ao_multi_channel_task: nidaqmx.Task, + ai_multi_channel_loopback_task: nidaqmx.Task, +) -> None: + writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) + num_channels = ao_multi_channel_task.number_of_channels + samples_to_write = 10 + expected = [_get_expected_voltage_for_chan(chan_index) for chan_index in range(num_channels)] + # sweep up to the expected values, the only one we'll validate + data = numpy.ascontiguousarray( + numpy.transpose( + numpy.linspace( + [0] * num_channels, + [_volts_to_codes(v) for v in expected], + num=samples_to_write, + dtype=numpy.uint16, + ) + ) + ) + + samples_written = writer.write_uint16(data) + + assert samples_written == samples_to_write + assert ai_multi_channel_loopback_task.read() == pytest.approx(expected, abs=RAW_VOLTAGE_EPSILON) + + +def test___analog_unscaled_writer___write_uint16_with_wrong_dtype___raises_error_with_correct_dtype( + ao_multi_channel_task: nidaqmx.Task, +) -> None: + writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) + num_channels = ao_multi_channel_task.number_of_channels + samples_to_write = 10 + data = numpy.full((num_channels, samples_to_write), 0.0, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + _ = writer.write_uint16(data) + + assert "uint16" in exc_info.value.args[0] + + +def test___analog_unscaled_writer___write_uint32___updates_output( + ao_multi_channel_task: nidaqmx.Task, + ai_multi_channel_loopback_task: nidaqmx.Task, +) -> None: + writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) + num_channels = ao_multi_channel_task.number_of_channels + samples_to_write = 10 + expected = [_get_expected_voltage_for_chan(chan_index) for chan_index in range(num_channels)] + # sweep up to the expected values, the only one we'll validate + data = numpy.ascontiguousarray( + numpy.transpose( + numpy.linspace( + [0] * num_channels, + [_volts_to_codes(v) for v in expected], + num=samples_to_write, + dtype=numpy.uint32, + ) + ) + ) + + samples_written = writer.write_uint32(data) + + assert samples_written == samples_to_write + assert ai_multi_channel_loopback_task.read() == pytest.approx(expected, abs=RAW_VOLTAGE_EPSILON) + + +def test___analog_unscaled_writer___write_uint32_with_wrong_dtype___raises_error_with_correct_dtype( + ao_multi_channel_task: nidaqmx.Task, +) -> None: + writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) + num_channels = ao_multi_channel_task.number_of_channels + samples_to_write = 10 + data = numpy.full((num_channels, samples_to_write), 0.0, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + _ = writer.write_uint32(data) + + assert "uint32" in exc_info.value.args[0] diff --git a/tests/component/stream_writers/test_digital_multi_channel_writer.py b/tests/component/stream_writers/test_digital_multi_channel_writer.py new file mode 100644 index 000000000..a7c67044a --- /dev/null +++ b/tests/component/stream_writers/test_digital_multi_channel_writer.py @@ -0,0 +1,265 @@ +from __future__ import annotations + +import ctypes +import math +from typing import Callable + +import numpy +import pytest + +import nidaqmx +import nidaqmx.system +from nidaqmx.constants import LineGrouping +from nidaqmx.stream_writers import DigitalMultiChannelWriter +from tests.component._digital_utils import ( + _get_digital_data, + _get_digital_port_data_for_sample, + _get_digital_port_data_port_major, + _get_digital_port_data_sample_major, + _get_num_do_lines_in_task, + _int_to_bool_array, + _start_do_task, +) + + +def test___digital_multi_channel_writer___write_one_sample_one_line___updates_output( + do_single_line_task: nidaqmx.Task, + di_single_line_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_single_line_task.out_stream) + num_lines = _get_num_do_lines_in_task(do_single_line_task) + samples_to_write = 256 + + # "sweep" up to the final value, the only one we'll validate + for datum in _get_digital_data(num_lines, samples_to_write): + writer.write_one_sample_one_line(_int_to_bool_array(num_lines, datum)) + + assert di_single_line_loopback_task.read() == datum + + +def test___digital_multi_channel_writer___write_one_sample_multi_line___updates_output( + do_multi_channel_multi_line_task: nidaqmx.Task, + di_multi_line_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_multi_channel_multi_line_task.out_stream) + num_channels = do_multi_channel_multi_line_task.number_of_channels + samples_to_write = 256 + + # "sweep" up to the final value, the only one we'll validate + for datum in _get_digital_data(num_channels, samples_to_write): + data_to_write = _int_to_bool_array(num_channels, datum).reshape((num_channels, 1)) + writer.write_one_sample_multi_line(data_to_write) + + assert di_multi_line_loopback_task.read() == datum + + +def test___digital_multi_channel_writer___write_one_sample_multi_line_jagged___updates_output( + di_port0_loopback_task_32dio: nidaqmx.Task, + di_port1_loopback_task_32dio: nidaqmx.Task, + di_port2_loopback_task_32dio: nidaqmx.Task, + generate_task: Callable[[], nidaqmx.Task], + real_x_series_device_32dio: nidaqmx.system.Device, +) -> None: + task = generate_task() + for port in real_x_series_device_32dio.do_ports: + task.do_channels.add_do_chan( + port.name, + line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, + ) + _start_do_task(task, is_port=True, num_chans=task.number_of_channels) + writer = DigitalMultiChannelWriter(task.out_stream) + num_channels = task.number_of_channels + samples_to_write = 0xA5 + + # "sweep" up to the final value, the only one we'll validate + for datum in _get_digital_data(num_channels * 32, samples_to_write): + data_to_write = _int_to_bool_array(num_channels * 32, datum).reshape((num_channels, 32)) + writer.write_one_sample_multi_line(data_to_write) + + assert di_port0_loopback_task_32dio.read() == datum & 0xFFFFFFFF + assert di_port1_loopback_task_32dio.read() == (datum >> 32) & 0xFF + assert di_port2_loopback_task_32dio.read() == (datum >> 64) & 0xFF + + +def test___digital_multi_channel_writer___write_one_sample_multi_line_with_wrong_dtype___raises_error_with_correct_dtype( + do_multi_channel_multi_line_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_multi_channel_multi_line_task.out_stream) + num_channels = do_multi_channel_multi_line_task.number_of_channels + sample = numpy.full((num_channels, 1), math.inf, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + writer.write_one_sample_multi_line(sample) + + assert "bool" in exc_info.value.args[0] + + +def test___digital_multi_channel_writer___write_one_sample_port_byte___updates_output( + do_multi_channel_port_task: nidaqmx.Task, + di_multi_channel_port_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) + samples_to_write = 256 + + # "sweep" up to the final value, the only one we'll validate + for datum in _get_digital_port_data_sample_major(do_multi_channel_port_task, samples_to_write): + writer.write_one_sample_port_byte(numpy.array(datum, dtype=numpy.uint8)) + + assert di_multi_channel_port_loopback_task.read() == datum + + +def test___digital_multi_channel_writer___write_one_sample_port_byte_with_wrong_dtype___raises_error_with_correct_dtype( + do_multi_channel_port_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) + num_channels = do_multi_channel_port_task.number_of_channels + data = numpy.full(num_channels, math.inf, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + writer.write_one_sample_port_byte(data) + + assert "uint8" in exc_info.value.args[0] + + +def test___digital_multi_channel_writer___write_many_sample_port_byte___updates_output( + do_multi_channel_port_task: nidaqmx.Task, + di_multi_channel_port_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) + samples_to_write = 256 + + # "sweep" up to the final value, the only one we'll validate + data = _get_digital_port_data_port_major(do_multi_channel_port_task, samples_to_write) + writer.write_many_sample_port_byte(numpy.array(data, dtype=numpy.uint8)) + + assert di_multi_channel_port_loopback_task.read() == _get_digital_port_data_for_sample( + do_multi_channel_port_task, samples_to_write - 1 + ) + + +def test___digital_multi_channel_writer___write_many_sample_port_byte_with_wrong_dtype___raises_error_with_correct_dtype( + do_multi_channel_port_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) + num_channels = do_multi_channel_port_task.number_of_channels + samples_to_write = 256 + data = numpy.full((num_channels, samples_to_write), math.inf, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + writer.write_many_sample_port_byte(data) + + assert "uint8" in exc_info.value.args[0] + + +def test___digital_multi_channel_writer___write_one_sample_port_uint16___updates_output( + do_multi_channel_port_task: nidaqmx.Task, + di_multi_channel_port_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) + samples_to_write = 256 + + # "sweep" up to the final value, the only one we'll validate + for datum in _get_digital_port_data_sample_major(do_multi_channel_port_task, samples_to_write): + writer.write_one_sample_port_uint16(numpy.array(datum, dtype=numpy.uint16)) + + assert di_multi_channel_port_loopback_task.read() == datum + + +def test___digital_multi_channel_writer___write_one_sample_port_uint16_with_wrong_dtype___raises_error_with_correct_dtype( + do_multi_channel_port_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) + num_channels = do_multi_channel_port_task.number_of_channels + data = numpy.full(num_channels, math.inf, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + writer.write_one_sample_port_uint16(data) + + assert "uint16" in exc_info.value.args[0] + + +def test___digital_multi_channel_writer___write_many_sample_port_uint16___updates_output( + do_multi_channel_port_task: nidaqmx.Task, + di_multi_channel_port_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) + samples_to_write = 256 + + # "sweep" up to the final value, the only one we'll validate + data = _get_digital_port_data_port_major(do_multi_channel_port_task, samples_to_write) + writer.write_many_sample_port_uint16(numpy.array(data, dtype=numpy.uint16)) + + assert di_multi_channel_port_loopback_task.read() == _get_digital_port_data_for_sample( + do_multi_channel_port_task, samples_to_write - 1 + ) + + +def test___digital_multi_channel_writer___write_many_sample_port_uint16_with_wrong_dtype___raises_error_with_correct_dtype( + do_multi_channel_port_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) + num_channels = do_multi_channel_port_task.number_of_channels + samples_to_write = 256 + data = numpy.full((num_channels, samples_to_write), math.inf, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + writer.write_many_sample_port_uint16(data) + + assert "uint16" in exc_info.value.args[0] + + +def test___digital_multi_channel_writer___write_one_sample_port_uint32___updates_output( + do_multi_channel_port_task: nidaqmx.Task, + di_multi_channel_port_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) + samples_to_write = 256 + + # "sweep" up to the final value, the only one we'll validate + for datum in _get_digital_port_data_sample_major(do_multi_channel_port_task, samples_to_write): + writer.write_one_sample_port_uint32(numpy.array(datum, dtype=numpy.uint32)) + + assert di_multi_channel_port_loopback_task.read() == datum + + +def test___digital_multi_channel_writer___write_one_sample_port_uint32_with_wrong_dtype___raises_error_with_correct_dtype( + do_multi_channel_port_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) + num_channels = do_multi_channel_port_task.number_of_channels + data = numpy.full(num_channels, math.inf, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + writer.write_one_sample_port_uint32(data) + + assert "uint32" in exc_info.value.args[0] + + +def test___digital_multi_channel_writer___write_many_sample_port_uint32___updates_output( + do_multi_channel_port_task: nidaqmx.Task, + di_multi_channel_port_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) + samples_to_write = 256 + + # "sweep" up to the final value, the only one we'll validate + data = _get_digital_port_data_port_major(do_multi_channel_port_task, samples_to_write) + writer.write_many_sample_port_uint32(numpy.array(data, dtype=numpy.uint32)) + + assert di_multi_channel_port_loopback_task.read() == _get_digital_port_data_for_sample( + do_multi_channel_port_task, samples_to_write - 1 + ) + + +def test___digital_multi_channel_writer___write_many_sample_port_uint32_with_wrong_dtype___raises_error_with_correct_dtype( + do_multi_channel_port_task: nidaqmx.Task, +) -> None: + writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) + num_channels = do_multi_channel_port_task.number_of_channels + samples_to_write = 256 + data = numpy.full((num_channels, samples_to_write), math.inf, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + writer.write_many_sample_port_uint32(data) + + assert "uint32" in exc_info.value.args[0] diff --git a/tests/component/stream_writers/test_digital_single_channel_writer.py b/tests/component/stream_writers/test_digital_single_channel_writer.py new file mode 100644 index 000000000..2c49be72b --- /dev/null +++ b/tests/component/stream_writers/test_digital_single_channel_writer.py @@ -0,0 +1,184 @@ +from __future__ import annotations + +import ctypes +import math + +import numpy +import pytest + +import nidaqmx +from nidaqmx.stream_writers import DigitalSingleChannelWriter +from tests.component._digital_utils import ( + _get_digital_data, + _get_num_do_lines_in_task, + _int_to_bool_array, +) + + +def test___digital_single_channel_writer___write_one_sample_one_line___updates_output( + do_single_line_task: nidaqmx.Task, + di_single_line_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalSingleChannelWriter(do_single_line_task.out_stream) + sample = True + + writer.write_one_sample_one_line(sample) + + assert di_single_line_loopback_task.read() == sample + + +def test___digital_single_channel_writer___write_one_sample_multi_line___updates_output( + do_single_channel_multi_line_task: nidaqmx.Task, + di_multi_line_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalSingleChannelWriter(do_single_channel_multi_line_task.out_stream) + num_lines = _get_num_do_lines_in_task(do_single_channel_multi_line_task) + samples_to_write = 256 + + # "sweep" up to the final value, the only one we'll validate + for datum in _get_digital_data(num_lines, samples_to_write): + writer.write_one_sample_multi_line(_int_to_bool_array(num_lines, datum)) + + assert di_multi_line_loopback_task.read() == datum + + +def test___digital_single_channel_writer___write_one_sample_multi_line_with_wrong_dtype___raises_error_with_correct_dtype( + do_single_channel_multi_line_task: nidaqmx.Task, +) -> None: + writer = DigitalSingleChannelWriter(do_single_channel_multi_line_task.out_stream) + num_lines = _get_num_do_lines_in_task(do_single_channel_multi_line_task) + sample = numpy.full(num_lines, math.inf, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + writer.write_one_sample_multi_line(sample) + + assert "bool" in exc_info.value.args[0] + + +def test___digital_single_channel_writer___write_one_sample_port_byte___updates_output( + do_port1_task: nidaqmx.Task, + di_port1_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalSingleChannelWriter(do_port1_task.out_stream) + num_lines = _get_num_do_lines_in_task(do_port1_task) + samples_to_write = 256 + + # "sweep" up to the final value, the only one we'll validate + for datum in _get_digital_data(num_lines, samples_to_write): + writer.write_one_sample_port_byte(datum) + + assert di_port1_loopback_task.read() == datum + + +def test___digital_single_channel_writer___write_many_sample_port_byte___updates_output( + do_port1_task: nidaqmx.Task, + di_port1_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalSingleChannelWriter(do_port1_task.out_stream) + num_lines = _get_num_do_lines_in_task(do_port1_task) + samples_to_write = 256 + data = numpy.array(_get_digital_data(num_lines, samples_to_write), dtype=numpy.uint8) + + # "sweep" up to the final value, the only one we'll validate + writer.write_many_sample_port_byte(data) + + assert di_port1_loopback_task.read() == data[-1] + + +def test___digital_single_channel_writer___write_many_sample_port_byte_with_wrong_dtype___raises_error_with_correct_dtype( + do_port1_task: nidaqmx.Task, +) -> None: + writer = DigitalSingleChannelWriter(do_port1_task.out_stream) + samples_to_write = 256 + data = numpy.full(samples_to_write, math.inf, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + writer.write_many_sample_port_byte(data) + + assert "uint8" in exc_info.value.args[0] + + +def test___digital_single_channel_writer___write_one_sample_port_uint16___updates_output( + do_port1_task: nidaqmx.Task, + di_port1_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalSingleChannelWriter(do_port1_task.out_stream) + num_lines = _get_num_do_lines_in_task(do_port1_task) + samples_to_write = 256 + + # "sweep" up to the final value, the only one we'll validate + for datum in _get_digital_data(num_lines, samples_to_write): + writer.write_one_sample_port_uint16(datum) + + assert di_port1_loopback_task.read() == datum + + +def test___digital_single_channel_writer___write_many_sample_port_uint16___updates_output( + do_port1_task: nidaqmx.Task, + di_port1_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalSingleChannelWriter(do_port1_task.out_stream) + num_lines = _get_num_do_lines_in_task(do_port1_task) + samples_to_write = 256 + data = numpy.array(_get_digital_data(num_lines, samples_to_write), dtype=numpy.uint16) + + # "sweep" up to the final value, the only one we'll validate + writer.write_many_sample_port_uint16(data) + + assert di_port1_loopback_task.read() == data[-1] + + +def test___digital_single_channel_writer___write_many_sample_port_uint16_with_wrong_dtype___raises_error_with_correct_dtype( + do_port1_task: nidaqmx.Task, +) -> None: + writer = DigitalSingleChannelWriter(do_port1_task.out_stream) + samples_to_write = 256 + data = numpy.full(samples_to_write, math.inf, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + writer.write_many_sample_port_uint16(data) + + assert "uint16" in exc_info.value.args[0] + + +def test___digital_single_channel_writer___write_one_sample_port_uint32___updates_output( + do_port0_task: nidaqmx.Task, + di_port0_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalSingleChannelWriter(do_port0_task.out_stream) + num_lines = _get_num_do_lines_in_task(do_port0_task) + samples_to_write = 256 + + # "sweep" up to the final value, the only one we'll validate + for datum in _get_digital_data(num_lines, samples_to_write): + writer.write_one_sample_port_uint32(datum) + + assert di_port0_loopback_task.read() == datum + + +def test___digital_single_channel_writer___write_many_sample_port_uint32___updates_output( + do_port0_task: nidaqmx.Task, + di_port0_loopback_task: nidaqmx.Task, +) -> None: + writer = DigitalSingleChannelWriter(do_port0_task.out_stream) + num_lines = _get_num_do_lines_in_task(do_port0_task) + samples_to_write = 256 + data = numpy.array(_get_digital_data(num_lines, samples_to_write), dtype=numpy.uint32) + + # "sweep" up to the final value, the only one we'll validate + writer.write_many_sample_port_uint32(data) + + assert di_port0_loopback_task.read() == data[-1] + + +def test___digital_single_channel_writer___write_many_sample_port_uint32_with_wrong_dtype___raises_error_with_correct_dtype( + do_port0_task: nidaqmx.Task, +) -> None: + writer = DigitalSingleChannelWriter(do_port0_task.out_stream) + samples_to_write = 256 + data = numpy.full(samples_to_write, math.inf, dtype=numpy.float64) + + with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: + writer.write_many_sample_port_uint32(data) + + assert "uint32" in exc_info.value.args[0] diff --git a/tests/component/task/test_task_read_ai.py b/tests/component/task/test_task_read_ai.py index c8809f4d7..614f0cac1 100644 --- a/tests/component/task/test_task_read_ai.py +++ b/tests/component/task/test_task_read_ai.py @@ -4,9 +4,9 @@ import nidaqmx from nidaqmx.constants import AcquisitionType -from tests.component.conftest import ( +from tests.component._analog_utils import ( POWER_EPSILON, - VOLTAGE_EPSILON, + AI_VOLTAGE_EPSILON, _assert_equal_2d, _get_current_setpoint_for_chan, _get_voltage_offset_for_chan, @@ -20,7 +20,7 @@ def test___analog_single_channel___read_unset_samples___returns_valid_scalar( data = ai_single_channel_task.read() expected = _get_voltage_offset_for_chan(0) - assert data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) def test___analog_single_channel___read_one_sample___returns_valid_1d_samples( @@ -29,7 +29,7 @@ def test___analog_single_channel___read_one_sample___returns_valid_1d_samples( data = ai_single_channel_task.read(1) expected = [_get_voltage_offset_for_chan(0)] - assert data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) def test___analog_single_channel___read_many_sample___returns_valid_1d_samples( @@ -40,7 +40,7 @@ def test___analog_single_channel___read_many_sample___returns_valid_1d_samples( data = ai_single_channel_task.read(samples_to_read) expected = [_get_voltage_offset_for_chan(0) for _ in range(samples_to_read)] - assert data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) def test___analog_single_channel_finite___read_too_many_sample___returns_valid_1d_samples_truncated( @@ -55,7 +55,7 @@ def test___analog_single_channel_finite___read_too_many_sample___returns_valid_1 data = ai_single_channel_task.read(samples_to_read) expected = [_get_voltage_offset_for_chan(0) for _ in range(samples_to_acquire)] - assert data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) def test___analog_multi_channel___read_unset_samples___returns_1d_channels( @@ -66,7 +66,7 @@ def test___analog_multi_channel___read_unset_samples___returns_1d_channels( data = ai_multi_channel_task.read() expected = [_get_voltage_offset_for_chan(chan_index) for chan_index in range(num_channels)] - assert data == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert data == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) def test___analog_multi_channel___read_one_sample___returns_valid_2d_channels_samples( @@ -77,7 +77,7 @@ def test___analog_multi_channel___read_one_sample___returns_valid_2d_channels_sa data = ai_multi_channel_task.read(1) expected = [[_get_voltage_offset_for_chan(chan_index)] for chan_index in range(num_channels)] - _assert_equal_2d(data, expected, abs=VOLTAGE_EPSILON) + _assert_equal_2d(data, expected, abs=AI_VOLTAGE_EPSILON) def test___analog_multi_channel___read_many_sample___returns_valid_2d_channels_samples( @@ -92,7 +92,7 @@ def test___analog_multi_channel___read_many_sample___returns_valid_2d_channels_s [_get_voltage_offset_for_chan(chan_index) for _ in range(samples_to_read)] for chan_index in range(num_channels) ] - _assert_equal_2d(data, expected, abs=VOLTAGE_EPSILON) + _assert_equal_2d(data, expected, abs=AI_VOLTAGE_EPSILON) @pytest.mark.xfail( @@ -115,7 +115,7 @@ def test___analog_multi_channel_finite___read_too_many_sample___returns_valid_2d [_get_voltage_offset_for_chan(chan_index) for _ in range(samples_to_acquire)] for chan_index in range(num_channels) ] - _assert_equal_2d(data, expected, abs=VOLTAGE_EPSILON) + _assert_equal_2d(data, expected, abs=AI_VOLTAGE_EPSILON) def test___power_single_channel___read_unset_samples___returns_valid_scalar( diff --git a/tests/component/task/test_task_read_waveform_ai.py b/tests/component/task/test_task_read_waveform_ai.py index 687cd8547..d017f7a91 100644 --- a/tests/component/task/test_task_read_waveform_ai.py +++ b/tests/component/task/test_task_read_waveform_ai.py @@ -4,8 +4,8 @@ from nitypes.waveform import AnalogWaveform import nidaqmx -from tests.component.conftest import ( - VOLTAGE_EPSILON, +from tests.component._analog_utils import ( + AI_VOLTAGE_EPSILON, _get_voltage_offset_for_chan, ) @@ -19,7 +19,7 @@ def test___analog_single_channel___read_waveform___returns_valid_waveform( assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(0) assert waveform.sample_count == 50 - assert waveform.raw_data[0] == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.raw_data[0] == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) @pytest.mark.grpc_skip(reason="read_analog_waveform not implemented in GRPC") @@ -31,7 +31,7 @@ def test___analog_single_channel___read_waveform_one_sample___returns_waveform_w assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(0) assert waveform.sample_count == 1 - assert waveform.raw_data[0] == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.raw_data[0] == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) @pytest.mark.grpc_skip(reason="read_analog_waveform not implemented in GRPC") @@ -45,7 +45,7 @@ def test___analog_single_channel___read_waveform_many_sample___returns_waveform_ assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(0) assert waveform.sample_count == samples_to_read - assert waveform.raw_data[0] == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.raw_data[0] == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) @pytest.mark.grpc_skip(reason="read_analog_waveform not implemented in GRPC") @@ -60,7 +60,7 @@ def test___analog_single_channel_finite___read_waveform_too_many_samples___retur assert isinstance(waveform, AnalogWaveform) expected = _get_voltage_offset_for_chan(0) assert waveform.sample_count == samples_available - assert waveform.raw_data[0] == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.raw_data[0] == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) @pytest.mark.grpc_skip(reason="read_analog_waveform not implemented in GRPC") @@ -77,7 +77,7 @@ def test___analog_multi_channel___read_waveform___returns_valid_waveforms( for chan_index, waveform in enumerate(waveforms): expected = _get_voltage_offset_for_chan(chan_index) assert waveform.sample_count == 50 - assert waveform.raw_data[0] == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.raw_data[0] == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) @pytest.mark.grpc_skip(reason="read_analog_waveform not implemented in GRPC") @@ -94,7 +94,7 @@ def test___analog_multi_channel___read_waveform_one_sample___returns_waveforms_w for chan_index, waveform in enumerate(waveforms): expected = _get_voltage_offset_for_chan(chan_index) assert waveform.sample_count == 1 - assert waveform.raw_data[0] == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.raw_data[0] == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) @pytest.mark.grpc_skip(reason="read_analog_waveform not implemented in GRPC") @@ -112,7 +112,7 @@ def test___analog_multi_channel___read_waveform_many_samples___returns_waveforms for chan_index, waveform in enumerate(waveforms): expected = _get_voltage_offset_for_chan(chan_index) assert waveform.sample_count == samples_to_read - assert waveform.raw_data[0] == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.raw_data[0] == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) @pytest.mark.grpc_skip(reason="read_analog_waveform not implemented in GRPC") @@ -130,4 +130,4 @@ def test___analog_multi_channel_finite___read_waveform_too_many_samples___return for chan_index, waveform in enumerate(waveforms): expected = _get_voltage_offset_for_chan(chan_index) assert waveform.sample_count == samples_available - assert waveform.raw_data[0] == pytest.approx(expected, abs=VOLTAGE_EPSILON) + assert waveform.raw_data[0] == pytest.approx(expected, abs=AI_VOLTAGE_EPSILON) diff --git a/tests/component/task/test_task_read_waveform_di.py b/tests/component/task/test_task_read_waveform_di.py index 657748b60..2b4872e83 100644 --- a/tests/component/task/test_task_read_waveform_di.py +++ b/tests/component/task/test_task_read_waveform_di.py @@ -5,7 +5,7 @@ import nidaqmx import nidaqmx.system -from tests.component.conftest import ( +from tests.component._digital_utils import ( _get_expected_data_for_line, _get_waveform_data, ) diff --git a/tests/component/test_stream_writers.py b/tests/component/test_stream_writers.py deleted file mode 100644 index f7770cf85..000000000 --- a/tests/component/test_stream_writers.py +++ /dev/null @@ -1,77 +0,0 @@ -import ctypes - -import numpy -import pytest - -import nidaqmx -import nidaqmx.system -from nidaqmx.stream_writers import AnalogMultiChannelWriter, AnalogSingleChannelWriter - - -@pytest.fixture -def ao_single_channel_task( - task: nidaqmx.Task, sim_6363_device: nidaqmx.system.Device -) -> nidaqmx.Task: - task.ao_channels.add_ao_voltage_chan(sim_6363_device.ao_physical_chans[0].name) - return task - - -@pytest.fixture -def ao_multi_channel_task( - task: nidaqmx.Task, sim_6363_device: nidaqmx.system.Device -) -> nidaqmx.Task: - task.ao_channels.add_ao_voltage_chan(sim_6363_device.ao_physical_chans[0].name) - task.ao_channels.add_ao_voltage_chan(sim_6363_device.ao_physical_chans[1].name) - return task - - -def test___analog_single_channel_writer___write_many_sample___returns_samples_written( - ao_single_channel_task: nidaqmx.Task, -): - writer = AnalogSingleChannelWriter(ao_single_channel_task.in_stream, auto_start=True) - samples_to_write = 10 - data = numpy.full(samples_to_write, 1.234, dtype=numpy.float64) - - samples_written = writer.write_many_sample(data) - - assert samples_written == samples_to_write - - -def test___analog_single_channel_writer___write_many_sample_with_wrong_dtype___raises_error_with_correct_dtype( - ao_single_channel_task: nidaqmx.Task, -): - writer = AnalogSingleChannelWriter(ao_single_channel_task.in_stream, auto_start=True) - samples_to_write = 10 - data = numpy.full(samples_to_write, 1.234, dtype=numpy.float32) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - _ = writer.write_many_sample(data) - - assert "float64" in exc_info.value.args[0] - - -def test___analog_multi_channel_writer___write_many_sample___returns_samples_written( - ao_multi_channel_task: nidaqmx.Task, -): - writer = AnalogMultiChannelWriter(ao_multi_channel_task.in_stream, auto_start=True) - num_channels = ao_multi_channel_task.number_of_channels - samples_to_write = 10 - data = numpy.full((num_channels, samples_to_write), 1.234, dtype=numpy.float64) - - samples_written = writer.write_many_sample(data) - - assert samples_written == samples_to_write - - -def test___analog_multi_channel_writer___write_many_sample_with_wrong_dtype___raises_error_with_correct_dtype( - ao_multi_channel_task: nidaqmx.Task, -): - writer = AnalogMultiChannelWriter(ao_multi_channel_task.in_stream, auto_start=True) - num_channels = ao_multi_channel_task.number_of_channels - samples_to_write = 10 - data = numpy.full((num_channels, samples_to_write), 1.234, dtype=numpy.float32) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - _ = writer.write_many_sample(data) - - assert "float64" in exc_info.value.args[0] diff --git a/tests/component/test_stream_writers_ao.py b/tests/component/test_stream_writers_ao.py deleted file mode 100644 index bef2e3e49..000000000 --- a/tests/component/test_stream_writers_ao.py +++ /dev/null @@ -1,400 +0,0 @@ -from __future__ import annotations - -import ctypes -from typing import Callable - -import numpy -import pytest - -import nidaqmx -import nidaqmx.system -from nidaqmx.stream_writers import ( - AnalogMultiChannelWriter, - AnalogSingleChannelWriter, - AnalogUnscaledWriter, -) - - -def _get_expected_voltage_for_chan(chan_index: int) -> float: - return float(chan_index + 1) - - -def _volts_to_codes(volts: float, max_code: int = 32767, max_voltage: float = 10.1) -> int: - return int(volts * max_code / max_voltage) - - -VOLTAGE_EPSILON = 1e-2 -# NOTE: You can't scale from volts to codes correctly without knowing the internal calibration -# constants. The internal reference has a healthy amount of overrange to ensure we can calibrate to -# device specifications. I've used 10.1 volts above to approximate that, but 100mv of accuracy is -# also fine since the expected output of each channel value will be 1 volt apart. -VOLTAGE_EPSILON_FOR_RAW = 1e-1 - - -@pytest.fixture -def ao_single_channel_task( - generate_task: Callable[[], nidaqmx.Task], - real_x_series_multiplexed_device: nidaqmx.system.Device, -) -> nidaqmx.Task: - task = generate_task() - chan_index = 0 - offset = _get_expected_voltage_for_chan(chan_index) - chan = task.ao_channels.add_ao_voltage_chan( - real_x_series_multiplexed_device.ao_physical_chans[chan_index].name, - min_val=0.0, - max_val=offset + VOLTAGE_EPSILON, - ) - # forcing the maximum range for binary read scaling to be predictable - chan.ao_dac_rng_high = 10 - chan.ao_dac_rng_low = -10 - - # we'll be doing simple on-demand, so start the task now - task.start() - - # set the output to a known initial value - task.write(0.0) - - return task - - -@pytest.fixture -def ai_single_channel_loopback_task( - generate_task: Callable[[], nidaqmx.Task], - real_x_series_multiplexed_device: nidaqmx.system.Device, -) -> nidaqmx.Task: - task = generate_task() - chan_index = 0 - task.ai_channels.add_ai_voltage_chan( - f"{real_x_series_multiplexed_device.name}/_ao{chan_index}_vs_aognd", - min_val=-10, - max_val=10, - ) - - # we'll be doing simple on-demand, so start the task now - task.start() - - return task - - -@pytest.fixture -def ao_multi_channel_task( - generate_task: Callable[[], nidaqmx.Task], - real_x_series_multiplexed_device: nidaqmx.system.Device, -) -> nidaqmx.Task: - task = generate_task() - num_chans = 2 - for chan_index in range(num_chans): - offset = _get_expected_voltage_for_chan(chan_index) - chan = task.ao_channels.add_ao_voltage_chan( - real_x_series_multiplexed_device.ao_physical_chans[chan_index].name, - min_val=0.0, - max_val=offset + VOLTAGE_EPSILON, - ) - # forcing the maximum range for binary read scaling to be predictable - chan.ao_dac_rng_high = 10 - chan.ao_dac_rng_low = -10 - - # we'll be doing simple on-demand, so start the task now - task.start() - - # set the output to a known initial value - task.write([0.0] * num_chans) - - return task - - -@pytest.fixture -def ai_multi_channel_loopback_task( - generate_task: Callable[[], nidaqmx.Task], - real_x_series_multiplexed_device: nidaqmx.system.Device, -) -> nidaqmx.Task: - task = generate_task() - num_chans = 2 - for chan_index in range(num_chans): - task.ai_channels.add_ai_voltage_chan( - f"{real_x_series_multiplexed_device.name}/_ao{chan_index}_vs_aognd", - min_val=-10, - max_val=10, - ) - - # we'll be doing simple on-demand, so start the task now - task.start() - - return task - - -def test___analog_single_channel_writer___write_one_sample___updates_output( - ao_single_channel_task: nidaqmx.Task, - ai_single_channel_loopback_task: nidaqmx.Task, -) -> None: - writer = AnalogSingleChannelWriter(ao_single_channel_task.out_stream) - expected = _get_expected_voltage_for_chan(0) - - writer.write_one_sample(expected) - - assert ai_single_channel_loopback_task.read() == pytest.approx(expected, abs=VOLTAGE_EPSILON) - - -def test___analog_single_channel_writer___write_many_sample___updates_output( - ao_single_channel_task: nidaqmx.Task, - ai_single_channel_loopback_task: nidaqmx.Task, -) -> None: - writer = AnalogSingleChannelWriter(ao_single_channel_task.out_stream) - samples_to_write = 10 - expected = _get_expected_voltage_for_chan(0) - # sweep up to the expected value, the only one we'll validate - data = numpy.linspace(0.0, expected, num=samples_to_write, dtype=numpy.float64) - - samples_written = writer.write_many_sample(data) - - assert samples_written == samples_to_write - assert ai_single_channel_loopback_task.read() == pytest.approx(expected, abs=VOLTAGE_EPSILON) - - -def test___analog_single_channel_writer___write_many_sample_with_wrong_dtype___raises_error_with_correct_dtype( - ao_single_channel_task: nidaqmx.Task, -) -> None: - writer = AnalogSingleChannelWriter(ao_single_channel_task.out_stream) - samples_to_write = 10 - expected = _get_expected_voltage_for_chan(0) - data = numpy.full(samples_to_write, expected, dtype=numpy.float32) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - _ = writer.write_many_sample(data) - - assert "float64" in exc_info.value.args[0] - - -def test___analog_multi_channel_writer___write_one_sample___updates_output( - ao_multi_channel_task: nidaqmx.Task, - ai_multi_channel_loopback_task: nidaqmx.Task, -) -> None: - writer = AnalogMultiChannelWriter(ao_multi_channel_task.out_stream) - num_channels = ao_multi_channel_task.number_of_channels - expected = [_get_expected_voltage_for_chan(chan_index) for chan_index in range(num_channels)] - data = numpy.asarray(expected, dtype=numpy.float64) - - writer.write_one_sample(data) - - assert ai_multi_channel_loopback_task.read() == pytest.approx(expected, abs=VOLTAGE_EPSILON) - - -def test___analog_multi_channel_writer___write_one_sample_with_wrong_dtype___raises_error_with_correct_dtype( - ao_multi_channel_task: nidaqmx.Task, -) -> None: - writer = AnalogMultiChannelWriter(ao_multi_channel_task.out_stream) - num_channels = ao_multi_channel_task.number_of_channels - data = numpy.full(num_channels, 0.0, dtype=numpy.float32) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - writer.write_one_sample(data) - - assert "float64" in exc_info.value.args[0] - - -def test___analog_multi_channel_writer___write_many_sample___updates_output( - ao_multi_channel_task: nidaqmx.Task, - ai_multi_channel_loopback_task: nidaqmx.Task, -) -> None: - writer = AnalogMultiChannelWriter(ao_multi_channel_task.out_stream) - num_channels = ao_multi_channel_task.number_of_channels - samples_to_write = 10 - expected = [_get_expected_voltage_for_chan(chan_index) for chan_index in range(num_channels)] - # sweep up to the expected values, the only one we'll validate - data = numpy.ascontiguousarray( - numpy.transpose( - numpy.linspace( - [0.0] * num_channels, - expected, - num=samples_to_write, - dtype=numpy.float64, - ) - ) - ) - - samples_written = writer.write_many_sample(data) - - assert samples_written == samples_to_write - assert ai_multi_channel_loopback_task.read() == pytest.approx(expected, abs=VOLTAGE_EPSILON) - - -def test___analog_multi_channel_writer___write_many_sample_with_wrong_dtype___raises_error_with_correct_dtype( - ao_multi_channel_task: nidaqmx.Task, -) -> None: - writer = AnalogMultiChannelWriter(ao_multi_channel_task.out_stream) - num_channels = ao_multi_channel_task.number_of_channels - samples_to_write = 10 - data = numpy.full((num_channels, samples_to_write), 0.0, dtype=numpy.float32) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - _ = writer.write_many_sample(data) - - assert "float64" in exc_info.value.args[0] - - -def test___analog_unscaled_writer___write_int16___updates_output( - ao_multi_channel_task: nidaqmx.Task, - ai_multi_channel_loopback_task: nidaqmx.Task, -) -> None: - writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) - num_channels = ao_multi_channel_task.number_of_channels - samples_to_write = 10 - expected = [_get_expected_voltage_for_chan(chan_index) for chan_index in range(num_channels)] - # sweep up to the expected values, the only one we'll validate - data = numpy.ascontiguousarray( - numpy.transpose( - numpy.linspace( - [0] * num_channels, - [_volts_to_codes(v) for v in expected], - num=samples_to_write, - dtype=numpy.int16, - ) - ) - ) - - samples_written = writer.write_int16(data) - - assert samples_written == samples_to_write - assert ai_multi_channel_loopback_task.read() == pytest.approx( - expected, abs=VOLTAGE_EPSILON_FOR_RAW - ) - - -def test___analog_unscaled_writer___write_int16_with_wrong_dtype___raises_error_with_correct_dtype( - ao_multi_channel_task: nidaqmx.Task, -) -> None: - writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) - num_channels = ao_multi_channel_task.number_of_channels - samples_to_write = 10 - data = numpy.full((num_channels, samples_to_write), 0.0, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - _ = writer.write_int16(data) - - assert "int16" in exc_info.value.args[0] - - -def test___analog_unscaled_writer___write_int32___updates_output( - ao_multi_channel_task: nidaqmx.Task, - ai_multi_channel_loopback_task: nidaqmx.Task, -) -> None: - writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) - num_channels = ao_multi_channel_task.number_of_channels - samples_to_write = 10 - expected = [_get_expected_voltage_for_chan(chan_index) for chan_index in range(num_channels)] - # sweep up to the expected values, the only one we'll validate - data = numpy.ascontiguousarray( - numpy.transpose( - numpy.linspace( - [0] * num_channels, - [_volts_to_codes(v) for v in expected], - num=samples_to_write, - dtype=numpy.int32, - ) - ) - ) - - samples_written = writer.write_int32(data) - - assert samples_written == samples_to_write - assert ai_multi_channel_loopback_task.read() == pytest.approx( - expected, abs=VOLTAGE_EPSILON_FOR_RAW - ) - - -def test___analog_unscaled_writer___write_int32_with_wrong_dtype___raises_error_with_correct_dtype( - ao_multi_channel_task: nidaqmx.Task, -) -> None: - writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) - num_channels = ao_multi_channel_task.number_of_channels - samples_to_write = 10 - data = numpy.full((num_channels, samples_to_write), 0.0, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - _ = writer.write_int32(data) - - assert "int32" in exc_info.value.args[0] - - -def test___analog_unscaled_writer___write_uint16___updates_output( - ao_multi_channel_task: nidaqmx.Task, - ai_multi_channel_loopback_task: nidaqmx.Task, -) -> None: - writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) - num_channels = ao_multi_channel_task.number_of_channels - samples_to_write = 10 - expected = [_get_expected_voltage_for_chan(chan_index) for chan_index in range(num_channels)] - # sweep up to the expected values, the only one we'll validate - data = numpy.ascontiguousarray( - numpy.transpose( - numpy.linspace( - [0] * num_channels, - [_volts_to_codes(v) for v in expected], - num=samples_to_write, - dtype=numpy.uint16, - ) - ) - ) - - samples_written = writer.write_uint16(data) - - assert samples_written == samples_to_write - assert ai_multi_channel_loopback_task.read() == pytest.approx( - expected, abs=VOLTAGE_EPSILON_FOR_RAW - ) - - -def test___analog_unscaled_writer___write_uint16_with_wrong_dtype___raises_error_with_correct_dtype( - ao_multi_channel_task: nidaqmx.Task, -) -> None: - writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) - num_channels = ao_multi_channel_task.number_of_channels - samples_to_write = 10 - data = numpy.full((num_channels, samples_to_write), 0.0, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - _ = writer.write_uint16(data) - - assert "uint16" in exc_info.value.args[0] - - -def test___analog_unscaled_writer___write_uint32___updates_output( - ao_multi_channel_task: nidaqmx.Task, - ai_multi_channel_loopback_task: nidaqmx.Task, -) -> None: - writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) - num_channels = ao_multi_channel_task.number_of_channels - samples_to_write = 10 - expected = [_get_expected_voltage_for_chan(chan_index) for chan_index in range(num_channels)] - # sweep up to the expected values, the only one we'll validate - data = numpy.ascontiguousarray( - numpy.transpose( - numpy.linspace( - [0] * num_channels, - [_volts_to_codes(v) for v in expected], - num=samples_to_write, - dtype=numpy.uint32, - ) - ) - ) - - samples_written = writer.write_uint32(data) - - assert samples_written == samples_to_write - assert ai_multi_channel_loopback_task.read() == pytest.approx( - expected, abs=VOLTAGE_EPSILON_FOR_RAW - ) - - -def test___analog_unscaled_writer___write_uint32_with_wrong_dtype___raises_error_with_correct_dtype( - ao_multi_channel_task: nidaqmx.Task, -) -> None: - writer = AnalogUnscaledWriter(ao_multi_channel_task.out_stream) - num_channels = ao_multi_channel_task.number_of_channels - samples_to_write = 10 - data = numpy.full((num_channels, samples_to_write), 0.0, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - _ = writer.write_uint32(data) - - assert "uint32" in exc_info.value.args[0] diff --git a/tests/component/test_stream_writers_do.py b/tests/component/test_stream_writers_do.py deleted file mode 100644 index c68740439..000000000 --- a/tests/component/test_stream_writers_do.py +++ /dev/null @@ -1,697 +0,0 @@ -from __future__ import annotations - -import ctypes -import math -from typing import Callable - -import numpy -import pytest - -import nidaqmx -import nidaqmx.system -from nidaqmx.constants import LineGrouping -from nidaqmx.stream_writers import DigitalMultiChannelWriter, DigitalSingleChannelWriter -from nidaqmx.utils import flatten_channel_string - - -def _start_do_task(task: nidaqmx.Task, is_port: bool = False, num_chans: int = 1) -> None: - # We'll be doing on-demand, so start the task and drive all lines low - task.start() - if is_port: - if num_chans == 8: - task.write(0) - else: - task.write([0] * num_chans) - else: - if num_chans == 1: - task.write(False) - else: - task.write([False] * num_chans) - - -@pytest.fixture -def do_single_line_task( - generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device -) -> nidaqmx.Task: - task = generate_task() - task.do_channels.add_do_chan( - real_x_series_device.do_lines[0].name, line_grouping=LineGrouping.CHAN_FOR_ALL_LINES - ) - _start_do_task(task) - return task - - -@pytest.fixture -def do_single_channel_multi_line_task( - generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device -) -> nidaqmx.Task: - task = generate_task() - chan = task.do_channels.add_do_chan( - flatten_channel_string(real_x_series_device.do_lines.channel_names[:8]), - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - _start_do_task(task, num_chans=chan.do_num_lines) - return task - - -@pytest.fixture -def do_multi_channel_multi_line_task( - generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device -) -> nidaqmx.Task: - task = generate_task() - task.do_channels.add_do_chan( - flatten_channel_string(real_x_series_device.do_lines.channel_names[:8]), - line_grouping=LineGrouping.CHAN_PER_LINE, - ) - _start_do_task(task, num_chans=task.number_of_channels) - return task - - -@pytest.fixture -def do_port0_task( - generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device -) -> nidaqmx.Task: - task = generate_task() - # X Series port 0 has either 32 or 8 lines. The former can only be used with 32-bit writes. The - # latter can be used with any sized port write. - task.do_channels.add_do_chan( - real_x_series_device.do_ports[0].name, - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - _start_do_task(task, is_port=True) - return task - - -@pytest.fixture -def do_port1_task( - generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device -) -> nidaqmx.Task: - task = generate_task() - # X Series port 1 has 8 lines, and can be used with any sized port write. - task.do_channels.add_do_chan( - real_x_series_device.do_ports[1].name, - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - _start_do_task(task, is_port=True) - return task - - -@pytest.fixture -def do_multi_channel_port_task( - generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device -) -> nidaqmx.Task: - task = generate_task() - # X Series port 1 has 8 lines, and can be used with any sized port write - task.do_channels.add_do_chan( - real_x_series_device.do_ports[1].name, - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - # X Series port 2 has 8 lines, and can be used with any sized port write - task.do_channels.add_do_chan( - real_x_series_device.do_ports[2].name, - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - _start_do_task(task, is_port=True, num_chans=task.number_of_channels) - return task - - -def _start_di_task(task: nidaqmx.Task) -> None: - # Don't reserve the lines, so we can read what DO is writing. - task.di_channels.all.di_tristate = False - task.start() - - -@pytest.fixture -def di_single_line_loopback_task( - generate_task: Callable[[], nidaqmx.Task], - real_x_series_device: nidaqmx.system.Device, -) -> nidaqmx.Task: - task = generate_task() - task.di_channels.add_di_chan( - real_x_series_device.di_lines[0].name, line_grouping=LineGrouping.CHAN_FOR_ALL_LINES - ) - _start_di_task(task) - return task - - -@pytest.fixture -def di_multi_line_loopback_task( - generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device -) -> nidaqmx.Task: - task = generate_task() - task.di_channels.add_di_chan( - flatten_channel_string(real_x_series_device.di_lines.channel_names[:8]), - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - _start_di_task(task) - return task - - -@pytest.fixture -def di_port0_loopback_task( - generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device -) -> nidaqmx.Task: - task = generate_task() - task.di_channels.add_di_chan( - real_x_series_device.di_ports[0].name, - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - _start_di_task(task) - return task - - -@pytest.fixture -def di_port0_loopback_task_32dio( - generate_task: Callable[[], nidaqmx.Task], real_x_series_device_32dio: nidaqmx.system.Device -) -> nidaqmx.Task: - task = generate_task() - task.di_channels.add_di_chan( - real_x_series_device_32dio.di_ports[0].name, - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - _start_di_task(task) - return task - - -@pytest.fixture -def di_port1_loopback_task( - generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device -) -> nidaqmx.Task: - task = generate_task() - task.di_channels.add_di_chan( - real_x_series_device.di_ports[1].name, - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - _start_di_task(task) - return task - - -@pytest.fixture -def di_port1_loopback_task_32dio( - generate_task: Callable[[], nidaqmx.Task], real_x_series_device_32dio: nidaqmx.system.Device -) -> nidaqmx.Task: - task = generate_task() - task.di_channels.add_di_chan( - real_x_series_device_32dio.di_ports[1].name, - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - _start_di_task(task) - return task - - -@pytest.fixture -def di_port2_loopback_task( - generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device -) -> nidaqmx.Task: - task = generate_task() - task.di_channels.add_di_chan( - real_x_series_device.di_ports[2].name, - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - _start_di_task(task) - return task - - -@pytest.fixture -def di_port2_loopback_task_32dio( - generate_task: Callable[[], nidaqmx.Task], real_x_series_device_32dio: nidaqmx.system.Device -) -> nidaqmx.Task: - task = generate_task() - task.di_channels.add_di_chan( - real_x_series_device_32dio.di_ports[2].name, - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - _start_di_task(task) - return task - - -@pytest.fixture -def di_multi_channel_port_loopback_task( - generate_task: Callable[[], nidaqmx.Task], real_x_series_device: nidaqmx.system.Device -) -> nidaqmx.Task: - task = generate_task() - task.di_channels.add_di_chan( - real_x_series_device.di_ports[1].name, - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - task.di_channels.add_di_chan( - real_x_series_device.di_ports[2].name, - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - _start_di_task(task) - return task - - -def _get_num_lines_in_task(task: nidaqmx.Task) -> int: - return sum([chan.do_num_lines for chan in task.channels]) - - -def _get_digital_data_for_sample(num_lines: int, sample_number: int) -> int: - result = 0 - # "Count" from 0 in binary within each group of 8 lines, like simulated data. - for _ in range((num_lines + 7) // 8): - result = (result << 8) | sample_number - - line_mask = (2**num_lines) - 1 - return result & line_mask - - -def _get_digital_data(num_lines: int, num_samples: int) -> list[int]: - return [ - _get_digital_data_for_sample(num_lines, sample_number) - for sample_number in range(num_samples) - ] - - -def _get_digital_port_data_for_sample(task: nidaqmx.Task, sample_number: int) -> list[int]: - return [ - _get_digital_data_for_sample(chan.do_num_lines, sample_number) for chan in task.channels - ] - - -def _get_digital_port_data_port_major(task: nidaqmx.Task, num_samples: int) -> list[list[int]]: - return [_get_digital_data(chan.do_num_lines, num_samples) for chan in task.channels] - - -def _get_digital_port_data_sample_major(task: nidaqmx.Task, num_samples: int) -> list[list[int]]: - result = _get_digital_port_data_port_major(task, num_samples) - return numpy.transpose(result).tolist() - - -def _int_to_bool_array(num_lines: int, input: int) -> numpy.typing.NDArray[numpy.bool_]: - result = numpy.full(num_lines, True, dtype=numpy.bool_) - for bit in range(num_lines): - result[bit] = (input & (1 << bit)) != 0 - return result - - -def test___digital_single_channel_writer___write_one_sample_one_line___updates_output( - do_single_line_task: nidaqmx.Task, - di_single_line_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalSingleChannelWriter(do_single_line_task.out_stream) - sample = True - - writer.write_one_sample_one_line(sample) - - assert di_single_line_loopback_task.read() == sample - - -def test___digital_single_channel_writer___write_one_sample_multi_line___updates_output( - do_single_channel_multi_line_task: nidaqmx.Task, - di_multi_line_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalSingleChannelWriter(do_single_channel_multi_line_task.out_stream) - num_lines = _get_num_lines_in_task(do_single_channel_multi_line_task) - samples_to_write = 256 - - # "sweep" up to the final value, the only one we'll validate - for datum in _get_digital_data(num_lines, samples_to_write): - writer.write_one_sample_multi_line(_int_to_bool_array(num_lines, datum)) - - assert di_multi_line_loopback_task.read() == datum - - -def test___digital_single_channel_writer___write_one_sample_multi_line_with_wrong_dtype___raises_error_with_correct_dtype( - do_single_channel_multi_line_task: nidaqmx.Task, -) -> None: - writer = DigitalSingleChannelWriter(do_single_channel_multi_line_task.out_stream) - num_lines = _get_num_lines_in_task(do_single_channel_multi_line_task) - sample = numpy.full(num_lines, math.inf, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - writer.write_one_sample_multi_line(sample) - - assert "bool" in exc_info.value.args[0] - - -def test___digital_single_channel_writer___write_one_sample_port_byte___updates_output( - do_port1_task: nidaqmx.Task, - di_port1_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalSingleChannelWriter(do_port1_task.out_stream) - num_lines = _get_num_lines_in_task(do_port1_task) - samples_to_write = 256 - - # "sweep" up to the final value, the only one we'll validate - for datum in _get_digital_data(num_lines, samples_to_write): - writer.write_one_sample_port_byte(datum) - - assert di_port1_loopback_task.read() == datum - - -def test___digital_single_channel_writer___write_many_sample_port_byte___updates_output( - do_port1_task: nidaqmx.Task, - di_port1_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalSingleChannelWriter(do_port1_task.out_stream) - num_lines = _get_num_lines_in_task(do_port1_task) - samples_to_write = 256 - data = numpy.array(_get_digital_data(num_lines, samples_to_write), dtype=numpy.uint8) - - # "sweep" up to the final value, the only one we'll validate - writer.write_many_sample_port_byte(data) - - assert di_port1_loopback_task.read() == data[-1] - - -def test___digital_single_channel_writer___write_many_sample_port_byte_with_wrong_dtype___raises_error_with_correct_dtype( - do_port1_task: nidaqmx.Task, -) -> None: - writer = DigitalSingleChannelWriter(do_port1_task.out_stream) - samples_to_write = 256 - data = numpy.full(samples_to_write, math.inf, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - writer.write_many_sample_port_byte(data) - - assert "uint8" in exc_info.value.args[0] - - -def test___digital_single_channel_writer___write_one_sample_port_uint16___updates_output( - do_port1_task: nidaqmx.Task, - di_port1_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalSingleChannelWriter(do_port1_task.out_stream) - num_lines = _get_num_lines_in_task(do_port1_task) - samples_to_write = 256 - - # "sweep" up to the final value, the only one we'll validate - for datum in _get_digital_data(num_lines, samples_to_write): - writer.write_one_sample_port_uint16(datum) - - assert di_port1_loopback_task.read() == datum - - -def test___digital_single_channel_writer___write_many_sample_port_uint16___updates_output( - do_port1_task: nidaqmx.Task, - di_port1_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalSingleChannelWriter(do_port1_task.out_stream) - num_lines = _get_num_lines_in_task(do_port1_task) - samples_to_write = 256 - data = numpy.array(_get_digital_data(num_lines, samples_to_write), dtype=numpy.uint16) - - # "sweep" up to the final value, the only one we'll validate - writer.write_many_sample_port_uint16(data) - - assert di_port1_loopback_task.read() == data[-1] - - -def test___digital_single_channel_writer___write_many_sample_port_uint16_with_wrong_dtype___raises_error_with_correct_dtype( - do_port1_task: nidaqmx.Task, -) -> None: - writer = DigitalSingleChannelWriter(do_port1_task.out_stream) - samples_to_write = 256 - data = numpy.full(samples_to_write, math.inf, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - writer.write_many_sample_port_uint16(data) - - assert "uint16" in exc_info.value.args[0] - - -def test___digital_single_channel_writer___write_one_sample_port_uint32___updates_output( - do_port0_task: nidaqmx.Task, - di_port0_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalSingleChannelWriter(do_port0_task.out_stream) - num_lines = _get_num_lines_in_task(do_port0_task) - samples_to_write = 256 - - # "sweep" up to the final value, the only one we'll validate - for datum in _get_digital_data(num_lines, samples_to_write): - writer.write_one_sample_port_uint32(datum) - - assert di_port0_loopback_task.read() == datum - - -def test___digital_single_channel_writer___write_many_sample_port_uint32___updates_output( - do_port0_task: nidaqmx.Task, - di_port0_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalSingleChannelWriter(do_port0_task.out_stream) - num_lines = _get_num_lines_in_task(do_port0_task) - samples_to_write = 256 - data = numpy.array(_get_digital_data(num_lines, samples_to_write), dtype=numpy.uint32) - - # "sweep" up to the final value, the only one we'll validate - writer.write_many_sample_port_uint32(data) - - assert di_port0_loopback_task.read() == data[-1] - - -def test___digital_single_channel_writer___write_many_sample_port_uint32_with_wrong_dtype___raises_error_with_correct_dtype( - do_port0_task: nidaqmx.Task, -) -> None: - writer = DigitalSingleChannelWriter(do_port0_task.out_stream) - samples_to_write = 256 - data = numpy.full(samples_to_write, math.inf, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - writer.write_many_sample_port_uint32(data) - - assert "uint32" in exc_info.value.args[0] - - -def test___digital_multi_channel_writer___write_one_sample_one_line___updates_output( - do_single_line_task: nidaqmx.Task, - di_single_line_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_single_line_task.out_stream) - num_lines = _get_num_lines_in_task(do_single_line_task) - samples_to_write = 256 - - # "sweep" up to the final value, the only one we'll validate - for datum in _get_digital_data(num_lines, samples_to_write): - writer.write_one_sample_one_line(_int_to_bool_array(num_lines, datum)) - - assert di_single_line_loopback_task.read() == datum - - -def test___digital_multi_channel_writer___write_one_sample_multi_line___updates_output( - do_multi_channel_multi_line_task: nidaqmx.Task, - di_multi_line_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_multi_channel_multi_line_task.out_stream) - num_channels = do_multi_channel_multi_line_task.number_of_channels - samples_to_write = 256 - - # "sweep" up to the final value, the only one we'll validate - for datum in _get_digital_data(num_channels, samples_to_write): - data_to_write = _int_to_bool_array(num_channels, datum).reshape((num_channels, 1)) - writer.write_one_sample_multi_line(data_to_write) - - assert di_multi_line_loopback_task.read() == datum - - -def test___digital_multi_channel_writer___write_one_sample_multi_line_jagged___updates_output( - di_port0_loopback_task_32dio: nidaqmx.Task, - di_port1_loopback_task_32dio: nidaqmx.Task, - di_port2_loopback_task_32dio: nidaqmx.Task, - generate_task: Callable[[], nidaqmx.Task], - real_x_series_device_32dio: nidaqmx.system.Device, -) -> None: - task = generate_task() - for port in real_x_series_device_32dio.do_ports: - task.do_channels.add_do_chan( - port.name, - line_grouping=LineGrouping.CHAN_FOR_ALL_LINES, - ) - _start_do_task(task, is_port=True, num_chans=task.number_of_channels) - writer = DigitalMultiChannelWriter(task.out_stream) - num_channels = task.number_of_channels - samples_to_write = 0xA5 - - # "sweep" up to the final value, the only one we'll validate - for datum in _get_digital_data(num_channels * 32, samples_to_write): - data_to_write = _int_to_bool_array(num_channels * 32, datum).reshape((num_channels, 32)) - writer.write_one_sample_multi_line(data_to_write) - - assert di_port0_loopback_task_32dio.read() == datum & 0xFFFFFFFF - assert di_port1_loopback_task_32dio.read() == (datum >> 32) & 0xFF - assert di_port2_loopback_task_32dio.read() == (datum >> 64) & 0xFF - - -def test___digital_multi_channel_writer___write_one_sample_multi_line_with_wrong_dtype___raises_error_with_correct_dtype( - do_multi_channel_multi_line_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_multi_channel_multi_line_task.out_stream) - num_channels = do_multi_channel_multi_line_task.number_of_channels - sample = numpy.full((num_channels, 1), math.inf, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - writer.write_one_sample_multi_line(sample) - - assert "bool" in exc_info.value.args[0] - - -def test___digital_multi_channel_writer___write_one_sample_port_byte___updates_output( - do_multi_channel_port_task: nidaqmx.Task, - di_multi_channel_port_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) - samples_to_write = 256 - - # "sweep" up to the final value, the only one we'll validate - for datum in _get_digital_port_data_sample_major(do_multi_channel_port_task, samples_to_write): - writer.write_one_sample_port_byte(numpy.array(datum, dtype=numpy.uint8)) - - assert di_multi_channel_port_loopback_task.read() == datum - - -def test___digital_multi_channel_writer___write_one_sample_port_byte_with_wrong_dtype___raises_error_with_correct_dtype( - do_multi_channel_port_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) - num_channels = do_multi_channel_port_task.number_of_channels - data = numpy.full(num_channels, math.inf, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - writer.write_one_sample_port_byte(data) - - assert "uint8" in exc_info.value.args[0] - - -def test___digital_multi_channel_writer___write_many_sample_port_byte___updates_output( - do_multi_channel_port_task: nidaqmx.Task, - di_multi_channel_port_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) - samples_to_write = 256 - - # "sweep" up to the final value, the only one we'll validate - data = _get_digital_port_data_port_major(do_multi_channel_port_task, samples_to_write) - writer.write_many_sample_port_byte(numpy.array(data, dtype=numpy.uint8)) - - assert di_multi_channel_port_loopback_task.read() == _get_digital_port_data_for_sample( - do_multi_channel_port_task, samples_to_write - 1 - ) - - -def test___digital_multi_channel_writer___write_many_sample_port_byte_with_wrong_dtype___raises_error_with_correct_dtype( - do_multi_channel_port_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) - num_channels = do_multi_channel_port_task.number_of_channels - samples_to_write = 256 - data = numpy.full((num_channels, samples_to_write), math.inf, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - writer.write_many_sample_port_byte(data) - - assert "uint8" in exc_info.value.args[0] - - -def test___digital_multi_channel_writer___write_one_sample_port_uint16___updates_output( - do_multi_channel_port_task: nidaqmx.Task, - di_multi_channel_port_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) - samples_to_write = 256 - - # "sweep" up to the final value, the only one we'll validate - for datum in _get_digital_port_data_sample_major(do_multi_channel_port_task, samples_to_write): - writer.write_one_sample_port_uint16(numpy.array(datum, dtype=numpy.uint16)) - - assert di_multi_channel_port_loopback_task.read() == datum - - -def test___digital_multi_channel_writer___write_one_sample_port_uint16_with_wrong_dtype___raises_error_with_correct_dtype( - do_multi_channel_port_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) - num_channels = do_multi_channel_port_task.number_of_channels - data = numpy.full(num_channels, math.inf, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - writer.write_one_sample_port_uint16(data) - - assert "uint16" in exc_info.value.args[0] - - -def test___digital_multi_channel_writer___write_many_sample_port_uint16___updates_output( - do_multi_channel_port_task: nidaqmx.Task, - di_multi_channel_port_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) - samples_to_write = 256 - - # "sweep" up to the final value, the only one we'll validate - data = _get_digital_port_data_port_major(do_multi_channel_port_task, samples_to_write) - writer.write_many_sample_port_uint16(numpy.array(data, dtype=numpy.uint16)) - - assert di_multi_channel_port_loopback_task.read() == _get_digital_port_data_for_sample( - do_multi_channel_port_task, samples_to_write - 1 - ) - - -def test___digital_multi_channel_writer___write_many_sample_port_uint16_with_wrong_dtype___raises_error_with_correct_dtype( - do_multi_channel_port_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) - num_channels = do_multi_channel_port_task.number_of_channels - samples_to_write = 256 - data = numpy.full((num_channels, samples_to_write), math.inf, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - writer.write_many_sample_port_uint16(data) - - assert "uint16" in exc_info.value.args[0] - - -def test___digital_multi_channel_writer___write_one_sample_port_uint32___updates_output( - do_multi_channel_port_task: nidaqmx.Task, - di_multi_channel_port_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) - samples_to_write = 256 - - # "sweep" up to the final value, the only one we'll validate - for datum in _get_digital_port_data_sample_major(do_multi_channel_port_task, samples_to_write): - writer.write_one_sample_port_uint32(numpy.array(datum, dtype=numpy.uint32)) - - assert di_multi_channel_port_loopback_task.read() == datum - - -def test___digital_multi_channel_writer___write_one_sample_port_uint32_with_wrong_dtype___raises_error_with_correct_dtype( - do_multi_channel_port_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) - num_channels = do_multi_channel_port_task.number_of_channels - data = numpy.full(num_channels, math.inf, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - writer.write_one_sample_port_uint32(data) - - assert "uint32" in exc_info.value.args[0] - - -def test___digital_multi_channel_writer___write_many_sample_port_uint32___updates_output( - do_multi_channel_port_task: nidaqmx.Task, - di_multi_channel_port_loopback_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) - samples_to_write = 256 - - # "sweep" up to the final value, the only one we'll validate - data = _get_digital_port_data_port_major(do_multi_channel_port_task, samples_to_write) - writer.write_many_sample_port_uint32(numpy.array(data, dtype=numpy.uint32)) - - assert di_multi_channel_port_loopback_task.read() == _get_digital_port_data_for_sample( - do_multi_channel_port_task, samples_to_write - 1 - ) - - -def test___digital_multi_channel_writer___write_many_sample_port_uint32_with_wrong_dtype___raises_error_with_correct_dtype( - do_multi_channel_port_task: nidaqmx.Task, -) -> None: - writer = DigitalMultiChannelWriter(do_multi_channel_port_task.out_stream) - num_channels = do_multi_channel_port_task.number_of_channels - samples_to_write = 256 - data = numpy.full((num_channels, samples_to_write), math.inf, dtype=numpy.float64) - - with pytest.raises((ctypes.ArgumentError, TypeError)) as exc_info: - writer.write_many_sample_port_uint32(data) - - assert "uint32" in exc_info.value.args[0]