From 0fcd1400c3da08961fbecc4e303b32ae75c6346a Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Tue, 28 Nov 2023 08:29:47 -0700 Subject: [PATCH 01/15] Update CI to use libiio-v1. Disable Windows conda builds for now Signed-off-by: Travis F. Collins --- .github/scripts/install_libiio.sh | 15 ++++++++++++++- .github/scripts/install_part_libs.sh | 8 +++++--- .github/scripts/install_pydeps.sh | 2 +- .github/workflows/win-test.yml | 3 ++- requirements.txt | 2 +- requirements_dev.txt | 2 +- 6 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.github/scripts/install_libiio.sh b/.github/scripts/install_libiio.sh index c4b0123f3..18b2cc1a8 100755 --- a/.github/scripts/install_libiio.sh +++ b/.github/scripts/install_libiio.sh @@ -1,9 +1,22 @@ #!/bin/bash sudo apt-get -qq update sudo apt-get install -y git cmake graphviz libavahi-common-dev libavahi-client-dev libaio-dev libusb-1.0-0-dev libxml2-dev rpm tar bzip2 gzip flex bison git -git clone -b 'v0.25' --single-branch --depth 1 https://github.com/analogdevicesinc/libiio.git +git clone -b 'main' --single-branch --depth 1 https://github.com/analogdevicesinc/libiio.git cd libiio cmake . -DHAVE_DNS_SD=OFF make sudo make install cd .. +rm -rf libiio + +# Python pieces +sudo apt-get install -y python3-pip python3-setuptools +git clone -b 'main' --single-branch --depth 1 https://github.com/analogdevicesinc/libiio.git +cd libiio +cmake . -DHAVE_DNS_SD=OFF -DPYTHON_BINDINGS=ON +make +cd bindings/python +pip install . +cd ../.. +cd .. +rm -rf libiio diff --git a/.github/scripts/install_part_libs.sh b/.github/scripts/install_part_libs.sh index 5109bb93a..82df7d733 100755 --- a/.github/scripts/install_part_libs.sh +++ b/.github/scripts/install_part_libs.sh @@ -1,7 +1,9 @@ #!/bin/bash +exit 0 + git clone -b 'master' --single-branch --depth 1 https://github.com/analogdevicesinc/libad9361-iio.git cd libad9361-iio -cmake -DPYTHON_BINDINGS=ON . +cmake -DPYTHON_BINDINGS=ON -DLIBIIO_INCLUDEDIR=/usr/include/iio . make cd bindings/python pip install . @@ -13,7 +15,7 @@ rm -rf libad9361-iio git clone -b 'master' --single-branch --depth 1 https://github.com/analogdevicesinc/libad9166-iio.git cd libad9166-iio -cmake -DPYTHON_BINDINGS=ON . +cmake -DPYTHON_BINDINGS=ON -DLIBIIO_INCLUDEDIR=/usr/include/iio . make cd bindings/python pip install . @@ -23,4 +25,4 @@ sudo ldconfig cd .. rm -rf libad9166-iio -pip install pylibiio==0.23.1 +# pip install pylibiio==0.23.1 diff --git a/.github/scripts/install_pydeps.sh b/.github/scripts/install_pydeps.sh index 64849051c..93351f065 100644 --- a/.github/scripts/install_pydeps.sh +++ b/.github/scripts/install_pydeps.sh @@ -2,4 +2,4 @@ sudo apt-get install -y python3-pip python3-setuptools pip install -r requirements.txt pip install -r requirements_dev.txt -pip install pylibiio +# pip install pylibiio diff --git a/.github/workflows/win-test.yml b/.github/workflows/win-test.yml index ff4d3f8bf..3441ce59c 100644 --- a/.github/workflows/win-test.yml +++ b/.github/workflows/win-test.yml @@ -1,6 +1,7 @@ name: Windows Tests -on: [push, pull_request] +# on: [push, pull_request] +on: [pull_request] jobs: CondaBased: diff --git a/requirements.txt b/requirements.txt index 1e46aa48a..0940b6f53 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ numpy>=1.20 -pylibiio==0.23.1 +pylibiio>=1.0 paramiko diff --git a/requirements_dev.txt b/requirements_dev.txt index 5b14a23b7..f6a684d50 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -8,7 +8,7 @@ scapy scipy pytest-cov coveralls -pytest-libiio==0.0.13 +pytest-libiio@git+https://github.com/tfcollins/pytest-libiio.git@libiio-v1-support bump2version pytest-html plotly-express From 24b27950120a0d854c0361d2a05d40bee6046966 Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Tue, 28 Nov 2023 13:38:59 -0700 Subject: [PATCH 02/15] Update RX interface to use stream and block APIs Signed-off-by: Travis F. Collins --- .github/scripts/install_iioemu.sh | 0 adi/rx_tx.py | 32 ++++++++++++++++++++----------- 2 files changed, 21 insertions(+), 11 deletions(-) mode change 100644 => 100755 .github/scripts/install_iioemu.sh diff --git a/.github/scripts/install_iioemu.sh b/.github/scripts/install_iioemu.sh old mode 100644 new mode 100755 diff --git a/adi/rx_tx.py b/adi/rx_tx.py index 23fbb8b7b..05115d168 100644 --- a/adi/rx_tx.py +++ b/adi/rx_tx.py @@ -43,6 +43,9 @@ class rx(rx_tx_common): _rx_unbuffered_data = False _rx_annotated = False _rx_stack_interleaved = True # Convert from channel to sample interleaved + _rx_buffer_mask = None + __rx_stream = None + _rx_buffer_num_blocks = 4 def __init__(self, rx_buffer_size=1024): if self._complex_data: @@ -178,23 +181,30 @@ def __get_rx_channel_offsets(self): return rx_offset def _rx_init_channels(self): - for m in self._rx_channel_names: - v = self._rxadc.find_channel(m) - if not v: - raise Exception(f"Channel {m} not found") - v.enabled = False + if not self._rx_buffer_mask: + self._rx_buffer_mask = iio.ChannelsMask(self._rxadc) + + channels = [] if self._complex_data: for m in self.rx_enabled_channels: v = self._rxadc.find_channel(self._rx_channel_names[m * 2]) - v.enabled = True + channels.append(v) v = self._rxadc.find_channel(self._rx_channel_names[m * 2 + 1]) - v.enabled = True + channels.append(v) else: for m in self.rx_enabled_channels: v = self._rxadc.find_channel(self._rx_channel_names[m]) - v.enabled = True - self.__rxbuf = iio.Buffer(self._rxadc, self.__rx_buffer_size, False) + channels.append(v) + + self._rx_buffer_mask.channels = channels + + self.__rxbuf = iio.Buffer(self._rxadc, self._rx_buffer_mask) + self.__rx_stream = iio.Stream( + buffer=self.__rxbuf, + nb_blocks=self._rx_buffer_num_blocks, + samples_count=self.rx_buffer_size, + ) def __rx_unbuffered_data(self): x = [] @@ -232,7 +242,7 @@ def __rx_buffered_data(self) -> Union[List[np.ndarray], np.ndarray]: """ if not self.__rxbuf: self._rx_init_channels() - self.__rxbuf.refill() + rx_block = next(self.__rx_stream) data_channel_interleaved = [] ecn = [] @@ -246,7 +256,7 @@ def __rx_buffered_data(self) -> Union[List[np.ndarray], np.ndarray]: for name in ecn: chan = self._rxadc.find_channel(name) - bytearray_data = chan.read(self.__rxbuf) # Do local type conversion + bytearray_data = chan.read(rx_block) # Do local type conversion # create format strings df = chan.data_format fmt = ("i" if df.is_signed is True else "u") + str(df.length // 8) From 0257a5416c7d65db8fda3845da50eca85a9c11ed Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Wed, 6 Dec 2023 11:30:33 -0700 Subject: [PATCH 03/15] Add compatibility layer for libiio 1.x Signed-off-by: Travis F. Collins --- adi/compat.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++ adi/dds.py | 2 ++ adi/rx_tx.py | 49 +++++++++++++-------------- 3 files changed, 115 insertions(+), 27 deletions(-) create mode 100644 adi/compat.py diff --git a/adi/compat.py b/adi/compat.py new file mode 100644 index 000000000..88c641f93 --- /dev/null +++ b/adi/compat.py @@ -0,0 +1,91 @@ +# Copyright (C) 2023 Analog Devices, Inc. +# +# SPDX short identifier: ADIBSD +"""Compatibility module for libiio v1.X.""" + +from typing import Any + +import iio + + +class compat_libiio: + """Compatibility class for libiio v1.X.""" + + def __is_libiio_v1(self): + """Check is we are using >= v1.X.""" + v = iio.version + return v[0] >= 1 + + def _setup_v1_rx(self): + """Setup for libiio v1.X RX side.""" + self._rx_buffer_mask = None + self.__rx_stream = None + self._rx_buffer_num_blocks = 4 + + def _setup_v1_tx(self): + """Setup for libiio v1.X TX side.""" + self._tx_buffer_mask = None + self.__tx_stream = None + self._tx_buffer_num_blocks = 4 + + def __getattr__(self, __name: str) -> Any: + if __name in ["_rx_init_channels", "_tx_init_channels", "__tx_buffer_push"]: + if self.__is_libiio_v1(): + return getattr(self, f"_{__name}_v1") + else: + return getattr(self, f"_{__name}") + + def _rx_init_channels_v1(self): + if not self._rx_buffer_mask: + self._rx_buffer_mask = iio.ChannelsMask(self._rxadc) + + channels = [] + if self._complex_data: + for m in self.rx_enabled_channels: + v = self._rxadc.find_channel(self._rx_channel_names[m * 2]) + channels.append(v) + v = self._rxadc.find_channel(self._rx_channel_names[m * 2 + 1]) + channels.append(v) + else: + for m in self.rx_enabled_channels: + v = self._rxadc.find_channel(self._rx_channel_names[m]) + channels.append(v) + + self._rx_buffer_mask.channels = channels + + self.__rxbuf = iio.Buffer(self._rxadc, self._rx_buffer_mask) + self.__rx_stream = iio.Stream( + buffer=self.__rxbuf, + nb_blocks=self._rx_buffer_num_blocks, + samples_count=self.rx_buffer_size, + ) + + def _tx_init_channels_v1(self): + if not self._tx_buffer_mask: + self._tx_buffer_mask = iio.ChannelsMask(self._txdac) + + channels = [] + if self._complex_data: + for m in self.tx_enabled_channels: + v = self._txdac.find_channel(self._tx_channel_names[m * 2], True) + channels.append(v) + v = self._txdac.find_channel(self._tx_channel_names[m * 2 + 1], True) + channels.append(v) + else: + for m in self.tx_enabled_channels: + v = self._txdac.find_channel(self._tx_channel_names[m], True) + channels.append(v) + + self._tx_buffer_mask.channels = channels + + self.__txbuf = iio.Buffer(self._txdac, self._tx_buffer_mask) + self.__block_tx = iio.Block(self.__txbuf, self._tx_buffer_size) + + def __tx_buffer_push_v1(self, data): + """Push data to TX buffer. + + data: bytearray + """ + self.__block_tx.write(data) + self.__block_tx.enqueue(None, self.__tx_cyclic_buffer) + self.__txbuf.enabled = True diff --git a/adi/dds.py b/adi/dds.py index 33c99d544..22247c8a0 100644 --- a/adi/dds.py +++ b/adi/dds.py @@ -142,6 +142,8 @@ def dds_single_tone(self, frequency, scale, channel=0): + "_F1", True, ) + if not chan: + raise Exception(f"DDS Channel {channel} not found") chan.attrs["frequency"].value = str(frequency) chan.attrs["phase"].value = str(90000) chan.attrs["scale"].value = str(scale) diff --git a/adi/rx_tx.py b/adi/rx_tx.py index 05115d168..6ae03a663 100644 --- a/adi/rx_tx.py +++ b/adi/rx_tx.py @@ -9,6 +9,7 @@ import numpy as np from adi.attribute import attribute +from adi.compat import compat_libiio from adi.context_manager import context_manager from adi.dds import dds @@ -20,7 +21,7 @@ def __del__(self): self._ctrl = [] -class rx_tx_common(attribute): +class rx_tx_common(attribute, compat_libiio): """Common functions for RX and TX""" def _annotate(self, data, cnames: List[str], echans: List[int]): @@ -43,9 +44,6 @@ class rx(rx_tx_common): _rx_unbuffered_data = False _rx_annotated = False _rx_stack_interleaved = True # Convert from channel to sample interleaved - _rx_buffer_mask = None - __rx_stream = None - _rx_buffer_num_blocks = 4 def __init__(self, rx_buffer_size=1024): if self._complex_data: @@ -56,6 +54,8 @@ def __init__(self, rx_buffer_size=1024): self._num_rx_channels = len(self._rx_channel_names) self.rx_enabled_channels = rx_enabled_channels self.rx_buffer_size = rx_buffer_size + if self.__is_libiio_v1(): + self._setup_v1_rx() @property def rx_channel_names(self) -> List[str]: @@ -181,30 +181,23 @@ def __get_rx_channel_offsets(self): return rx_offset def _rx_init_channels(self): + for m in self._rx_channel_names: + v = self._rxadc.find_channel(m) + if not v: + raise Exception(f"Channel {m} not found") + v.enabled = False - if not self._rx_buffer_mask: - self._rx_buffer_mask = iio.ChannelsMask(self._rxadc) - - channels = [] if self._complex_data: for m in self.rx_enabled_channels: v = self._rxadc.find_channel(self._rx_channel_names[m * 2]) - channels.append(v) + v.enabled = True v = self._rxadc.find_channel(self._rx_channel_names[m * 2 + 1]) - channels.append(v) + v.enabled = True else: for m in self.rx_enabled_channels: v = self._rxadc.find_channel(self._rx_channel_names[m]) - channels.append(v) - - self._rx_buffer_mask.channels = channels - - self.__rxbuf = iio.Buffer(self._rxadc, self._rx_buffer_mask) - self.__rx_stream = iio.Stream( - buffer=self.__rxbuf, - nb_blocks=self._rx_buffer_num_blocks, - samples_count=self.rx_buffer_size, - ) + v.enabled = True + self.__rxbuf = iio.Buffer(self._rxadc, self.__rx_buffer_size, False) def __rx_unbuffered_data(self): x = [] @@ -242,7 +235,7 @@ def __rx_buffered_data(self) -> Union[List[np.ndarray], np.ndarray]: """ if not self.__rxbuf: self._rx_init_channels() - rx_block = next(self.__rx_stream) + self.__rxbuf.refill() data_channel_interleaved = [] ecn = [] @@ -256,7 +249,7 @@ def __rx_buffered_data(self) -> Union[List[np.ndarray], np.ndarray]: for name in ecn: chan = self._rxadc.find_channel(name) - bytearray_data = chan.read(rx_block) # Do local type conversion + bytearray_data = chan.read(self.__rxbuf) # Do local type conversion # create format strings df = chan.data_format fmt = ("i" if df.is_signed is True else "u") + str(df.length // 8) @@ -332,6 +325,8 @@ def __init__(self, tx_cyclic_buffer=False): self.tx_enabled_channels = tx_enabled_channels self.tx_cyclic_buffer = tx_cyclic_buffer dds.__init__(self) + if self.__is_libiio_v1(): + self._setup_v1_tx() def __del__(self): self.__txbuf = [] @@ -518,8 +513,11 @@ def tx(self, data_np=None): f.write(bytearray(data)) f.close() else: - self.__txbuf.write(bytearray(data)) - self.__txbuf.push() + self.__tx_buffer_push(data) + + def __tx_buffer_push(self, data): + self.__txbuf.write(bytearray(data)) + self.__txbuf.push() class rx_tx(rx, tx, phy): @@ -594,7 +592,6 @@ def __handle_init_args(self, args, kwargs): def __init__( self, *args: Union[str, iio.Context], **kwargs: Union[str, iio.Context] ) -> None: - # Handle Older API and Newer APIs uri_ctx = self.__handle_init_args(args, kwargs) @@ -653,7 +650,6 @@ def _rx_data_device_name(self) -> None: def __init__( self, *args: Union[str, iio.Context], **kwargs: Union[str, iio.Context] ) -> None: - shared_def.__init__(self, *args, **kwargs) if self._rx_data_device_name: @@ -701,7 +697,6 @@ def _tx_data_device_name(self) -> None: def __init__( self, *args: Union[str, iio.Context], **kwargs: Union[str, iio.Context] ) -> None: - shared_def.__init__(self, *args, **kwargs) if self._tx_data_device_name: From 1d9e73ac6e73871a4db6c0cbdc64666c2d7158d0 Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Wed, 6 Dec 2023 11:40:15 -0700 Subject: [PATCH 04/15] Add report generation for JUNIT tests Signed-off-by: Travis F. Collins --- .github/workflows/test.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0e7233e14..0a5b00ca6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,6 +50,11 @@ jobs: - name: Test run: | pytest -vs --cov=adi --scan-verbose --emu --junitxml="results.xml" -k 'not prod' + - name: Publish Test Report + uses: mikepenz/action-junit-report@v4 + if: success() || failure() # always run even if the previous step fails + with: + report_paths: 'results.xml' - name: Report coverage if: (github.event_name != 'pull_request') && (matrix.python-version == 3.6) @@ -67,6 +72,11 @@ jobs: run: | pip uninstall -y paramiko pytest -vs --scan-verbose --emu --junitxml="results.xml" -k 'not prod' + - name: Publish Test Report + uses: mikepenz/action-junit-report@v4 + if: success() || failure() # always run even if the previous step fails + with: + report_paths: 'results.xml' Lint: From 876b94b9e8b7c4eaa59430591b02e29b8bc109aa Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Wed, 6 Dec 2023 11:50:58 -0700 Subject: [PATCH 05/15] Add ignore for new compat class Signed-off-by: Travis F. Collins --- doc/update_devs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/update_devs.py b/doc/update_devs.py index 58080053b..0176e4499 100644 --- a/doc/update_devs.py +++ b/doc/update_devs.py @@ -46,6 +46,7 @@ def update_devs(): "jesd_internal", "sync_start", "dsp", + "compat", ] adi_rst_path = os.path.join(root, "source", "devices", "adi.rst") with open(adi_rst_path, "r") as f: From 1aca6691c9aed2165792f9472bd56d271b4f9b53 Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Wed, 6 Dec 2023 12:42:15 -0700 Subject: [PATCH 06/15] Create derivative build for libiio v1 Signed-off-by: Travis F. Collins --- .github/scripts/install_libiio.sh | 11 +++++++++-- .github/workflows/test.yml | 4 ++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/scripts/install_libiio.sh b/.github/scripts/install_libiio.sh index 18b2cc1a8..7ec08826b 100755 --- a/.github/scripts/install_libiio.sh +++ b/.github/scripts/install_libiio.sh @@ -1,7 +1,14 @@ #!/bin/bash + +# Set LIBIIO_BRANCH if not set +if [ -z "$LIBIIO_BRANCH" ]; then + LIBIIO_BRANCH="v0.25" +fi + + sudo apt-get -qq update sudo apt-get install -y git cmake graphviz libavahi-common-dev libavahi-client-dev libaio-dev libusb-1.0-0-dev libxml2-dev rpm tar bzip2 gzip flex bison git -git clone -b 'main' --single-branch --depth 1 https://github.com/analogdevicesinc/libiio.git +git clone -b $LIBIIO_BRANCH --single-branch --depth 1 https://github.com/analogdevicesinc/libiio.git cd libiio cmake . -DHAVE_DNS_SD=OFF make @@ -11,7 +18,7 @@ rm -rf libiio # Python pieces sudo apt-get install -y python3-pip python3-setuptools -git clone -b 'main' --single-branch --depth 1 https://github.com/analogdevicesinc/libiio.git +git clone -b $LIBIIO_BRANCH --single-branch --depth 1 https://github.com/analogdevicesinc/libiio.git cd libiio cmake . -DHAVE_DNS_SD=OFF -DPYTHON_BINDINGS=ON make diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0a5b00ca6..c8c90452f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,6 +8,7 @@ jobs: strategy: matrix: python-version: [3.7, 3.8, 3.9, "3.10"] + libiio: ['main', 'v0.25'] steps: - uses: actions/checkout@v2 @@ -18,6 +19,7 @@ jobs: - name: Install dependencies run: | + export LIBIIO_BRANCH=${{ matrix.libiio }} bash ./.github/scripts/install_libiio.sh bash ./.github/scripts/install_pydeps.sh bash ./.github/scripts/install_part_libs.sh @@ -31,6 +33,7 @@ jobs: fail-fast: false matrix: python-version: [3.7, 3.8, 3.9, "3.10"] + libiio: ['main', 'v0.25'] steps: - uses: actions/checkout@v2 @@ -41,6 +44,7 @@ jobs: - name: Install dependencies run: | + export LIBIIO_BRANCH=${{ matrix.libiio }} bash ./.github/scripts/install_libiio.sh bash ./.github/scripts/install_part_libs.sh bash ./.github/scripts/install_iioemu.sh From 7dffb6bf0910b6349f22172dafb76a79aa370791 Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Wed, 6 Dec 2023 14:40:39 -0700 Subject: [PATCH 07/15] Update pytest package versions Signed-off-by: Travis F. Collins --- .github/scripts/install_pydeps.sh | 8 +++++++- requirements_dev.txt | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/scripts/install_pydeps.sh b/.github/scripts/install_pydeps.sh index 93351f065..5018fa337 100644 --- a/.github/scripts/install_pydeps.sh +++ b/.github/scripts/install_pydeps.sh @@ -2,4 +2,10 @@ sudo apt-get install -y python3-pip python3-setuptools pip install -r requirements.txt pip install -r requirements_dev.txt -# pip install pylibiio + + +if [ "$LIBIIO_BRANCH" = "main" ]; then + pip install --force-reinstall pytest-libiio@git+https://github.com/tfcollins/pytest-libiio.git@libiio-v1-support +else + pip install pytest-libiio==0.0.14 +fi diff --git a/requirements_dev.txt b/requirements_dev.txt index f6a684d50..3ee28d494 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -8,7 +8,7 @@ scapy scipy pytest-cov coveralls -pytest-libiio@git+https://github.com/tfcollins/pytest-libiio.git@libiio-v1-support +pytest-libiio==0.0.14 bump2version pytest-html plotly-express From 55b5687499a5261044ab98f3d8ffd3b15fe5879e Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Wed, 6 Dec 2023 14:50:17 -0700 Subject: [PATCH 08/15] Don't install dependencies when using pytest-libiio v1 Signed-off-by: Travis F. Collins --- .github/scripts/install_pydeps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/install_pydeps.sh b/.github/scripts/install_pydeps.sh index 5018fa337..c065a6219 100644 --- a/.github/scripts/install_pydeps.sh +++ b/.github/scripts/install_pydeps.sh @@ -5,7 +5,7 @@ pip install -r requirements_dev.txt if [ "$LIBIIO_BRANCH" = "main" ]; then - pip install --force-reinstall pytest-libiio@git+https://github.com/tfcollins/pytest-libiio.git@libiio-v1-support + pip install --no-deps --force-reinstall pytest-libiio@git+https://github.com/tfcollins/pytest-libiio.git@libiio-v1-support else pip install pytest-libiio==0.0.14 fi From 2eeb678ba8249ed38ff834a82fbabcc8ccd22cf2 Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Wed, 6 Dec 2023 15:00:45 -0700 Subject: [PATCH 09/15] Roll back required pylibiio Signed-off-by: Travis F. Collins --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0940b6f53..efa7cc2c6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ numpy>=1.20 -pylibiio>=1.0 +pylibiio>=0.23.1 paramiko From 255e74a25048f93f3bf53cee07b455cc9a8fc578 Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Wed, 6 Dec 2023 15:13:53 -0700 Subject: [PATCH 10/15] Fix __ name mangling issue Signed-off-by: Travis F. Collins --- adi/compat.py | 2 +- adi/rx_tx.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/adi/compat.py b/adi/compat.py index 88c641f93..00598a7a3 100644 --- a/adi/compat.py +++ b/adi/compat.py @@ -11,7 +11,7 @@ class compat_libiio: """Compatibility class for libiio v1.X.""" - def __is_libiio_v1(self): + def _is_libiio_v1(self) -> bool: """Check is we are using >= v1.X.""" v = iio.version return v[0] >= 1 diff --git a/adi/rx_tx.py b/adi/rx_tx.py index 6ae03a663..4b2682555 100644 --- a/adi/rx_tx.py +++ b/adi/rx_tx.py @@ -54,7 +54,7 @@ def __init__(self, rx_buffer_size=1024): self._num_rx_channels = len(self._rx_channel_names) self.rx_enabled_channels = rx_enabled_channels self.rx_buffer_size = rx_buffer_size - if self.__is_libiio_v1(): + if self._is_libiio_v1(): self._setup_v1_rx() @property @@ -325,7 +325,7 @@ def __init__(self, tx_cyclic_buffer=False): self.tx_enabled_channels = tx_enabled_channels self.tx_cyclic_buffer = tx_cyclic_buffer dds.__init__(self) - if self.__is_libiio_v1(): + if self._is_libiio_v1(): self._setup_v1_tx() def __del__(self): From 7fa4e3b1758c489f1c440da8e26007a57840ae7d Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Wed, 6 Dec 2023 17:00:12 -0700 Subject: [PATCH 11/15] Reorg unique methods that have different implementations between libiio v0/v1 Signed-off-by: Travis F. Collins --- adi/compat.py | 140 +++++++++++++++++++++++++++++++++++----------- adi/rx_tx.py | 152 ++++++++++++++++++++------------------------------ 2 files changed, 166 insertions(+), 126 deletions(-) diff --git a/adi/compat.py b/adi/compat.py index 00598a7a3..6bcb1bbd5 100644 --- a/adi/compat.py +++ b/adi/compat.py @@ -3,37 +3,25 @@ # SPDX short identifier: ADIBSD """Compatibility module for libiio v1.X.""" -from typing import Any +from typing import Any, Dict, List, Optional, Tuple, Union import iio +import numpy as np -class compat_libiio: - """Compatibility class for libiio v1.X.""" - def _is_libiio_v1(self) -> bool: - """Check is we are using >= v1.X.""" - v = iio.version - return v[0] >= 1 +def _is_libiio_v1() -> bool: + """Check is we are using >= v1.X.""" + v = iio.version + return v[0] >= 1 - def _setup_v1_rx(self): - """Setup for libiio v1.X RX side.""" - self._rx_buffer_mask = None - self.__rx_stream = None - self._rx_buffer_num_blocks = 4 - def _setup_v1_tx(self): - """Setup for libiio v1.X TX side.""" - self._tx_buffer_mask = None - self.__tx_stream = None - self._tx_buffer_num_blocks = 4 +class compat_libiio_v1_rx: + """Compatibility class for libiio v1.X RX.""" - def __getattr__(self, __name: str) -> Any: - if __name in ["_rx_init_channels", "_tx_init_channels", "__tx_buffer_push"]: - if self.__is_libiio_v1(): - return getattr(self, f"_{__name}_v1") - else: - return getattr(self, f"_{__name}") + _rx_buffer_mask = None + _rx_stream = None + _rx_buffer_num_blocks = 4 def _rx_init_channels_v1(self): if not self._rx_buffer_mask: @@ -53,14 +41,23 @@ def _rx_init_channels_v1(self): self._rx_buffer_mask.channels = channels - self.__rxbuf = iio.Buffer(self._rxadc, self._rx_buffer_mask) - self.__rx_stream = iio.Stream( - buffer=self.__rxbuf, + self._rxbuf = iio.Buffer(self._rxadc, self._rx_buffer_mask) + self._rx_stream = iio.Stream( + buffer=self._rxbuf, nb_blocks=self._rx_buffer_num_blocks, samples_count=self.rx_buffer_size, ) - def _tx_init_channels_v1(self): + +class compat_libiio_v1_tx: + """Compatibility class for libiio v1.X TX.""" + + _tx_buffer_mask = None + _tx_stream = None + _tx_buffer_num_blocks = 4 + _tx_block = None + + def _tx_init_channels(self): if not self._tx_buffer_mask: self._tx_buffer_mask = iio.ChannelsMask(self._txdac) @@ -78,14 +75,91 @@ def _tx_init_channels_v1(self): self._tx_buffer_mask.channels = channels - self.__txbuf = iio.Buffer(self._txdac, self._tx_buffer_mask) - self.__block_tx = iio.Block(self.__txbuf, self._tx_buffer_size) + self._txbuf = iio.Buffer(self._txdac, self._tx_buffer_mask) + self._tx_block = iio.Block(self._txbuf, self._tx_buffer_size) - def __tx_buffer_push_v1(self, data): + def _tx_buffer_push(self, data): """Push data to TX buffer. data: bytearray """ - self.__block_tx.write(data) - self.__block_tx.enqueue(None, self.__tx_cyclic_buffer) - self.__txbuf.enabled = True + self._tx_block.write(data) + self._tx_block.enqueue(None, self._tx_cyclic_buffer) + self._txbuf.enabled = True + + +class compat_libiio_v0_rx: + """Compatibility class for libiio v0.X RX.""" + + def _rx_init_channels(self): + for m in self._rx_channel_names: + v = self._rxadc.find_channel(m) + if not v: + raise Exception(f"Channel {m} not found") + v.enabled = False + + if self._complex_data: + for m in self.rx_enabled_channels: + v = self._rxadc.find_channel(self._rx_channel_names[m * 2]) + v.enabled = True + v = self._rxadc.find_channel(self._rx_channel_names[m * 2 + 1]) + v.enabled = True + else: + for m in self.rx_enabled_channels: + v = self._rxadc.find_channel(self._rx_channel_names[m]) + v.enabled = True + self._rxbuf = iio.Buffer(self._rxadc, self._rx_buffer_size, False) + + def _rx_buffered_data(self) -> Union[List[np.ndarray], np.ndarray]: + """_rx_buffered_data: Read data from RX buffer + + Returns: + List of numpy arrays containing the data from the RX buffer that are + channel interleaved + """ + if not self._rxbuf: + self._rx_init_channels() + self._rxbuf.refill() + + data_channel_interleaved = [] + ecn = [] + if self._complex_data: + for m in self.rx_enabled_channels: + ecn.extend( + (self._rx_channel_names[m * 2], self._rx_channel_names[m * 2 + 1]) + ) + else: + ecn = [self._rx_channel_names[m] for m in self.rx_enabled_channels] + + for name in ecn: + chan = self._rxadc.find_channel(name) + bytearray_data = chan.read(self._rxbuf) # Do local type conversion + # create format strings + df = chan.data_format + fmt = ("i" if df.is_signed is True else "u") + str(df.length // 8) + data_channel_interleaved.append(np.frombuffer(bytearray_data, dtype=fmt)) + + return data_channel_interleaved + + +class compat_libiio_v0_tx: + """Compatibility class for libiio v0.X TX.""" + + def _tx_init_channels(self): + if self._complex_data: + for m in self.tx_enabled_channels: + v = self._txdac.find_channel(self._tx_channel_names[m * 2], True) + v.enabled = True + v = self._txdac.find_channel(self._tx_channel_names[m * 2 + 1], True) + v.enabled = True + else: + for m in self.tx_enabled_channels: + v = self._txdac.find_channel(self._tx_channel_names[m], True) + v.enabled = True + self._txbuf = iio.Buffer( + self._txdac, self._tx_buffer_size, self._tx_cyclic_buffer + ) + + def _tx_buffer_push(self, data): + self._txbuf.write(bytearray(data)) + self._txbuf.push() diff --git a/adi/rx_tx.py b/adi/rx_tx.py index 4b2682555..8cd418f86 100644 --- a/adi/rx_tx.py +++ b/adi/rx_tx.py @@ -7,12 +7,19 @@ import iio +import adi.compat as cl import numpy as np from adi.attribute import attribute -from adi.compat import compat_libiio from adi.context_manager import context_manager from adi.dds import dds +if cl._is_libiio_v1(): + from adi.compat import compat_libiio_v1_rx as crx + from adi.compat import compat_libiio_v1_tx as ctx +else: + from adi.compat import compat_libiio_v0_rx as crx + from adi.compat import compat_libiio_v0_tx as ctx + class phy(attribute): _ctrl: iio.Device = [] @@ -21,14 +28,14 @@ def __del__(self): self._ctrl = [] -class rx_tx_common(attribute, compat_libiio): +class rx_tx_common(attribute): """Common functions for RX and TX""" def _annotate(self, data, cnames: List[str], echans: List[int]): return {cnames[ec]: data[i] for i, ec in enumerate(echans)} -class rx(rx_tx_common): +class rx_core(rx_tx_common, metaclass=ABCMeta): """Buffer handling for receive devices""" _rxadc: iio.Device = [] @@ -37,10 +44,10 @@ class rx(rx_tx_common): _rx_data_type = np.int16 _rx_data_si_type = np.int16 _rx_shift = 0 - __rx_buffer_size = 1024 + _rx_buffer_size = 1024 __rx_enabled_channels = [0] _rx_output_type = "raw" - __rxbuf = None + _rxbuf = None _rx_unbuffered_data = False _rx_annotated = False _rx_stack_interleaved = True # Convert from channel to sample interleaved @@ -54,8 +61,6 @@ def __init__(self, rx_buffer_size=1024): self._num_rx_channels = len(self._rx_channel_names) self.rx_enabled_channels = rx_enabled_channels self.rx_buffer_size = rx_buffer_size - if self._is_libiio_v1(): - self._setup_v1_rx() @property def rx_channel_names(self) -> List[str]: @@ -87,11 +92,11 @@ def rx_output_type(self, value: str): @property def rx_buffer_size(self): """rx_buffer_size: Size of receive buffer in samples""" - return self.__rx_buffer_size + return self._rx_buffer_size @rx_buffer_size.setter def rx_buffer_size(self, value): - self.__rx_buffer_size = value + self._rx_buffer_size = value @property def rx_enabled_channels(self) -> Union[List[int], List[str]]: @@ -148,10 +153,10 @@ def _num_rx_channels_enabled(self): def rx_destroy_buffer(self): """rx_destroy_buffer: Clears RX buffer""" - self.__rxbuf = None + self._rxbuf = None def __del__(self): - self.__rxbuf = [] + self._rxbuf = [] if hasattr("self", "_rxadc") and self._rxadc: for m in self._rx_channel_names: v = self._rxadc.find_channel(m) @@ -180,25 +185,6 @@ def __get_rx_channel_offsets(self): rx_offset.append(offset) return rx_offset - def _rx_init_channels(self): - for m in self._rx_channel_names: - v = self._rxadc.find_channel(m) - if not v: - raise Exception(f"Channel {m} not found") - v.enabled = False - - if self._complex_data: - for m in self.rx_enabled_channels: - v = self._rxadc.find_channel(self._rx_channel_names[m * 2]) - v.enabled = True - v = self._rxadc.find_channel(self._rx_channel_names[m * 2 + 1]) - v.enabled = True - else: - for m in self.rx_enabled_channels: - v = self._rxadc.find_channel(self._rx_channel_names[m]) - v.enabled = True - self.__rxbuf = iio.Buffer(self._rxadc, self.__rx_buffer_size, False) - def __rx_unbuffered_data(self): x = [] t = ( @@ -226,39 +212,8 @@ def __rx_unbuffered_data(self): return x - def __rx_buffered_data(self) -> Union[List[np.ndarray], np.ndarray]: - """__rx_buffered_data: Read data from RX buffer - - Returns: - List of numpy arrays containing the data from the RX buffer that are - channel interleaved - """ - if not self.__rxbuf: - self._rx_init_channels() - self.__rxbuf.refill() - - data_channel_interleaved = [] - ecn = [] - if self._complex_data: - for m in self.rx_enabled_channels: - ecn.extend( - (self._rx_channel_names[m * 2], self._rx_channel_names[m * 2 + 1]) - ) - else: - ecn = [self._rx_channel_names[m] for m in self.rx_enabled_channels] - - for name in ecn: - chan = self._rxadc.find_channel(name) - bytearray_data = chan.read(self.__rxbuf) # Do local type conversion - # create format strings - df = chan.data_format - fmt = ("i" if df.is_signed is True else "u") + str(df.length // 8) - data_channel_interleaved.append(np.frombuffer(bytearray_data, dtype=fmt)) - - return data_channel_interleaved - def __rx_complex(self): - x = self.__rx_buffered_data() + x = self._rx_buffered_data() if len(x) % 2 != 0: raise Exception( "Complex data must have an even number of component channels" @@ -268,7 +223,7 @@ def __rx_complex(self): return out[0] if len(x) == 2 else out def __rx_non_complex(self): - x = self.__rx_buffered_data() + x = self._rx_buffered_data() if self._rx_output_type == "SI": rx_scale = self.__get_rx_channel_scales() rx_offset = self.__get_rx_channel_offsets() @@ -302,8 +257,18 @@ def rx(self): ) return data + @abstractmethod + def _rx_init_channels(self): + """Initialize RX channels""" + raise NotImplementedError -class tx(dds, rx_tx_common): + @abstractmethod + def _rx_buffered_data(self): + """Read data from RX buffer""" + raise NotImplementedError + + +class tx_core(dds, rx_tx_common, metaclass=ABCMeta): """Buffer handling for transmit devices""" _tx_buffer_size = 1024 @@ -311,9 +276,10 @@ class tx(dds, rx_tx_common): _tx_channel_names: List[str] = [] _complex_data = False _tx_data_type = None - __txbuf = None + _txbuf = None _output_byte_filename = "out.bin" _push_to_file = False + _tx_cyclic_buffer = False def __init__(self, tx_cyclic_buffer=False): if self._complex_data: @@ -325,11 +291,9 @@ def __init__(self, tx_cyclic_buffer=False): self.tx_enabled_channels = tx_enabled_channels self.tx_cyclic_buffer = tx_cyclic_buffer dds.__init__(self) - if self._is_libiio_v1(): - self._setup_v1_tx() def __del__(self): - self.__txbuf = [] + self._txbuf = [] if hasattr("self", "_txdac") and self._txdac: for m in self._tx_channel_names: v = self._txdac.find_channel(m) @@ -339,16 +303,16 @@ def __del__(self): @property def tx_cyclic_buffer(self): """tx_cyclic_buffer: Enable cyclic buffer for TX""" - return self.__tx_cyclic_buffer + return self._tx_cyclic_buffer @tx_cyclic_buffer.setter def tx_cyclic_buffer(self, value): - if self.__txbuf: + if self._txbuf: raise Exception( "TX buffer already created, buffer must be " "destroyed then recreated to modify tx_cyclic_buffer" ) - self.__tx_cyclic_buffer = value + self._tx_cyclic_buffer = value @property def _num_tx_channels_enabled(self): @@ -411,22 +375,7 @@ def tx_enabled_channels(self, value): def tx_destroy_buffer(self): """tx_destroy_buffer: Clears TX buffer""" - self.__txbuf = None - - def _tx_init_channels(self): - if self._complex_data: - for m in self.tx_enabled_channels: - v = self._txdac.find_channel(self._tx_channel_names[m * 2], True) - v.enabled = True - v = self._txdac.find_channel(self._tx_channel_names[m * 2 + 1], True) - v.enabled = True - else: - for m in self.tx_enabled_channels: - v = self._txdac.find_channel(self._tx_channel_names[m], True) - v.enabled = True - self.__txbuf = iio.Buffer( - self._txdac, self._tx_buffer_size, self.__tx_cyclic_buffer - ) + self._txbuf = None def tx(self, data_np=None): """Transmit data to hardware buffers for each channel index in @@ -460,7 +409,7 @@ def tx(self, data_np=None): return raise Exception("No DDS channels found for TX, TX zeroing does not apply") - if self.__txbuf and self.tx_cyclic_buffer: + if self._txbuf and self.tx_cyclic_buffer: raise Exception( "TX buffer has been submitted in cyclic mode. " "To push more data the tx buffer must be destroyed first." @@ -496,7 +445,7 @@ def tx(self, data_np=None): data[indx::stride] = chan.astype(self._tx_data_type) indx = indx + 1 - if not self.__txbuf: + if not self._txbuf: self.disable_dds() self._tx_buffer_size = len(data) // stride self._tx_init_channels() @@ -513,11 +462,28 @@ def tx(self, data_np=None): f.write(bytearray(data)) f.close() else: - self.__tx_buffer_push(data) + self._tx_buffer_push(data) + + @abstractmethod + def _tx_buffer_push(self, data): + """Push data to TX buffer. + + data: bytearray + """ + raise NotImplementedError + + @abstractmethod + def _tx_init_channels(self): + """Initialize TX channels""" + raise NotImplementedError + + +class rx(crx, rx_core): + pass + - def __tx_buffer_push(self, data): - self.__txbuf.write(bytearray(data)) - self.__txbuf.push() +class tx(ctx, tx_core): + pass class rx_tx(rx, tx, phy): From 7b831d45ac43430799c84c5d2733321478825b0e Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Wed, 6 Dec 2023 19:14:21 -0700 Subject: [PATCH 12/15] Add rx buffer interface for libiio v1 Signed-off-by: Travis F. Collins --- adi/compat.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/adi/compat.py b/adi/compat.py index 6bcb1bbd5..86f15a9c2 100644 --- a/adi/compat.py +++ b/adi/compat.py @@ -48,6 +48,19 @@ def _rx_init_channels_v1(self): samples_count=self.rx_buffer_size, ) + def _rx_buffered_data(self): + block = next(self._rx_stream) + + data_channel_interleaved = [] + for chan in self._rx_buffer_mask.channels: + bytearray_data = chan.read(block) + # create format strings + df = chan.data_format + fmt = ("i" if df.is_signed is True else "u") + str(df.length // 8) + data_channel_interleaved.append(np.frombuffer(bytearray_data, dtype=fmt)) + + return data_channel_interleaved + class compat_libiio_v1_tx: """Compatibility class for libiio v1.X TX.""" From a1b143d096f77ce6b7f20ea3e29777f07af8d4e2 Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Thu, 7 Dec 2023 16:07:47 -0700 Subject: [PATCH 13/15] Updates for libiio v1 rx/tx buffering Signed-off-by: Travis F. Collins --- adi/compat.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/adi/compat.py b/adi/compat.py index 86f15a9c2..27c4af2cd 100644 --- a/adi/compat.py +++ b/adi/compat.py @@ -23,7 +23,7 @@ class compat_libiio_v1_rx: _rx_stream = None _rx_buffer_num_blocks = 4 - def _rx_init_channels_v1(self): + def _rx_init_channels(self): if not self._rx_buffer_mask: self._rx_buffer_mask = iio.ChannelsMask(self._rxadc) @@ -49,6 +49,9 @@ def _rx_init_channels_v1(self): ) def _rx_buffered_data(self): + if not self._rx_stream: + self._rx_init_channels() + block = next(self._rx_stream) data_channel_interleaved = [] @@ -89,16 +92,27 @@ def _tx_init_channels(self): self._tx_buffer_mask.channels = channels self._txbuf = iio.Buffer(self._txdac, self._tx_buffer_mask) - self._tx_block = iio.Block(self._txbuf, self._tx_buffer_size) + if not self._tx_cyclic_buffer: + self._tx_stream = iio.Stream( + buffer=self._txbuf, + nb_blocks=self._tx_buffer_num_blocks, + samples_count=self._tx_buffer_size, + ) + else: + self._tx_block = iio.Block(self._txbuf, self._tx_buffer_size) def _tx_buffer_push(self, data): """Push data to TX buffer. data: bytearray """ - self._tx_block.write(data) - self._tx_block.enqueue(None, self._tx_cyclic_buffer) - self._txbuf.enabled = True + if self._tx_cyclic_buffer: + self._tx_block.write(data) + self._tx_block.enqueue(None, self._tx_cyclic_buffer) + self._txbuf.enabled = True + else: + block = next(self._tx_stream) + block.write(data) class compat_libiio_v0_rx: From e1bdf51ed8c868837e550e7a516ba95e9bb74f01 Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Thu, 7 Dec 2023 16:08:19 -0700 Subject: [PATCH 14/15] Update dependencies for libiio v1 Signed-off-by: Travis F. Collins --- .github/scripts/install_libiio.sh | 2 +- .github/scripts/install_pydeps.sh | 0 2 files changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 .github/scripts/install_pydeps.sh diff --git a/.github/scripts/install_libiio.sh b/.github/scripts/install_libiio.sh index 7ec08826b..6a090a041 100755 --- a/.github/scripts/install_libiio.sh +++ b/.github/scripts/install_libiio.sh @@ -7,7 +7,7 @@ fi sudo apt-get -qq update -sudo apt-get install -y git cmake graphviz libavahi-common-dev libavahi-client-dev libaio-dev libusb-1.0-0-dev libxml2-dev rpm tar bzip2 gzip flex bison git +sudo apt-get install -y git cmake graphviz libzstd-dev libavahi-common-dev libavahi-client-dev libaio-dev libusb-1.0-0-dev libxml2-dev rpm tar bzip2 gzip flex bison git git clone -b $LIBIIO_BRANCH --single-branch --depth 1 https://github.com/analogdevicesinc/libiio.git cd libiio cmake . -DHAVE_DNS_SD=OFF diff --git a/.github/scripts/install_pydeps.sh b/.github/scripts/install_pydeps.sh old mode 100644 new mode 100755 From e2adae58615b2e9ad88fb289e4a899e60b4acd29 Mon Sep 17 00:00:00 2001 From: "Travis F. Collins" Date: Thu, 7 Dec 2023 18:40:38 -0700 Subject: [PATCH 15/15] Fix pytest-html to 3.2.0 otherwise plotly wont work Signed-off-by: Travis F. Collins --- requirements_dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_dev.txt b/requirements_dev.txt index 3ee28d494..6959ee78c 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -10,7 +10,7 @@ pytest-cov coveralls pytest-libiio==0.0.14 bump2version -pytest-html +pytest-html==3.2.0 plotly-express pyvisa matplotlib