Skip to content

Commit

Permalink
deploy: 559ce47
Browse files Browse the repository at this point in the history
  • Loading branch information
tfcollins committed Oct 1, 2024
1 parent 5207383 commit b818341
Show file tree
Hide file tree
Showing 326 changed files with 72,449 additions and 0 deletions.
4 changes: 4 additions & 0 deletions prs/tfcollins/xdist/.buildinfo
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 49f6e9498ae2c8369e0064ee2a9b860e
tags: 645f666f9bcd5a90fca523b33c5a78b7
Empty file added prs/tfcollins/xdist/.nojekyll
Empty file.
22 changes: 22 additions & 0 deletions prs/tfcollins/xdist/_sources/attr/index.rst.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Attributes
==================

To simplify hardware configuration through different IIO drivers, basic class properties are exposed at the top-level of each device specific class. These properties abstract away the need to know a specific channel name, attribute type, source device name, and other details required in the libIIO API. Instead properties have easy to understand names, documentation, and error handling to help manage interfacing with different hardware. Property data can be read and written as follows from a given device interface class:

.. code-block:: python
import adi
lidar = adi.fmclidar1()
# Read current pulse width
print(lidar.laser_pulse_width)
# Change laser frequency to 1 MHz
lidar.laser_frequency = 1000000
If more detail is required about a specific property it can be directly inspected in the class definitions documnentation or in python itself through the help methods:


.. literalinclude:: pluto_help.cli
:language: none

For complete documentation about class properties reference the :doc:`supported devices</devices/index>` classes.
164 changes: 164 additions & 0 deletions prs/tfcollins/xdist/_sources/buffers/index.rst.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
Buffers
==================

Using buffers or transmitting and receiving data is done through interacting with two methods.

For receivers this is the **rx** method. How data is captured and therefore produced by this method is dependent on two main properties:

* **rx_enabled_channels**: This is an array of integers (or channel names) and the number of elements in the array will determine the number of list items returned by **rx**. For devices with complex data types these are the indexes of the complex channels, not the individual I or Q channels. When len(**rx_enabled_channels**) == 1, **rx** will return just a single array and not a single array within a list.
* **rx_buffer_size**: This is the number of samples returned in each array within the list. If the device produces complex data, like a transceiver, it will return complex data. This is defined by the author of each device specific class.

For transmitters this is the **tx** method. How data is sent and therefore must be passed by this method is dependent on one main property:

* **tx_enabled_channels**: This is an array of integers and the number of elements in the array will determine the number of items in the list to be submitted to **tx**. Like for **rx_enabled_channels**, devices with complex data types these are the indexes of the complex channels, not the individual I or Q channels. When only a single channel is enabled the data can be passed to **tx** as just an array and not an array within a list.

**rx_enabled_channels** must have a length greater than zero but **tx_enabled_channels** can be set to None or an empty list. In this case when **tx** is called it must be called without inputs. This is a special case and will connect a zero source into the TX input stream within the FPGA for FPGA based devices. For background on how this internally works with FPGA based devices reference the generic `DAC driver <https://wiki.analog.com/resources/tools-software/linux-drivers/iio-dds/axi-dac-dds-hdl>`_.

Cyclic Mode
--------------
In many cases, it can be useful to continuously transmit a signal over and over, even for just debugging and testing. This can be especially handy when the hardware you are using has very high transmit or receive rates, and therefore impossible to keep providing data to in real-time. To complement these use cases it is possible to create transmit buffer which repeats, which we call **cyclic buffers**. Cyclic buffers are identical or normal or non-cyclic buffers, except when they reach hardware they will continuously repeat or be transmitted. Here is a small example on how to create a cyclic buffer:

.. code-block:: python
import adi
sdr = adi.ad9361()
# Create a complex sinusoid
fc = 3000000
N = 1024
ts = 1 / 30000000.0
t = np.arange(0, N * ts, ts)
i = np.cos(2 * np.pi * t * fc) * 2 ** 14
q = np.sin(2 * np.pi * t * fc) * 2 ** 14
iq = i + 1j * q
# Enable cyclic buffers
sdr.tx_cyclic_buffer = True
# Send data cyclically
sdr.tx(iq)
At this point, the transmitter will keep transmitting the create sinusoid indefinitely until the buffer is destroyed or the *sdr* object destructor is called. Once data is pushed to hardware with a cyclic buffer the buffer must be manually destroyed or an error will occur if more data push. To update the buffer use the **tx_destroy_buffer** method before passing a new vector to the **tx** method.

Annotated Buffers
------------------

By default buffers appear as an array or a list of arrays. This can be confusing if all your channels do not produce similar data. For example, for IMUs like ADI16495 certain channels are for acceleration data and others are for angular velocity. To label this data the *rx_annotated* property can be used. When setting it to True the output of the **rx** method will be a dictionary with keys as channel names. Here an example:

.. code-block:: python
import adi
dev = adi.adis16495()
dev.rx_enabled_channels = [0, 3]
print(dev.rx())
dev.rx_annotated = True
print(dev.rx())
With output

.. code-block:: bash
[array([ 35681, 84055, -175914, -203645, 698249, -51670,
-1770250, 1529968, 2586191, -5353355, -827741, 11736339,
-9847894, -17242014, 97421833, 277496774], dtype=int32),
array([ 49151, 753663, 3571711, 9928703, 18956287,
25165823, 18612223, -10125313, -60850176, -114491392,
-131350528, -61521920, 135069695, 466845695, 899235839,
1362378751], dtype=int32)]
{'accel_x': array([1775091711, 2072264703, 2147483647, 2147483647, 2147483647,
2147483647, 2143404031, 2125430783, 2123120639, 2130821119,
2139488255, 2144911359, 2147041279, 2147467263, 2147483647,
2147483647], dtype=int32),
'anglvel_x': array([357750219, 335109279, 323033231, 337667193, 337100396, 330408402,
333459194, 335322576, 333247166, 333223475, 333996322, 333805525,
333659152, 333664680, 333718473, 333895650], dtype=int32)}
Buffer Units
---------------

For certain devices it is possible to convert types to scientific units, such as volts, degrees, or meters per second among others. This is controlled by setting the property **rx_output_type** to either *raw* or *SI*. If set to *SI*, returned data from the **rx** method will be in scientific units (assuming its supported by the driver). Below is an example using an IMU:

.. code-block:: python
import adi
dev = adi.adis16495()
dev.rx_annotated = True # Make channel names appear in data
dev.rx_enabled_channels = [3] # channel 0 is angular velocity in the x direction
print(dev.rx())
dev.rx_output_type = "SI"
print(dev.rx())
With output

.. code-block:: bash
{'anglvel_x': array([ 35644, 84039, -175647, -203867, 697612, -50201,
-1770177, 1526291, 2589741, -5349126, -839188, 11738313,
-9824911, -17267701, 97333042, 277410285], dtype=int32)}
{'anglvel_x': array([9.29996712, 9.71257202, 9.40097973, 9.78345151, 9.77009362,
9.59662456, 9.67300333, 9.71593538, 9.65847317, 9.6580597 ,
9.68022501, 9.67715545, 9.67511814, 9.67609361, 9.67323293,
9.67104074])}
To understand the exact scaling the driver documentation should be reviewed.

Members
--------------
.. automodule:: adi.rx_tx
:members:


Buffer Examples
---------------

Collect data from one channel

.. code-block:: python
import adi
sdr = adi.ad9361()
# Get complex data back
sdr.rx_enabled_channels = [0]
chan1 = sdr.rx()
Collect data from two channels

.. code-block:: python
import adi
sdr = adi.ad9361()
# Get both complex channel back
sdr.rx_enabled_channels = [0, 1]
data = sdr.rx()
chan1 = data[0]
chan2 = data[1]
Send data on two channels

.. code-block:: python
import adi
import numpy as np
# Create radio
sdr = adi.ad9371()
sdr.tx_enabled_channels = [0, 1]
# Create a sinewave waveform
N = 1024
fs = int(sdr.tx_sample_rate)
fc = 40000000
ts = 1 / float(fs)
t = np.arange(0, N * ts, ts)
i = np.cos(2 * np.pi * t * fc) * 2 ** 14
q = np.sin(2 * np.pi * t * fc) * 2 ** 14
iq = i + 1j * q
fc = -30000000
i = np.cos(2 * np.pi * t * fc) * 2 ** 14
q = np.sin(2 * np.pi * t * fc) * 2 ** 14
iq2 = i + 1j * q
# Send data to both channels
sdr.tx([iq, iq2])
175 changes: 175 additions & 0 deletions prs/tfcollins/xdist/_sources/dev/index.rst.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
Developers
===================

.. warning::
This section is only for developers and advanced users.

When submitting code or running tests, there are a few ways things are done in pyadi-iio.

Invoke
---------------------------
To make repetitve tasks easier, pyadi-iio utilizes pyinvoke. To see the available options (once pyinvoke is installed) run:

.. code-block:: console
invoke --list
Available tasks:
build Build python package
builddoc Build sphinx doc
changelog Print changelog from last release
checkparts Check for missing parts in supported_parts.md
createrelease Create GitHub release
libiiopath Search for libiio python bindings
precommit Run precommit checks
setup Install required python packages for development through pip
test Run pytest tests
Precommit
---------------------------
**pre-commit** is heavily relied on for keeping code in order and for eliminating certain bugs. Be sure to run these checks before submitting code. This can be run through pyinvoke or directly from the repo root as:

.. code-block:: console
invoke precommit
.. code-block:: console
pre-commit run --all-files
Testing
---------------------------

Testing pyadi-iio requires hardware, but fortunately by default it assumes no hardware is connected unless found. It will only load specific tests for hardware it can find and skip all other tests. **pytest**, which is the framework pyadi-iio uses, can be call as following:

.. code-block:: console
invoke test
.. code-block:: console
python3 -m pytest <add more arguments as needed>
Test Configuration
^^^^^^^^^^^^^^^^^^

There are several advanced features of pytest that are utilized by pyadi-iio. Specifically custom markers and custom plugins.

Markers are a way of labeling tests, which can be then used to filter specific tests. Markers are provided through the `test_map.py <https://github.com/analogdevicesinc/pyadi-iio/blob/master/test/test_map.py>`_ file in the test directory. These markers are used to map FPGA based boards with daughtercards to specific tests. `Reference design folder names <https://wiki.analog.com/resources/tools-software/linux-software/embedded_arm_images>`_ from the ADI SD cards are using as the markers, which them can be passed through the *-m* flag to enabled certain tests. For example, the following would enable all tests related to *ADRV9009*, assuming the hardware is available:

.. code-block:: console
python3 -m pytest -m zynqmp-zcu102-rev10-adrv9009
To help manage libiio contexts, filter tests based on those contexts, and map drivers to board definitions, pyadi-iio utilizes the pytest plugin `pytest-libiio <https://pypi.org/project/pytest-libiio/>`_. This must be installed before tests are run since all test implementations rely on `pytest-libiio fixtures <https://pytest-libiio.readthedocs.io/en/latest/fixtures/>`_. Generally, pyadi-iio will also use the `standard hardware map <https://pytest-libiio.readthedocs.io/en/latest/cli/#hardware-maps>`_ provided by *pytest-libiio* to map drivers to board definitions. To enable the hardware make requires the *--adi-hw-map* flag as:

.. code-block:: console
python3 -m pytest --adi-hw-map
If you are working on a driver or board that is not in the hardware map, a custom one can be created as documentation in the `pytest-libiio CLI <https://pytest-libiio.readthedocs.io/en/latest/cli/#hardware-maps>`_.



New Hardware Requirements
^^^^^^^^^^^^^^^^^^^^^^^^^

In order to maintain pyadi-iio, for all new drivers the development team will require emulation contexts to be submitted alongside the new class interfaces. This is to ensure that the new drivers are tested and maintained. Emulation contexts can be created using `xml_gen <https://pytest-libiio.readthedocs.io/en/latest/emulation/#adding-device-support>`_. CI will automatically validate that all hardware interfaces have emulation contexts and prevent merging if they are missing.

.. note::
Note that xml_gen is not the same as iio_genxml, as iio_genxml does not capture default values of properties required for emulation.


Test Functions and Fixtures
^^^^^^^^^^^^^^^^^^^^^^^^^^^

pyadi-iio has a large set of parameterizable fixtures for testing different device specific class interfaces. See the links belows to the different test categories:

.. toctree::
:maxdepth: 4

test_attr
test_dma
test_generics


Set Up Isolated Environment
---------------------------

This section will discuss a method to do isolated development with the correct package versions. The main purpose here is to eliminate any discrepancies that can arise (especially with the linting tools) when running precommit and other checks. This is also useful to not pollute your local global packages. The approach here relies upon leveraging **pyenv** and **pipenv** together.


Install pyenv
^^^^^^^^^^^^^^^^^

**pyenv** is a handy tool for installing different and isolated versions of python on your system. Since distributions can ship with rather random versions of python, pyenv can help us install exactly the versions we want. The quick way to install pyenv is with their bash script:


.. code-block:: bash
curl https://pyenv.run | bash
Add to your path and shell startup script (.bashrc, .zshrc, ...)

.. code-block:: bash
export PATH="/home/<username>/.pyenv/bin:$PATH
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
Install the desired python version
.. code-block:: bash
pyenv install 3.6.9
Create isolated install with pipenv
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Get the repo, set python version, and setup env
.. code-block:: bash
pip3 install -U pipenv
pyenv local 3.6.9
git clone [email protected]:analogdevicesinc/pyadi-iio.git
pipenv install
pipenv shell
pipenv install -r requirements.txt
pipenv install -r requirements_dev.txt
Now at this point we have all the necessary development packages to start working. If you close the current shell you will lose the environment. To return to it, go to the project folder and run:
.. code-block:: bash
cd <project folder>
pyenv local 3.6.9
pipenv shell
Emulation
---------------------------
By leveraging `iio-emu <https://github.com/analogdevicesinc/iio-emu>`_, hardware or contexts can be emulated for testing without physical devices. However, currently this emulation does not validate attribute rates, states of drivers, or equivalent data sources. This feature should be used to test a library itself rather than hardware drivers.
**pyadi-iio** uses *iio-emu* through *pytest-libiio*, which handles loading the correct context files based on the fixtures used for each test. Essentially, when *pytest* is run, based on the fixture below, *pytest-libiio* will spawn the correct context with *iio-emu* and pass the URI of that context to the test.
.. code-block:: python
import pytest
import iio
@pytest.mark.iio_hardware("pluto", False) # Set True disables test during emulation
def test_libiio_device(iio_uri):
ctx = iio.Context(iio_uri)
...
To create and add more context files for testing with **pyadi-iio** follow `this page <https://pytest-libiio.readthedocs.io/en/latest/emulation/>`_.
7 changes: 7 additions & 0 deletions prs/tfcollins/xdist/_sources/dev/test_attr.rst.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Attribute Tests
=================

Functions used by test fixtures for evaluating attributes and driver state.

.. automodule:: test.attr_tests
:members:
7 changes: 7 additions & 0 deletions prs/tfcollins/xdist/_sources/dev/test_dma.rst.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
DMA Tests
=================

Functions used by test fixtures for evaluating receive and transmit DMA/buffers

.. automodule:: test.dma_tests
:members:
7 changes: 7 additions & 0 deletions prs/tfcollins/xdist/_sources/dev/test_generics.rst.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Generic Tests
=================

Functions used by test fixtures for evaluating drivers without pyadi-iio classes or not by using pyadi-iio classes.

.. automodule:: test.generics
:members:
Loading

0 comments on commit b818341

Please sign in to comment.