diff --git a/adi/__init__.py b/adi/__init__.py index dd8372a0a..5f49b8de8 100644 --- a/adi/__init__.py +++ b/adi/__init__.py @@ -12,7 +12,7 @@ from adi.ad4020 import ad4020 from adi.ad4110 import ad4110 from adi.ad4130 import ad4130 -from adi.ad4630 import ad4630 +from adi.ad4630 import ad4630, adaq42xx from adi.ad4858 import ad4858 from adi.ad5592r import ad5592r from adi.ad5686 import ad5686 diff --git a/adi/ad4630.py b/adi/ad4630.py index cd9039864..571353475 100644 --- a/adi/ad4630.py +++ b/adi/ad4630.py @@ -2,6 +2,7 @@ # # SPDX short identifier: ADIBSD +from decimal import Decimal import numpy as np from adi.attribute import attribute @@ -25,6 +26,7 @@ class ad4630(rx, context_manager, attribute): """ AD4630 is low power 24-bit precision SAR ADC """ + _compatible_parts = ["ad4630-24", "ad4030-24", "ad4630-16"] _complex_data = False _data_type = np.uint32 _device_name = "" @@ -36,14 +38,12 @@ def __init__(self, uri="", device_name="ad4630-24"): context_manager.__init__(self, uri, self._device_name) - compatible_parts = ["ad4630-24", "ad4030-24", "ad4630-16"] - - if device_name not in compatible_parts: + if device_name not in self._compatible_parts: raise Exception( "Not a compatible device: " + str(device_name) + ". Please select from " - + str(compatible_parts) + + str(self.self._compatible_parts) ) else: self._ctrl = self._ctx.find_device(device_name) @@ -166,3 +166,30 @@ def calibscale(self): def calibscale(self, calibscale): """Set calibration scale value.""" self._set_iio_attr(self.name, "calibscale", False, calibscale, self._ctrl) + + +class adaq42xx(ad4630): + + """ ADAQ4224 is a 24-bit precision SAR ADC data acquisition module """ + + _compatible_parts = ["adaq4224", "adaq4216", "adaq4220"] + + def __init__(self, uri="", device_name="adaq4224"): + super().__init__(uri, device_name) + + class _channel(ad4630._channel): + """ADAQ42xx differential channel.""" + + @property + def scale_available(self): + """Provides all available scale(gain) settings for the ADAQ42xx channel""" + return self._get_iio_attr(self.name, "scale_available", False) + + @property + def scale(self): + """ADAQ42xx channel scale""" + return float(self._get_iio_attr_str(self.name, "scale", False)) + + @scale.setter + def scale(self, value): + self._set_iio_attr(self.name, "scale", False, str(Decimal(value).real)) diff --git a/examples/ad4630/ad4630_example_simple_plot.py b/examples/ad4630/ad4630_example_simple_plot.py index 2571b2c22..25be7279c 100644 --- a/examples/ad4630/ad4630_example_simple_plot.py +++ b/examples/ad4630/ad4630_example_simple_plot.py @@ -29,6 +29,6 @@ for ch in range(0, len(data)): x = np.arange(0, len(data[ch])) - plt.figure(adc._ctrl._channels[ch]._name) + plt.figure(adc._ctrl.channels[ch]._name) plt.plot(x, data[ch]) plt.show() diff --git a/examples/adaq4224_example.py b/examples/adaq4224_example.py new file mode 100644 index 000000000..44c3669b2 --- /dev/null +++ b/examples/adaq4224_example.py @@ -0,0 +1,53 @@ +# Copyright (C) 2023 Analog Devices, Inc. +# +# SPDX short identifier: ADIBSD + +import sys + +import adi +import matplotlib.pyplot as plt +import numpy as np + +# Optionally pass URI as command line argument, +# else use default context manager search +my_uri = sys.argv[1] if len(sys.argv) >= 2 else None +print("uri: " + str(my_uri)) + +device_name = "adaq4224" + +adc = adi.adaq42xx(uri=my_uri, device_name=device_name) +adc.rx_buffer_size = 500 +adc.sample_rate = 2000000 +try: + adc.sample_averaging = 16 +except: + print("Sample average not supported in this mode") + +# Available gains are 0.33, 0.56, 2.22, 6.67 +# but due to Linux ABI technicalities they must be expressed with different values. + +gains_avail = adc.chan0.scale_available + +adc.chan0.scale = gains_avail[0] +print("Sampling with gain set to 0.33") + +data = adc.rx() + +for ch in range(0, len(data)): + x = np.arange(0, len(data[ch])) + plt.figure(adc._ctrl.channels[ch]._name) + plt.plot(x, data[ch]) +plt.title("Samples read with 0.33 gain") +plt.show() + +adc.chan0.scale = gains_avail[1] +print("Sampling with gain set to 0.56") + +data = adc.rx() + +for ch in range(0, len(data)): + x = np.arange(0, len(data[ch])) + plt.figure(adc._ctrl.channels[ch]._name) + plt.plot(x, data[ch]) +plt.title("Samples read with 0.56 gain") +plt.show() diff --git a/supported_parts.md b/supported_parts.md index 1ca860e33..c612e0846 100644 --- a/supported_parts.md +++ b/supported_parts.md @@ -105,6 +105,9 @@ - AD4858 - AD9739A - ADA4961 +- ADAQ4216 +- ADAQ4220 +- ADAQ4224 - ADAQ8092 - ADAR1000 - ADF4159 diff --git a/test/attr_tests.py b/test/attr_tests.py index a3fa0ab6f..b7f6dc08d 100644 --- a/test/attr_tests.py +++ b/test/attr_tests.py @@ -183,7 +183,9 @@ def attribute_single_value_pow2(uri, classname, attr, max_pow, tol, repeats=1): assert dev_interface(uri, classname, val, attr, tol) -def attribute_multiple_values(uri, classname, attr, values, tol, repeats=1, sleep=0): +def attribute_multiple_values( + uri, classname, attr, values, tol, repeats=1, sleep=0, sub_channel=None +): """attribute_multiple_values: Write and read back multiple class properties in a loop where all values are pre-defined. This is performed a defined number of times. @@ -201,11 +203,19 @@ def attribute_multiple_values(uri, classname, attr, values, tol, repeats=1, slee Allowable error of written value compared to read back value repeats: type=integer Number of times to repeatedly write values + sleep: type=integer + Seconds to sleep between writing to attribute and reading it back + sub_channel: type=string + Name of sub channel (nested class) to be tested """ for _ in range(repeats): for val in values: if isinstance(val, str): - assert dev_interface(uri, classname, val, attr, 0, sleep=sleep) + tol = 0 + if sub_channel: + assert dev_interface_sub_channel( + uri, classname, sub_channel, val, attr, tol, sleep=sleep + ) else: assert dev_interface(uri, classname, val, attr, tol, sleep=sleep) diff --git a/test/common.py b/test/common.py index 81001e56c..1c41c24e0 100644 --- a/test/common.py +++ b/test/common.py @@ -138,7 +138,7 @@ def dev_interface(uri, classname, val, attr, tol, sub_channel=None, sleep=0): def dev_interface_sub_channel( - uri, classname, sub_channel, val, attr, tol, readonly=False + uri, classname, sub_channel, val, attr, tol, readonly=False, sleep=0, ): sdr = eval(classname + "(uri='" + uri + "')") # Check hardware @@ -155,6 +155,8 @@ def dev_interface_sub_channel( if readonly is False: setattr(getattr(sdr, sub_channel), attr, val) + if sleep > 0: + time.sleep(sleep) rval = getattr(getattr(sdr, sub_channel), attr) del sdr diff --git a/test/emu/devices/adaq4224.xml b/test/emu/devices/adaq4224.xml new file mode 100644 index 000000000..9921dd9c5 --- /dev/null +++ b/test/emu/devices/adaq4224.xml @@ -0,0 +1 @@ +]> \ No newline at end of file diff --git a/test/emu/hardware_map.yml b/test/emu/hardware_map.yml index 8ed157055..a49b5d79c 100644 --- a/test/emu/hardware_map.yml +++ b/test/emu/hardware_map.yml @@ -309,6 +309,14 @@ ad4020: - filename: ad4020.xml - data_devices: - iio:device0 +adaq4224: + - adaq4224 + - pyadi_iio_class_support: + - adaq42xx + - emulate: + - filename: adaq4224.xml + - data_devices: + - iio:device0 ltc2387: - ltc2387 - pyadi_iio_class_support: diff --git a/test/test_ad4630.py b/test/test_ad4630.py index 35ab3ba37..53fdc8ec0 100644 --- a/test/test_ad4630.py +++ b/test/test_ad4630.py @@ -1,3 +1,4 @@ +import adi import pytest hardware = ["ad4030-24", "ad4630-24"] @@ -55,3 +56,40 @@ def test_ad4630_channel_attrs( test_attribute_single_value( iio_uri, classname, attr, start, stop, step, tol, repeats, sub_channel ) + + +hardware = ["adaq4224"] +classname = "adi.adaq42xx" + +######################################### +@pytest.mark.iio_hardware(hardware) +@pytest.mark.parametrize("classname", [(classname)]) +@pytest.mark.parametrize( + "attr, avail_attr, tol, repeats, sub_channel", + [("scale", "scale_available", 0, 1, "chan0",),], +) +def test_adaq42xx_scale_attr( + test_attribute_multiple_values, + iio_uri, + classname, + attr, + avail_attr, + tol, + repeats, + sub_channel, +): + # Get the device + sdr = eval(classname + "(uri='" + iio_uri + "')") + + # Check hardware + if not hasattr(sdr, sub_channel): + raise AttributeError(sub_channel + " not defined in " + classname) + if not hasattr(getattr(sdr, sub_channel), avail_attr): + raise AttributeError(avail_attr + " not defined in " + classname) + + # Get the list of available scale values + val = getattr(getattr(sdr, sub_channel), avail_attr) + + test_attribute_multiple_values( + iio_uri, classname, attr, val, tol, repeats, sub_channel=sub_channel + )