Source code for dwfpy.analog_output

"""
Analog Output module for Digilent WaveForms devices.
"""

#
# This file is part of dwfpy: https://github.com/mariusgreuel/dwfpy
# Copyright (C) 2019 Marius Greuel
#
# SPDX-License-Identifier: MIT
#

from typing import Optional, Tuple
from . import bindings as api
from . import device as fwd  # pylint: disable=unused-import
from .constants import (
    AnalogOutputIdle,
    AnalogOutputMode,
    AnalogOutputNode,
    Function,
    Status,
    TriggerSlope,
    TriggerSource,
)
from .helpers import Helpers


[docs]class AnalogOutputChannelTrigger: """Represents the trigger unit of an Analog Output channel.""" def __init__(self, channel): self._device = channel.device self._channel = channel.index @property def source(self) -> TriggerSource: """Gets or sets the current trigger source setting for the instrument.""" return TriggerSource( api.dwf_analog_out_trigger_source_get(self._device.handle, self._channel) ) @source.setter def source(self, value: TriggerSource) -> None: api.dwf_analog_out_trigger_source_set(self._device.handle, self._channel, value) @property def slope(self) -> TriggerSlope: """Gets or sets the trigger slope for the instrument.""" return TriggerSlope( api.dwf_analog_out_trigger_slope_get(self._device.handle, self._channel) ) @slope.setter def slope(self, value: TriggerSlope) -> None: api.dwf_analog_out_trigger_slope_set(self._device.handle, self._channel, value)
[docs]class AnalogOutputChannelNode: """Represents an Analog Output channel node.""" def __init__(self, channel, node): self._device = channel.device self._channel = channel.index self._node = node.value @property def type(self) -> AnalogOutputNode: """Gets the node type.""" return AnalogOutputNode(self._node) @property def enabled(self) -> bool: """Enables the node.""" return bool( api.dwf_analog_out_node_enable_get(self._device.handle, self._channel, self._node) ) @enabled.setter def enabled(self, value: bool) -> None: api.dwf_analog_out_node_enable_set(self._device.handle, self._channel, self._node, value) @property def function_info(self) -> Tuple[Function, ...]: """Gets the supported channel nodes.""" return Helpers.map_enum_values( Function, api.dwf_analog_out_node_function_info(self._device.handle, self._channel, self._node), ) @property def function(self) -> Function: """Gets or sets the generator function.""" return Function( api.dwf_analog_out_node_function_get(self._device.handle, self._channel, self._node) ) @function.setter def function(self, value: Function) -> None: api.dwf_analog_out_node_function_set(self._device.handle, self._channel, self._node, value) @property def frequency_min(self) -> float: """Gets the minimum frequency.""" return api.dwf_analog_out_node_frequency_info( self._device.handle, self._channel, self._node )[0] @property def frequency_max(self) -> float: """Gets the maximum frequency.""" return api.dwf_analog_out_node_frequency_info( self._device.handle, self._channel, self._node )[1] @property def frequency(self) -> float: """Gets or sets the frequency.""" return api.dwf_analog_out_node_frequency_get(self._device.handle, self._channel, self._node) @frequency.setter def frequency(self, value: float) -> None: api.dwf_analog_out_node_frequency_set(self._device.handle, self._channel, self._node, value) @property def amplitude_min(self) -> float: """Gets the minimum amplitude.""" return api.dwf_analog_out_node_amplitude_info( self._device.handle, self._channel, self._node )[0] @property def amplitude_max(self) -> float: """Gets the maximum amplitude.""" return api.dwf_analog_out_node_amplitude_info( self._device.handle, self._channel, self._node )[1] @property def amplitude(self) -> float: """Gets or sets the amplitude.""" return api.dwf_analog_out_node_amplitude_get(self._device.handle, self._channel, self._node) @amplitude.setter def amplitude(self, value: float) -> None: api.dwf_analog_out_node_amplitude_set(self._device.handle, self._channel, self._node, value) @property def offset_min(self) -> float: """Gets the minimum offset.""" return api.dwf_analog_out_node_offset_info(self._device.handle, self._channel, self._node)[ 0 ] @property def offset_max(self) -> float: """Gets the maximum offset.""" return api.dwf_analog_out_node_offset_info(self._device.handle, self._channel, self._node)[ 1 ] @property def offset(self) -> float: """Gets or sets the offset.""" return api.dwf_analog_out_node_offset_get(self._device.handle, self._channel, self._node) @offset.setter def offset(self, value: float) -> None: api.dwf_analog_out_node_offset_set(self._device.handle, self._channel, self._node, value) @property def symmetry_min(self) -> float: """Gets the minimum symmetry percentage.""" return api.dwf_analog_out_node_symmetry_info( self._device.handle, self._channel, self._node )[0] @property def symmetry_max(self) -> float: """Gets the maximum symmetry percentage.""" return api.dwf_analog_out_node_symmetry_info( self._device.handle, self._channel, self._node )[1] @property def symmetry(self) -> float: """Gets or sets the symmetry percentage.""" return api.dwf_analog_out_node_symmetry_get(self._device.handle, self._channel, self._node) @symmetry.setter def symmetry(self, value: float) -> None: api.dwf_analog_out_node_symmetry_set(self._device.handle, self._channel, self._node, value) @property def phase_min(self) -> float: """Gets the minimum phase.""" return api.dwf_analog_out_node_phase_info(self._device.handle, self._channel, self._node)[0] @property def phase_max(self) -> float: """Gets the maximum phase.""" return api.dwf_analog_out_node_phase_info(self._device.handle, self._channel, self._node)[1] @property def phase(self) -> float: """Gets or sets the phase.""" return api.dwf_analog_out_node_phase_get(self._device.handle, self._channel, self._node) @phase.setter def phase(self, value: float) -> None: api.dwf_analog_out_node_phase_set(self._device.handle, self._channel, self._node, value) @property def data_samples_min(self) -> int: """Gets the minimum number of samples allowed for custom data generation.""" return int( api.dwf_analog_out_node_data_info(self._device.handle, self._channel, self._node)[0] ) @property def data_samples_max(self) -> int: """Gets the maximum number of samples allowed for custom data generation.""" return int( api.dwf_analog_out_node_data_info(self._device.handle, self._channel, self._node)[1] ) @property def play_status(self) -> Tuple[int, int, int]: """Gets information about the recording process. Returns (samples_free, lost_samples, corrupted_samples)""" return api.dwf_analog_out_node_play_status(self._device.handle, self._channel, self._node)
[docs] def set_data_samples(self, samples) -> None: """Sets the custom data or to prefill the buffer with play samples.""" api.dwf_analog_out_node_data_set( self._device.handle, self._channel, self._node, samples, len(samples) )
[docs] def set_play_samples(self, samples) -> None: """Sets new data samples for play mode.""" api.dwf_analog_out_node_play_data( self._device.handle, self._channel, self._node, samples, len(samples) )
[docs]class AnalogOutputChannel: """Represents an Analog Output channel.""" def __init__(self, module, channel): self._device = module.device self._module = module self._channel = channel self._label = 'ch' + str(channel + 1) self._trigger = AnalogOutputChannelTrigger(self) nodes = api.dwf_analog_out_node_info(self._device.handle, self._channel) self._nodes = tuple( AnalogOutputChannelNode(self, v) for v in AnalogOutputNode if (nodes & (1 << v.value)) != 0 ) @property def device(self) -> 'fwd.Device': """Gets the device.""" return self._device @property def module(self) -> 'AnalogOutput': """Gets the Analog Output module.""" return self._module @property def index(self) -> int: """Gets the channel index.""" return self._channel @property def nodes(self) -> Tuple[AnalogOutputChannelNode, ...]: """Gets the channel nodes.""" return self._nodes def __getitem__(self, key) -> AnalogOutputChannelNode: if isinstance(key, int): return self._nodes[key] raise IndexError(key) @property def label(self) -> str: """Gets or sets the channel label.""" return self._label @label.setter def label(self, value: str) -> None: self._label = value @property def trigger(self) -> AnalogOutputChannelTrigger: """Gets the trigger unit.""" return self._trigger @property def master(self) -> int: """Gets or sets the master node index.""" return bool(api.dwf_analog_out_master_get(self._device.handle, self._channel)) @master.setter def master(self, value: int) -> None: api.dwf_analog_out_master_set(self._device.handle, self._channel, value) @property def run_length_min(self) -> float: """Gets the minimum run length in seconds.""" return api.dwf_analog_out_run_info(self._device.handle, self._channel)[0] @property def run_length_max(self) -> float: """Gets the maximum run length in seconds.""" return api.dwf_analog_out_run_info(self._device.handle, self._channel)[1] @property def run_length(self) -> float: """Gets or sets the run length in seconds.""" return api.dwf_analog_out_run_get(self._device.handle, self._channel) @run_length.setter def run_length(self, value: float) -> None: api.dwf_analog_out_run_set(self._device.handle, self._channel, value) @property def run_length_status(self) -> float: """Gets the remaining run length in seconds.""" return api.dwf_analog_out_run_status(self._device.handle, self._channel) @property def wait_length_min(self) -> float: """Gets the minimum wait length in seconds.""" return api.dwf_analog_out_wait_info(self._device.handle, self._channel)[0] @property def wait_length_max(self) -> float: """Gets the maximum wait length in seconds.""" return api.dwf_analog_out_wait_info(self._device.handle, self._channel)[1] @property def wait_length(self) -> float: """Gets or sets the wait length in seconds.""" return api.dwf_analog_out_wait_get(self._device.handle, self._channel) @wait_length.setter def wait_length(self, value: float) -> None: api.dwf_analog_out_wait_set(self._device.handle, self._channel, value) @property def repeat_count_min(self) -> int: """Gets the minimum repeat count.""" return api.dwf_analog_out_repeat_info(self._device.handle, self._channel)[0] @property def repeat_count_max(self) -> int: """Gets the maximum repeat count.""" return api.dwf_analog_out_repeat_info(self._device.handle, self._channel)[1] @property def repeat_count(self) -> int: """Gets or sets the repeat count.""" return api.dwf_analog_out_repeat_get(self._device.handle, self._channel) @repeat_count.setter def repeat_count(self, value: int) -> None: api.dwf_analog_out_repeat_set(self._device.handle, self._channel, value) @property def repeat_count_status(self) -> float: """Gets the remaining repeat count.""" return api.dwf_analog_out_repeat_status(self._device.handle, self._channel) @property def enable_repeat_trigger(self) -> bool: """Enables the repeat count trigger in wait-run repeat cycles.""" return bool(api.dwf_analog_out_repeat_trigger_get(self._device.handle, self._channel)) @enable_repeat_trigger.setter def enable_repeat_trigger(self, value: bool) -> None: api.dwf_analog_out_repeat_trigger_set(self._device.handle, self._channel, value) @property def limitation_min(self) -> float: """Gets the minimum limitation value.""" return api.dwf_analog_out_limitation_info(self._device.handle, self._channel)[0] @property def limitation_max(self) -> float: """Gets the maximum limitation value.""" return api.dwf_analog_out_limitation_info(self._device.handle, self._channel)[1] @property def limitation(self) -> float: """Gets or sets the limitation value. Voltage offset in volts or modulation offset percentage.""" return api.dwf_analog_out_limitation_get(self._device.handle, self._channel) @limitation.setter def limitation(self, value: float) -> None: api.dwf_analog_out_limitation_set(self._device.handle, self._channel, value) @property def mode(self) -> AnalogOutputMode: """Gets or sets the generator mode option.""" return AnalogOutputMode(api.dwf_analog_out_mode_get(self._device.handle, self._channel)) @mode.setter def mode(self, value: AnalogOutputMode) -> None: api.dwf_analog_out_mode_set(self._device.handle, self._channel, value) @property def idle_info(self) -> Tuple[AnalogOutputIdle, ...]: """Gets the supported channel nodes.""" return Helpers.map_enum_values( AnalogOutputIdle, api.dwf_analog_out_idle_info(self._device.handle, self._channel) ) @property def idle(self) -> AnalogOutputIdle: """Gets or sets the generator idle output option.""" return AnalogOutputIdle(api.dwf_analog_out_idle_get(self._device.handle, self._channel)) @idle.setter def idle(self, value: AnalogOutputIdle) -> None: api.dwf_analog_out_idle_set(self._device.handle, self._channel, value)
[docs] def reset(self) -> None: """Resets and configures all instrument parameters to default values.""" api.dwf_analog_out_reset(self._device.handle, self._channel)
[docs] def configure(self, start: bool = False) -> None: """Configures and optionally starts the instrument.""" api.dwf_analog_out_configure(self._device.handle, self._channel, start)
[docs] def apply(self) -> None: """Applies changes to the instrument.""" api.dwf_analog_out_configure(self._device.handle, self._channel, 3)
[docs] def read_status(self) -> Status: """Gets the instrument status.""" return Status(api.dwf_analog_out_status(self._device.handle, self._channel))
[docs] def setup( self, function: Optional[str] = None, frequency: Optional[float] = None, amplitude: Optional[float] = None, offset: Optional[float] = None, symmetry: Optional[float] = None, phase: Optional[float] = None, enabled: bool = True, configure: bool = False, start: bool = False, ) -> None: """Sets up a new carrier waveform. Parameters ---------- function : str, optional The generator function. Can be 'dc', 'sine', 'square', 'triangle', 'ramp-up', 'ramp-down', 'noise', 'pulse', 'trapezium', or 'sine-power'. frequency : float, optional The waveform frequency in Hz. amplitude : float, optional The waveform amplitude in Volts. offset : float, optional The waveform offset in Volts. symmetry : float, optional The waveform symmetry (or duty cycle) in percent. phase : float, optional The waveform phase in degree. enabled : bool, optional If True, then the node is enabled (default True). configure : bool, optional If True, then the instrument is configured (default False). start : bool, optional If True, then the instrument is started (default False). """ self._setup_node( AnalogOutputNode.CARRIER, function=function, frequency=frequency, amplitude=amplitude, offset=offset, symmetry=symmetry, phase=phase, enabled=enabled, configure=configure, start=start, )
[docs] def setup_am( self, function: Optional[str] = None, frequency: Optional[float] = None, amplitude: Optional[float] = None, offset: Optional[float] = None, symmetry: Optional[float] = None, phase: Optional[float] = None, enabled: bool = True, configure: bool = False, start: bool = False, ) -> None: """Applies an AM modulation to a waveform. Parameters ---------- function : str, optional The generator function. Can be 'dc', 'sine', 'square', 'triangle', 'ramp-up', 'ramp-down', 'noise', 'pulse', 'trapezium', or 'sine-power'. frequency : float, optional The waveform frequency in Hz. amplitude : float, optional The waveform amplitude in percent. offset : float, optional The waveform offset in percent. symmetry : float, optional The waveform symmetry (or duty cycle) in percent. phase : float, optional The waveform phase in degree. enabled : bool, optional If True, then the node is enabled (default True). configure : bool, optional If True, then the instrument is configured (default False). start : bool, optional If True, then the instrument is started (default False). """ self._setup_node( AnalogOutputNode.AM, function=function, frequency=frequency, amplitude=amplitude, offset=offset, symmetry=symmetry, phase=phase, enabled=enabled, configure=configure, start=start, )
[docs] def setup_fm( self, function: Optional[str] = None, frequency: Optional[float] = None, amplitude: Optional[float] = None, offset: Optional[float] = None, symmetry: Optional[float] = None, phase: Optional[float] = None, enabled: bool = True, configure: bool = False, start: bool = False, ) -> None: """Applies an FM modulation to a waveform. Parameters ---------- function : str, optional The generator function. Can be 'dc', 'sine', 'square', 'triangle', 'ramp-up', 'ramp-down', 'noise', 'pulse', 'trapezium', or 'sine-power'. frequency : float, optional The waveform frequency in Hz. amplitude : float, optional The waveform amplitude in percent. offset : float, optional The waveform offset in percent. symmetry : float, optional The waveform symmetry (or duty cycle) in percent. phase : float, optional The waveform phase in degree. enabled : bool, optional If True, then the node is enabled (default True). configure : bool, optional If True, then the instrument is configured (default False). start : bool, optional If True, then the instrument is started (default False). """ self._setup_node( AnalogOutputNode.FM, function=function, frequency=frequency, amplitude=amplitude, offset=offset, symmetry=symmetry, phase=phase, enabled=enabled, configure=configure, start=start, )
def _setup_node( self, node_type, function, frequency, amplitude, offset, symmetry, phase, enabled, configure, start, ) -> None: node = self.nodes[node_type] if function is not None: node.function = Helpers.map_function(function) if frequency is not None: node.frequency = frequency if amplitude is not None: node.amplitude = amplitude if offset is not None: node.offset = offset if symmetry is not None: node.symmetry = symmetry if phase is not None: node.phase = phase if enabled is not None: node.enabled = enabled if configure or start: self.configure(start=start)
[docs]class AnalogOutput: """Analog Output module (Arbitrary Waveform Generator).""" def __init__(self, device): self._device = device self._channels = tuple( AnalogOutputChannel(self, i) for i in range(api.dwf_analog_out_count(self._device.handle)) ) def __enter__(self): return self def __exit__(self, exception_type, exception_value, traceback) -> None: del exception_type, exception_value, traceback for channel in self._channels: channel.reset() @property def device(self) -> 'fwd.Device': """Gets the device.""" return self._device @property def channels(self) -> Tuple[AnalogOutputChannel, ...]: """Gets a collection of Analog Output channels.""" return self._channels def __getitem__(self, key) -> AnalogOutputChannel: if isinstance(key, int): return self._channels[key] if isinstance(key, str): for channel in self._channels: if channel.label == key: return channel raise IndexError(key)