Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pyadi support for ADAQ4001 and ADAQ4003 #571

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

machschmitt
Copy link
Contributor

@machschmitt machschmitt commented May 23, 2024

Description

This PR adds pyadi support for ADAQ4001 and ADAQ4003.

AD4003/AD4007/AD4011, AD4020/AD4021/AD4022, and ADAQ4001/ADAQ4003 belong
to a series of differential SAR ADC devices which are supported by the
same Linux kernel driver.

This PR:

  • Adds a python class for ADAQ4001 and ADAQ4003.
  • Adds scale_available attribute to classes supporting AD4000 series devices.
  • Adds iio-emu emulation model for ADAQ4003.
  • Adds automated tests for ADAQ4003 class.
  • Updates to ad4020 example so it works for other devices belonging to the AD4000 series.

Related JIRA task: https://jira.analog.com/browse/COSGTM-277

Type of change

  • New feature (non-breaking change which adds functionality)

How has this been tested?

  • Tested with iio-emu emulation model generated from ADAQ4003 device on Linux
  • Tested on CoraZ7 with Eval-ADAQ4003 and m2k+Scopy for input signal generation.

Test Configuration:

  • Hardware: iio-emu model generated from a ADAQ4003 device running on Linux
  • OS: Kuiper Linux

Documentation

The new python class was added to the list of documented classes provided by the ad4020 python module.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have signed off all commits and they contain "Signed-off by: "
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Comment on lines +79 to +81
@property
def scale_available(self):
"""Provides all available scale(gain) settings for the ADC channel"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@machschmitt I believe these are all fixed-scale devices. Accessing the scale_available property throws a KeyError.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well yes, at least what is in ADI Linux right now.
Though, the driver will provide a scale_available attribute in the future for configuring span compression.
Some explanation taken from commit log:
AD4000 series of devices have a span compression feature that allows
reducing the ADC input range while keeping the same range of raw output
codes, thus providing a slight increase in measurement precision.
The span compression selection (enable/disable) is done by writing to
the scale attribute. The list of valid scale values is listed by the
scale_available attribute.

@tfcollins, can we have scale_available attribute (even if it is not working right now) or would you prefer to only introduce it when the changes get to ADI Linux?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@machschmitt noted - although has the decision been made whether the compression will be changeable at run time, or set in the device tree? Typically the decision would be made as the analog front end circuit is being designed, then "set and forget" in software / device tree.
But if it will be run-time adjustable, this makes perfect sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @mthoren-adi, I was developing this as a runtime feature but it could definitely be set in device tree as you said so that's indeed a good question. Who can I ask about this configuration decision? Part application engineer?

adi/ad4020.py Outdated
Comment on lines 84 to 86
def __call__(self, mV=None):
"""Convenience function, set / get voltages in SI units (millivolts)"""
if mV is not None:
self.raw = int(float(mV) / float(self.scale))
return self.raw * self.scale
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@machschmitt raw is a read-only attribute, attempting to set throws an error. (the mV=None argument is not needed.) Looks like this was carried over from a DAC device.
Also the formula should include the offset:
((self.raw + self.offset) * self.scale)
so an offset property should also be added. (The device does have an offset attribute.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's true. I don't even recall where I got that from but am fixing it now.
The driver doesn't provide offset attribute right now but it may in the future for scenarios where differential ADCs get set in single-ended to differential configuration.

@pytest.mark.parametrize("classname", [(classname)])
@pytest.mark.parametrize(
"attr, avail_attr, tol, repeats, sub_channel",
[("scale", "scale_available", 0, 1, "voltage0-voltage1",),],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted above, there is no scale_available. Also this is a single-channel device with a single differential input, voltage0. The voltage0-voltage-1 nomenclature is typical of multichannel devices that can be arbitrarily configured.
Note that I am looking at the ADAQ4003 on Cora, but I see zynq-zed-adv7511-adxxxx noted below. The ADAQ4003 attributes, channel, etc.

@thorenscientific
Copy link
Contributor

thorenscientific commented May 26, 2024

@machschmitt the only other peculiarity I notice is that the sampling_frequency can only be set if there's no buffer created. That is, initially I can set the sample rate, but after calling rx(), attempting to set the sample rate throws:
OSError: [Errno 16] Resource device
Calling rx_destroy_buffer() allows the sample rate to be changed again. If this is expected behavior that's fine, just wondering if there's a cleaner way of handling it, either by calling rx_destroy_buffer() within the sampling_frequency setter, or just calling it in the example with a comment to the effect of:
"can't set the sampling frequency if a buffer is created"
(@tfcollins any thoughts?)

@tfcollins
Copy link
Collaborator

@machschmitt the only other peculiarity I notice is that the sampling_frequency can only be set if there's no buffer created. That is, initially I can set the sample rate, but after calling rx(), attempting to set the sample rate throws: OSError: [Errno 16] Resource device Calling rx_destroy_buffer() allows the sample rate to be changed again. If this is expected behavior that's fine, just wondering if there's a cleaner way of handling it, either by calling rx_destroy_buffer() within the sampling_frequency setter, or just calling it in the example with a comment to the effect of: "can't set the sampling frequency if a buffer is created" (@tfcollins any thoughts?)

The "nice" way to do this is with a decorator that manages it for you like so: https://github.com/analogdevicesinc/pyadi-iio/blob/main/adi/cn0540.py#L81

@machschmitt
Copy link
Contributor Author

Updated according to suggestions.
Regarding the voltage0-voltage-1 nomenclature, the devices can be set in Single-Ended to Differential Configuration, where they would sample like a single-ended ADC, and thus the channel type would be configurable. The current driver version does not support that but it may in the future, at which point, the interface would not reflect the channel type configuration if it is called just voltage0.

Change log v1 -> v2:

  • replace match and case device selection (which were introduced only in Python 3.10) by cascaded if-else that are supported in Python >=3.7.
  • Added reset_buffer convenience decorator to automatically disable the read buffer when a device configuration operation is issued.

@machschmitt machschmitt force-pushed the adaq4003-rebased branch 3 times, most recently from aef7b26 to 3814809 Compare July 8, 2024 13:16
@machschmitt
Copy link
Contributor Author

I reverted the patch adding the reset_buffer convenience decorator because it was failing pytest and I am having trouble trying to run pytests with iio-emu locally. It looks like pytest is not starting iio-emu on my setup for it was failing with
"Exception: No device found" and "Connection timed out".

The launch command was

pytest -vs --emu --emu-xml-dir=test/emu/devices/ --junitxml="results.xml" -k 'not prod' test/test_adaq4003.py

I updated and reinstalled libtinyiiod, iio-emu, and pyadi-iio, but the issue was not solved so I reverted the offending patch.

Comment on lines 25 to 26
# if not hasattr(sdr, attr):
# raise AttributeError(attr + " not defined in " + classname)
Copy link
Collaborator

@tfcollins tfcollins Aug 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy paste error on the last 2 lines?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy paste error on the last 2 lines?

Yes. Removed those in newer version.

AD4003/AD4007/AD4011, AD4020/AD4021/AD4022, and ADAQ4001/ADAQ4003 belong
to a series of differential SAR ADC devices which are supported by the
same Linux kernel driver.
Add python class for ADAQ4001 and ADAQ4003.

Signed-off-by: Marcelo Schmitt <[email protected]>
Add adaq4003 python class to the list of classes provided by ad4020 module.

Signed-off-by: Marcelo Schmitt <[email protected]>
AD4000 series of devices have a span compression feature that allows
reducing the ADC input range while keeping the same range of raw output
codes, thus providing a slightly increase in measurement precision.
The span compression selection (enable/disable) is done by writing to
the scale attribute. The list of valid scale values is listed by the
scale_available attribute.

Signed-off-by: Marcelo Schmitt <[email protected]>
Add iio-emu xml for emulating ADAQ4003 devices.
Add hardware map entry for ADAQ4003.

Signed-off-by: Marcelo Schmitt <[email protected]>
Add tests for adaq4003 python class.

Tests included:
- Buffered capture test with DMA
- Sampling frequency attribute test
- Scale attribute test

Signed-off-by: Marcelo Schmitt <[email protected]>
Use python argparse [1] to handle command-line arguments thus making the
example more flexible with respect to its input arguments.
This also extends the example to work with other ADCs that belong to the
AD4000 series by enabling a device argument and using it to instantiate
the correct ADC object.

[1]: https://docs.python.org/3/library/argparse.html

Signed-off-by: Marcelo Schmitt <[email protected]>
The scale may vary according to ADC characteristics such as precision
bits, span compression, gain, etc. and also with setup properties such
as voltage reference.
Use driver provided scale to convert back to milli-voltage units which
is the standard IIO unit for voltage type channels.

Signed-off-by: Marcelo Schmitt <[email protected]>
The voltage spectrum plot quickly closed with almost no time the user to
see it. Keep the plot visible for the user to analyse it.

Signed-off-by: Marcelo Schmitt <[email protected]>
@machschmitt
Copy link
Contributor Author

Hi @tfcollins,

Only changes to this PR were rebasing to main branch and removing leftover comments.
Meanwhile, the driver for this parts made its way to mainline Linux
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/iio/adc/ad4000.c?h=v6.12-rc2
Would you like me to make any additional changes?

Unrelated to this PR, I re-run pytest and got Exception: No device found for every pytest I tried (tests that are and are not in this PR). I had pytest running a few months ago but couldn't ran them anymore after I started getting this error.
I have installed libiio v1 because was getting some error about iio_get_backends_count with libiio v0.25.
I have installed pytest-libiio (https://pytest-libiio.readthedocs.io/en/latest/#installation).
I have also re-installed iio-emu and tinyiiod after the no device error.
Well, anyway, just wondering if anybody has seen something like that.

@tfcollins
Copy link
Collaborator

tfcollins commented Oct 9, 2024

Unrelated to this PR, I re-run pytest and got Exception: No device found for every pytest I tried (tests that are and are not in this PR). I had pytest running a few months ago but couldn't ran them anymore after I started getting this error.

This is what I get when I target those updated tests:

python -m pytest -vs -k adaq400 --emu
============================ test session starts ============================
platform linux -- Python 3.10.12, pytest-8.3.3, pluggy-1.5.0 -- /tmp/ci/pyadi-iio/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.10.12', 'Platform': 'Linux-6.5.0-45-generic-x86_64-with-glibc2.35', 'Packages': {'pytest': '8.3.3', 'pluggy': '1.5.0'}, 'Plugins': {'cov': '5.0.0', 'xdist': '3.6.1', 'libiio': '0.0.21', 'html': '3.2.0', 'metadata': '3.1.1'}}
rootdir: /tmp/ci/pyadi-iio
configfile: pyproject.toml
testpaths: test
plugins: cov-5.0.0, xdist-3.6.1, libiio-0.0.21, html-3.2.0, metadata-3.1.1
collected 1645 items / 1642 deselected / 3 selected                         

test/test_adaq4003.py::test_adaq4003_attr[sampling_frequency-val0-adi.adaq4003] exml test/emu/devices/adaq4003.xml
Starting iio-emu
Virtual device: generic
Waiting for connections ...
SKIPPED
test/test_adaq4003.py::test_adaq4003_scale_attr[scale-scale_available-0-1-voltage0-voltage1-adi.adaq4003] PASSED
test/test_adaq4003.py::test_adaq4003_rx_data[0-adi.adaq4003] SKIPPEDServer stopped


=============== 1 passed, 2 skipped, 1642 deselected in 4.67s ===============

That expected?

I have installed libiio v1 because was getting some error about iio_get_backends_count with libiio v0.25.

iio_get_backends_count is in the compat layer of v1 so it may not exist in your build. My guess is you are using the old v0.25 python bindings with v1. Be careful not to mix these.

@machschmitt
Copy link
Contributor Author

This is what I get when I target those updated tests:

python -m pytest -vs -k adaq400 --emu
============================ test session starts ============================
platform linux -- Python 3.10.12, pytest-8.3.3, pluggy-1.5.0 -- /tmp/ci/pyadi-iio/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.10.12', 'Platform': 'Linux-6.5.0-45-generic-x86_64-with-glibc2.35', 'Packages': {'pytest': '8.3.3', 'pluggy': '1.5.0'}, 'Plugins': {'cov': '5.0.0', 'xdist': '3.6.1', 'libiio': '0.0.21', 'html': '3.2.0', 'metadata': '3.1.1'}}
rootdir: /tmp/ci/pyadi-iio
configfile: pyproject.toml
testpaths: test
plugins: cov-5.0.0, xdist-3.6.1, libiio-0.0.21, html-3.2.0, metadata-3.1.1
collected 1645 items / 1642 deselected / 3 selected                         

test/test_adaq4003.py::test_adaq4003_attr[sampling_frequency-val0-adi.adaq4003] exml test/emu/devices/adaq4003.xml
Starting iio-emu
Virtual device: generic
Waiting for connections ...
SKIPPED
test/test_adaq4003.py::test_adaq4003_scale_attr[scale-scale_available-0-1-voltage0-voltage1-adi.adaq4003] PASSED
test/test_adaq4003.py::test_adaq4003_rx_data[0-adi.adaq4003] SKIPPEDServer stopped


=============== 1 passed, 2 skipped, 1642 deselected in 4.67s ===============

That expected?

I get this:

python -m pytest -vs -k adaq400 --emu                                                                                                                                                        
======================================== test session starts ======================================
platform linux -- Python 3.11.9, pytest-7.4.2, pluggy-1.3.0 -- /home/marcelo/linux_kernel/pyadi-iio/venv/bin/python
cachedir: .pytest_cache                                                                                                                                                                        
metadata: {'Python': '3.11.9', 'Platform': 'Linux-6.9.9-amd64-x86_64-with-glibc2.38', 'Packages': {'pytest': '7.4.2', 'pluggy': '1.3.0'}, 'Plugins': {'anyio': '4.0.0', 'html': '3.2.0', 'metad
ata': '3.0.0', 'cov': '4.1.0', 'libiio': '0.0.21', 'xdist': '3.6.1'}}                                                                                                                          
rootdir: /home/marcelo/linux_kernel/pyadi-iio                                                                                                                                                  
configfile: pyproject.toml                                                                                                                                                                     
testpaths: test                                                                                
plugins: anyio-4.0.0, html-3.2.0, metadata-3.0.0, cov-4.1.0, libiio-0.0.21, xdist-3.6.1                                                                                                        
collected 1645 items / 1642 deselected / 3 selected                                                                                                                                           

test/test_adaq4003.py::test_adaq4003_attr[sampling_frequency-val0-adi.adaq4003] exml test/emu/devices/adaq4003.xml
Starting iio-emu
Virtual device: generic
FAILED
test/test_adaq4003.py::test_adaq4003_scale_attr[scale-scale_available-0-1-voltage0-voltage1-adi.adaq4003] FAILED
test/test_adaq4003.py::test_adaq4003_rx_data[0-adi.adaq4003] SKIPPED (Test not valid in emulation mode)

<lots of error logs>

=================================== short test summary info ===================================
FAILED test/test_adaq4003.py::test_adaq4003_attr[sampling_frequency-val0-adi.adaq4003] - Exception: No device found
FAILED test/test_adaq4003.py::test_adaq4003_scale_attr[scale-scale_available-0-1-voltage0-voltage1-adi.adaq4003] - Exception: No device found
======================== 2 failed, 1 skipped, 1642 deselected in 4.80s ========================

It looks like pyadi can't connect to the IIO context.
I also see iio.py:75: ConnectionRefusedError in the error logs.

iio_get_backends_count is in the compat layer of v1 so it may not exist in your build. My guess is you are using the old v0.25 python bindings with v1. Be careful not to mix these.

In the same venv I use to run pyadi tests and examples I have the following:

$ python
Python 3.11.9 (main, Apr 10 2024, 13:16:36) [GCC 13.2.0] on linux
>>> import iio
>>> iio.version
(1, 0, 'fea2a834')
>>> import adi
>>> adi.__version__
'0.0.19'

I did try to update libiio for my venv, by re-installing libiio v1 locally and running the python bindings setup script.

cd <path_to_libiio>/build/bindings/python
chmod +x setup.py
sudo ./setup.py install_egg_info --install-dir <path_to_pyadi-iio>/venv/lib/python3.11/site-packages
cp <path_to_libiio>/build/bindings/python/iio.py <path_to_pyadi-iio>/venv/lib/python3.11/site-packages/

Don't want to drag anyone into debugging specific setups so I'm happy with any advice on how to (re)set pyadi venv installation to any known working configuration.

@tfcollins
Copy link
Collaborator

O sorry forgot to mention. iio-emu does not support libiio v1 so all the tests will fail ish.
If CI is working and the necessary tests are running that's fine

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants