def firmware_version(self): """ Read the board firmware and bootloader versions. Returns: namedtuple: a namedtuple containing the following field names * **version** (string): The firmware version, i.e "1.03". * **bootloader_version** (string): The bootloader version, i.e "1.01". Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") version = c_ushort() boot_version = c_ushort() if (self._lib.mcc118_firmware_version(self._address, byref(version), byref(boot_version)) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") version_str = "{0:X}.{1:02X}".format(version.value >> 8, version.value & 0x00FF) boot_str = "{0:X}.{1:02X}".format(boot_version.value >> 8, boot_version.value & 0x00FF) version_info = namedtuple('MCC118VersionInfo', ['version', 'bootloader_version']) return version_info(version=version_str, bootloader_version=boot_str)
def tc_type_read(self, channel): """ Read the thermocouple type for a channel. Reads the current thermocouple type for the specified channel. The type is one of :py:class:`TcTypes` and the board will default to all channels disable (set to :py:const:`TcTypes.DISABLED`) when it is first opened. Args: channel (int): The analog input channel number, 0-3. Returns int: The thermocouple type. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") type_value = c_ubyte() if (self._lib.mcc134_tc_type_read(self._address, channel, byref(type_value)) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") return type_value.value
def calibration_coefficient_read(self, a_in_range): """ Read the calibration coefficients for a specified input range. The coefficients are applied in the library as: :: calibrated_ADC_code = (raw_ADC_code * slope) + offset Args: a_in_range (:py:class:`AnalogInputRange`): The input range. Returns: namedtuple: A namedtuple containing the following field names: * **slope** (float): The slope. * **offset** (float): The offset. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") slope = c_double() offset = c_double() if (self._lib.mcc128_calibration_coefficient_read( self._address, a_in_range, byref(slope), byref(offset)) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") cal_info = namedtuple('MCC128CalInfo', ['slope', 'offset']) return cal_info( slope=slope.value, offset=offset.value)
def calibration_coefficient_write(self, channel, slope, offset): """ Temporarily write the calibration coefficients for a single channel. The user can apply their own calibration coefficients by writing to these values. The values will reset to the factory values from the EEPROM whenever the class is initialized. This function will fail and raise a HatError exception if a scan is active when it is called. The coefficients are applied in the library as: :: calibrated_ADC_code = (raw_ADC_code * slope) + offset Args: slope (float): The new slope value. offset (float): The new offset value. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") if (self._lib.mcc118_calibration_coefficient_write( self._address, channel, slope, offset) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") return
def calibration_coefficient_read(self, channel): """ Read the calibration coefficients for a single channel. The coefficients are applied in the library as: :: calibrated_ADC_code = (raw_ADC_code * slope) + offset Returns: namedtuple: a namedtuple containing the following field names * **slope** (float): The slope. * **offset** (float): The offset. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") slope = c_double() offset = c_double() if (self._lib.mcc118_calibration_coefficient_read( self._address, channel, byref(slope), byref(offset)) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") cal_info = namedtuple('MCC118CalInfo', ['slope', 'offset']) return cal_info(slope=slope.value, offset=offset.value)
def trigger_mode(self, mode): """ Set the external trigger input mode. The available modes are: * :py:const:`TriggerModes.RISING_EDGE`: Start the scan when the TRIG input transitions from low to high. * :py:const:`TriggerModes.FALLING_EDGE`: Start the scan when the TRIG input transitions from high to low. * :py:const:`TriggerModes.ACTIVE_HIGH`: Start the scan when the TRIG input is high. * :py:const:`TriggerModes.ACTIVE_LOW`: Start the scan when the TRIG input is low. Args: mode (:py:class:`TriggerModes`): The trigger mode. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") if (self._lib.mcc118_trigger_mode(self._address, mode) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") return
def a_in_range_write(self, a_in_range): """ This sets the analog input range to one of the valid values: * :py:const:`AnalogInputRange.BIP_10V`: +/- 10V range * :py:const:`AnalogInputRange.BIP_5V`: +/- 5V range * :py:const:`AnalogInputRange.BIP_2V`: +/- 2V range * :py:const:`AnalogInputRange.BIP_1V`: +/- 1V range Args: a_in_range (:py:class:`AnalogInputRange`): The input range. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") result = self._lib.mcc128_a_in_range_write(self._address, a_in_range) if result == self._RESULT_BAD_PARAMETER: raise ValueError("Invalid mode argument.") elif result == self._RESULT_BUSY: raise HatError(self._address, "The range cannot be changed while a scan is " "running.") elif result != self._RESULT_SUCCESS: raise HatError(self._address, "Incorrect response {}.".format( result)) return
def a_in_mode_write(self, a_in_mode): """ This sets the analog input mode to one of the valid values: * :py:const:`AnalogInputMode.SE`: Single-ended (8 inputs relative to ground.) * :py:const:`AnalogInputMode.DIFF`: Differential (4 channels with positive and negative inputs.) Args: a_in_mode (:py:class:`AnalogInputMode`): The input mode. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") result = self._lib.mcc128_a_in_mode_write(self._address, a_in_mode) if result == self._RESULT_BAD_PARAMETER: raise ValueError("Invalid mode argument.") elif result == self._RESULT_BUSY: raise HatError(self._address, "The mode cannot be changed while " "a scan is running.") elif result != self._RESULT_SUCCESS: raise HatError(self._address, "Incorrect response {}.".format( result)) return
def cjc_read(self, channel): """ Read the cold junction compensation temperature for a specified channel. Reads the cold junction sensor temperature for the specified thermocouple terminal. The library automatically performs cold junction compensation, so this function is only needed for informational use or if you want to perform your own compensation. The temperature is returned in degress C. Args: channel (int): The analog input channel number, 0-3. Returns: float: The read value. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. ValueError: the channel number is invalid. """ if not self._initialized: raise HatError(self._address, "Not initialized.") if channel not in range(self._AIN_NUM_CHANNELS): raise ValueError("Invalid channel {0}. Must be 0-{1}.".format( channel, self._AIN_NUM_CHANNELS - 1)) data_value = c_double() if (self._lib.mcc134_cjc_read(self._address, channel, byref(data_value)) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") return data_value.value
def a_in_scan_buffer_size(self): """ Read the internal scan data buffer size. An internal data buffer is allocated for the scan when :py:func:`a_in_scan_start` is called. This function returns the total size of that buffer in samples. Returns: int: the buffer size in samples Raises: HatError: the board is not initialized or no scan buffer is allocated (a scan is not active). """ if not self._initialized: raise HatError(self._address, "Not initialized.") data_value = c_ulong() result = self._lib.mcc118_a_in_scan_buffer_size( self._address, byref(data_value)) if result == self._RESULT_RESOURCE_UNAVAIL: raise HatError(self._address, "No scan is active.") elif result != self._RESULT_SUCCESS: raise HatError(self._address, "Incorrect response {}.".format(result)) return data_value.value
def t_in_read(self, channel): """ Read a thermocouple input channel temperature. The channel must be enabled with :py:func:`tc_type_write` or the method will raise a ValueError exception. This method returns immediately with the most recent temperature reading for the specified channel. When a board is open, the library will read each channel approximately once per second. There will be a delay when the board is first opened because the read thread has to read the cold junction compensation sensors and thermocouple inputs before it can return the first value. The method returns the value as degrees Celsius. The temperature value can have some special values for abnormal conditions: - :py:const:`mcc134.OPEN_TC_VALUE` if an open thermocouple is detected. - :py:const:`mcc134.OVERRANGE_TC_VALUE` if a value outside valid thermocouple voltage is detected. - :py:const:`mcc134.COMMON_MODE_TC_VALUE` if a common-mode voltage error is detected. This occurs when thermocouples on the same MCC 134 are at different voltages. Args: channel (int): The analog input channel number, 0-3. Returns: float: The thermocouple temperature. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. ValueError: the channel number is invalid or the channel is disabled. """ if not self._initialized: raise HatError(self._address, "Not initialized.") if channel not in range(self._AIN_NUM_CHANNELS): raise ValueError("Invalid channel {0}. Must be 0-{1}.".format( channel, self._AIN_NUM_CHANNELS - 1)) temp = c_double() result = self._lib.mcc134_t_in_read(self._address, channel, byref(temp)) if result == self._RESULT_BAD_PARAMETER: raise ValueError( "Invalid channel {}, the channel must be enabled.".format( channel)) elif result != self._RESULT_SUCCESS: raise HatError(self._address, "Incorrect response.") return temp.value
def a_in_read(self, channel, options=OptionFlags.DEFAULT): """ Read an analog input channel and return the value. The channel must be enabled with :py:func:`tc_type_write` or the method will raise a ValueError exception. The returned voltage can have a special value to indicate abnormal conditions: * :py:const:`mcc134.COMMON_MODE_TC_VALUE` if a common-mode voltage error is detected. This occurs when thermocouples on the same MCC 134 are at different voltages. **options** is an ORed combination of OptionFlags. Valid flags for this method are: * :py:const:`OptionFlags.DEFAULT`: Return a calibrated voltage value. Any other flags will override DEFAULT behavior. * :py:const:`OptionFlags.NOSCALEDATA`: Return an ADC code (a value between -8,388,608 and 8,388,607) rather than voltage. * :py:const:`OptionFlags.NOCALIBRATEDATA`: Return data without the calibration factors applied. Args: channel (int): The analog input channel number, 0-3. options (int): ORed combination of :py:class:`OptionFlags`, :py:const:`OptionFlags.DEFAULT` if unspecified. Returns: float: The read value. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. ValueError: the channel number is invalid. """ if not self._initialized: raise HatError(self._address, "Not initialized.") if channel not in range(self._AIN_NUM_CHANNELS): raise ValueError("Invalid channel {0}. Must be 0-{1}.".format( channel, self._AIN_NUM_CHANNELS - 1)) data_value = c_double() result = self._lib.mcc134_a_in_read(self._address, channel, options, byref(data_value)) if result == self._RESULT_BAD_PARAMETER: raise ValueError( "Invalid channel {}, the channel must be enabled.".format( channel)) elif result != self._RESULT_SUCCESS: raise HatError(self._address, "Incorrect response.") return data_value.value
def a_in_scan_status(self): """ Read scan status and number of available samples per channel. The analog input scan is started with :py:func:`a_in_scan_start` and runs in the background. This function reads the status of that background scan and the number of samples per channel available in the scan thread buffer. Returns: namedtuple: a namedtuple containing the following field names: * **running** (bool): True if the scan is running, False if it has stopped or completed. * **hardware_overrun** (bool): True if the hardware could not acquire and unload samples fast enough and data was lost. * **buffer_overrun** (bool): True if the background scan buffer was not read fast enough and data was lost. * **triggered** (bool): True if the trigger conditions have been met and data acquisition started. * **samples_available** (int): The number of samples per channel currently in the scan buffer. Raises: HatError: A scan is not active, the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") status = c_ushort(0) samples_available = c_ulong(0) result = self._lib.mcc118_a_in_scan_status(self._address, byref(status), byref(samples_available)) if result == self._RESULT_RESOURCE_UNAVAIL: raise HatError(self._address, "Scan not active.") elif result != self._RESULT_SUCCESS: raise HatError(self._address, "Incorrect response {}.".format(result)) scan_status = namedtuple('MCC118ScanStatus', [ 'running', 'hardware_overrun', 'buffer_overrun', 'triggered', 'samples_available' ]) return scan_status( running=(status.value & self._STATUS_RUNNING) != 0, hardware_overrun=(status.value & self._STATUS_HW_OVERRUN) != 0, buffer_overrun=(status.value & self._STATUS_BUFFER_OVERRUN) != 0, triggered=(status.value & self._STATUS_TRIGGERED) != 0, samples_available=samples_available.value)
def a_in_scan_actual_rate(self, channel_count, sample_rate_per_channel): """ Read the actual sample rate per channel for a requested sample rate. The internal scan clock is generated from a 16 MHz clock source so only discrete frequency steps can be achieved. This function will return the actual rate for a requested channel count and rate setting. This function does not perform any actions with a board, it simply calculates the rate. Args: channel_count (int): The number of channels in the scan, 1-8. sample_rate_per_channel (float): The desired per-channel rate of the internal sampling clock, max 100,000.0. Returns: float: the actual sample rate Raises: ValueError: a scan argument is invalid. """ if not self._initialized: raise HatError(self._address, "Not initialized.") data_value = c_double() if (self._lib.mcc118_a_in_scan_actual_rate( channel_count, sample_rate_per_channel, byref(data_value)) != self._RESULT_SUCCESS): raise ValueError( "The specified parameters are invalid or outside the device " "capabilities.") return data_value.value
def a_in_read(self, channel, options=OptionFlags.DEFAULT): """ Perform a single reading of an analog input channel and return the value. **options** is an ORed combination of OptionFlags. Valid flags for this method are: * :py:const:`OptionFlags.DEFAULT`: Return a calibrated voltage value. Any other flags will override DEFAULT behavior. * :py:const:`OptionFlags.NOSCALEDATA`: Return an ADC code (a value between 0 and 4095) rather than voltage. * :py:const:`OptionFlags.NOCALIBRATEDATA`: Return data without the calibration factors applied. Args: channel (int): The analog input channel number, 0-7. options (int): ORed combination of :py:class:`OptionFlags`, :py:const:`OptionFlags.DEFAULT` if unspecified. Returns: float: the read value Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. ValueError: the channel number is invalid. """ if not self._initialized: raise HatError(self._address, "Not initialized.") if channel not in range(self._AIN_NUM_CHANNELS): raise ValueError("Invalid channel {0}. Must be 0-{1}.".format( channel, self._AIN_NUM_CHANNELS - 1)) data_value = c_double() if (self._lib.mcc118_a_in_read(self._address, channel, options, byref(data_value)) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") return data_value.value
def a_in_read(self, channel, options=OptionFlags.DEFAULT): """ Perform a single reading of an analog input channel and return the value. **options** is an ORed combination of OptionFlags. Valid flags for this method are: * :py:const:`OptionFlags.DEFAULT`: Return a calibrated voltage value. Any other flags will override DEFAULT behavior. * :py:const:`OptionFlags.NOSCALEDATA`: Return an ADC code (a value between 0 and 65535) rather than voltage. * :py:const:`OptionFlags.NOCALIBRATEDATA`: Return data without the calibration factors applied. Args: channel (int): The analog input channel number, 0-7. options (int): ORed combination of :py:class:`OptionFlags`, :py:const:`OptionFlags.DEFAULT` if unspecified. Returns: float: The read value. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") data_value = c_double() result = self._lib.mcc128_a_in_read( self._address, channel, options, byref(data_value)) if result == self._RESULT_BUSY: raise HatError(self._address, "The input cannot be read during a scan.") elif result == self._RESULT_BAD_PARAMETER: raise HatError(self._address, "Invalid argument.") elif result != self._RESULT_SUCCESS: raise HatError(self._address, "Incorrect response.") return data_value.value
def blink_led(self, count): """ Blink the MCC 118 LED. Setting count to 0 will cause the LED to blink continuously until blink_led() is called again with a non-zero count. Args: count (int): The number of times to blink (max 255). Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") if (self._lib.mcc118_blink_led(self._address, count) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") return
def a_in_range_read(self): """ Read the analog input range. Reads the current analog input range. Returns: :py:class:`AnalogInputRange`: The current analog input range. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") a_in_range = c_ubyte() if (self._lib.mcc128_a_in_range_read(self._address, byref(a_in_range)) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") return a_in_range.value
def a_in_scan_cleanup(self): """ Free analog input scan resources after the scan is complete. This will free the scan buffer and other resources used by the background scan and make it possible to start another scan with :py:func:`a_in_scan_start`. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") if (self._lib.mcc118_a_in_scan_cleanup(self._address) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") return
def calibration_date(self): """ Read the calibration date. Returns: string: The calibration date in the format "YYYY-MM-DD". Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") # create string to hold the result my_buffer = create_string_buffer(11) if (self._lib.mcc118_calibration_date(self._address, my_buffer) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") my_date = my_buffer.value.decode('ascii') return my_date
def a_in_scan_stop(self): """ Stops an analog input scan. The device stops acquiring data immediately. The scan data that has been read into the scan buffer is available until :py:func:`a_in_scan_cleanup` is called. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") if (self._lib.mcc118_a_in_scan_stop(self._address) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") return
def serial(self): """ Read the serial number. Returns: string: The serial number. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") # create string to hold the result my_buffer = create_string_buffer(9) if (self._lib.mcc118_serial(self._address, my_buffer) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") my_serial = my_buffer.value.decode('ascii') return my_serial
def update_interval_read(self): """ Read the temperature update interval. Reads the library temperature update rate in seconds. Returns int: The update interval. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") interval = c_ubyte() if (self._lib.mcc134_update_interval_read( self._address, byref(interval)) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") return interval.value
def test_trigger(self): """ Test the external trigger pin (TRIG). This value read at the pin for input testing. Returns: int: The value read at the TRIG pin (0 or 1.) Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") data_value = c_ubyte() result = self._lib.mcc128_test_trigger(self._address, byref(data_value)) if result != self._RESULT_SUCCESS: raise HatError(self._address, "Incorrect response.") return data_value.value
def test_clock(self, mode): """ Test the sample clock pin (CLK). This function exercises the CLK pin in output mode and returns the value read at the pin for input testing. Return the mode to input after testing the pin. Args: mode (int): The CLK pin mode * 0 = input * 1 = output low * 2 = output high * 3 = output 1 kHz square wave Returns: int: the value read at the CLK pin after applying the mode (0 or 1). Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. ValueError: the mode is invalid. """ if not self._initialized: raise HatError(self._address, "Not initialized.") if mode not in range(4): raise ValueError("Invalid mode. Must be 0-3.") data_value = c_ubyte() result = self._lib.mcc118_test_clock(self._address, mode, byref(data_value)) if result == self._RESULT_BUSY: raise HatError(self._address, "Cannot test the CLK pin while a scan is running.") elif result != self._RESULT_SUCCESS: raise HatError(self._address, "Incorrect response.") return data_value.value
def update_interval_write(self, interval): """ Write the temperature update interval. Tells the MCC 134 library how often to update temperatures, with the interval specified in seconds. The library defaults to updating every second, but you may increase this interval if you do not plan to call :py:func:`t_in_read` very often. This will reduce the load on shared resources for other DAQ HATs. Args: interval (int): The interval in seconds, 1 - 255. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") if (self._lib.mcc134_update_interval_write(self._address, interval) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") return
def tc_type_write(self, channel, tc_type): """ Write the thermocouple type for a channel. Enables a channel and tells the library what thermocouple type is connected to the channel. This is needed for correct temperature calculations. The type is one of :py:class:`TcTypes` and the board will default to all channels disabled (set to :py:const:`TcTypes.DISABLED`) when it is first opened. Args: channel (int): The analog input channel number, 0-3. tc_type (:py:class:`TcTypes`): The thermocouple type. Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") if (self._lib.mcc134_tc_type_write(self._address, channel, tc_type) != self._RESULT_SUCCESS): raise HatError(self._address, "Incorrect response.") return
def a_in_scan_channel_count(self): """ Read the number of channels in the current analog input scan. Returns: int: the number of channels (0 if no scan is active, 1-8 otherwise) Raises: HatError: the board is not initialized, does not respond, or responds incorrectly. """ if not self._initialized: raise HatError(self._address, "Not initialized.") num_channels = self._lib.mcc118_a_in_scan_channel_count(self._address) return num_channels
def a_in_scan_read_numpy(self, samples_per_channel, timeout): # pylint: disable=too-many-locals """ Read scan status and data (as a NumPy array). This function is similar to :py:func:`a_in_scan_read` except that the *data* key in the returned namedtuple is a NumPy array of float64 values and may be used directly with NumPy functions. Args: samples_per_channel (int): The number of samples per channel to read from the scan buffer. Specify a negative number to read all available samples or 0 to only read the scan status and return no data. timeout (float): The amount of time in seconds to wait for the samples to be read. Specify a negative number to wait indefinitely, or 0 to return immediately with the samples that are already in the scan buffer. If the timeout is met and the specified number of samples have not been read, then the function will return with the amount that has been read and the timeout status set. Returns: namedtuple: a namedtuple containing the following field names: * **running** (bool): True if the scan is running, False if it has stopped or completed. * **hardware_overrun** (bool): True if the hardware could not acquire and unload samples fast enough and data was lost. * **buffer_overrun** (bool): True if the background scan buffer was not read fast enough and data was lost. * **triggered** (bool): True if the trigger conditions have been met and data acquisition started. * **timeout** (bool): True if the timeout time expired before the specified number of samples were read. * **data** (NumPy array of float64): The data that was read from the scan buffer. Raises: HatError: A scan is not active, the board is not initialized, does not respond, or responds incorrectly. ValueError: Incorrect argument. """ try: import numpy from numpy.ctypeslib import ndpointer except ImportError: raise if not self._initialized: raise HatError(self._address, "Not initialized.") self._lib.mcc118_a_in_scan_read.argtypes = [ c_ubyte, POINTER(c_ushort), c_long, c_double, ndpointer(c_double, flags="C_CONTIGUOUS"), c_ulong, POINTER(c_ulong) ] num_channels = self._lib.mcc118_a_in_scan_channel_count(self._address) samples_read_per_channel = c_ulong() status = c_ushort() timed_out = False samples_to_read = 0 if samples_per_channel < 0: # read all available data # first, get the number of available samples samples_available = c_ulong(0) result = self._lib.mcc118_a_in_scan_status( self._address, byref(status), byref(samples_available)) if result != self._RESULT_SUCCESS: raise HatError(self._address, "Incorrect response {}.".format(result)) # allocate a buffer large enough for all the data samples_to_read = samples_available.value buffer_size = samples_available.value * num_channels data_buffer = numpy.empty(buffer_size, dtype=numpy.float64) elif samples_per_channel == 0: # only read the status samples_to_read = 0 buffer_size = 0 data_buffer = None elif samples_per_channel > 0: # read the specified number of samples samples_to_read = samples_per_channel # create a C buffer for the read buffer_size = samples_per_channel * num_channels data_buffer = numpy.empty(buffer_size, dtype=numpy.float64) else: # invalid samples_per_channel raise ValueError( "Invalid samples_per_channel {}.".format(samples_per_channel)) result = self._lib.mcc118_a_in_scan_read( self._address, byref(status), samples_to_read, timeout, data_buffer, buffer_size, byref(samples_read_per_channel)) if result == self._RESULT_BAD_PARAMETER: raise ValueError("Invalid parameter.") elif result == self._RESULT_RESOURCE_UNAVAIL: raise HatError(self._address, "Scan not active.") elif result == self._RESULT_TIMEOUT: timed_out = True elif result != self._RESULT_SUCCESS: raise HatError(self._address, "Incorrect response {}.".format(result)) total_read = samples_read_per_channel.value * num_channels if total_read < buffer_size: data_buffer = numpy.resize(data_buffer, (total_read, )) scan_status = namedtuple('MCC118ScanRead', [ 'running', 'hardware_overrun', 'buffer_overrun', 'triggered', 'timeout', 'data' ]) return scan_status( running=(status.value & self._STATUS_RUNNING) != 0, hardware_overrun=(status.value & self._STATUS_HW_OVERRUN) != 0, buffer_overrun=(status.value & self._STATUS_BUFFER_OVERRUN) != 0, triggered=(status.value & self._STATUS_TRIGGERED) != 0, timeout=timed_out, data=data_buffer)
def a_in_scan_read(self, samples_per_channel, timeout): # pylint: disable=too-many-locals """ Read scan status and data (as a list). The analog input scan is started with :py:func:`a_in_scan_start` and runs in the background. This function reads the status of that background scan and optionally reads sampled data from the scan buffer. Args: samples_per_channel (int): The number of samples per channel to read from the scan buffer. Specify a negative number to return all available samples immediately and ignore **timeout** or 0 to only read the scan status and return no data. timeout (float): The amount of time in seconds to wait for the samples to be read. Specify a negative number to wait indefinitely, or 0 to return immediately with the samples that are already in the scan buffer (up to **samples_per_channel**.) If the timeout is met and the specified number of samples have not been read, then the function will return all the available samples and the timeout status set. Returns: namedtuple: a namedtuple containing the following field names: * **running** (bool): True if the scan is running, False if it has stopped or completed. * **hardware_overrun** (bool): True if the hardware could not acquire and unload samples fast enough and data was lost. * **buffer_overrun** (bool): True if the background scan buffer was not read fast enough and data was lost. * **triggered** (bool): True if the trigger conditions have been met and data acquisition started. * **timeout** (bool): True if the timeout time expired before the specified number of samples were read. * **data** (list of float): The data that was read from the scan buffer. Raises: HatError: A scan is not active, the board is not initialized, does not respond, or responds incorrectly. ValueError: Incorrect argument. """ if not self._initialized: raise HatError(self._address, "Not initialized.") num_channels = self._lib.mcc118_a_in_scan_channel_count(self._address) self._lib.mcc118_a_in_scan_read.argtypes = [ c_ubyte, POINTER(c_ushort), c_long, c_double, POINTER(c_double), c_ulong, POINTER(c_ulong) ] samples_read_per_channel = c_ulong(0) samples_to_read = 0 status = c_ushort(0) timed_out = False if samples_per_channel < 0: # read all available data # first, get the number of available samples samples_available = c_ulong(0) result = self._lib.mcc118_a_in_scan_status( self._address, byref(status), byref(samples_available)) if result != self._RESULT_SUCCESS: raise HatError(self._address, "Incorrect response {}.".format(result)) # allocate a buffer large enough for all the data samples_to_read = samples_available.value buffer_size = samples_available.value * num_channels data_buffer = (c_double * buffer_size)() elif samples_per_channel == 0: # only read the status samples_to_read = 0 buffer_size = 0 data_buffer = None elif samples_per_channel > 0: # read the specified number of samples samples_to_read = samples_per_channel # create a C buffer for the read buffer_size = samples_per_channel * num_channels data_buffer = (c_double * buffer_size)() else: # invalid samples_per_channel raise ValueError( "Invalid samples_per_channel {}.".format(samples_per_channel)) result = self._lib.mcc118_a_in_scan_read( self._address, byref(status), samples_to_read, timeout, data_buffer, buffer_size, byref(samples_read_per_channel)) if result == self._RESULT_BAD_PARAMETER: raise ValueError("Invalid parameter.") elif result == self._RESULT_RESOURCE_UNAVAIL: raise HatError(self._address, "Scan not active.") elif result == self._RESULT_TIMEOUT: timed_out = True elif result != self._RESULT_SUCCESS: raise HatError(self._address, "Incorrect response {}.".format(result)) total_read = samples_read_per_channel.value * num_channels # python 2 / 3 workaround for xrange if sys.version_info.major > 2: data_list = [data_buffer[i] for i in range(total_read)] else: data_list = [data_buffer[i] for i in xrange(total_read)] scan_status = namedtuple('MCC118ScanRead', [ 'running', 'hardware_overrun', 'buffer_overrun', 'triggered', 'timeout', 'data' ]) return scan_status( running=(status.value & self._STATUS_RUNNING) != 0, hardware_overrun=(status.value & self._STATUS_HW_OVERRUN) != 0, buffer_overrun=(status.value & self._STATUS_BUFFER_OVERRUN) != 0, triggered=(status.value & self._STATUS_TRIGGERED) != 0, timeout=timed_out, data=data_list)