├── .github ├── scripts │ └── install_deps.sh └── workflows │ └── build_wheels.yml ├── .gitignore ├── .travis.yml ├── CI ├── build_win.ps1 ├── generate_exe.ps1 ├── install_deps_win.ps1 ├── publish_deps.ps1 └── travis │ ├── before_deploy │ ├── before_deploy_darwin.sh │ ├── before_deploy_linux.sh │ ├── before_install_darwin.sh │ ├── before_install_linux.sh │ ├── build_deploy_doc.sh │ ├── install_linux.sh │ ├── lib.sh │ ├── make_darwin.sh │ └── make_linux.sh ├── CMakeLists.txt ├── Distribution.xml.cmakein ├── LICENSE ├── README.md ├── azure-pipelines.yml ├── bindings └── python │ ├── .gitignore │ ├── CMakeLists.txt │ ├── MANIFEST.in │ ├── README.md │ ├── bin │ └── pysmu │ ├── doc │ ├── conf.py │ └── index.rst │ ├── examples │ ├── get_samples.py │ ├── hotplug.py │ ├── leds.py │ ├── multi-cyclic-run.py │ ├── plot-voltage.py │ ├── read-continuous.py │ ├── read-write-continuous.py │ ├── read-write.py │ └── read.py │ ├── pyproject.toml │ ├── pysmu │ ├── __init__.py │ ├── _vendor │ │ ├── __init__.py │ │ ├── enum.py │ │ └── vendored.txt │ ├── array.pxd │ ├── cpp_libsmu.pxd │ ├── exceptions.py │ ├── libsmu.pyx │ └── utils.py │ ├── setup.py.cmakein │ ├── tests │ ├── misc.py │ ├── test_channel.py │ ├── test_device.py │ ├── test_read.py │ ├── test_read_write.py │ ├── test_session.py │ └── test_signal.py │ ├── tox.dist │ └── tox.ini ├── cmake ├── DarwinPackaging.cmake └── LinuxPackaging.cmake ├── contrib └── calib.txt ├── dist ├── 53-adi-m1k-usb.rules ├── amd64 │ ├── WdfCoInstaller01011.dll │ └── winusbcoinstaller2.dll ├── build-osx-pkg.sh.cmakein ├── libsmu-x64.iss.cmakein ├── libsmu-x86.iss.cmakein ├── libsmu.pc.cmakein ├── m1k-winusb.cat ├── m1k-winusb.inf ├── modpath.iss ├── version.hpp.in └── x86 │ ├── WdfCoInstaller01011.dll │ └── winusbcoinstaller2.dll ├── doc ├── CMakeLists.txt ├── Doxyfile.in ├── img │ ├── reading.svg │ ├── reading.xml │ ├── writing.svg │ └── writing.xml └── mainpage.dox ├── examples ├── CMakeLists.txt ├── hotplug.cpp ├── leds.cpp ├── read-continuous.cpp ├── read-write-continuous.cpp ├── read-write.cpp ├── read.cpp └── smu_stream_multi_out.cpp ├── include └── libsmu │ └── libsmu.hpp ├── src ├── CMakeLists.txt ├── cli │ ├── CMakeLists.txt │ ├── getopt_internal.c │ ├── getopt_internal.h │ └── smu.cpp ├── debug.hpp ├── device.cpp ├── device_m1000.cpp ├── device_m1000.hpp ├── session.cpp ├── signal.cpp ├── usb.cpp └── usb.hpp └── tests ├── CMakeLists.txt ├── CMakeModules ├── DownloadProject.CMakeLists.cmake.in ├── DownloadProject.cmake └── LICENSE ├── fixtures.hpp ├── gtest-output.hpp ├── run-tests.bat ├── test-device.cpp ├── test-multi-read.cpp ├── test-read-write.cpp ├── test-read.cpp └── test-session.cpp /.github/scripts/install_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | PACKAGE_DIR=${1-build} 4 | echo $PACKAGE_DIR 5 | 6 | apt-get -qq update 7 | apt-get install -y git devscripts fakeroot libusb-1.0-0-dev libboost-all-dev python3 python3-dev python3-setuptools python3-pip python3-all 8 | 9 | python3 --version 10 | python3 -m pip install --upgrade pip 11 | python3 -m pip install --upgrade cmake setuptools 12 | cmake --version 13 | 14 | cd $PACKAGE_DIR 15 | python3 -m pip install setuptools build cython 16 | PYTHON_INCLUDE_DIR=$(python3 -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())") \ 17 | PYTHON_LIBRARY=$(python3 -c "import distutils.sysconfig as sysconfig; print(sysconfig.get_config_var('LIBDIR'))") 18 | PYTHON_EXECUTABLE=$(which python3) 19 | echo "==== "$PYTHON_EXECUTABLE 20 | echo "==== "$PYTHON_LIBRARY 21 | echo "==== "$PYTHON_INCLUDE_DIR 22 | cmake -DENABLE_PACKAGING=ON -DBUILD_PYTHON=ON -DPython_EXECUTABLE=$PYTHON_EXECUTABLE -DPython_INCLUDE_DIRS=$PYTHON_INCLUDE_DIR -DPython_LIBRARIES=$PYTHON_LIBRARY .. 23 | cat setup.py 24 | make 25 | make install 26 | 27 | -------------------------------------------------------------------------------- /.github/workflows/build_wheels.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build_wheels: 6 | name: Build wheels on ${{ matrix.os }} 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ubuntu-20.04] 11 | python_version: ['3.10'] 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-python@v2 16 | name: Install Python ${{ matrix.python_version }} 17 | with: 18 | python-version: ${{ matrix.python_version }} 19 | - name: Create empty setup.py file 20 | shell: bash 21 | run: | 22 | mkdir ${{ github.workspace }}/build/ 23 | touch ${{ github.workspace }}/build/setup.py 24 | - name: Build wheels 25 | run: | 26 | python -m pip install --upgrade pip cibuildwheel twine 27 | cibuildwheel --output-dir ${{ github.workspace }}/wheelhouse ${{ github.workspace }}/build 28 | env: 29 | CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_24 30 | CIBW_MANYLINUX_I686_IMAGE: manylinux_2_24 31 | CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_24 32 | # Skip building PyPy wheels on all platforms 33 | # Skip musllinux builds 34 | CIBW_SKIP: "pp* *-musllinux*" 35 | CIBW_BUILD_VERBOSITY: 1 36 | CIBW_BEFORE_ALL: > 37 | cd {project} && ./.github/scripts/install_deps.sh {package} 38 | CIBW_BEFORE_BUILD: pip install cython 39 | CIBW_BUILD: "cp37-* cp38-* cp39-* cp310-*" 40 | - uses: actions/upload-artifact@v2 41 | with: 42 | name: manylinux_wheels 43 | path: ${{ github.workspace }}/wheelhouse 44 | 45 | deploy_wheels: 46 | name: Deploy wheels on test PyPI 47 | runs-on: ubuntu-latest 48 | needs: [build_wheels] 49 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') 50 | steps: 51 | - uses: actions/checkout@v2 52 | - uses: actions/setup-python@v2 53 | name: Install Python 3.10 54 | with: 55 | python-version: 3.10 56 | - uses: actions/download-artifact@v2 57 | with: 58 | name: manylinux_wheels 59 | - name: Upload to PyPI 60 | shell: bash 61 | run: | 62 | python -m twine upload --repository "testpypi" ${{ github.workspace }}/wheelhouse/*.whl 63 | env: 64 | TWINE_USERNAME: __token__ 65 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # generated files 2 | *.so 3 | *.so.* 4 | *.pc 5 | smu 6 | include/libsmu/version.hpp 7 | 8 | # cmake 9 | /build 10 | CMakeCache.txt 11 | CMakeFiles 12 | CMakeScripts 13 | Makefile 14 | cmake_install.cmake 15 | install_manifest.txt 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | env: 2 | global: 3 | # coverity token 4 | - secure: UP0F0/tspgDnPsxZ4uCdRUcfhBrmHKtdWZ7fhGuTq4rHBeC9CxwqVB//JKEr0XTqzDHhQ2J53S7j7XY90h9P+9kQoTmkuOxLs1FNYXg011/rVkwEwu0427M+Oye2CbENWgaf4OycPgkbZR2USKwKg53oGniAyyz6/xm8IlAZRng= 5 | # GITHUB_DOC_TOKEN 6 | - secure: XaFrbDbgsUb5ZKjHYsjFDHWEScquJxqMhxL/jZtHCAVO/btQgY5MrSjI8VFG1SS9sq2zo5ZGQ6Z6t3itbZQgX9wfcssJjOT3/4w38m47wAcAqZQLaQQocmPNtc6bFukcL9tUTf6V/6v0ujBdnahc5gH6GYJ9Dg6Thv4Kuvln3vQ= 7 | 8 | cache: 9 | directories: 10 | - ${TRAVIS_BUILD_DIR}/deps 11 | 12 | matrix: 13 | fast_finish: true 14 | include: 15 | - os: linux 16 | sudo: required 17 | dist: bionic 18 | language: python 19 | python: 3.6 20 | env: PYTHON=python PIP=pip 21 | cache: pip 22 | - os: linux 23 | sudo: required 24 | dist: xenial 25 | language: python 26 | python: 3.5 27 | env: PYTHON=python PIP=pip 28 | cache: pip 29 | - os: linux 30 | sudo: required 31 | dist: focal 32 | language: python 33 | python: 3.8 34 | env: PYTHON=python PIP=pip 35 | cache: pip 36 | - os: osx 37 | osx_image: xcode10.1 38 | env: PYTHON=python3 PIP=pip3 39 | cache: "$HOME/Library/Caches/pip" 40 | - os: osx 41 | osx_image: xcode11 42 | env: PYTHON=python3 PIP=pip3 43 | cache: "$HOME/Library/Caches/pip" 44 | - os: osx 45 | osx_image: xcode12 46 | env: PYTHON=python3 PIP=pip3 47 | cache: "$HOME/Library/Caches/pip" 48 | - os: linux 49 | dist: bionic 50 | env: 51 | - OS_TYPE=doxygen 52 | 53 | addons: 54 | apt: 55 | packages: 56 | - libusb-1.0-0-dev 57 | - libboost-all-dev 58 | coverity_scan: 59 | project: 60 | name: analogdevicesinc/libsmu 61 | description: Software abstractions for the analog signal exploration tools (ADALM1000) 62 | notification_email: alexandra.trifan@analog.com 63 | build_command_prepend: mkdir -p build && cd build && cmake -DBUILD_PYTHON=OFF .. 64 | build_command: make 65 | branch_pattern: coverity_scan 66 | 67 | # build from all branches except appveyor (windows builds) 68 | branches: 69 | except: 70 | - appveyor 71 | before_install: 72 | - export DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" 73 | - mkdir -p ${DEPS_DIR} 74 | 75 | # Configure git user for Travis CI to push doc 76 | - git config --global user.name "Travis Bot" 77 | - git config --global user.email "<>" 78 | 79 | - if [[ ${TRAVIS_OS_NAME} == osx ]]; then brew update; fi 80 | - if [[ ${TRAVIS_OS_NAME} == linux ]]; then sudo apt-get update ; fi 81 | 82 | install: 83 | - if [[ ${TRAVIS_OS_NAME} == osx ]]; then brew install libusb; fi 84 | - if [[ ${TRAVIS_OS_NAME} == osx ]]; then brew install ${PYTHON} || true; fi 85 | - if [[ ${TRAVIS_OS_NAME} == osx ]]; then brew upgrade cmake || true; fi 86 | 87 | # for python bindings 88 | - if [[ ${TRAVIS_OS_NAME} == osx ]]; then sudo easy_install pip; fi 89 | - if [[ ${OS_TYPE} != "doxygen" ]]; then ${PIP} install --only-binary ":all:" --disable-pip-version-check --upgrade pip; fi 90 | - if [[ ${TRAVIS_OS_NAME} == osx ]]; then sudo ${PIP} install --only-binary ":all:" wheel cython ; fi 91 | - if [[ ${TRAVIS_OS_NAME} == linux && ${OS_TYPE} != "doxygen" ]]; then ${PYTHON} -m pip install --upgrade pip stdeb stdeb3 setuptools cython ; fi 92 | - if [[ ${TRAVIS_OS_NAME} == linux && ${OS_TYPE} != "doxygen" ]]; then ${PYTHON} -m pip install setuptools wheel stdeb ; fi 93 | - if [[ ${TRAVIS_OS_NAME} == linux ]]; then sudo apt-get install devscripts cython3 python3-setuptools python3-stdeb fakeroot dh-python python3-all debhelper python3-dev python3-all-dev python3-wheel ; fi 94 | 95 | script: 96 | # build tar containg python files 97 | - if [[ ${TRAVIS_OS_NAME} == linux && ${OS_TYPE} != "doxygen" ]]; then mkdir -p build_tar && cd build_tar; fi 98 | - if [[ ${TRAVIS_OS_NAME} == linux && ${OS_TYPE} != "doxygen" ]]; then cmake -DDEB_DETECT_DEPENDENCIES=ON -DENABLE_PACKAGING=ON -DUSE_PYTHON2=OFF -DBUILD_PYTHON=ON ..; make package; rm *.deb; cd .. ; fi 99 | 100 | - mkdir -p build && cd build 101 | - if [[ ${TRAVIS_OS_NAME} == linux ]]; then cmake -DDEB_DETECT_DEPENDENCIES=ON -DENABLE_PACKAGING=ON -DUSE_PYTHON2=OFF -DBUILD_PYTHON=OFF ..; make package; rm *.tar.gz; fi 102 | - if [[ ${TRAVIS_OS_NAME} == "linux" && ${OS_TYPE} == "doxygen" ]]; then "${TRAVIS_BUILD_DIR}/CI/travis/build_deploy_doc.sh"; fi 103 | 104 | 105 | # Build tar.gz for OSX (contains libraries and smu) 106 | - if [[ ${TRAVIS_OS_NAME} == osx ]]; then cmake -DENABLE_PACKAGING=ON -DBUILD_PYTHON=OFF ..; make package; fi 107 | 108 | # Build pkg for OSX (contains libsmu.framework - only base library) 109 | - if [[ ${TRAVIS_OS_NAME} == osx ]]; then cmake -DOSX_PACKAGE=ON -DENABLE_PACKAGING=OFF -DUSE_PYTHON2=OFF .. ; fi 110 | - if [[ ${OS_TYPE} != "doxygen" ]]; then make && sudo make install ; fi 111 | 112 | - if [[ ${OS_TYPE} != "doxygen" ]]; then cd "${TRAVIS_BUILD_DIR}"/bindings/python; fi 113 | - if [[ ${OS_TYPE} != "doxygen" ]]; then ${PYTHON} --version; fi 114 | - if [[ ${OS_TYPE} != "doxygen" ]]; then ${PYTHON} -c "import struct; print(struct.calcsize('P') * 8)"; fi 115 | - if [[ ${OS_TYPE} != "doxygen" ]]; then ${PYTHON} setup.py build_ext -L "${TRAVIS_BUILD_DIR}"/build/src; fi 116 | - if [[ ${OS_TYPE} != "doxygen" ]]; then ${PYTHON} setup.py build; fi 117 | - if [[ ${TRAVIS_OS_NAME} == linux && ${OS_TYPE} != "doxygen" ]]; then ${PYTHON} -c "from Cython.Build import cythonize" ; fi 118 | - if [[ ${OS_TYPE} != "doxygen" ]]; then ${PYTHON} setup.py bdist_wheel --skip-build; fi 119 | - if [[ ${TRAVIS_OS_NAME} == linux && ${OS_TYPE} != "doxygen" ]]; then sudo sed -i 's/my\ \$ignore_missing_info\ =\ 0;/my\ \$ignore_missing_info\ =\ 1;/' /usr/bin/dpkg-shlibdeps ; fi 120 | - if [[ ${TRAVIS_OS_NAME} == linux && ${OS_TYPE} != "doxygen" ]]; then ${PYTHON} setup.py --command-packages=stdeb.command sdist_dsc ; fi 121 | - if [[ ${TRAVIS_OS_NAME} == linux && ${OS_TYPE} != "doxygen" ]]; then cd "$(find . -type d -name "debian" | head -n 1)"/.. ; fi 122 | - if [[ ${TRAVIS_OS_NAME} == linux && ${OS_TYPE} != "doxygen" ]]; then debuild -us -uc ; fi 123 | - if [[ ${TRAVIS_OS_NAME} == linux && ${OS_TYPE} != "doxygen" ]]; then cp ${TRAVIS_BUILD_DIR}/bindings/python/deb_dist/*.deb ${TRAVIS_BUILD_DIR}/bindings/python ; fi 124 | 125 | 126 | before_deploy: 127 | - if [[ ${OS_TYPE} != "doxygen" ]]; then . ${TRAVIS_BUILD_DIR}/CI/travis/before_deploy; fi 128 | 129 | 130 | deploy: 131 | - provider: releases 132 | api_key: 133 | secure: LW6KL6nixaLLEkZKE/knz9qGtbUmJU3G3F1Rexo8xMEQyUOePS99OTNANjI38zlOWP3Cbcrrl2Palz7B9ZXkFYnT9NjRNB15ox1LOEeZTGKT65+N11Te8WpHHcQVXFc5e3BupmYdwv7vMh+a4RTbq/nRF0X/OzbV/Nyxlzlw6fA= 134 | file: 135 | - "${RELEASE_PKG_FILE_DEB}" 136 | - "${RELEASE_PKG_FILE_TGZ}" 137 | - "${RELEASE_PY_FILE_DEB}" 138 | skip_cleanup: true 139 | on: 140 | repo: analogdevicesinc/libsmu 141 | tags: true 142 | condition: "($TRAVIS_OS_NAME = linux) && ($OS_TYPE != doxygen)" 143 | - provider: releases 144 | api_key: 145 | secure: LW6KL6nixaLLEkZKE/knz9qGtbUmJU3G3F1Rexo8xMEQyUOePS99OTNANjI38zlOWP3Cbcrrl2Palz7B9ZXkFYnT9NjRNB15ox1LOEeZTGKT65+N11Te8WpHHcQVXFc5e3BupmYdwv7vMh+a4RTbq/nRF0X/OzbV/Nyxlzlw6fA= 146 | file: 147 | - "${RELEASE_PKG_FILE_PKG}" 148 | - "${RELEASE_PKG_FILE_TGZ}" 149 | skip_cleanup: true 150 | on: 151 | repo: analogdevicesinc/libsmu 152 | tags: true 153 | condition: "$TRAVIS_OS_NAME = osx" 154 | -------------------------------------------------------------------------------- /CI/build_win.ps1: -------------------------------------------------------------------------------- 1 | $COMPILER=$Env:COMPILER 2 | $ARCH=$Env:ARCH 3 | 4 | $src_dir=$pwd 5 | python3 --version 6 | cmake --version 7 | python3 -m pip install --upgrade pip 8 | python3 -m pip install wheel build virtualenv cython 9 | 10 | 11 | cp .\dist\modpath.iss $env:BUILD_ARTIFACTSTAGINGDIRECTORY 12 | if ($ARCH -eq "Win32") { 13 | echo "Running cmake for $COMPILER on 32 bit..." 14 | mkdir build 15 | cp .\dist\libsmu-x86.iss.cmakein .\build 16 | cd build 17 | 18 | cmake -G "$COMPILER" -A "$ARCH" -DCMAKE_SYSTEM_PREFIX_PATH="C:" -DLIBUSB_LIBRARIES="C:\\libs\\32\\libusb-1.0.lib" -DLIBUSB_INCLUDE_DIRS="C:\include\libusb-1.0" -DBOOST_ROOT="C:\\boost_1_73_0" -DBoost_USE_STATIC_LIBS=ON -DBUILD_STATIC_LIB=ON -DBUILD_EXAMPLES=ON -DBUILD_TESTS=ON -DBUILD_PYTHON=ON .. 19 | cmake --build . --config Release 20 | cp .\dist\libsmu-x86.iss $env:BUILD_ARTIFACTSTAGINGDIRECTORY 21 | } else { 22 | echo "Running cmake for $COMPILER on 64 bit..." 23 | mkdir build 24 | cp .\dist\libsmu-x64.iss.cmakein .\build 25 | cd build 26 | 27 | cmake -G "$COMPILER" -A "$ARCH" -DCMAKE_SYSTEM_PREFIX_PATH="C:" -DLIBUSB_LIBRARIES="C:\\libs\\64\\libusb-1.0.lib" -DLIBUSB_INCLUDE_DIRS="C:\include\libusb-1.0" -DBOOST_ROOT="C:\\boost_1_73_0" -DBoost_USE_STATIC_LIBS=ON -DBUILD_STATIC_LIB=ON -DBUILD_EXAMPLES=ON -DBUILD_TESTS=ON -DBUILD_PYTHON=ON .. 28 | cmake --build . --config Release 29 | cp .\dist\libsmu-x64.iss $env:BUILD_ARTIFACTSTAGINGDIRECTORY 30 | } 31 | -------------------------------------------------------------------------------- /CI/generate_exe.ps1: -------------------------------------------------------------------------------- 1 | SET PATH=packages\Tools.InnoSetup.5.6.1\tools 2 | iscc $env:BUILD_ARTIFACTSTAGINGDIRECTORY\Windows-VS-16-2019-Win32\libsmu-x86.iss 3 | iscc $env:BUILD_ARTIFACTSTAGINGDIRECTORY\Windows-VS-16-2019-x64\libsmu-x64.iss 4 | 5 | cp C:\libsmu-setup-x86.exe $env:BUILD_ARTIFACTSTAGINGDIRECTORY 6 | cp C:\libsmu-setup-x64.exe $env:BUILD_ARTIFACTSTAGINGDIRECTORY 7 | rm -Recurse $env:BUILD_ARTIFACTSTAGINGDIRECTORY\Windows-VS-16-2019-Win32\ 8 | rm -Recurse $env:BUILD_ARTIFACTSTAGINGDIRECTORY\Windows-VS-16-2019-x64\ 9 | -------------------------------------------------------------------------------- /CI/install_deps_win.ps1: -------------------------------------------------------------------------------- 1 | 2 | $ARCH=$Env:ARCH 3 | 4 | git submodule update --init 5 | 6 | echo "Downloading deps..." 7 | cd C:\ 8 | wget http://swdownloads.analog.com/cse/build/libiio-win-deps.zip -OutFile "libiio-win-deps.zip" 9 | 7z x -y "C:\libiio-win-deps.zip" 10 | 11 | echo "Downloading boost..." 12 | wget https://boostorg.jfrog.io/artifactory/main/release/1.73.0/source/boost_1_73_0.7z -OutFile "boost.7z" 13 | 7z x -y "C:\boost.7z" 14 | 15 | # Note: InnoSetup is already installed on Azure images; so don't run this step 16 | # Running choco seems a bit slow; seems to save about 40-60 seconds here 17 | #choco install InnoSetup 18 | 19 | set PATH=%PATH%;"C:\Program Files (x86)\Inno Setup 6" 20 | -------------------------------------------------------------------------------- /CI/publish_deps.ps1: -------------------------------------------------------------------------------- 1 | $src_dir=$pwd 2 | $ARCH=$Env:ARCH 3 | 4 | cd 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Redist\MSVC\14.29.30133' 5 | if ($ARCH -eq "Win32") { 6 | echo "$PWD" 7 | cp .\x86\Microsoft.VC142.CRT\msvcp140.dll $env:BUILD_ARTIFACTSTAGINGDIRECTORY 8 | cp .\x86\Microsoft.VC142.CRT\vcruntime140.dll $env:BUILD_ARTIFACTSTAGINGDIRECTORY 9 | cp .\x86\Microsoft.VC142.OpenMP\vcomp140.dll $env:BUILD_ARTIFACTSTAGINGDIRECTORY 10 | cp -R 'C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x86\*' $env:BUILD_ARTIFACTSTAGINGDIRECTORY 11 | }else { 12 | echo "$PWD" 13 | cp .\x64\Microsoft.VC142.CRT\msvcp140.dll $env:BUILD_ARTIFACTSTAGINGDIRECTORY 14 | cp .\x64\Microsoft.VC142.CRT\vcruntime140.dll $env:BUILD_ARTIFACTSTAGINGDIRECT 15 | cp .\x64\Microsoft.VC142.CRT\vcruntime140_1.dll $env:BUILD_ARTIFACTSTAGINGDIRECT 16 | cp .\x64\Microsoft.VC142.OpenMP\vcomp140.dll $env:BUILD_ARTIFACTSTAGINGDIRECTORY 17 | cp -R 'C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x64\*' $env:BUILD_ARTIFACTSTAGINGDIRECTORY 18 | } 19 | 20 | cd $src_dir 21 | mkdir dependencies 22 | cd dependencies 23 | wget http://swdownloads.analog.com/cse/build/libiio-win-deps.zip -OutFile "libiio-win-deps.zip" 24 | 7z x -y "libiio-win-deps.zip" 25 | 26 | echo "Downloading dpinst..." 27 | wget http://swdownloads.analog.com/cse/m1k/drivers/dpinst.zip -OutFile "dpinst.zip" 28 | 7z x -y "dpinst.zip" 29 | 30 | cp -R ..\src\ $env:BUILD_ARTIFACTSTAGINGDIRECTORY 31 | cp -R ..\include\ $env:BUILD_ARTIFACTSTAGINGDIRECTORY 32 | cp .\include\libusb-1.0\libusb.h $env:BUILD_ARTIFACTSTAGINGDIRECTORY\include\libsmu\ 33 | 34 | if (!(Test-Path $env:BUILD_ARTIFACTSTAGINGDIRECTORY\drivers)) { 35 | mkdir $env:BUILD_ARTIFACTSTAGINGDIRECTORY\drivers 36 | } 37 | cp ..\dist\m1k-winusb.* $env:BUILD_ARTIFACTSTAGINGDIRECTORY\drivers\ 38 | cp -R ..\dist\x86 $env:BUILD_ARTIFACTSTAGINGDIRECTORY\drivers 39 | cp -R ..\dist\amd64 $env:BUILD_ARTIFACTSTAGINGDIRECTORY\drivers 40 | 41 | if ($ARCH -eq "Win32") { 42 | cp .\libs\32\libusb-1.0.dll $env:BUILD_ARTIFACTSTAGINGDIRECTORY 43 | cp .\dpinst.exe $env:BUILD_ARTIFACTSTAGINGDIRECTORY\drivers 44 | } else { 45 | cp .\libs\64\libusb-1.0.dll $env:BUILD_ARTIFACTSTAGINGDIRECTORY 46 | cp .\dpinst_amd64.exe $env:BUILD_ARTIFACTSTAGINGDIRECTORY\drivers 47 | } 48 | -------------------------------------------------------------------------------- /CI/travis/before_deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . "$BUILD_SOURCESDIRECTORY"/CI/travis/lib.sh 4 | 5 | # Don't prepare a deploy on a Coverity build 6 | # if [ "x${COVERITY_SCAN_PROJECT_NAME}" != "x" ] ; then exit 0; fi 7 | 8 | deploy=0 9 | if [ -z "$BUILD_SOURCESDIRECTORY" ] ; then 10 | t=$(find ./ -name CMakeCache.txt|head -1) 11 | if [ -n "${t}" ] ; then 12 | cd $(dirname $(dirname ${t})) 13 | BUILD_SOURCESDIRECTORY=$(pwd) 14 | else 15 | echo "I am confused - can't find CMakeCache.txt" 16 | exit 17 | fi 18 | else 19 | cd $BUILD_SOURCESDIRECTORY 20 | fi 21 | pwd 22 | 23 | if [ -z "${LDIST}" -a -f "build/.LDIST" ] ; then 24 | export LDIST="-$(cat build/.LDIST)" 25 | fi 26 | if [ -z "${LDIST}" ] ; then 27 | export LDIST="-$(get_ldist)" 28 | fi 29 | 30 | rename_python_deb() 31 | { 32 | name="${LDIST}.deb" 33 | for x in ${BUILD_SOURCESDIRECTORY}/bindings/python/*.deb ; do mv $x ${x%_*.deb}$name ; done 34 | for x in ${BUILD_SOURCESDIRECTORY}/bindings/python/*.deb ; do RELEASE_PY_FILE_DEB=$x ; done 35 | } 36 | rename_python_deb 37 | 38 | check_file() 39 | { 40 | temp="" 41 | for i in $(find ./ -name CMakeCache.txt) 42 | do 43 | hit=$(find $(dirname ${i}) -maxdepth 1 -name "libsmu*.$1" -a ! -name "*${LDIST}*") 44 | if [ "$(echo ${hit} | wc -w)" -gt "1" ] ; then 45 | echo "I am confused - more than 2 $1 files!" 46 | echo $hit 47 | exit 1 48 | else 49 | if [ "$(echo ${hit} | wc -w)" -eq "1" ] ; then 50 | if [ -z "${temp}" ] ; then 51 | temp=$hit 52 | else 53 | echo "I am confused - more than 2 $1 files" 54 | echo $temp 55 | echo $hit 56 | exit 1 57 | fi 58 | fi 59 | fi 60 | done 61 | } 62 | 63 | check_file deb 64 | if [ -n "${temp}" ] ; then 65 | deploy=$(expr ${deploy} + 1) 66 | if [ -z "${TARGET_DEB}" ] ; then 67 | export TARGET_DEB=$(echo ${temp} | \ 68 | sed -e 's:^./.*/::' -e 's:-Linux::' -e 's:.deb$::')${LDIST}.deb 69 | fi 70 | echo "deploying ${temp} to nightly $TARGET_DEB" 71 | if [ -z "${RELEASE_PKG_FILE_DEB}" ] ; then 72 | export RELEASE_PKG_FILE_DEB=$(dirname ${temp})/${TARGET_DEB} 73 | cp ${temp} ${RELEASE_PKG_FILE_DEB} 74 | fi 75 | echo ${TARGET_DEB} 76 | ls -lh ${temp} 77 | echo ${RELEASE_PKG_FILE_DEB} 78 | ls -lh ${RELEASE_PKG_FILE_DEB} 79 | else 80 | echo "Skipping deployment of debian package" 81 | fi 82 | 83 | check_file rpm 84 | if [ -n "${temp}" ] ; then 85 | deploy=$(expr ${deploy} + 1) 86 | if [ -z "${TARGET_RPM}" ] ; then 87 | export TARGET_RPM=$(echo ${temp} | \ 88 | sed -e 's:^./.*/::' -e 's:-Linux::' -e 's:.rpm$::')${LDIST}.rpm 89 | fi 90 | echo "deploying ${temp} to nightly $TARGET_RPM" 91 | if [ -z "${RELEASE_PKG_FILE_RPM}" ] ; then 92 | export RELEASE_PKG_FILE_RPM=$(dirname ${temp})/${TARGET_RPM} 93 | cp ${temp} ${RELEASE_PKG_FILE_RPM} 94 | fi 95 | echo ${TARGET_RPM} 96 | ls -lh ${temp} 97 | echo ${RELEASE_PKG_FILE_RPM} 98 | ls -lh ${RELEASE_PKG_FILE_RPM} 99 | else 100 | echo "Skipping deployment of rpm package" 101 | fi 102 | 103 | check_file tar.gz 104 | if [ -n "${temp}" ] ; then 105 | deploy=$(expr ${deploy} + 1) 106 | if [ -z "${TARGET_TGZ}" ] ; then 107 | export TARGET_TGZ=$(echo ${temp} | \ 108 | sed -e 's:^./.*/::' -e 's:-Linux::' -e 's:-Darwin::' -e 's:.tar.gz$::')${LDIST}.tar.gz; 109 | fi 110 | echo "deploying ${temp} to $TARGET_TGZ" 111 | if [ -z "${RELEASE_PKG_FILE_TGZ}" ] ; then 112 | export RELEASE_PKG_FILE_TGZ=$(dirname ${temp})/${TARGET_TGZ} 113 | cp ${temp} ${RELEASE_PKG_FILE_TGZ} 114 | fi 115 | echo ${TARGET_TGZ} 116 | ls -lh ${temp} 117 | echo ${RELEASE_PKG_FILE_TGZ} 118 | ls -lh ${RELEASE_PKG_FILE_TGZ} 119 | else 120 | echo "Skipping deployment of tarball" 121 | fi 122 | 123 | check_file pkg 124 | if [ -n "${temp}" ] ; then 125 | deploy=$(expr ${deploy} + 1) 126 | if [ -z "${TARGET_PKG}" ] ; then 127 | export TARGET_PKG=$(echo ${temp} | \ 128 | sed -e 's:^./.*/::' -e 's:.pkg$::')${LDIST}.pkg 129 | fi 130 | echo "deploying ${temp} to nightly $TARGET_PKG" 131 | if [ -z "${RELEASE_PKG_FILE_PKG}" ] ; then 132 | export RELEASE_PKG_FILE_PKG=$(dirname ${temp})/${TARGET_PKG} 133 | cp ${temp} ${RELEASE_PKG_FILE_PKG} 134 | fi 135 | echo ${TARGET_PKG} 136 | ls -lh ${temp} 137 | echo ${RELEASE_PKG_FILE_PKG} 138 | ls -lh ${RELEASE_PKG_FILE_PKG} 139 | else 140 | echo "Skipping deployment of OS X package" 141 | fi 142 | 143 | if [ "${deploy}" -eq "0" ] ; then 144 | echo did not deploy any files 145 | exit 1 146 | fi 147 | 148 | echo "===" ${RELEASE_PKG_FILE_PKG} 149 | echo "===" ${RELEASE_PKG_FILE_TGZ} 150 | echo "===" ${RELEASE_PKG_FILE_DEB} 151 | echo "===" ${RELEASE_PY_FILE_DEB} 152 | -------------------------------------------------------------------------------- /CI/travis/before_deploy_darwin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . ${BUILD_SOURCESDIRECTORY}/CI/travis/before_deploy 4 | -------------------------------------------------------------------------------- /CI/travis/before_deploy_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ ${OS_TYPE} != "doxygen" ]]; then . ${BUILD_SOURCESDIRECTORY}/CI/travis/before_deploy ; fi 4 | -------------------------------------------------------------------------------- /CI/travis/before_install_darwin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DEPS_DIR="${BUILD_SOURCESDIRECTORY}/deps" 4 | mkdir -p ${DEPS_DIR} 5 | 6 | # Configure git user for Azure to push doc 7 | git config --global user.name "Azure Bot" 8 | git config --global user.email "<>" 9 | 10 | brew update 11 | 12 | brew install libusb 13 | brew install python3 14 | brew install boost 15 | brew upgrade cmake 16 | 17 | # for python bindings 18 | python3 -m pip install --upgrade pip 19 | python3 -m pip install twine build virtualenv wheel cython 20 | -------------------------------------------------------------------------------- /CI/travis/before_install_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DEPS_DIR="${BUILD_SOURCESDIRECTORY}/deps" 4 | mkdir -p ${DEPS_DIR} 5 | 6 | # Configure git user for Azure to push doc 7 | git config --global user.name "Azure Bot" 8 | git config --global user.email "<>" 9 | 10 | sudo apt-get update 11 | sudo apt-get install doxygen graphviz devscripts cython3 python3-setuptools python3-stdeb fakeroot dh-python python3-all debhelper python3-dev python3-all-dev python3-wheel libusb-1.0-0-dev libboost-all-dev 12 | -------------------------------------------------------------------------------- /CI/travis/build_deploy_doc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd ${BUILD_SOURCESDIRECTORY} 3 | TOP_DIR=$(pwd) 4 | 5 | handle_doxygen() { 6 | # Install a recent version of doxygen 7 | DOXYGEN_URL="wget https://sourceforge.net/projects/doxygen/files/rel-1.8.15/doxygen-1.8.15.src.tar.gz" 8 | cd ${DEPS_DIR} 9 | [ -d "doxygen" ] || { 10 | mkdir doxygen && wget --quiet -O - ${DOXYGEN_URL} | tar --strip-components=1 -xz -C doxygen 11 | } 12 | cd doxygen 13 | mkdir -p build && cd build 14 | cmake .. 15 | make -j${NUM_JOBS} 16 | sudo make install 17 | cd .. 18 | cd .. 19 | 20 | cd ${TOP_DIR} 21 | mkdir -p build && cd build 22 | cmake -DWITH_DOC=ON .. 23 | cd .. 24 | cd .. 25 | } 26 | 27 | # Install Doxygen 28 | handle_doxygen 29 | 30 | echo_red() { printf "\033[1;31m$*\033[m\n"; } 31 | echo_green() { printf "\033[1;32m$*\033[m\n"; } 32 | 33 | ############################################################################ 34 | # Check if the documentation will be generated w/o warnings or errors 35 | ############################################################################ 36 | pushd ${TOP_DIR}/build 37 | (! make doc 2>&1 | grep -E "warning|error") || { 38 | echo_red "Documentation incomplete or errors in the generation of it have occured!" 39 | exit 1 40 | } 41 | 42 | echo_green "Documentation was generated successfully!" 43 | 44 | ############################################################################ 45 | # If the current build is not a pull request and it is on master the 46 | # documentation will be pushed to the gh-pages branch 47 | ############################################################################ 48 | if [[ "${IS_PULL_REQUEST}" == "False" && "${BRANCH_NAME}" == "master" ]] 49 | then 50 | echo_green "Running Github docs update on commit '$CURRENT_COMMIT'" 51 | git config --global user.email "cse-ci-notifications@analog.com" 52 | git config --global user.name "CSE-CI" 53 | 54 | pushd ${TOP_DIR} 55 | git fetch --depth 1 origin +refs/heads/gh-pages:gh-pages 56 | git checkout gh-pages 57 | 58 | cp -R ${TOP_DIR}/build/doc/doxygen_doc/html/* ${TOP_DIR} 59 | 60 | sudo rm -rf ${TOP_DIR}/build/doc 61 | 62 | GH_CURRENT_COMMIT=$(git log -1 --pretty=%B) 63 | if [[ ${GH_CURRENT_COMMIT:(-7)} != ${CURRENT_COMMIT:0:7} ]] 64 | then 65 | git add --all . 66 | git commit --allow-empty --amend -m "Update documentation to ${CURRENT_COMMIT:0:7}" 67 | if [ -n "$GITHUB_DOC_TOKEN" ] ; then 68 | git push https://${GITHUB_DOC_TOKEN}@github.com/${REPO_SLUG} gh-pages -f 69 | else 70 | git push origin gh-pages -f 71 | fi 72 | echo_green "Documentation updated!" 73 | else 74 | echo_green "Documentation already up to date!" 75 | fi 76 | else 77 | echo_green "Documentation will be updated when this commit gets on master!" 78 | fi 79 | 80 | git fetch origin 81 | git checkout ${BRANCH_NAME} 82 | cd ${TOP_DIR} 83 | -------------------------------------------------------------------------------- /CI/travis/install_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ ${OS_TYPE} == "doxygen" ]]; then make && sudo make install ; fi 4 | -------------------------------------------------------------------------------- /CI/travis/lib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export TRAVIS_API_URL="https://api.travis-ci.org" 4 | LOCAL_BUILD_DIR=${LOCAL_BUILD_DIR:-build} 5 | 6 | HOMEBREW_NO_INSTALL_CLEANUP=1 7 | export HOMEBREW_NO_INSTALL_CLEANUP 8 | 9 | COMMON_SCRIPTS="jobs_running_cnt.py inside_docker.sh" 10 | 11 | echo_red() { printf "\033[1;31m$*\033[m\n"; } 12 | echo_green() { printf "\033[1;32m$*\033[m\n"; } 13 | echo_blue() { printf "\033[1;34m$*\033[m\n"; } 14 | 15 | get_script_path() { 16 | local script="$1" 17 | 18 | [ -n "$script" ] || return 1 19 | 20 | if [ -f "CI/travis/$script" ] ; then 21 | echo "CI/travis/$script" 22 | elif [ -f "ci/travis/$script" ] ; then 23 | echo "ci/travis/$script" 24 | elif [ -f "${LOCAL_BUILD_DIR}/$script" ] ; then 25 | echo "${LOCAL_BUILD_DIR}/$script" 26 | else 27 | return 1 28 | fi 29 | } 30 | 31 | pipeline_branch() { 32 | local branch=$1 33 | 34 | [ -n "$branch" ] || return 1 35 | 36 | # master is a always a pipeline branch 37 | [ "$branch" = "master" ] && return 0 38 | 39 | set +x 40 | # Check if branch name is 20XX_RY where: 41 | # XX - 14 to 99 /* wooh, that's a lot of years */ 42 | # Y - 1 to 9 /* wooh, that's a lot of releases per year */ 43 | for year in $(seq 2014 2099) ; do 44 | for rel_num in $(seq 1 9) ; do 45 | [ "$branch" = "${year}_R${rel_num}" ] && \ 46 | return 0 47 | done 48 | done 49 | 50 | return 1 51 | } 52 | 53 | should_trigger_next_builds() { 54 | local branch="$1" 55 | 56 | # [ -z "${COVERITY_SCAN_PROJECT_NAME}" ] || return 1 57 | 58 | # These Travis-CI vars have to be non-empty 59 | [ -n "$TRAVIS_PULL_REQUEST" ] || return 1 60 | [ -n "$branch" ] || return 1 61 | set +x 62 | [ -n "$TRAVIS_API_TOKEN" ] || return 1 63 | 64 | # Has to be a non-pull-request 65 | [ "$TRAVIS_PULL_REQUEST" = "false" ] || return 1 66 | 67 | pipeline_branch "$branch" || return 1 68 | 69 | local python_script="$(get_script_path jobs_running_cnt.py)" 70 | if [ -z "$python_script" ] ; then 71 | echo "Could not find 'jobs_running_cnt.py'" 72 | return 1 73 | fi 74 | 75 | local jobs_cnt=$(python $python_script) 76 | 77 | # Trigger next job if we are the last job running 78 | [ "$jobs_cnt" = "1" ] 79 | } 80 | 81 | trigger_build() { 82 | local repo_slug="$1" 83 | local branch="$2" 84 | 85 | [ -n "$repo_slug" ] || return 1 86 | [ -n "$branch" ] || return 1 87 | 88 | local body="{ 89 | \"request\": { 90 | \"branch\":\"$branch\" 91 | } 92 | }" 93 | 94 | # Turn off tracing here (shortly) 95 | set +x 96 | curl -s -X POST \ 97 | -H "Content-Type: application/json" \ 98 | -H "Accept: application/json" \ 99 | -H "Travis-API-Version: 3" \ 100 | -H "Authorization: token $TRAVIS_API_TOKEN" \ 101 | -d "$body" \ 102 | https://api.travis-ci.org/repo/$repo_slug/requests 103 | } 104 | 105 | trigger_adi_build() { 106 | local adi_repo="$1" 107 | local branch="$2" 108 | 109 | [ -n "$adi_repo" ] || return 1 110 | trigger_build "analogdevicesinc%2F$adi_repo" "$branch" 111 | } 112 | 113 | command_exists() { 114 | local cmd=$1 115 | [ -n "$cmd" ] || return 1 116 | type "$cmd" >/dev/null 2>&1 117 | } 118 | 119 | get_ldist() { 120 | case "$(uname)" in 121 | Linux*) 122 | if [ ! -f /etc/os-release ] ; then 123 | if [ -f /etc/centos-release ] ; then 124 | echo "centos-$(sed -e 's/CentOS release //' -e 's/(.*)$//' \ 125 | -e 's/ //g' /etc/centos-release)-$(uname -m)" 126 | return 0 127 | fi 128 | ls /etc/*elease 129 | [ -z "${OSTYPE}" ] || { 130 | echo "${OSTYPE}-unknown" 131 | return 0 132 | } 133 | echo "linux-unknown" 134 | return 0 135 | fi 136 | . /etc/os-release 137 | if ! command_exists dpkg ; then 138 | echo $ID-$VERSION_ID-$(uname -m) 139 | else 140 | echo $ID-$VERSION_ID-$(dpkg --print-architecture) 141 | fi 142 | ;; 143 | Darwin*) 144 | echo "darwin-$(sw_vers -productVersion)" 145 | ;; 146 | *) 147 | echo "$(uname)-unknown" 148 | ;; 149 | esac 150 | return 0 151 | } 152 | 153 | __brew_install_or_upgrade() { 154 | brew install $1 || \ 155 | brew upgrade $1 || \ 156 | brew ls --versions $1 157 | } 158 | 159 | brew_install_or_upgrade() { 160 | while [ -n "$1" ] ; do 161 | __brew_install_or_upgrade "$1" || return 1 162 | shift 163 | done 164 | } 165 | 166 | __brew_install_if_not_exists() { 167 | brew ls --versions $1 || \ 168 | brew install $1 169 | } 170 | 171 | brew_install_if_not_exists() { 172 | while [ -n "$1" ] ; do 173 | __brew_install_if_not_exists "$1" || return 1 174 | shift 175 | done 176 | } 177 | 178 | sftp_cmd_pipe() { 179 | sftp ${EXTRA_SSH} ${SSHUSER}@${SSHHOST} 180 | } 181 | 182 | sftp_rm_artifact() { 183 | local artifact="$1" 184 | sftp_cmd_pipe <<-EOF 185 | cd ${DEPLOY_TO} 186 | rm ${artifact} 187 | EOF 188 | } 189 | 190 | sftp_upload() { 191 | local FROM="$1" 192 | local TO="$2" 193 | local LATE="$3" 194 | 195 | sftp_cmd_pipe <<-EOF 196 | cd ${DEPLOY_TO} 197 | 198 | put ${FROM} ${TO} 199 | ls -l ${TO} 200 | 201 | symlink ${TO} ${LATE} 202 | ls -l ${LATE} 203 | bye 204 | EOF 205 | } 206 | 207 | upload_file_to_swdownloads() { 208 | 209 | if [ "$#" -ne 4 ] ; then 210 | echo "skipping deployment of something" 211 | echo "send called with $@" 212 | return 0 213 | fi 214 | 215 | local LIBNAME=$1 216 | local FROM=$2 217 | local FNAME=$3 218 | local EXT=$4 219 | 220 | if [ -z "$FROM" ] ; then 221 | echo no file to send 222 | return 1 223 | fi 224 | 225 | if [ ! -r "$FROM" ] ; then 226 | echo "file $FROM is not readable" 227 | return 1 228 | fi 229 | 230 | if [ -n "$TRAVIS_PULL_REQUEST_BRANCH" ] ; then 231 | local branch="$TRAVIS_PULL_REQUEST_BRANCH" 232 | else 233 | local branch="$TRAVIS_BRANCH" 234 | fi 235 | 236 | local TO=${branch}_${FNAME} 237 | local LATE=${branch}_latest_${LIBNAME}${LDIST}${EXT} 238 | local GLOB=${DEPLOY_TO}/${branch}_${LIBNAME}-* 239 | 240 | echo attemting to deploy $FROM to $TO 241 | echo and ${branch}_${LIBNAME}${LDIST}${EXT} 242 | ssh -V 243 | 244 | for rmf in ${TO} ${LATE} ; do 245 | sftp_rm_artifact ${rmf} || \ 246 | echo_blue "Could not delete ${rmf}" 247 | done 248 | 249 | sftp_upload "${FROM}" "${TO}" "${LATE}" || { 250 | echo_red "Failed to upload artifact from '${FROM}', to '${TO}', symlink '${LATE}'" 251 | return 1 252 | } 253 | 254 | # limit things to a few files, so things don't grow forever 255 | if [ "${EXT}" = ".deb" ] ; then 256 | for files in $(ssh ${EXTRA_SSH} ${SSHUSER}@${SSHHOST} \ 257 | "ls -lt ${GLOB}" | tail -n +100 | awk '{print $NF}') 258 | do 259 | ssh ${EXTRA_SSH} ${SSHUSER}@${SSHHOST} \ 260 | "rm ${DEPLOY_TO}/${files}" || \ 261 | return 1 262 | done 263 | fi 264 | 265 | return 0 266 | } 267 | 268 | prepare_docker_image() { 269 | local DOCKER_IMAGE="$1" 270 | sudo apt-get -qq update 271 | echo 'DOCKER_OPTS="-H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock -s devicemapper"' | sudo tee /etc/default/docker > /dev/null 272 | sudo service docker restart 273 | sudo docker pull "$DOCKER_IMAGE" 274 | } 275 | 276 | run_docker_script() { 277 | local DOCKER_SCRIPT="$(get_script_path $1)" 278 | local DOCKER_IMAGE="$2" 279 | local OS_TYPE="$3" 280 | local MOUNTPOINT="${4:-docker_build_dir}" 281 | sudo docker run --rm=true \ 282 | -v "$(pwd):/${MOUNTPOINT}:rw" \ 283 | $DOCKER_IMAGE \ 284 | /bin/bash -e "/${MOUNTPOINT}/${DOCKER_SCRIPT}" "${MOUNTPOINT}" "${OS_TYPE}" 285 | } 286 | 287 | ensure_command_exists() { 288 | local cmd="$1" 289 | local package="$2" 290 | [ -n "$cmd" ] || return 1 291 | [ -n "$package" ] || package="$cmd" 292 | ! command_exists "$cmd" || return 0 293 | # go through known package managers 294 | for pacman in apt-get brew yum ; do 295 | command_exists $pacman || continue 296 | $pacman install -y $package || { 297 | # Try an update if install doesn't work the first time 298 | $pacman -y update && \ 299 | $pacman install -y $package 300 | } 301 | return $? 302 | done 303 | return 1 304 | } 305 | 306 | ensure_command_exists sudo 307 | 308 | # Other scripts will download lib.sh [this script] and lib.sh will 309 | # in turn download the other scripts it needs. 310 | # This gives way more flexibility when changing things, as they propagate 311 | for script in $COMMON_SCRIPTS ; do 312 | [ ! -f "CI/travis/$script" ] || continue 313 | [ ! -f "ci/travis/$script" ] || continue 314 | [ ! -f "${LOCAL_BUILD_DIR}/$script" ] || continue 315 | mkdir -p ${LOCAL_BUILD_DIR} 316 | ensure_command_exists wget 317 | wget https://raw.githubusercontent.com/analogdevicesinc/libiio/master/CI/travis/$script \ 318 | -O $LOCAL_BUILD_DIR/$script 319 | done 320 | -------------------------------------------------------------------------------- /CI/travis/make_darwin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | OS_VERSION=$( /usr/libexec/PlistBuddy -c "Print:ProductVersion" /System/Library/CoreServices/SystemVersion.plist ) 4 | echo $OS_VERSION 5 | mkdir -p build && cd build 6 | 7 | # Build tar.gz for OSX (contains libraries and smu) 8 | cmake -DBUILD_PYTHON=ON -DOSX_PACKAGE=ON -DCMAKE_OSX_DEPLOYMENT_TARGET=$OS_VERSION ..; make && sudo make install 9 | -------------------------------------------------------------------------------- /CI/travis/make_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # build tar containg python files 4 | if [[ ${OS_TYPE} != "doxygen" ]]; then 5 | mkdir -p build_tar && cd build_tar 6 | cmake -DDEB_DETECT_DEPENDENCIES=ON -DENABLE_PACKAGING=ON -DBUILD_PYTHON=OFF ..; make package; rm *.deb; rm *.rpm; cd .. ; 7 | fi 8 | 9 | mkdir -p build && cd build 10 | cmake -DDEB_DETECT_DEPENDENCIES=ON -DENABLE_PACKAGING=ON -DBUILD_PYTHON=OFF ..; make package; rm *.tar.gz 11 | 12 | if [[ ${OS_TYPE} == "doxygen" ]]; then "${BUILD_SOURCESDIRECTORY}/CI/travis/build_deploy_doc.sh" ; fi 13 | 14 | if [[ ${OS_TYPE} != "doxygen" ]]; then make && sudo make install ; fi 15 | 16 | cd ${BUILD_SOURCESDIRECTORY} -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(MSVC) 2 | # needed for CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS support 3 | cmake_minimum_required(VERSION 3.4) 4 | else() 5 | cmake_minimum_required(VERSION 2.8.7) 6 | endif() 7 | project(libsmu CXX C) 8 | 9 | if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.18.0") 10 | # https://cmake.org/cmake/help/v3.18/policy/CMP0068.html 11 | cmake_policy(SET CMP0068 NEW) 12 | endif() 13 | 14 | # libsmu versioning 15 | set(LIBSMU_VERSION_MAJOR 1) 16 | set(LIBSMU_VERSION_MINOR 0) 17 | set(LIBSMU_VERSION_PATCH 4) 18 | set(LIBSMU_VERSION ${LIBSMU_VERSION_MAJOR}.${LIBSMU_VERSION_MINOR}.${LIBSMU_VERSION_PATCH}) 19 | 20 | # determine a more descriptive project version using git info if available 21 | set(LIBSMU_VERSION_STR ${LIBSMU_VERSION}) 22 | if(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/.git) 23 | # determine if the current revision is a tag 24 | execute_process(COMMAND git describe --exact-match --tags HEAD 25 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 26 | OUTPUT_VARIABLE LIBSMU_TAG_VERSION 27 | OUTPUT_STRIP_TRAILING_WHITESPACE 28 | ERROR_QUIET 29 | ) 30 | # if the current revision isn't a tag, add git revision info 31 | if(LIBSMU_TAG_VERSION STREQUAL "") 32 | execute_process(COMMAND git rev-parse --short HEAD 33 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 34 | OUTPUT_VARIABLE LIBSMU_GIT_REV 35 | OUTPUT_STRIP_TRAILING_WHITESPACE 36 | ERROR_QUIET 37 | ) 38 | set(LIBSMU_VERSION_STR ${LIBSMU_VERSION_STR}-g${LIBSMU_GIT_REV}) 39 | endif() 40 | endif() 41 | # write version info to file -- used for CI artifact versioning 42 | file(WRITE ${CMAKE_BINARY_DIR}/.version ${LIBSMU_VERSION_STR}) 43 | 44 | # generate version header 45 | configure_file(${CMAKE_SOURCE_DIR}/dist/version.hpp.in 46 | ${CMAKE_SOURCE_DIR}/include/libsmu/version.hpp @ONLY) 47 | include_directories(${CMAKE_SOURCE_DIR}/include/) 48 | 49 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 50 | set(CMAKE_CXX_FLAGS "-Wall -pedantic -std=c++11 ${CMAKE_CXX_FLAGS}") 51 | endif() 52 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG_BUILD") 53 | 54 | # don't complain about extra format args for g++ 55 | if(CMAKE_COMPILER_IS_GNUCXX) 56 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format-extra-args") 57 | endif() 58 | 59 | # build a shared library by default 60 | set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries") 61 | # build python support by default 62 | set(BUILD_PYTHON ON CACHE BOOL "Build python support") 63 | # build command line smu application by default 64 | set(BUILD_CLI ON CACHE BOOL "Build command line smu application") 65 | # don't build examples by default 66 | set(BUILD_EXAMPLES OFF CACHE BOOL "Build examples") 67 | # don't build tests by default 68 | set(BUILD_TESTS OFF CACHE BOOL "Build unit tests") 69 | # install udev rules 70 | set(INSTALL_UDEV_RULES ON CACHE BOOL "Install udev rules for the M1K") 71 | # don't generate docs by default 72 | set(WITH_DOC OFF CACHE BOOL "Generate documentation with Doxygen") 73 | 74 | include(GNUInstallDirs) 75 | 76 | # set default install path to /usr/local 77 | if (NOT WIN32 AND CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 78 | set(CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "default install path" FORCE) 79 | endif() 80 | 81 | # handle RPATH issues on OS X 82 | if(APPLE) 83 | set(CMAKE_MACOSX_RPATH ON) 84 | set(CMAKE_SKIP_BUILD_RPATH FALSE) 85 | set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) 86 | set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_LIBDIR}") 87 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 88 | list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_LIBDIR}" isSystemDir) 89 | if("${isSystemDir}" STREQUAL "-1") 90 | set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_LIBDIR}") 91 | endif() 92 | endif() 93 | 94 | add_subdirectory(src) 95 | if(BUILD_PYTHON) 96 | add_subdirectory(bindings/python) 97 | endif() 98 | if(BUILD_CLI) 99 | add_subdirectory(src/cli) 100 | endif() 101 | if(BUILD_EXAMPLES) 102 | add_subdirectory(examples) 103 | endif() 104 | if(BUILD_TESTS) 105 | add_subdirectory(tests) 106 | endif() 107 | 108 | # windows installer file 109 | if(WIN32) 110 | configure_file(dist/libsmu-x86.iss.cmakein ${CMAKE_CURRENT_BINARY_DIR}/dist/libsmu-x86.iss @ONLY) 111 | configure_file(dist/libsmu-x64.iss.cmakein ${CMAKE_CURRENT_BINARY_DIR}/dist/libsmu-x64.iss @ONLY) 112 | endif() 113 | 114 | # install pkgconfig file 115 | set(LIBSMU_PC ${CMAKE_CURRENT_BINARY_DIR}/libsmu.pc) 116 | configure_file(dist/libsmu.pc.cmakein ${LIBSMU_PC} @ONLY) 117 | install(FILES ${LIBSMU_PC} DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 118 | 119 | # install udev rules on Linux 120 | if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND INSTALL_UDEV_RULES) 121 | set(LIBSMU_UDEV_RULES "${CMAKE_CURRENT_SOURCE_DIR}/dist/53-adi-m1k-usb.rules") 122 | set(UDEV_RULES_PATH "/etc/udev/rules.d" CACHE STRING "Target directory for udev rule installation.") 123 | install(FILES ${LIBSMU_UDEV_RULES} DESTINATION ${UDEV_RULES_PATH}) 124 | endif() 125 | 126 | # generate API docs with doxygen 127 | if(WITH_DOC) 128 | add_subdirectory(doc) 129 | endif() 130 | 131 | # Create an installer if compiling for OSX 132 | if(OSX_PACKAGE) 133 | set(LIBSMU_PKG ${CMAKE_CURRENT_BINARY_DIR}/libsmu-${LIBSMU_VERSION_STR}.pkg) 134 | set(LIBSMU_TEMP_PKG ${CMAKE_CURRENT_BINARY_DIR}/libsmu-${LIBSMU_VERSION_STR}-temp.pkg) 135 | set(LIBSMU_DISTRIBUTION_XML ${CMAKE_CURRENT_BINARY_DIR}/Distribution.xml) 136 | set(LIBSMU_FRAMEWORK_DIR ${CMAKE_CURRENT_BINARY_DIR}/src/libsmu.framework) 137 | configure_file(Distribution.xml.cmakein ${LIBSMU_DISTRIBUTION_XML} @ONLY) 138 | 139 | find_program(PKGBUILD_EXECUTABLE 140 | NAMES pkgbuild 141 | DOC "OSX Package builder (pkgbuild)") 142 | mark_as_advanced(PKGBUILD_EXECUTABLE) 143 | 144 | find_program(PRODUCTBUILD_EXECUTABLE 145 | NAMES productbuild 146 | DOC "OSX Package builder (productbuild)") 147 | mark_as_advanced(PRODUCTBUILD_EXECUTABLE) 148 | 149 | set(COPY_TOOLS_COMMAND) 150 | if (BUILD_CLI) 151 | list(APPEND COPY_TOOLS_COMMAND 152 | COMMAND ${CMAKE_COMMAND} -E copy $ ${LIBSMU_FRAMEWORK_DIR}/cli) 153 | endif() 154 | 155 | add_custom_command(OUTPUT ${LIBSMU_PKG} 156 | COMMAND ${CMAKE_COMMAND} -E make_directory ${LIBSMU_FRAMEWORK_DIR}/cli 157 | ${COPY_TOOLS_COMMAND} 158 | COMMAND ${PKGBUILD_EXECUTABLE} 159 | --component ${LIBSMU_FRAMEWORK_DIR} 160 | --identifier libsmu --version ${LIBSMU_VERSION_STR} 161 | --install-location ${OSX_INSTALL_FRAMEWORKSDIR} ${LIBSMU_TEMP_PKG} 162 | COMMAND ${PRODUCTBUILD_EXECUTABLE} 163 | --distribution ${LIBSMU_DISTRIBUTION_XML} ${LIBSMU_PKG} 164 | COMMAND ${CMAKE_COMMAND} -E remove ${LIBSMU_TEMP_PKG} 165 | DEPENDS smu ${SMUCLI_TARGET} ${LIBSMU_DISTRIBUTION_XML} 166 | ) 167 | 168 | if (PKGBUILD_EXECUTABLE AND PRODUCTBUILD_EXECUTABLE) 169 | add_custom_target(libsmu-pkg ALL DEPENDS ${LIBSMU_PKG}) 170 | 171 | install(CODE "execute_process(COMMAND /usr/sbin/installer -pkg ${LIBSMU_PKG} -target /)") 172 | else() 173 | message(WARNING "Missing pkgbuild or productbuild: OSX installer won't be created.") 174 | endif() 175 | endif() 176 | 177 | if (NOT OSX_PACKAGE) 178 | # Support creating some basic binpkgs via `make package`. 179 | # Disabled if OSX_PACKAGE is enabled, as tarballs would end up empty otherwise. 180 | option(ENABLE_PACKAGING "Create .deb/.rpm or .tar.gz packages via 'make package'" OFF) 181 | 182 | if(ENABLE_PACKAGING) 183 | if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 184 | include(cmake/DarwinPackaging.cmake) 185 | endif() 186 | if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 187 | include(cmake/LinuxPackaging.cmake) 188 | endif() 189 | endif() 190 | endif() 191 | -------------------------------------------------------------------------------- /Distribution.xml.cmakein: -------------------------------------------------------------------------------- 1 | 2 | 3 | libsmu 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | @LIBSMU_TEMP_PKG@ 20 | 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2015 Kevin Mehall Ian Daniher 2 | Copyright (c) 2014-2016 Analog Devices, Inc. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of Analog Devices nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /bindings/python/.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | /build 3 | /dist 4 | /.coverage 5 | /*.egg-info 6 | pysmu/*.cpp 7 | 8 | /doc/_build/ 9 | -------------------------------------------------------------------------------- /bindings/python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4) 2 | 3 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") 4 | set(CMAKE_VERBOSE_MAKEFILE ON) 5 | string(REGEX MATCHALL "([^\ ]+\ |[^\ ]+$)" CMAKE_CXX_LIST "${CMAKE_CXX_FLAGS}") 6 | FOREACH(CXX_COMPILE_FLAG ${CMAKE_CXX_LIST}) 7 | string(REPLACE " " "" CXX_COMPILE_FLAG "${CXX_COMPILE_FLAG}") 8 | set(EXTRA_COMPILE_FLAGS '${CXX_COMPILE_FLAG}',${EXTRA_COMPILE_FLAGS}) 9 | ENDFOREACH() 10 | 11 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") 12 | set(CMAKE_VERBOSE_MAKEFILE ON) 13 | string(REGEX MATCHALL "([^\ ]+\ |[^\ ]+$)" CMAKE_CXX_LIST "${CMAKE_CXX_FLAGS}") 14 | FOREACH(CXX_COMPILE_FLAG ${CMAKE_CXX_LIST}) 15 | string(REPLACE " " "" CXX_COMPILE_FLAG "${CXX_COMPILE_FLAG}") 16 | set(EXTRA_COMPILE_FLAGS '${CXX_COMPILE_FLAG}',${EXTRA_COMPILE_FLAGS}) 17 | ENDFOREACH() 18 | 19 | if(${CMAKE_VERSION} VERSION_LESS "3.12.0") 20 | if (NOT WIN32) 21 | set(Python_ADDITIONAL_VERSIONS 3) 22 | endif() 23 | 24 | include(FindPythonInterp) 25 | include(FindPythonLibs) 26 | 27 | set(Python_Interpreter_FOUND ${PYTHONINTERP_FOUND}) 28 | set(Python_EXECUTABLE ${PYTHON_EXECUTABLE}) 29 | set(Python_LIBRARIES ${PYTHON_LIBRARIES}) 30 | set(Python_INCLUDE_PATH ${PYTHON_INCLUDE_PATH}) 31 | set(Python_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS}) 32 | else() 33 | find_package(Python COMPONENTS Interpreter Development) 34 | endif() 35 | 36 | string(REPLACE " " "','" CXX_COMPILE_FLAG "${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") 37 | set(EXTRA_COMPILE_FLAGS '${CXX_COMPILE_FLAG}',${EXTRA_COMPILE_FLAGS}) 38 | 39 | if(NOT APPLE) 40 | string(REPLACE " " "','" CXX_LINK_FLAG "${CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS}") 41 | set(EXTRA_LINK_FLAGS '${CXX_LINK_FLAG}',${EXTRA_LINK_FLAGS}) 42 | else() 43 | set(EXTRA_LINK_FLAGS '-flat_namespace','-undefined','suppress',${EXTRA_LINK_FLAGS}) 44 | if (${OSX_PACKAGE}) 45 | set(EXTRA_LINK_FLAGS '-F${CMAKE_BINARY_DIR}/src',${EXTRA_LINK_FLAGS}) 46 | set(EXTRA_LINK_FLAGS '-framework','${PROJECT_NAME}',${EXTRA_LINK_FLAGS}) 47 | endif() 48 | message(${CMAKE_OSX_DEPLOYMENT_TARGET}) 49 | set(EXTRA_LINK_FLAGS '-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}',${EXTRA_LINK_FLAGS}) 50 | string(REPLACE " " "','" CXX_COMPILE_FLAG "${CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS}") 51 | set(EXTRA_LINK_FLAGS '${CXX_COMPILE_FLAG}',${EXTRA_LINK_FLAGS}) 52 | endif() 53 | 54 | set(SETUP_PY_IN ${CMAKE_CURRENT_SOURCE_DIR}/setup.py.cmakein) 55 | set(SETUP_PY ${CMAKE_BINARY_DIR}/setup.py) 56 | configure_file(${SETUP_PY_IN} ${SETUP_PY}) 57 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pyproject.toml ${CMAKE_BINARY_DIR}/pyproject.toml) 58 | configure_file(${CMAKE_SOURCE_DIR}/bindings/python/README.md ${CMAKE_BINARY_DIR}/README.md COPYONLY) 59 | set(DEPS "${CMAKE_CURRENT_SOURCE_DIR}/pysmu/__init__.py") 60 | set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/timestamp") 61 | 62 | #${SETUP_PY} build build_ext -L "${CMAKE_BINARY_DIR}/src" 63 | add_custom_command(OUTPUT ${OUTPUT} 64 | COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/pysmu ${CMAKE_BINARY_DIR}/pysmu 65 | COMMAND ${Python_EXECUTABLE} -m build 66 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" 67 | DEPENDS ${DEPS}) 68 | add_custom_target(python ALL DEPENDS smu ${OUTPUT}) 69 | 70 | install(CODE "execute_process(WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 71 | COMMAND ${Python_EXECUTABLE} ${SETUP_PY} install --root=\$ENV{DESTDIR}/ --prefix=${CMAKE_INSTALL_PREFIX})") 72 | -------------------------------------------------------------------------------- /bindings/python/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include tox.ini 2 | recursive-include bin * 3 | recursive-include doc * 4 | recursive-include examples * 5 | recursive-include tests * 6 | recursive-include pysmu * 7 | global-exclude CMakeLists.txt *.pyc *.pyo __pycache__ 8 | -------------------------------------------------------------------------------- /bindings/python/README.md: -------------------------------------------------------------------------------- 1 | # libsmu : Python bindings 2 | 3 | This package contains the python bindings for libsmu. 4 | libsmu contains abstractions for streaming data to and from USB-connected analog interface devices, currently supporting the Analog Devices' ADALM1000. Building off of LibUSB for cross-platform operation, it offers the sourcing of repeated waveforms, configuration of hardware, and measuring of signals. 5 | 6 | [![Build Status (Linux, Windows, MacOS)](https://dev.azure.com/AnalogDevices/Libsmu/_apis/build/status/analogdevicesinc.libsmu?branchName=master)](https://dev.azure.com/AnalogDevices/Libsmu/_build/latest?definitionId=32&branchName=master) 7 | 8 | [[Docs](http://analogdevicesinc.github.io/libsmu/)] 9 | [[Support](http://ez.analog.com)] 10 | [[Wiki](https://wiki.analog.com/university/tools/m1k/libsmu)] 11 | 12 | ## Requirements 13 | To use these bindings you need the core C++ library they depend upon. This is not packaged with the pypi release but you can install the [latest release](https://github.com/analogdevicesinc/libsmu/releases/latest) or the latest **untested** binaries from the [master branch](https://ci.appveyor.com/project/analogdevicesinc/libsmu). 14 | 15 | ### Installing 16 | If you want to install pysmu (the Python bindings for libsmu), you can download the specific wheel for your version. 17 | We provide python wheel packages for the following Python versions: py3.7, py3.8, py3.9, py3.10. You can download the .whl 18 | for your Python version from the official releases or use the ones provided on test.pypi.org (soon from the official pypi.org as well). 19 | On Linux: 20 | ```shell 21 | # If you are installing from test.pypi.org: 22 | analog@analog:~$ python3 -m pip install --index-url https://test.pypi.org/simple/ pysmu 23 | # If you are installing the .whl downloaded from our official github release: 24 | analog@analog:~$ python3 -m pip install pysmu-1.x.y-cp3x-cp3x-your-os-type.whl 25 | ``` 26 | Please note that in order to use these bindings you need the core C++ library they depend upon. This is not packaged with the pypi release but you can install the [latest release](https://github.com/analogdevicesinc/libsmu/releases/latest) or the latest **untested** binaries from the [master branch](https://ci.appveyor.com/project/analogdevicesinc/libsmu). 27 | 28 | If you want to build them manually, please check the [build guide](https://wiki.analog.com/university/tools/m1k/libsmu#how_to_build_it) for your specific operating system. 29 | -------------------------------------------------------------------------------- /bindings/python/doc/index.rst: -------------------------------------------------------------------------------- 1 | .. pysmu documentation master file, created by 2 | sphinx-quickstart on Sat Dec 3 22:21:07 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to pysmu's documentation! 7 | ================================= 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | 15 | 16 | Indices and tables 17 | ================== 18 | 19 | * :ref:`genindex` 20 | * :ref:`modindex` 21 | * :ref:`search` 22 | 23 | -------------------------------------------------------------------------------- /bindings/python/examples/get_samples.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Simple script showing how to get a set number of samples from all devices 4 | # connected to a system. 5 | 6 | from __future__ import print_function 7 | 8 | import sys 9 | 10 | from pysmu import Session, Mode 11 | 12 | 13 | if __name__ == '__main__': 14 | session = Session() 15 | 16 | if not session.devices: 17 | sys.exit(1) 18 | 19 | for idx, dev in enumerate(session.devices): 20 | # Set all devices in the session to use source voltage, measure current 21 | # mode for channel A with a constant value based on their index. 22 | dev.channels['A'].mode = Mode.SVMI 23 | dev.channels['A'].constant(idx % 6) 24 | # Set all devices in the session to use source current, measure voltage 25 | # mode for channel B with a constant value of 0.05. 26 | dev.channels['B'].mode = Mode.SIMV 27 | dev.channels['B'].constant(0.05) 28 | 29 | # Run the session for at least 10 captured samples in noncontinuous mode. 30 | for dev_idx, samples in enumerate(session.get_samples(1000)): 31 | print('dev: {}'.format(dev_idx)) 32 | for x in samples: 33 | print("{: 6f} {: 6f} {: 6f} {: 6f}".format(x[0][0], x[0][1], x[1][0], x[1][1])) 34 | -------------------------------------------------------------------------------- /bindings/python/examples/hotplug.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Simple script showing how custom functions can be executed during hotplug 4 | # events, use Ctrl-C to exit. 5 | 6 | from __future__ import print_function 7 | 8 | from signal import signal, SIG_DFL, SIGINT 9 | import time 10 | 11 | from pysmu import Session 12 | 13 | session = Session() 14 | last_devices = session.available_devices 15 | 16 | while True: 17 | time.sleep(2) 18 | 19 | session.scan() 20 | available_devices = session.available_devices 21 | 22 | for other_device in last_devices: 23 | found = False 24 | 25 | for device in available_devices: 26 | if other_device.serial == device.serial: 27 | found = True 28 | break 29 | 30 | if not found: 31 | print("Device detached!") 32 | tmp = list(last_devices) 33 | tmp.remove(other_device) 34 | last_devices = tuple(tmp) 35 | 36 | for device in available_devices: 37 | found = False 38 | 39 | for other_device in last_devices: 40 | if other_device.serial == device.serial: 41 | found = True 42 | break 43 | 44 | if not found: 45 | print("Device attached") 46 | 47 | last_devices = available_devices 48 | print("Number of available devices: " + str(len(last_devices))) 49 | -------------------------------------------------------------------------------- /bindings/python/examples/leds.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Iterate through various random RGB LED states for all connected devices. 4 | 5 | from signal import signal, SIG_DFL, SIGINT 6 | import sys 7 | import time 8 | from random import randrange 9 | 10 | from pysmu import Session, LED 11 | 12 | 13 | if __name__ == '__main__': 14 | # don't throw KeyboardInterrupt on Ctrl-C 15 | signal(SIGINT, SIG_DFL) 16 | 17 | session = Session() 18 | 19 | if not session.devices: 20 | sys.exit(1) 21 | 22 | while True: 23 | val = randrange(0, 8) 24 | for dev in session.devices: 25 | dev.set_led(val) 26 | time.sleep(.25) 27 | -------------------------------------------------------------------------------- /bindings/python/examples/multi-cyclic-run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Simple script showing how to perform a non-continuous, multi-run session 4 | # while changing cyclic buffer values. 5 | 6 | from __future__ import print_function 7 | 8 | import sys 9 | 10 | from pysmu import Session, Mode 11 | 12 | 13 | if __name__ == '__main__': 14 | session = Session() 15 | 16 | if not session.devices: 17 | sys.exit(1) 18 | 19 | for dev in session.devices: 20 | dev.channels['A'].mode = Mode.SVMI 21 | dev.channels['B'].mode = Mode.SIMV 22 | 23 | for x in range(21): 24 | # Write 1000 samples of incrementing voltage and current values to 25 | # channel A and B, respectively, for every device in the session. 26 | v = x * (5 / 20.0) 27 | i = ((x * (4 / 20.0)) / 10.0) - 0.2 28 | data = (([v] * 1000, [i] * 1000) for dev in session.devices) 29 | session.write(data, cyclic=True) 30 | 31 | for dev, samples in enumerate(session.get_samples(10)): 32 | print('dev: {}: chan A voltage: {}, chan B current: {}'.format(dev, v, i)) 33 | for x in samples: 34 | print("{: 6f} {: 6f} {: 6f} {: 6f}".format(x[0][0], x[0][1], x[1][0], x[1][1])) 35 | -------------------------------------------------------------------------------- /bindings/python/examples/plot-voltage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Simple script showing how to write a sine/cosine wave to the attached devices and 4 | # plot the resulting voltage output for both channels using matplotlib. 5 | 6 | from __future__ import print_function 7 | 8 | from collections import defaultdict 9 | import sys 10 | 11 | from pysmu import Session, Mode 12 | import matplotlib.pyplot as plt 13 | 14 | 15 | if __name__ == '__main__': 16 | session = Session() 17 | 18 | if not session.devices: 19 | sys.exit(1) 20 | 21 | waveform_samples = 500 22 | 23 | for i, dev in enumerate(session.devices): 24 | # Output a sine wave for channel A voltage. 25 | dev.channels['A'].mode = Mode.SVMI 26 | dev.channels['A'].sine(0, 5, waveform_samples, -(waveform_samples / 4)) 27 | # Output a cosine wave for channel B voltage. 28 | dev.channels['B'].mode = Mode.SVMI 29 | dev.channels['B'].sine(0, 5, waveform_samples, 0) 30 | 31 | chan_a = defaultdict(list) 32 | chan_b = defaultdict(list) 33 | 34 | # Run the session in noncontinuous mode. 35 | for _x in range(10): 36 | for i, samples in enumerate(session.get_samples(waveform_samples / 5)): 37 | chan_a[i].extend([x[0][0] for x in samples]) 38 | chan_b[i].extend([x[1][0] for x in samples]) 39 | 40 | for i, dev in enumerate(session.devices): 41 | plt.figure(i) 42 | plt.plot(chan_a[i], label='Channel A') 43 | plt.plot(chan_b[i], label='Channel B') 44 | plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) 45 | plt.title('Device {}: {}'.format(i, str(session.devices[i]))) 46 | plt.ylabel('Voltage') 47 | plt.xlabel('Sample number') 48 | 49 | # Show all channel voltage plots for attached devices. 50 | plt.show() 51 | -------------------------------------------------------------------------------- /bindings/python/examples/read-continuous.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Simple script showing how to stream data from a device in continuous mode, 4 | # use Ctrl-C to exit. 5 | 6 | from __future__ import print_function 7 | 8 | from signal import signal, SIG_DFL, SIGINT 9 | import sys 10 | 11 | from pysmu import Session, Mode 12 | 13 | 14 | # If stdout is a terminal continuously overwrite a single line, otherwise 15 | # output each line individually. 16 | if sys.stdout.isatty(): 17 | output = lambda s: sys.stdout.write("\r" + s) 18 | else: 19 | output = print 20 | 21 | 22 | if __name__ == '__main__': 23 | # don't throw KeyboardInterrupt on Ctrl-C 24 | signal(SIGINT, SIG_DFL) 25 | 26 | session = Session() 27 | 28 | if session.devices: 29 | # Grab the first device from the session. 30 | dev = session.devices[0] 31 | 32 | # Set both channels to high impedance mode. 33 | chan_a = dev.channels['A'] 34 | chan_b = dev.channels['B'] 35 | chan_a.mode = Mode.HI_Z 36 | chan_b.mode = Mode.HI_Z 37 | 38 | # Ignore read buffer sample drops when printing to stdout. 39 | dev.ignore_dataflow = sys.stdout.isatty() 40 | 41 | # Start a continuous session. 42 | session.start(0) 43 | 44 | while True: 45 | # Read incoming samples from both channels which are in HI-Z mode 46 | # by default in a blocking fashion. 47 | samples = dev.read(1000, -1) 48 | for x in samples: 49 | output("{: 6f} {: 6f} {: 6f} {: 6f}".format(x[0][0], x[0][1], x[1][0], x[1][1])) 50 | else: 51 | print('no devices attached') 52 | -------------------------------------------------------------------------------- /bindings/python/examples/read-write-continuous.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Simple script showing how to read and write data to a device in continuous 4 | # mode, use Ctrl-C to exit. 5 | 6 | from __future__ import print_function 7 | 8 | from signal import signal, SIG_DFL, SIGINT 9 | import random 10 | import sys 11 | import time 12 | 13 | from pysmu import Session, Mode 14 | 15 | 16 | # If stdout is a terminal continuously overwrite a single line, otherwise 17 | # output each line individually. 18 | if sys.stdout.isatty(): 19 | output = lambda s: sys.stdout.write("\r" + s) 20 | else: 21 | output = print 22 | 23 | 24 | def refill_data(num_samples, v=None): 25 | if v is None: 26 | # fill channels with a static, random integer between 0 and 5 27 | v = random.randint(0, 5) 28 | return [v] * num_samples 29 | # fill channels with a static, random float between -0.2 and 0.2 30 | #return [random.uniform(-0.2,0.2)] * num_samples 31 | 32 | 33 | if __name__ == '__main__': 34 | # don't throw KeyboardInterrupt on Ctrl-C 35 | signal(SIGINT, SIG_DFL) 36 | 37 | session = Session() 38 | 39 | if session.devices: 40 | # Grab the first device from the session. 41 | dev = session.devices[0] 42 | 43 | # Ignore read buffer sample drops when printing to stdout. 44 | dev.ignore_dataflow = sys.stdout.isatty() 45 | 46 | # Set both channels to source voltage, measure current mode. 47 | chan_a = dev.channels['A'] 48 | chan_b = dev.channels['B'] 49 | chan_a.mode = Mode.SVMI 50 | chan_b.mode = Mode.SVMI 51 | #chan_a.mode = Mode.SIMV 52 | #chan_b.mode = Mode.SIMV 53 | 54 | # Start a continuous session. 55 | session.start(0) 56 | i = 0 57 | num_samples = session.queue_size + 1 58 | start = time.time() 59 | 60 | while True: 61 | # Change written value approximately every second. 62 | if time.time() - start > 1: 63 | i += 1 64 | start = time.time() 65 | 66 | # Write iterating voltage values to both channels. 67 | chan_a.write(refill_data(num_samples, i % 6), -1) 68 | chan_b.write(refill_data(num_samples, i % 6), -1) 69 | 70 | # Read incoming samples in a non-blocking fashion. 71 | samples = dev.read(num_samples) 72 | for x in samples: 73 | output("{: 6f} {: 6f} {: 6f} {: 6f}".format(x[0][0], x[0][1], x[1][0], x[1][1])) 74 | else: 75 | print('no devices attached') 76 | -------------------------------------------------------------------------------- /bindings/python/examples/read-write.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Simple script showing how to read and write data to a device in noncontinuous 4 | # mode, use Ctrl-C to exit. 5 | 6 | from __future__ import print_function 7 | 8 | from signal import signal, SIG_DFL, SIGINT 9 | import random 10 | import sys 11 | import time 12 | 13 | from pysmu import Session, Mode 14 | 15 | 16 | # If stdout is a terminal continuously overwrite a single line, otherwise 17 | # output each line individually. 18 | if sys.stdout.isatty(): 19 | output = lambda s: sys.stdout.write("\r" + s) 20 | else: 21 | output = print 22 | 23 | 24 | def refill_data(num_samples, v=None): 25 | if v is None: 26 | # fill channels with a static, random integer between 0 and 5 27 | v = random.randint(0, 5) 28 | return [v] * num_samples 29 | # fill channels with a static, random float between -0.2 and 0.2 30 | #return [random.uniform(-0.2,0.2)] * num_samples 31 | 32 | 33 | if __name__ == '__main__': 34 | # don't throw KeyboardInterrupt on Ctrl-C 35 | signal(SIGINT, SIG_DFL) 36 | 37 | session = Session() 38 | 39 | if session.devices: 40 | # Grab the first device from the session. 41 | dev = session.devices[0] 42 | 43 | # Set both channels to source voltage, measure current mode. 44 | chan_a = dev.channels['A'] 45 | chan_b = dev.channels['B'] 46 | chan_a.mode = Mode.SVMI 47 | chan_b.mode = Mode.SVMI 48 | #chan_a.mode = Mode.SIMV 49 | #chan_b.mode = Mode.SIMV 50 | 51 | i = 0 52 | num_samples = session.queue_size + 1 53 | start = time.time() 54 | 55 | while True: 56 | # Change written value approximately every second. 57 | if time.time() - start > 1: 58 | i += 1 59 | start = time.time() 60 | 61 | # Write iterating voltage values to both channels. 62 | chan_a.write(refill_data(num_samples, i % 6)) 63 | chan_b.write(refill_data(num_samples, i % 6)) 64 | 65 | # Read incoming samples in a blocking fashion. 66 | samples = dev.get_samples(num_samples) 67 | for x in samples: 68 | output("{: 6f} {: 6f} {: 6f} {: 6f}".format(x[0][0], x[0][1], x[1][0], x[1][1])) 69 | else: 70 | print('no devices attached') 71 | -------------------------------------------------------------------------------- /bindings/python/examples/read.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Simple script showing how to stream data from a device in noncontinuous mode, 4 | # use Ctrl-C to exit. 5 | 6 | from __future__ import print_function 7 | 8 | from signal import signal, SIG_DFL, SIGINT 9 | import sys 10 | 11 | from pysmu import Session, Mode 12 | 13 | 14 | # If stdout is a terminal continuously overwrite a single line, otherwise 15 | # output each line individually. 16 | if sys.stdout.isatty(): 17 | output = lambda s: sys.stdout.write("\r" + s) 18 | else: 19 | output = print 20 | 21 | 22 | if __name__ == '__main__': 23 | # don't throw KeyboardInterrupt on Ctrl-C 24 | signal(SIGINT, SIG_DFL) 25 | 26 | session = Session() 27 | 28 | if session.devices: 29 | # Grab the first device from the session. 30 | dev = session.devices[0] 31 | 32 | # Set both channels to high impedance mode. 33 | chan_a = dev.channels['A'] 34 | chan_b = dev.channels['B'] 35 | chan_a.mode = Mode.HI_Z 36 | chan_b.mode = Mode.HI_Z 37 | 38 | while True: 39 | # Run the session for 1000 samples in noncontinuous mode and read 40 | # incoming samples from both channels of the first device in a 41 | # blocking fashion. 42 | samples = dev.get_samples(1000) 43 | for x in samples: 44 | output("{: 6f} {: 6f} {: 6f} {: 6f}".format(x[0][0], x[0][1], x[1][0], x[1][1])) 45 | else: 46 | print('no devices attached') 47 | -------------------------------------------------------------------------------- /bindings/python/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel", "cython", "six"] 3 | build-backend = "setuptools.build_meta" -------------------------------------------------------------------------------- /bindings/python/pysmu/__init__.py: -------------------------------------------------------------------------------- 1 | # Released under the terms of the BSD License 2 | # (C) 2016, Analog Devices, Inc. 3 | 4 | __version__ = '1.0.4' 5 | 6 | import os 7 | 8 | #Import DLLs for Python versions >= 3.8 9 | for path in os.environ.get("PATH", "").split(os.pathsep): 10 | if path and os.path.isabs(path): 11 | try: 12 | os.add_dll_directory(path) 13 | except (OSError, AttributeError): 14 | continue 15 | 16 | from .libsmu import * 17 | from .exceptions import * 18 | -------------------------------------------------------------------------------- /bindings/python/pysmu/_vendor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analogdevicesinc/libsmu/dbb484f004d9eb5251aa4667f5cf09b3ff5610e2/bindings/python/pysmu/_vendor/__init__.py -------------------------------------------------------------------------------- /bindings/python/pysmu/_vendor/vendored.txt: -------------------------------------------------------------------------------- 1 | enum34==1.1.6 2 | -------------------------------------------------------------------------------- /bindings/python/pysmu/array.pxd: -------------------------------------------------------------------------------- 1 | # slightly modified version of https://github.com/cython/cython/pull/472 2 | 3 | cdef extern from "" namespace "std" nogil: 4 | cdef cppclass array[T, N]: 5 | cppclass iterator: 6 | T& operator*() 7 | iterator operator++() 8 | iterator operator--() 9 | iterator operator+(size_t) 10 | iterator operator-(size_t) 11 | bint operator==(iterator) 12 | bint operator!=(iterator) 13 | bint operator<(iterator) 14 | bint operator>(iterator) 15 | bint operator<=(iterator) 16 | bint operator>=(iterator) 17 | cppclass reverse_iterator: 18 | T& operator*() 19 | iterator operator++() 20 | iterator operator--() 21 | iterator operator+(size_t) 22 | iterator operator-(size_t) 23 | bint operator==(reverse_iterator) 24 | bint operator!=(reverse_iterator) 25 | bint operator<(reverse_iterator) 26 | bint operator>(reverse_iterator) 27 | bint operator<=(reverse_iterator) 28 | bint operator>=(reverse_iterator) 29 | cppclass const_iterator(iterator): 30 | pass 31 | cppclass const_reverse_iterator(reverse_iterator): 32 | pass 33 | array() except + 34 | array(array&) except + 35 | array(size_t) except + 36 | array(size_t, T&) except + 37 | #array[input_iterator](input_iterator, input_iterator) 38 | T& operator[](size_t) 39 | #array& operator=(array&) 40 | bint operator==(array&, array&) 41 | bint operator!=(array&, array&) 42 | bint operator<(array&, array&) 43 | bint operator>(array&, array&) 44 | bint operator<=(array&, array&) 45 | bint operator>=(array&, array&) 46 | void assign(size_t, const T&) 47 | void assign[input_iterator](input_iterator, input_iterator) except + 48 | T& at(size_t) except + 49 | T& back() 50 | iterator begin() 51 | const_iterator const_begin "begin"() 52 | bint empty() 53 | iterator end() 54 | const_iterator const_end "end"() 55 | T& front() 56 | size_t max_size() 57 | void pop_back() 58 | void push_back(T&) except + 59 | reverse_iterator rbegin() 60 | const_reverse_iterator const_rbegin "rbegin"() 61 | reverse_iterator rend() 62 | const_reverse_iterator const_rend "rend"() 63 | size_t size() 64 | void swap(array&) 65 | 66 | # C++11 methods 67 | T* data() 68 | -------------------------------------------------------------------------------- /bindings/python/pysmu/cpp_libsmu.pxd: -------------------------------------------------------------------------------- 1 | # Interface wrapper for the libsmu library. 2 | # distutils: language = c++ 3 | 4 | from libc.stdint cimport uint32_t, uint64_t 5 | from libcpp.set cimport set 6 | from libcpp.string cimport string 7 | from libcpp.vector cimport vector 8 | 9 | from .array cimport array 10 | 11 | 12 | # Hack to allow integer template parameters, unnecessary when 13 | # https://github.com/cython/cython/pull/426 is merged in some form. 14 | cdef extern from *: 15 | ctypedef int three "3" 16 | ctypedef int four "4" 17 | 18 | 19 | cdef extern from "libsmu/version.hpp": 20 | const char* libsmu_version_str() 21 | 22 | 23 | cdef extern from "libsmu/libsmu.hpp" namespace "smu" nogil: 24 | cdef cppclass Session: 25 | vector[Device*] m_available_devices 26 | set[Device*] m_devices 27 | int m_active_devices 28 | int m_queue_size 29 | int m_sample_rate 30 | bint m_continuous 31 | 32 | int scan() 33 | int add(Device* dev) 34 | int add_all() 35 | int remove(Device* dev, bint detached) 36 | int destroy(Device* dev) 37 | int configure(uint32_t sample_rate) 38 | int run(int samples) except + 39 | int start(int samples) 40 | int cancel() 41 | bint cancelled() 42 | void flush() 43 | int flash_firmware(const char* path, vector[Device*]) except + 44 | int end() 45 | 46 | cdef cppclass Device: 47 | string m_serial 48 | string m_fwver 49 | string m_hwver 50 | bint m_overcurrent 51 | 52 | Signal* signal(unsigned channel, unsigned signal) 53 | int set_mode(int channel, int mode) 54 | int get_mode(int channel) 55 | int fwver_sem(array[unsigned, three]& components) 56 | int set_serial(string serial) 57 | ssize_t read(vector[array[float, four]]& buf, size_t samples, int timeout, bint skipsamples) except + 58 | int write(vector[float]& buf, unsigned channel, bint cyclic) except + 59 | void flush(int channel, bint read) 60 | int ctrl_transfer( 61 | int bmRequestType, int bRequest, int wValue, int wIndex, 62 | unsigned char* data, int wLength, int timeout) 63 | int samba_mode() 64 | int get_default_rate() 65 | int sync() 66 | void lock() 67 | void unlock() 68 | int write_calibration(const char* path) 69 | void calibration(vector[vector[float]]* cal) 70 | int set_led(unsigned leds) 71 | int set_adc_mux(unsigned adc_mux) 72 | 73 | cdef cppclass Signal: 74 | sl_signal_info* info() 75 | void constant(vector[float]& buf, uint64_t samples, float val) 76 | void square(vector[float]& buf, uint64_t samples, float midpoint, float peak, double period, double phase, double duty) 77 | void sawtooth(vector[float]& buf, uint64_t samples, float midpoint, float peak, double period, double phase) 78 | void stairstep(vector[float]& buf, uint64_t samples, float midpoint, float peak, double period, double phase) 79 | void sine(vector[float]& buf, uint64_t samples, float midpoint, float peak, double period, double phase) 80 | void triangle(vector[float]& buf, uint64_t samples, float midpoint, float peak, double period, double phase) 81 | 82 | ctypedef struct sl_signal_info: 83 | const char* label 84 | uint32_t inputModes 85 | uint32_t outputModes 86 | double min 87 | double max 88 | double resolution 89 | -------------------------------------------------------------------------------- /bindings/python/pysmu/exceptions.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | class LibsmuError(Exception): 5 | """Generic libsmu exception.""" 6 | 7 | def __init__(self, msg=None, errcode=None): 8 | if msg is not None: 9 | self.msg = msg 10 | self.errcode = abs(errcode) if errcode is not None else None 11 | 12 | def __str__(self): 13 | if self.msg is not None: 14 | msg = self.msg 15 | if self.errcode is not None: 16 | msg += ': {}'.format(os.strerror(self.errcode)) 17 | return msg 18 | return repr(self) 19 | 20 | 21 | class SessionError(LibsmuError): 22 | """Generic libsmu session error.""" 23 | pass 24 | 25 | 26 | class DeviceError(LibsmuError): 27 | """Generic libsmu device error.""" 28 | pass 29 | 30 | 31 | class DataflowError(LibsmuError): 32 | """Generic data flow error.""" 33 | pass 34 | 35 | 36 | class SampleDrop(DataflowError): 37 | """An incoming sample has been dropped.""" 38 | pass 39 | 40 | 41 | class WriteTimeout(DataflowError): 42 | """An outgoing write buffer has timed out.""" 43 | pass 44 | -------------------------------------------------------------------------------- /bindings/python/pysmu/utils.py: -------------------------------------------------------------------------------- 1 | try: 2 | from collections.abc import Iterable 3 | except ImportError: 4 | from collections import Iterable 5 | 6 | def iterify(x): 7 | """Return an iterable form of a given value.""" 8 | if isinstance(x, Iterable): 9 | return x 10 | else: 11 | return (x,) 12 | -------------------------------------------------------------------------------- /bindings/python/setup.py.cmakein: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import copy 4 | from io import open 5 | import glob 6 | import os 7 | import re 8 | import shlex 9 | import shutil 10 | import subprocess 11 | import sys 12 | 13 | from setuptools.command.build_py import build_py 14 | from setuptools.command.build_ext import build_ext as dst_build_ext 15 | from setuptools.command.sdist import sdist as dst_sdist 16 | from setuptools import setup, find_packages, Extension, Command 17 | 18 | long_description="" 19 | with open("README.md", "r") as fh: 20 | long_description = fh.read() 21 | 22 | # top level bindings directory 23 | BINDINGS_DIR = '${CMAKE_CURRENT_SOURCE_DIR}' 24 | # top level repo directory 25 | TOPDIR = os.path.dirname(os.path.dirname(BINDINGS_DIR)) 26 | 27 | 28 | def pkgconfig(*packages, **kw): 29 | """Translate pkg-config data to compatible Extension parameters.""" 30 | flag_map = {'-I': 'include_dirs', '-L': 'library_dirs', '-l': 'libraries'} 31 | 32 | try: 33 | tokens = subprocess.check_output( 34 | ['pkg-config', '--libs', '--cflags'] + list(packages)).split() 35 | except OSError as e: 36 | sys.stderr.write('running pkg-config failed: {}\n'.format(e.strerror)) 37 | sys.exit(1) 38 | 39 | for token in tokens: 40 | token = token.decode() 41 | if token[:2] in flag_map: 42 | kw.setdefault(flag_map.get(token[:2]), []).append(token[2:]) 43 | else: 44 | kw.setdefault('extra_compile_args', []).append(token) 45 | return kw 46 | 47 | 48 | # configure various required compile flags 49 | ext_kwargs = {'include_dirs': [os.path.join(TOPDIR, 'include'), '${LIBUSB_INCLUDE_DIRS}']} 50 | if os.path.exists(os.path.join('${CMAKE_BINARY_DIR}', 'src')): 51 | # link against locally built library if build dir exists 52 | ext_kwargs['library_dirs'] = [os.path.join('${CMAKE_BINARY_DIR}', 'src')] 53 | 54 | if sys.platform == 'win32': 55 | ext_kwargs['libraries'] = ['libsmu'] 56 | elif sys.platform == "linux": 57 | ext_kwargs['libraries'] = ['smu'] 58 | ext_kwargs = pkgconfig('libusb-1.0', **ext_kwargs) 59 | elif sys.platform == "darwin": 60 | ext_kwargs['libraries'] = [] 61 | ext_kwargs = pkgconfig('libusb-1.0', **ext_kwargs) 62 | 63 | ext_kwargs['extra_compile_args']=[${EXTRA_COMPILE_FLAGS}] 64 | ext_kwargs['extra_link_args']=[${EXTRA_LINK_FLAGS}] 65 | 66 | # cython extensions to generate/build 67 | extensions = [] 68 | extensions.extend([ 69 | Extension( 70 | 'pysmu.libsmu', 71 | [os.path.join(BINDINGS_DIR, 'pysmu', 'libsmu.pyx')], **ext_kwargs), 72 | ]) 73 | 74 | # Embed function signature information into built cython modules for sphinx doc generation. 75 | for e in extensions: 76 | e.cython_directives = {"embedsignature": True} 77 | 78 | class sdist(dst_sdist): 79 | """Make sure generated cython extensions are included.""" 80 | 81 | def run(self): 82 | from Cython.Build import cythonize 83 | build_ext = self.reinitialize_command('build_ext') 84 | build_ext.ensure_finalized() 85 | cythonize(build_ext.extensions, 86 | compiler_directives={'language_level' : "3"}) 87 | dst_sdist.run(self) 88 | 89 | class build_ext(dst_build_ext): 90 | """Add custom compile flags for compiled extensions.""" 91 | 92 | @staticmethod 93 | def determine_ext_lang(ext_path): 94 | """Determine file extensions for generated cython extensions.""" 95 | with open(ext_path) as f: 96 | for line in f: 97 | line = line.lstrip() 98 | if not line: 99 | continue 100 | elif line[0] != '#': 101 | return None 102 | line = line[1:].lstrip() 103 | if line[:10] == 'distutils:': 104 | key, _, value = [s.strip() for s in line[10:].partition('=')] 105 | if key == 'language': 106 | return value 107 | else: 108 | return None 109 | 110 | def no_cythonize(self, **_ignore): 111 | """Determine file paths for generated cython extensions.""" 112 | extensions = copy.deepcopy(self.extensions) 113 | for extension in extensions: 114 | sources = [] 115 | for sfile in extension.sources: 116 | path, ext = os.path.splitext(sfile) 117 | if ext in ('.pyx', '.py'): 118 | lang = build_ext.determine_ext_lang(sfile) 119 | if lang == 'c++': 120 | ext = '.cpp' 121 | else: 122 | ext = '.c' 123 | sfile = path + ext 124 | sources.append(sfile) 125 | extension.sources[:] = sources 126 | return extensions 127 | 128 | def build_extensions(self): 129 | compiler = self.compiler.compiler_type 130 | cxxflags = [] 131 | if compiler != 'msvc': 132 | cxxflags.append('-std=c++11') 133 | for ext in self.extensions: 134 | ext.extra_compile_args.extend(cxxflags) 135 | return dst_build_ext.build_extensions(self) 136 | 137 | def run(self): 138 | from Cython.Build import cythonize 139 | cythonize(self.extensions, 140 | compiler_directives={'language_level' : "3"}) 141 | 142 | self.extensions = self.no_cythonize() 143 | return dst_build_ext.run(self) 144 | 145 | version = '' 146 | PY_INIT_FILE = os.path.join(BINDINGS_DIR, 'pysmu', '__init__.py') 147 | if (os.path.isfile(PY_INIT_FILE)): 148 | with open(PY_INIT_FILE, 'r') as fd: 149 | reg = re.compile(r'__version__\s*=\s*[\'"]([^\'"]*)[\'"]') 150 | for line in fd: 151 | m = reg.match(line) 152 | if m: 153 | version = m.group(1) 154 | break 155 | 156 | with open(PY_INIT_FILE, 'r+') as fd: 157 | contents = fd.readlines() 158 | pattern = "from .libsmu import" 159 | import_pattern = "import os" 160 | 161 | import_command = u"import os\n\n" + \ 162 | u"#Import DLLs for Python versions >= 3.8\n" + \ 163 | u"for path in os.environ.get(\"PATH\", \"\").split(os.pathsep):\n" + \ 164 | u"\tif path and os.path.isabs(path):\n" + \ 165 | u"\t\ttry:\n" + \ 166 | u"\t\t\tos.add_dll_directory(path)\n" + \ 167 | u"\t\texcept (OSError, AttributeError):\n" + \ 168 | u"\t\t\tcontinue\n\n" 169 | 170 | for index, line in enumerate(contents): 171 | #the script is run multiple times; this line prevents inserting the import command multuple times 172 | if import_pattern in line: 173 | break 174 | if pattern in line: 175 | contents.insert(index, import_command) 176 | break 177 | 178 | fd.seek(0) 179 | fd.writelines(contents) 180 | 181 | if not version: 182 | raise RuntimeError('Cannot find version information') 183 | 184 | 185 | test_requirements = ['pytest'] 186 | if sys.hexversion < 0x03030000: 187 | test_requirements.append('mock') 188 | 189 | setup( 190 | name='pysmu', 191 | version=version, 192 | description='python library and utility for the ADALM1000 device', 193 | long_description = long_description, 194 | long_description_content_type="text/markdown", 195 | url='https://github.com/analogdevicesinc/libsmu', 196 | license='BSD', 197 | maintainer='Analog Devices, Inc.', 198 | maintainer_email='alexandra.trifan@analog.com', 199 | packages=find_packages(), 200 | ext_modules=extensions, 201 | scripts=glob.glob('${CMAKE_CURRENT_SOURCE_DIR}/bin/*'), 202 | tests_require=test_requirements, 203 | cmdclass={ 204 | 'build_py': build_py, 205 | 'build_ext': build_ext, 206 | 'sdist': sdist 207 | }, 208 | classifiers=[ 209 | 'Intended Audience :: Developers', 210 | 'License :: OSI Approved :: BSD License', 211 | 'Programming Language :: Python :: 3.7', 212 | 'Programming Language :: Python :: 3.8', 213 | 'Programming Language :: Python :: 3.9', 214 | 'Programming Language :: Python :: 3.10', 215 | ], 216 | ) 217 | -------------------------------------------------------------------------------- /bindings/python/tests/misc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import tempfile 3 | 4 | try: 5 | from urllib import urlretrieve 6 | except ImportError: 7 | from urllib.request import urlretrieve 8 | 9 | OLD_FW_URL = 'https://github.com/analogdevicesinc/m1k-fw/releases/download/v2.02/m1000.bin' 10 | NEW_FW_URL = 'https://github.com/analogdevicesinc/m1k-fw/releases/download/v2.06/m1000.bin' 11 | DEV_FW_URL = 'https://github.com/analogdevicesinc/m1k-fw/raw/master/m1000.bin' 12 | 13 | # input = raw_input in py3, copy this for py2 14 | if sys.hexversion < 0x03000000: 15 | input = raw_input 16 | 17 | 18 | def prompt(s): 19 | """Prompt the user to verify test setup before continuing.""" 20 | input('ACTION: {} (hit Enter to continue)'.format(s)) 21 | 22 | 23 | # assumes an internet connection is available and github is up 24 | # fetch old/new firmware files from github 25 | OLD_FW = tempfile.NamedTemporaryFile().name 26 | NEW_FW = tempfile.NamedTemporaryFile().name 27 | DEV_FW = tempfile.NamedTemporaryFile().name 28 | urlretrieve(OLD_FW_URL, OLD_FW) 29 | urlretrieve(NEW_FW_URL, NEW_FW) 30 | urlretrieve(DEV_FW_URL, DEV_FW) 31 | -------------------------------------------------------------------------------- /bindings/python/tests/test_channel.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, print_function 2 | 3 | import random 4 | import sys 5 | 6 | import pytest 7 | 8 | from pysmu import Session, Mode, WriteTimeout 9 | 10 | 11 | @pytest.fixture(scope='function') 12 | def session(request): 13 | """Default session adding all attached devices.""" 14 | s = Session() 15 | yield s 16 | 17 | # force session destruction 18 | s._close() 19 | 20 | 21 | @pytest.fixture(scope='function') 22 | def device(session): 23 | """First device in the session fixture.""" 24 | return session.devices[0] 25 | 26 | 27 | @pytest.fixture(scope='function') 28 | def chan_a(device): 29 | """Channel A of the first device in the session fixture.""" 30 | return device.channels['A'] 31 | 32 | 33 | @pytest.fixture(scope='function') 34 | def chan_b(device): 35 | """Channel B of the first device in the session fixture.""" 36 | return device.channels['B'] 37 | 38 | 39 | def test_chan_write_timeout(chan_a, chan_b): 40 | """Performing multiple writes before starting a session causes write timeouts.""" 41 | with pytest.raises(WriteTimeout): 42 | chan_a.mode = Mode.SVMI 43 | chan_a.sine(0, 5, 100, 0) 44 | chan_a.constant(2) 45 | 46 | with pytest.raises(WriteTimeout): 47 | chan_b.mode = Mode.SVMI 48 | chan_b.sine(0, 5, 100, 25) 49 | chan_b.constant(4) 50 | 51 | 52 | def test_chan_mode(chan_a, chan_b): 53 | """Simple channel mode setting.""" 54 | # channels start in HI_Z mode by default 55 | assert chan_a.mode == chan_b.mode == Mode.HI_Z 56 | 57 | # invalid mode assignment raises ValueError 58 | with pytest.raises(ValueError): 59 | chan_a.mode = 4 60 | 61 | # raw values can't be used for assignment, enum aliases must be used 62 | with pytest.raises(ValueError): 63 | chan_a.mode = 1 64 | 65 | chan_a.mode = chan_b.mode = Mode.SVMI 66 | assert chan_a.mode == chan_b.mode == Mode.SVMI 67 | 68 | 69 | def test_chan_read(session, chan_a): 70 | """Simple channel data acquisition.""" 71 | session.run(1000) 72 | samples = chan_a.read(1000, -1) 73 | assert len(samples) == 1000 74 | assert len(samples[0]) == 2 75 | 76 | 77 | def test_chan_write(chan_a, chan_b): 78 | pass 79 | 80 | 81 | def test_chan_get_samples(chan_a, chan_b): 82 | """Simple channel data acquisition via get_samples().""" 83 | samples = chan_a.get_samples(1000) 84 | assert len(samples) == 1000 85 | assert len(samples[0]) == 2 86 | 87 | 88 | def test_chan_arbitrary(chan_a, chan_b): 89 | pass 90 | 91 | 92 | def test_chan_constant(chan_a, chan_b): 93 | """Write a constant value to both channels of a device and verify reteurned data.""" 94 | chan_a.mode = Mode.SVMI 95 | chan_a.constant(2) 96 | chan_b.mode = Mode.SVMI 97 | chan_b.constant(4) 98 | 99 | # verify sample values are near 2 for channel A 100 | samples = chan_a.get_samples(1000) 101 | assert len(samples) == 1000 102 | for x in samples: 103 | assert abs(round(x[0])) == 2 104 | 105 | # verify sample values are near 4 for channel B 106 | samples = chan_b.get_samples(1000) 107 | assert len(samples) == 1000 108 | for x in samples: 109 | assert abs(round(x[0])) == 4 110 | 111 | 112 | def test_chan_sine(chan_a, chan_b, device): 113 | """Write a sine wave to both channels of a device and verify a matching, returned frequency.""" 114 | try: 115 | import numpy as np 116 | from scipy import signal 117 | except ImportError: 118 | pytest.skip("test requires numpy and scipy installed") 119 | 120 | sys.stdout.write('\n') 121 | for _x in xrange(5): 122 | freq = random.randint(10, 100) 123 | print('testing frequency: {}'.format(freq)) 124 | period = freq * 10 125 | num_samples = period * freq 126 | 127 | # write a sine wave to both channels, one as a voltage source and the 128 | # other as current 129 | chan_a.mode = Mode.SVMI 130 | chan_a.sine(chan_a.signal.min, chan_a.signal.max, period, -(period / 4)) 131 | chan_b.mode = Mode.SIMV 132 | chan_b.sine(chan_b.signal.min, chan_b.signal.max, period, 0) 133 | 134 | chan_a_samples = [] 135 | chan_b_samples = [] 136 | 137 | # split data acquisition across multiple runs in order to test waveform continuity 138 | for i in xrange(10): 139 | samples = device.get_samples(period * freq / 10) 140 | chan_a_samples.extend([x[0][0] for x in samples]) 141 | chan_b_samples.extend([x[1][1] for x in samples]) 142 | assert len(chan_a_samples) == len(chan_b_samples) == (i + 1) * (period * freq / 10) 143 | 144 | assert len(chan_a_samples) == len(chan_b_samples) == num_samples 145 | 146 | # Verify the frequencies of the resulting waveforms 147 | hanning = signal.get_window('hanning', num_samples) 148 | chan_a_freqs, chan_a_psd = signal.welch(chan_a_samples, window=hanning, nperseg=num_samples) 149 | chan_b_freqs, chan_b_psd = signal.welch(chan_b_samples, window=hanning, nperseg=num_samples) 150 | assert np.argmax(chan_a_psd) == np.argmax(chan_b_psd) 151 | assert abs(freq - np.argmax(chan_a_psd)) <= 1 152 | -------------------------------------------------------------------------------- /bindings/python/tests/test_device.py: -------------------------------------------------------------------------------- 1 | import filecmp 2 | import os 3 | import sys 4 | import tempfile 5 | 6 | import pytest 7 | 8 | from pysmu import Session, DeviceError 9 | from misc import prompt, OLD_FW, NEW_FW, DEV_FW 10 | 11 | 12 | @pytest.fixture(scope='module') 13 | def session(request): 14 | session = Session(add_all=False) 15 | session.scan() 16 | return session 17 | 18 | 19 | @pytest.fixture(scope='module') 20 | def device(session): 21 | if not len(session.available_devices): 22 | sys.stdout.write('\n') 23 | prompt('plug in a device') 24 | session.scan() 25 | return session.available_devices[0] 26 | 27 | 28 | def test_device_serial(device): 29 | assert device.serial 30 | 31 | 32 | @pytest.mark.interactive 33 | def test_device_serial_custom(session, device): 34 | assert len(session.available_devices) == 1 35 | 36 | if float(device.fwver) < 2.10: 37 | # update to the latest firmware version (that supports custom serial numbers) 38 | session.add(device) 39 | session.devices[0].flash_firmware(DEV_FW) 40 | sys.stdout.write('\n') 41 | prompt('unplug/replug the device') 42 | session.scan() 43 | device = session.available_devices[0] 44 | 45 | # can't set serial numbers for devices in active sessions 46 | session.add_all() 47 | session.start(0) 48 | with pytest.raises(DeviceError): 49 | session.devices[0].serial = '' 50 | session.end() 51 | 52 | assert float(device.fwver) >= 2.10 53 | orig_serial = device.serial 54 | device.serial = 'foo' 55 | device = session.available_devices[0] 56 | assert device.serial == 'foo' 57 | device.serial = '' 58 | device = session.available_devices[0] 59 | assert device.serial == orig_serial 60 | device.serial = 'my device' 61 | device = session.available_devices[0] 62 | assert device.serial == 'my device' 63 | device.serial = orig_serial 64 | device = session.available_devices[0] 65 | assert device.serial == orig_serial 66 | 67 | 68 | def test_device_fwver(device): 69 | assert device.fwver 70 | 71 | 72 | def test_device_hwver(device): 73 | assert device.hwver 74 | 75 | 76 | def test_calibration(device): 77 | assert len(device.calibration) == 8 78 | 79 | 80 | @pytest.mark.interactive 81 | def test_write_calibration(session, device): 82 | default_cal = [[0.0, 1.0, 1.0] for x in range(8)] 83 | 84 | # old firmware versions don't support writing calibration data 85 | session.add_all() 86 | session.flash_firmware(OLD_FW) 87 | sys.stdout.write('\n') 88 | prompt('unplug/replug the device') 89 | session.add_all() 90 | device = session.devices[0] 91 | assert float(device.fwver) < 2.06 92 | assert device.calibration == default_cal 93 | with pytest.raises(DeviceError): 94 | device.write_calibration(None) 95 | 96 | # update to firmware supporting calibration 97 | session.flash_firmware(NEW_FW) 98 | prompt('unplug/replug the device') 99 | session.add_all() 100 | device = session.devices[0] 101 | 102 | # writing nonexistent calibration file 103 | with pytest.raises(DeviceError): 104 | device.write_calibration('nonexistent') 105 | 106 | # writing bad calibration file 107 | cal_data = tempfile.NamedTemporaryFile() 108 | with open(cal_data.name, 'w') as f: 109 | f.write('foo') 110 | with pytest.raises(DeviceError): 111 | device.write_calibration(cal_data.name) 112 | 113 | # find default calibration file in repo 114 | dn = os.path.dirname 115 | default_cal_data = os.path.join(dn(dn(dn(dn(__file__)))), 'contrib', 'calib.txt') 116 | 117 | # writing modified calibration file 118 | cal_data = tempfile.NamedTemporaryFile() 119 | with open(default_cal_data) as default, open(cal_data.name, 'w') as f: 120 | for line in default: 121 | # randomly munge cal data 122 | if line.strip() == "<0.0000, 0.0000>": 123 | f.write("<1.0000, 2.0000>\n") 124 | else: 125 | f.write(line) 126 | # verify that the new file differs from the default 127 | assert not filecmp.cmp(cal_data.name, default_cal_data) 128 | device.write_calibration(cal_data.name) 129 | new_cal = device.calibration 130 | assert new_cal != default_cal 131 | 132 | # make sure calibration data survives firmware updates 133 | session.flash_firmware(NEW_FW) 134 | prompt('unplug/replug the device') 135 | session.add_all() 136 | device = session.devices[0] 137 | assert new_cal == device.calibration 138 | 139 | # reset calibration 140 | device.write_calibration(None) 141 | assert device.calibration == default_cal 142 | 143 | # writing good calibration file 144 | device.write_calibration(default_cal_data) 145 | assert device.calibration == default_cal 146 | 147 | 148 | @pytest.mark.interactive 149 | def test_samba_mode(session, device): 150 | # supported devices exist in the session 151 | num_available_devices = len(session.available_devices) 152 | assert num_available_devices 153 | orig_serial = session.available_devices[0].serial 154 | 155 | # pushing one into SAM-BA mode drops it from the session after rescan 156 | device.samba_mode() 157 | session.scan() 158 | assert len(session.available_devices) == num_available_devices - 1 159 | assert not any(d.serial == orig_serial for d in session.available_devices) 160 | 161 | # flash device in SAM-BA mode 162 | session.flash_firmware(NEW_FW) 163 | sys.stdout.write('\n') 164 | prompt('unplug/replug the device') 165 | session.scan() 166 | 167 | # device is re-added after hotplug 168 | assert len(session.available_devices) == num_available_devices 169 | dev = session.available_devices[0] 170 | assert dev.serial == orig_serial 171 | assert dev.fwver == '2.06' 172 | 173 | 174 | def test_ctrl_transfer(device): 175 | # set pin input and get pin value 176 | val = device.ctrl_transfer(0xc0, 0x91, 0, 0, 0, 1, 100) 177 | assert val == [1] 178 | val = device.ctrl_transfer(0xc0, 0x91, 2, 0, 0, 1, 100) 179 | assert val == [0] 180 | -------------------------------------------------------------------------------- /bindings/python/tests/test_session.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import errno 4 | import sys 5 | import time 6 | 7 | try: 8 | from unittest import mock 9 | except ImportError: 10 | import mock 11 | 12 | import pytest 13 | 14 | from pysmu import Session, SessionError 15 | from misc import prompt, OLD_FW, NEW_FW 16 | 17 | 18 | @pytest.yield_fixture(scope='function') 19 | def session(): 20 | s = Session(add_all=False) 21 | yield s 22 | 23 | # force session destruction 24 | s._close() 25 | 26 | 27 | def test_empty(session): 28 | assert len(session.devices) == 0 29 | 30 | 31 | @pytest.mark.interactive 32 | def test_scan(session): 33 | sys.stdout.write('\n') 34 | prompt('make sure at least one device is plugged in') 35 | session.scan() 36 | 37 | # available devices haven't been added to the session yet 38 | assert session.available_devices 39 | assert len(session.available_devices) != len(session.devices) 40 | 41 | 42 | def test_add(session): 43 | assert not session.devices 44 | 45 | session.scan() 46 | assert session.available_devices 47 | dev = session.available_devices[0] 48 | session.add(dev) 49 | assert len(session.devices) == 1 50 | assert session.devices[0].serial == dev.serial 51 | 52 | # re-adding the same device does nothing 53 | session.add(dev) 54 | assert len(session.devices) == 1 55 | assert session.devices[0].serial == dev.serial 56 | 57 | 58 | def test_add_all(session): 59 | assert not session.devices 60 | session.add_all() 61 | 62 | # all available devices should be in the session 63 | assert session.devices 64 | assert len(session.available_devices) == len(session.devices) 65 | 66 | 67 | def test_remove(session): 68 | session.add_all() 69 | assert session.devices 70 | assert len(session.available_devices) == len(session.devices) 71 | dev = session.devices[0] 72 | session.remove(dev) 73 | assert not any(d.serial == dev.serial for d in session.devices) 74 | assert len(session.available_devices) != len(session.devices) 75 | 76 | # removing already removed devices fails 77 | with pytest.raises(SessionError) as excinfo: 78 | session.remove(dev) 79 | assert excinfo.value.errcode == errno.ENXIO 80 | 81 | 82 | def test_destroy(session): 83 | session.scan() 84 | # available devices haven't been added to the session yet 85 | assert session.available_devices 86 | serial = session.available_devices[0].serial 87 | session.destroy(session.available_devices[0]) 88 | assert not any(d.serial == serial for d in session.available_devices) 89 | 90 | 91 | def test_session_continuous(session): 92 | """Test session continuous attribute.""" 93 | session.add_all() 94 | assert not session.continuous 95 | session.start(0) 96 | assert session.continuous 97 | session.end() 98 | assert not session.continuous 99 | 100 | 101 | @pytest.mark.interactive 102 | def test_flash_firmware(session): 103 | session.add_all() 104 | assert len(session.devices) == 1 105 | serial = session.devices[0].serial 106 | 107 | # flash old firmware 108 | sys.stdout.write('\n') 109 | print('flashing firmware 2.02...') 110 | session.flash_firmware(OLD_FW) 111 | prompt('unplug/replug the device') 112 | session.add_all() 113 | assert len(session.devices) == 1 114 | assert session.devices[0].serial == serial 115 | assert session.devices[0].fwver == '2.02' 116 | 117 | # flash new firmware 118 | print('flashing firmware 2.06...') 119 | session.flash_firmware(NEW_FW) 120 | prompt('unplug/replug the device') 121 | session.add_all() 122 | assert len(session.devices) == 1 123 | assert session.devices[0].serial == serial 124 | assert session.devices[0].fwver == '2.06' 125 | 126 | 127 | @pytest.mark.interactive 128 | def test_hotplug(session): 129 | sys.stdout.write('\n') 130 | prompt('unplug/plug a device within 10 seconds') 131 | session.add_all() 132 | 133 | # create fake attach/detach callbacks to check basic triggering 134 | fake_attach = mock.Mock() 135 | fake_detach = mock.Mock() 136 | session.hotplug_attach(fake_attach) 137 | session.hotplug_detach(fake_detach) 138 | 139 | # create more realistic callbacks that try adding/removing the 140 | # hotplugged device from a session 141 | def attach(dev): 142 | serial = dev.serial 143 | session.add(dev) 144 | assert any(d.serial == serial for d in session.devices) 145 | 146 | def detach(dev): 147 | serial = dev.serial 148 | session.remove(dev, detached=True) 149 | assert not any(d.serial == serial for d in session.devices) 150 | 151 | session.hotplug_attach(attach) 152 | session.hotplug_detach(detach) 153 | 154 | start = time.time() 155 | print('waiting hotplug events...') 156 | while (True): 157 | time.sleep(1) 158 | end = time.time() 159 | elapsed = end - start 160 | if elapsed > 10 or (fake_attach.called and fake_detach.called): 161 | break 162 | 163 | assert fake_attach.called, 'attach callback not called' 164 | assert fake_detach.called, 'detach callback not called' 165 | -------------------------------------------------------------------------------- /bindings/python/tests/test_signal.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import pytest 4 | 5 | from pysmu import Session, Mode, Signal 6 | 7 | 8 | @pytest.fixture(scope='function') 9 | def session(request): 10 | s = Session() 11 | yield s 12 | 13 | # force session destruction 14 | s._close() 15 | 16 | 17 | @pytest.fixture(scope='function') 18 | def device(session): 19 | return session.devices[0] 20 | 21 | 22 | @pytest.fixture(scope='function') 23 | def chan_a(device): 24 | return device.channels['A'] 25 | 26 | 27 | @pytest.fixture(scope='function') 28 | def chan_b(device): 29 | return device.channels['B'] 30 | 31 | 32 | @pytest.fixture(scope='function') 33 | def signal(): 34 | return Signal() 35 | 36 | 37 | def test_signal_info(chan_a, chan_b): 38 | chan_a.mode = Mode.SVMI 39 | chan_b.mode = Mode.SIMV 40 | assert chan_a.signal.label == 'Voltage' 41 | assert chan_a.signal.min == 0 42 | assert chan_a.signal.max == 5 43 | assert chan_b.signal.label == 'Current' 44 | assert chan_b.signal.min == -0.2 45 | assert chan_b.signal.max == 0.2 46 | 47 | 48 | def test_constant(signal): 49 | data = signal.constant(100, 4) 50 | assert len(data) == 100 51 | for x in data: 52 | assert x == 4 53 | 54 | 55 | def test_square(signal): 56 | data = signal.square(101, 0, 5, 100, 0, 0.5) 57 | assert len(data) == 101 58 | for i in range(50): 59 | assert data[i] == 0 60 | assert data[i + 50] == 5 61 | assert data[100] == 0 62 | 63 | 64 | def test_sawtooth(signal): 65 | data = signal.sawtooth(101, -5, 5, 100, 0) 66 | assert data[0] == 5 67 | assert data[99] == -5 68 | assert data[100] == 5 69 | assert len(data) == 101 70 | 71 | 72 | def test_stairstep(signal): 73 | data = signal.stairstep(101, -5, 5, 100, 0) 74 | assert len(data) == 101 75 | assert data[0] == 5 76 | assert data[100] == 5 77 | 78 | for i in range(10): 79 | step_vals = [data[i * 10 + j] for j in range(10)] 80 | check_val = step_vals[0] 81 | for i, x in enumerate(step_vals): 82 | assert x == check_val 83 | 84 | 85 | def test_sine(signal): 86 | data = signal.sine(101, -5, 5, 100, -25) 87 | assert len(data) == 101 88 | assert round(data[0], 4) == 0 89 | assert round(data[25], 4) == 5 90 | assert round(data[50], 4) == 0 91 | assert round(data[75], 4) == -5 92 | assert round(data[100], 4) == 0 93 | 94 | try: 95 | import numpy as np 96 | samples = 10000 97 | period = 10 98 | data = signal.sine(samples + 1, -5, 5, period, -25) 99 | ps = np.abs(np.fft.fft(data)) ** 2 100 | freq = np.argmax(ps) 101 | assert freq == samples / period 102 | except ImportError: 103 | pass 104 | 105 | 106 | def test_triangle(signal): 107 | data = signal.triangle(101, -5, 5, 100, 0) 108 | assert len(data) == 101 109 | assert data[0] == 5 110 | assert data[50] == -5 111 | assert data[100] == 5 112 | -------------------------------------------------------------------------------- /bindings/python/tox.dist: -------------------------------------------------------------------------------- 1 | # generate dist files -- source tarball and wheels 2 | # 3 | # Make sure to build the C++ library first in the root repo directory via: 4 | # mkdir build && cd build && cmake .. && make 5 | 6 | [tox] 7 | envlist = py27, py34, py35, py36 8 | skipsdist = True 9 | [testenv] 10 | whitelist_externals = bash 11 | deps = 12 | cython 13 | wheel 14 | py36: auditwheel 15 | commands = 16 | python "{toxinidir}"/setup.py bdist_wheel 17 | py36: python "{toxinidir}"/setup.py sdist 18 | py36: bash -c "if [[ $OSTYPE =~ linux-* ]]; then for wheel in dist/*.whl; do auditwheel repair -w dist $wheel && rm $wheel; done; fi" 19 | -------------------------------------------------------------------------------- /bindings/python/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27, py34, py35, py36 3 | [testenv] 4 | deps = 5 | cython 6 | pytest 7 | mock 8 | commands = 9 | pip install "{toxinidir}" 10 | py.test -s -v 11 | -------------------------------------------------------------------------------- /cmake/DarwinPackaging.cmake: -------------------------------------------------------------------------------- 1 | # support creating some basic binpkgs via `make package` 2 | 3 | set(CPACK_SET_DESTDIR ON) 4 | set(CPACK_GENERATOR TGZ) 5 | 6 | set(CPACK_PACKAGE_VERSION_MAJOR ${LIBSMU_VERSION_MAJOR}) 7 | set(CPACK_PACKAGE_VERSION_MINOR ${LIBSMU_VERSION_MINOR}) 8 | set(CPACK_PACKAGE_VERSION_PATCH g${LIBSMU_VERSION_PATCH}) 9 | set(CPACK_BUNDLE_NAME libsmu) 10 | set(CPACK_PACKAGE_VERSION ${LIBSMU_VERSION_STR}) 11 | 12 | include(CPack) 13 | -------------------------------------------------------------------------------- /cmake/LinuxPackaging.cmake: -------------------------------------------------------------------------------- 1 | # support creating some basic binpkgs via `make package` 2 | set(CPACK_SET_DESTDIR ON) 3 | set(CPACK_GENERATOR TGZ;DEB) 4 | 5 | FIND_PROGRAM(RPMBUILD_CMD rpmbuild) 6 | if (RPMBUILD_CMD) 7 | set(CPACK_PACKAGE_RELOCATABLE OFF) 8 | set(CPACK_GENERATOR ${CPACK_GENERATOR};RPM) 9 | set(CPACK_RPM_PACKAGE_REQUIRES "libboost-all >= 0.1.0, libusb-1.0-0 >= 1.0.21") 10 | endif() 11 | #-- Package dependencies (.deb): libusb-1.0-0 (>= 2:1.0.21), libboost-date-time1.65.1 (>= 1.65.1), libboost-filesystem1.65.1 (>= 1.65.1), libboost-iostreams1.65.1 (>= 1.65.1), libboost-locale1.65.1 (>= 1.65.1), libboost-system1.65.1 (>= 1.65.1), libboost-thread1.65.1 (>= 1.65.1) 12 | 13 | # Add these for CentOS 7 14 | set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION 15 | /lib 16 | /lib/udev 17 | /lib/udev/rules.d 18 | /usr/sbin 19 | /usr/lib/python2.7 20 | /usr/lib/python2.7/site-packages 21 | /usr/lib/pkgconfig 22 | /usr/lib64/pkgconfig 23 | ) 24 | 25 | set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) 26 | set(CPACK_PACKAGE_VERSION_MAJOR ${LIBSMU_VERSION_MAJOR}) 27 | set(CPACK_PACKAGE_VERSION_MINOR ${LIBSMU_VERSION_MINOR}) 28 | set(CPACK_PACKAGE_VERSION_PATCH g${LIBSMU_VERSION_PATCH}) 29 | set(CPACK_BUNDLE_NAME libsmu) 30 | set(CPACK_PACKAGE_VERSION ${LIBSMU_VERSION_STR}) 31 | # debian specific package settings 32 | set(CPACK_PACKAGE_CONTACT "Engineerzone ") 33 | 34 | option(DEB_DETECT_DEPENDENCIES "Detect dependencies for .deb packages" OFF) 35 | 36 | # if we are going to be looking for things, make sure we have the utilities 37 | if(DEB_DETECT_DEPENDENCIES) 38 | FIND_PROGRAM(DPKG_CMD dpkg) 39 | FIND_PROGRAM(DPKGQ_CMD dpkg-query) 40 | endif() 41 | 42 | # if we want to, and have the capabilities find what is needed, 43 | # based on what backends are turned on and what libraries are installed 44 | if(DEB_DETECT_DEPENDENCIES AND DPKG_CMD AND DPKGQ_CMD) 45 | message(STATUS "querying installed packages on system for dependancies") 46 | execute_process(COMMAND "${DPKG_CMD}" --print-architecture 47 | OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE 48 | OUTPUT_STRIP_TRAILING_WHITESPACE) 49 | # don't add a package dependancy if it is not installed locally 50 | # these should be the debian package names 51 | set(PACKAGES "${PACKAGES} libusb-1.0-0") 52 | set(PACKAGES "${PACKAGES} libboost-all") 53 | 54 | # find the version of the installed package, which is hard to do in 55 | # cmake first, turn the list into an list (seperated by semicolons) 56 | string(REGEX REPLACE " " ";" PACKAGES ${PACKAGES}) 57 | # then iterate over each 58 | foreach(package ${PACKAGES}) 59 | # List packages matching given pattern ${package}, 60 | # key is the glob (*) at the end of the ${package} name, 61 | # so we don't need to be so specific 62 | execute_process(COMMAND "${DPKG_CMD}" -l ${package}* 63 | OUTPUT_VARIABLE DPKG_PACKAGES) 64 | # returns a string, in a format: 65 | # ii libxml2:amd64 2.9.4+dfsg1- amd64 GNOME XML library 66 | # 'ii' means installed - which is what we are looking for 67 | STRING(REGEX MATCHALL "ii ${package}[a-z0-9A-Z.-]*" 68 | DPKG_INSTALLED_PACKAGES ${DPKG_PACKAGES}) 69 | # get rid of the 'ii', and split things up, so we can look 70 | # at the name 71 | STRING(REGEX REPLACE "ii " ";" NAME_INSTALLED_PACKAGES 72 | ${DPKG_INSTALLED_PACKAGES}) 73 | foreach(match ${NAME_INSTALLED_PACKAGES}) 74 | # ignore packages marked as dev, debug, 75 | # documentations, or utils 76 | STRING(REGEX MATCHALL "dev|dbg|doc|utils" TEMP_TEST 77 | ${match}) 78 | if("${TEMP_TEST}" STREQUAL "") 79 | # find the actual version, executes: 80 | # dpkg-query --showformat='\${Version}' 81 | # --show libusb-1.0-0 82 | execute_process(COMMAND "${DPKGQ_CMD}" 83 | --showformat='\${Version}' 84 | --show "${match}" 85 | OUTPUT_VARIABLE DPKGQ_VER) 86 | # debian standard is package_ver-debian_ver, 87 | # "'2.9.4+dfsg1-2.1'" 88 | # ignore patches and debian suffix version, and 89 | # remove single quote 90 | string(REGEX REPLACE "[+-][a-z0-9A-Z.]*" "" 91 | DPKGQ_VER ${DPKGQ_VER}) 92 | string(REGEX REPLACE "'" "" DPKGQ_VER 93 | ${DPKGQ_VER}) 94 | # build the string for the Debian dependancy 95 | set(CPACK_DEBIAN_PACKAGE_DEPENDS 96 | "${CPACK_DEBIAN_PACKAGE_DEPENDS}" 97 | "${match} (>= ${DPKGQ_VER}), ") 98 | endif() 99 | endforeach(match) 100 | endforeach(package) 101 | # remove the dangling end comma 102 | string(REGEX REPLACE ", $" "" CPACK_DEBIAN_PACKAGE_DEPENDS 103 | ${CPACK_DEBIAN_PACKAGE_DEPENDS}) 104 | else() 105 | # assume everything is turned on, and running on a modern OS 106 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-all (>=0.1.0), libusb-1.0-0 (>= 2:1.0.21)") 107 | message(STATUS "Using default dependencies for packaging") 108 | endif() 109 | 110 | message(STATUS "Package dependencies (.deb): " ${CPACK_DEBIAN_PACKAGE_DEPENDS}) 111 | if (CPACK_RPM_PACKAGE_REQUIRES) 112 | message(STATUS "Package dependencies (.rpm): " ${CPACK_RPM_PACKAGE_REQUIRES}) 113 | endif() 114 | 115 | if(${CMAKE_MAJOR_VERSION} LESS 3) 116 | # old versions of cmake dont include this, but the same vintage of dpkg requires it 117 | IF(NOT CPACK_DEBIAN_PACKAGE_ARCHITECTURE) 118 | FIND_PROGRAM(DPKG_CMD dpkg) 119 | IF(NOT DPKG_CMD) 120 | MESSAGE(STATUS "Can not find dpkg in your path, default to i386.") 121 | SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE i386) 122 | ENDIF(NOT DPKG_CMD) 123 | EXECUTE_PROCESS(COMMAND "${DPKG_CMD}" --print-architecture 124 | OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE 125 | OUTPUT_STRIP_TRAILING_WHITESPACE) 126 | ENDIF(NOT CPACK_DEBIAN_PACKAGE_ARCHITECTURE) 127 | endif() 128 | include(CPack) 129 | 130 | -------------------------------------------------------------------------------- /contrib/calib.txt: -------------------------------------------------------------------------------- 1 | # Example M1K calibration file assuming perfect operation (no offset or gain errors). 2 | # 3 | # See https://wiki.analog.com/university/tools/m1k-calibration to create a 4 | # calibration file for a real device. 5 | 6 | # Channel A, measure V 7 | 8 | <0.0000, 0.0000> 9 | <2.5000, 2.5000> 10 | <\> 11 | 12 | # Channel A, measure I 13 | 14 | <0.0000, 0.0000> 15 | <0.1000, 0.1000> 16 | <-0.1000, -0.1000> 17 | <\> 18 | 19 | # Channel A, source V 20 | 21 | <0.0000, 0.0000> 22 | <2.5000, 2.5000> 23 | <\> 24 | 25 | # Channel A, source I 26 | 27 | <0.0000, 0.0000> 28 | <0.1000, 0.1000> 29 | <-0.1000, -0.1000> 30 | <\> 31 | 32 | # Channel B, measure V 33 | 34 | <0.0000, 0.0000> 35 | <2.5000, 2.5000> 36 | <\> 37 | 38 | # Channel B, measure I 39 | 40 | <0.0000, 0.0000> 41 | <0.1000, 0.1000> 42 | <-0.1000, -0.1000> 43 | <\> 44 | 45 | # Channel B, source V 46 | 47 | <0.0000, 0.0000> 48 | <2.5000, 2.5000> 49 | <\> 50 | 51 | # Channel B, source I 52 | 53 | <0.0000, 0.0000> 54 | <0.1000, 0.1000> 55 | <-0.1000, -0.1000> 56 | <\> 57 | -------------------------------------------------------------------------------- /dist/53-adi-m1k-usb.rules: -------------------------------------------------------------------------------- 1 | # allow "plugdev" group read/write access to ADALM1000 devices 2 | SUBSYSTEM=="usb", ATTRS{idVendor}=="064b", ATTRS{idProduct}=="784c", MODE="0664", GROUP="plugdev" 3 | # allow "plugdev" group read/write access to ADALM1000 devices in SAM-BA mode 4 | SUBSYSTEM=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="6124", MODE="0664", GROUP="plugdev" 5 | -------------------------------------------------------------------------------- /dist/amd64/WdfCoInstaller01011.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analogdevicesinc/libsmu/dbb484f004d9eb5251aa4667f5cf09b3ff5610e2/dist/amd64/WdfCoInstaller01011.dll -------------------------------------------------------------------------------- /dist/amd64/winusbcoinstaller2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analogdevicesinc/libsmu/dbb484f004d9eb5251aa4667f5cf09b3ff5610e2/dist/amd64/winusbcoinstaller2.dll -------------------------------------------------------------------------------- /dist/build-osx-pkg.sh.cmakein: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Create an installer for OS X. 3 | # 4 | # Run this script from within the build directory created by cmake after 5 | # building the project. 6 | 7 | DESTDIR=libsmu-osx 8 | 9 | mkdir ${DESTDIR} 10 | make DESTDIR=${DESTDIR} install 11 | 12 | pkgbuild \ 13 | --root ${DESTDIR} \ 14 | --identifier libsmu --version @LIBSMU_VERSION_STR@ \ 15 | --install-location / "@LIBSMU_TEMP_PKG@" 16 | 17 | productbuild --distribution distribution.xml "@LIBSMU_PKG@" 18 | -------------------------------------------------------------------------------- /dist/libsmu-x64.iss.cmakein: -------------------------------------------------------------------------------- 1 | [Setup] 2 | AppId={{351792B2-1445-4489-945F-DD0649DD403F} 3 | AppName="libsmu (x64)" 4 | AppVersion="@LIBSMU_VERSION_STR@" 5 | AppPublisher="Analog Devices, Inc." 6 | AppPublisherURL="http://www.analog.com" 7 | AppSupportURL="http://www.analog.com" 8 | AppUpdatesURL="http://www.analog.com" 9 | LicenseFile="D:\a\1\s\LICENSE" 10 | OutputBaseFilename=libsmu-setup-x64 11 | OutputDir="C:\" 12 | DefaultDirName="{pf64}\Analog Devices\libsmu" 13 | Compression=lzma 14 | SolidCompression=yes 15 | ChangesEnvironment=yes 16 | ArchitecturesInstallIn64BitMode=x64 17 | 18 | [Languages] 19 | Name: "english"; MessagesFile: "compiler:Default.isl" 20 | Name: "brazilianportuguese"; MessagesFile: "compiler:Languages\BrazilianPortuguese.isl" 21 | Name: "catalan"; MessagesFile: "compiler:Languages\Catalan.isl" 22 | Name: "corsican"; MessagesFile: "compiler:Languages\Corsican.isl" 23 | Name: "czech"; MessagesFile: "compiler:Languages\Czech.isl" 24 | Name: "danish"; MessagesFile: "compiler:Languages\Danish.isl" 25 | Name: "dutch"; MessagesFile: "compiler:Languages\Dutch.isl" 26 | Name: "finnish"; MessagesFile: "compiler:Languages\Finnish.isl" 27 | Name: "french"; MessagesFile: "compiler:Languages\French.isl" 28 | Name: "german"; MessagesFile: "compiler:Languages\German.isl" 29 | Name: "hebrew"; MessagesFile: "compiler:Languages\Hebrew.isl" 30 | Name: "italian"; MessagesFile: "compiler:Languages\Italian.isl" 31 | Name: "japanese"; MessagesFile: "compiler:Languages\Japanese.isl" 32 | Name: "norwegian"; MessagesFile: "compiler:Languages\Norwegian.isl" 33 | Name: "polish"; MessagesFile: "compiler:Languages\Polish.isl" 34 | Name: "portuguese"; MessagesFile: "compiler:Languages\Portuguese.isl" 35 | Name: "russian"; MessagesFile: "compiler:Languages\Russian.isl" 36 | Name: "slovenian"; MessagesFile: "compiler:Languages\Slovenian.isl" 37 | Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl" 38 | Name: "turkish"; MessagesFile: "compiler:Languages\Turkish.isl" 39 | Name: "ukrainian"; MessagesFile: "compiler:Languages\Ukrainian.isl" 40 | 41 | [Files] 42 | Source: "D:\a\1\a\Windows-VS-16-2019-x64\*.dll"; DestDir: "{app}"; Flags: onlyifdoesntexist 43 | 44 | Source: "D:\a\1\a\Windows-VS-16-2019-x64\drivers\dpinst_amd64.exe"; DestDir: "{app}\drivers"; DestName: "dpinst.exe"; Flags: ignoreversion 45 | Source: "D:\a\1\a\Windows-VS-16-2019-x64\drivers\m1k-winusb.inf"; DestDir: "{app}\drivers"; Tasks: drivers 46 | Source: "D:\a\1\a\Windows-VS-16-2019-x64\drivers\m1k-winusb.cat"; DestDir: "{app}\drivers"; Tasks: drivers 47 | Source: "D:\a\1\a\Windows-VS-16-2019-x64\drivers\amd64\*"; DestDir: "{app}\drivers\amd64"; Tasks: drivers 48 | Source: "D:\a\1\a\Windows-VS-16-2019-x64\drivers\x86\*"; DestDir: "{app}\drivers\x86"; Tasks: drivers 49 | 50 | Source: "D:\a\1\a\Windows-VS-16-2019-x64\libsmu.lib"; DestDir: "{pf32}\Microsoft Visual Studio 14.0\VC\lib\amd64"; Tasks: visualstudio 51 | Source: "D:\a\1\a\Windows-VS-16-2019-x64\include\libsmu\*.hpp"; DestDir: "{pf32}\Microsoft Visual Studio 14.0\VC\include"; Tasks: visualstudio 52 | Source: "D:\a\1\a\Windows-VS-16-2019-x64\smu.exe"; DestDir: "{app}" 53 | 54 | [Tasks] 55 | Name: drivers; Description: Install WinUSB drivers for the M1K; 56 | Name: modifypath; Description: Add application directory to your environmental path (required for smu utility); 57 | Name: visualstudio; Description: Install Visual Studio support for building/linking against libsmu; Flags: unchecked 58 | 59 | [Run] 60 | Filename: "{app}\drivers\dpinst.exe"; Parameters: "/PATH ""{app}\drivers"" {param:dpinstflags|/F}"; Flags: waituntilterminated; Tasks: drivers 61 | 62 | 63 | [Code] 64 | const 65 | ModPathName = 'modifypath'; 66 | ModPathType = 'system'; 67 | 68 | function ModPathDir(): TArrayOfString; 69 | begin 70 | setArrayLength(Result, 1) 71 | Result[0] := ExpandConstant('{app}'); 72 | end; 73 | #include "modpath.iss" 74 | -------------------------------------------------------------------------------- /dist/libsmu-x86.iss.cmakein: -------------------------------------------------------------------------------- 1 | [Setup] 2 | AppId={{FF9A09A0-130C-11E6-A148-3E1D05DEFE78} 3 | AppName="libsmu (x86)" 4 | AppVersion="@LIBSMU_VERSION_STR@" 5 | AppPublisher="Analog Devices, Inc." 6 | AppPublisherURL="http://www.analog.com" 7 | AppSupportURL="http://www.analog.com" 8 | AppUpdatesURL="http://www.analog.com" 9 | LicenseFile="D:\a\1\s\LICENSE" 10 | OutputBaseFilename=libsmu-setup-x86 11 | OutputDir="C:\" 12 | DefaultDirName="{pf32}\Analog Devices\libsmu" 13 | Compression=lzma 14 | SolidCompression=yes 15 | ChangesEnvironment=yes 16 | ArchitecturesInstallIn64BitMode=x64 17 | 18 | [Languages] 19 | Name: "english"; MessagesFile: "compiler:Default.isl" 20 | Name: "brazilianportuguese"; MessagesFile: "compiler:Languages\BrazilianPortuguese.isl" 21 | Name: "catalan"; MessagesFile: "compiler:Languages\Catalan.isl" 22 | Name: "corsican"; MessagesFile: "compiler:Languages\Corsican.isl" 23 | Name: "czech"; MessagesFile: "compiler:Languages\Czech.isl" 24 | Name: "danish"; MessagesFile: "compiler:Languages\Danish.isl" 25 | Name: "dutch"; MessagesFile: "compiler:Languages\Dutch.isl" 26 | Name: "finnish"; MessagesFile: "compiler:Languages\Finnish.isl" 27 | Name: "french"; MessagesFile: "compiler:Languages\French.isl" 28 | Name: "german"; MessagesFile: "compiler:Languages\German.isl" 29 | Name: "hebrew"; MessagesFile: "compiler:Languages\Hebrew.isl" 30 | Name: "italian"; MessagesFile: "compiler:Languages\Italian.isl" 31 | Name: "japanese"; MessagesFile: "compiler:Languages\Japanese.isl" 32 | Name: "norwegian"; MessagesFile: "compiler:Languages\Norwegian.isl" 33 | Name: "polish"; MessagesFile: "compiler:Languages\Polish.isl" 34 | Name: "portuguese"; MessagesFile: "compiler:Languages\Portuguese.isl" 35 | Name: "russian"; MessagesFile: "compiler:Languages\Russian.isl" 36 | Name: "slovenian"; MessagesFile: "compiler:Languages\Slovenian.isl" 37 | Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl" 38 | Name: "turkish"; MessagesFile: "compiler:Languages\Turkish.isl" 39 | Name: "ukrainian"; MessagesFile: "compiler:Languages\Ukrainian.isl" 40 | 41 | [Files] 42 | Source: "D:\a\1\a\Windows-VS-16-2019-Win32\*.dll"; DestDir: "{app}"; Flags: onlyifdoesntexist 43 | Source: "D:\a\1\a\Windows-VS-16-2019-Win32\drivers\dpinst.exe"; DestDir: "{app}\drivers"; Flags: ignoreversion 44 | Source: "D:\a\1\a\Windows-VS-16-2019-Win32\drivers\m1k-winusb.inf"; DestDir: "{app}\drivers"; Tasks: drivers 45 | Source: "D:\a\1\a\Windows-VS-16-2019-Win32\drivers\m1k-winusb.cat"; DestDir: "{app}\drivers"; Tasks: drivers 46 | Source: "D:\a\1\a\Windows-VS-16-2019-Win32\drivers\amd64\*"; DestDir: "{app}\drivers\amd64"; Tasks: drivers 47 | Source: "D:\a\1\a\Windows-VS-16-2019-Win32\drivers\x86\*"; DestDir: "{app}\drivers\x86"; Tasks: drivers 48 | 49 | Source: "D:\a\1\a\Windows-VS-16-2019-Win32\libsmu.lib"; DestDir: "{pf32}\Microsoft Visual Studio 14.0\VC\lib"; Tasks: visualstudio 50 | Source: "D:\a\1\a\Windows-VS-16-2019-Win32\include\libsmu\*.hpp"; DestDir: "{pf32}\Microsoft Visual Studio 14.0\VC\include"; Tasks: visualstudio 51 | Source: "D:\a\1\a\Windows-VS-16-2019-Win32\smu.exe"; DestDir: "{app}" 52 | 53 | [Tasks] 54 | Name: drivers; Description: Install WinUSB drivers for the M1K; 55 | Name: modifypath; Description: Add application directory to your environmental path (required for smu utility); 56 | Name: visualstudio; Description: Install Visual Studio support for building/linking against libsmu; Flags: unchecked 57 | 58 | [Run] 59 | Filename: "{app}\drivers\dpinst.exe"; Parameters: "/PATH ""{app}\drivers"" {param:dpinstflags|/F}"; Flags: waituntilterminated; Tasks: drivers 60 | 61 | [Code] 62 | const 63 | ModPathName = 'modifypath'; 64 | ModPathType = 'system'; 65 | 66 | function ModPathDir(): TArrayOfString; 67 | begin 68 | setArrayLength(Result, 1) 69 | Result[0] := ExpandConstant('{app}'); 70 | end; 71 | #include "modpath.iss" 72 | -------------------------------------------------------------------------------- /dist/libsmu.pc.cmakein: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=${prefix} 3 | libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ 4 | includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ 5 | 6 | Name: libsmu 7 | Description: Library for interfacing with M1K devices 8 | Version: @LIBSMU_VERSION@ 9 | 10 | Requires.private: libusb-1.0 11 | Libs: -L${libdir} -lsmu 12 | Cflags: -I${includedir} 13 | -------------------------------------------------------------------------------- /dist/m1k-winusb.cat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analogdevicesinc/libsmu/dbb484f004d9eb5251aa4667f5cf09b3ff5610e2/dist/m1k-winusb.cat -------------------------------------------------------------------------------- /dist/m1k-winusb.inf: -------------------------------------------------------------------------------- 1 | ; M1K.inf 2 | ; Copyright (c) 2010-2014 libusb (GNU LGPL) 3 | [Strings] 4 | VendorName = "Analog Devices, Inc." 5 | SourceName = "M1k Install Disk" 6 | M1kID = "VID_064B&PID_784C" 7 | M1kDeviceName = "ADALM1000 WinUSB driver" 8 | M1kSambaID = "VID_03EB&PID_6124" 9 | M1kSambaName = "ADALM1000 SAM-BA WinUSB driver" 10 | 11 | [Version] 12 | Signature = "$Windows NT$" 13 | Class = "Universal Serial Bus devices" 14 | ClassGuid = {88bae032-5a81-49f0-bc3d-a4ff138216d6} 15 | Provider = "Analog Devices, Inc." 16 | CatalogFile = m1k-winusb.cat 17 | DriverVer = 09/10/2018, 6.1.7600.16385 18 | 19 | [ClassInstall32] 20 | Addreg = WinUSBDeviceClassReg 21 | 22 | [WinUSBDeviceClassReg] 23 | HKR,,,0,"Universal Serial Bus devices" 24 | HKR,,Icon,,-20 25 | 26 | [Manufacturer] 27 | %VendorName% = libusbDevice_WinUSB,NTx86,NTamd64,NTia64 28 | 29 | [libusbDevice_WinUSB.NTx86] 30 | %M1kDeviceName% = USB_Install, USB\%M1kID% 31 | %M1kSambaName% = USB_Install, USB\%M1kSambaID% 32 | 33 | [libusbDevice_WinUSB.NTamd64] 34 | %M1kDeviceName% = USB_Install, USB\%M1kID% 35 | %M1kSambaName% = USB_Install, USB\%M1kSambaID% 36 | 37 | [libusbDevice_WinUSB.NTia64] 38 | %M1kDeviceName% = USB_Install, USB\%M1kID% 39 | %M1kSambaName% = USB_Install, USB\%M1kSambaID% 40 | 41 | [USB_Install] 42 | Include = winusb.inf 43 | Needs = WINUSB.NT 44 | 45 | [USB_Install.Services] 46 | Include = winusb.inf 47 | AddService = WinUSB,0x00000002,WinUSB_ServiceInstall 48 | 49 | [WinUSB_ServiceInstall] 50 | DisplayName = "WinUSB - Kernel Driver 09/10/2018 6.1.7600.16385" 51 | ServiceType = 1 52 | StartType = 3 53 | ErrorControl = 1 54 | ServiceBinary = %12%\WinUSB.sys 55 | 56 | [USB_Install.Wdf] 57 | KmdfService = WINUSB, WinUsb_Install 58 | 59 | [WinUSB_Install] 60 | KmdfLibraryVersion = 1.11 61 | 62 | [USB_Install.HW] 63 | AddReg = AddDeviceInterfaceGUID 64 | 65 | [NoDeviceInterfaceGUID] 66 | ; Avoids adding a DeviceInterfaceGUID for generic driver 67 | 68 | [AddDeviceInterfaceGUID] 69 | HKR,,DeviceInterfaceGUIDs,0x10000,%DeviceGUID% 70 | 71 | [USB_Install.CoInstallers] 72 | AddReg = CoInstallers_AddReg 73 | CopyFiles = CoInstallers_CopyFiles 74 | 75 | [CoInstallers_AddReg] 76 | HKR,,CoInstallers32,0x00010000,"WdfCoInstaller01011.dll,WdfCoInstaller","WinUSBCoInstaller2.dll" 77 | 78 | [CoInstallers_CopyFiles] 79 | WinUSBCoInstaller2.dll 80 | WdfCoInstaller01011.dll 81 | 82 | [DestinationDirs] 83 | CoInstallers_CopyFiles = 11 84 | 85 | [SourceDisksNames] 86 | 1 = %SourceName% 87 | 88 | [SourceDisksFiles.x86] 89 | WinUSBCoInstaller2.dll = 1,x86 90 | WdfCoInstaller01011.dll = 1,x86 91 | 92 | [SourceDisksFiles.amd64] 93 | WinUSBCoInstaller2.dll = 1,amd64 94 | WdfCoInstaller01011.dll = 1,amd64 95 | 96 | [SourceDisksFiles.ia64] 97 | WinUSBCoInstaller2.dll = 1,ia64 98 | WdfCoInstaller01011.dll = 1,ia64 -------------------------------------------------------------------------------- /dist/modpath.iss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // 3 | // Inno Setup Ver: 5.4.2 4 | // Script Version: 1.4.2 5 | // Author: Jared Breland 6 | // Homepage: http://www.legroom.net/software 7 | // License: GNU Lesser General Public License (LGPL), version 3 8 | // http://www.gnu.org/licenses/lgpl.html 9 | // 10 | // Script Function: 11 | // Allow modification of environmental path directly from Inno Setup installers 12 | // 13 | // Instructions: 14 | // Copy modpath.iss to the same directory as your setup script 15 | // 16 | // Add this statement to your [Setup] section 17 | // ChangesEnvironment=true 18 | // 19 | // Add this statement to your [Tasks] section 20 | // You can change the Description or Flags 21 | // You can change the Name, but it must match the ModPathName setting below 22 | // Name: modifypath; Description: &Add application directory to your environmental path; Flags: unchecked 23 | // 24 | // Add the following to the end of your [Code] section 25 | // ModPathName defines the name of the task defined above 26 | // ModPathType defines whether the 'user' or 'system' path will be modified; 27 | // this will default to user if anything other than system is set 28 | // setArrayLength must specify the total number of dirs to be added 29 | // Result[0] contains first directory, Result[1] contains second, etc. 30 | // const 31 | // ModPathName = 'modifypath'; 32 | // ModPathType = 'user'; 33 | // 34 | // function ModPathDir(): TArrayOfString; 35 | // begin 36 | // setArrayLength(Result, 1); 37 | // Result[0] := ExpandConstant('{app}'); 38 | // end; 39 | // #include "modpath.iss" 40 | // ---------------------------------------------------------------------------- 41 | 42 | procedure ModPath(); 43 | var 44 | oldpath: String; 45 | newpath: String; 46 | updatepath: Boolean; 47 | pathArr: TArrayOfString; 48 | aExecFile: String; 49 | aExecArr: TArrayOfString; 50 | i, d: Integer; 51 | pathdir: TArrayOfString; 52 | regroot: Integer; 53 | regpath: String; 54 | 55 | begin 56 | // Get constants from main script and adjust behavior accordingly 57 | // ModPathType MUST be 'system' or 'user'; force 'user' if invalid 58 | if ModPathType = 'system' then begin 59 | regroot := HKEY_LOCAL_MACHINE; 60 | regpath := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'; 61 | end else begin 62 | regroot := HKEY_CURRENT_USER; 63 | regpath := 'Environment'; 64 | end; 65 | 66 | // Get array of new directories and act on each individually 67 | pathdir := ModPathDir(); 68 | for d := 0 to GetArrayLength(pathdir)-1 do begin 69 | updatepath := true; 70 | 71 | // Modify WinNT path 72 | if UsingWinNT() = true then begin 73 | 74 | // Get current path, split into an array 75 | RegQueryStringValue(regroot, regpath, 'Path', oldpath); 76 | oldpath := oldpath + ';'; 77 | i := 0; 78 | 79 | while (Pos(';', oldpath) > 0) do begin 80 | SetArrayLength(pathArr, i+1); 81 | pathArr[i] := Copy(oldpath, 0, Pos(';', oldpath)-1); 82 | oldpath := Copy(oldpath, Pos(';', oldpath)+1, Length(oldpath)); 83 | i := i + 1; 84 | 85 | // Check if current directory matches app dir 86 | if pathdir[d] = pathArr[i-1] then begin 87 | // if uninstalling, remove dir from path 88 | if IsUninstaller() = true then begin 89 | continue; 90 | // if installing, flag that dir already exists in path 91 | end else begin 92 | updatepath := false; 93 | end; 94 | end; 95 | 96 | // Add current directory to new path 97 | if i = 1 then begin 98 | newpath := pathArr[i-1]; 99 | end else begin 100 | newpath := newpath + ';' + pathArr[i-1]; 101 | end; 102 | end; 103 | 104 | // Append app dir to path if not already included 105 | if (IsUninstaller() = false) AND (updatepath = true) then 106 | newpath := newpath + ';' + pathdir[d]; 107 | 108 | // Write new path 109 | RegWriteStringValue(regroot, regpath, 'Path', newpath); 110 | 111 | // Modify Win9x path 112 | end else begin 113 | 114 | // Convert to shortened dirname 115 | pathdir[d] := GetShortName(pathdir[d]); 116 | 117 | // If autoexec.bat exists, check if app dir already exists in path 118 | aExecFile := 'C:\AUTOEXEC.BAT'; 119 | if FileExists(aExecFile) then begin 120 | LoadStringsFromFile(aExecFile, aExecArr); 121 | for i := 0 to GetArrayLength(aExecArr)-1 do begin 122 | if IsUninstaller() = false then begin 123 | // If app dir already exists while installing, skip add 124 | if (Pos(pathdir[d], aExecArr[i]) > 0) then 125 | updatepath := false; 126 | break; 127 | end else begin 128 | // If app dir exists and = what we originally set, then delete at uninstall 129 | if aExecArr[i] = 'SET PATH=%PATH%;' + pathdir[d] then 130 | aExecArr[i] := ''; 131 | end; 132 | end; 133 | end; 134 | 135 | // If app dir not found, or autoexec.bat didn't exist, then (create and) append to current path 136 | if (IsUninstaller() = false) AND (updatepath = true) then begin 137 | SaveStringToFile(aExecFile, #13#10 + 'SET PATH=%PATH%;' + pathdir[d], True); 138 | 139 | // If uninstalling, write the full autoexec out 140 | end else begin 141 | SaveStringsToFile(aExecFile, aExecArr, False); 142 | end; 143 | end; 144 | end; 145 | end; 146 | 147 | // Split a string into an array using passed delimeter 148 | procedure MPExplode(var Dest: TArrayOfString; Text: String; Separator: String); 149 | var 150 | i: Integer; 151 | begin 152 | i := 0; 153 | repeat 154 | SetArrayLength(Dest, i+1); 155 | if Pos(Separator,Text) > 0 then begin 156 | Dest[i] := Copy(Text, 1, Pos(Separator, Text)-1); 157 | Text := Copy(Text, Pos(Separator,Text) + Length(Separator), Length(Text)); 158 | i := i + 1; 159 | end else begin 160 | Dest[i] := Text; 161 | Text := ''; 162 | end; 163 | until Length(Text)=0; 164 | end; 165 | 166 | 167 | procedure CurStepChanged(CurStep: TSetupStep); 168 | var 169 | taskname: String; 170 | begin 171 | taskname := ModPathName; 172 | if CurStep = ssPostInstall then 173 | if IsTaskSelected(taskname) then 174 | ModPath(); 175 | end; 176 | 177 | procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); 178 | var 179 | aSelectedTasks: TArrayOfString; 180 | i: Integer; 181 | taskname: String; 182 | regpath: String; 183 | regstring: String; 184 | appid: String; 185 | begin 186 | // only run during actual uninstall 187 | if CurUninstallStep = usUninstall then begin 188 | // get list of selected tasks saved in registry at install time 189 | appid := '{#emit SetupSetting("AppId")}'; 190 | if appid = '' then appid := '{#emit SetupSetting("AppName")}'; 191 | regpath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\'+appid+'_is1'); 192 | RegQueryStringValue(HKLM, regpath, 'Inno Setup: Selected Tasks', regstring); 193 | if regstring = '' then RegQueryStringValue(HKCU, regpath, 'Inno Setup: Selected Tasks', regstring); 194 | 195 | // check each task; if matches modpath taskname, trigger patch removal 196 | if regstring <> '' then begin 197 | taskname := ModPathName; 198 | MPExplode(aSelectedTasks, regstring, ','); 199 | if GetArrayLength(aSelectedTasks) > 0 then begin 200 | for i := 0 to GetArrayLength(aSelectedTasks)-1 do begin 201 | if comparetext(aSelectedTasks[i], taskname) = 0 then 202 | ModPath(); 203 | end; 204 | end; 205 | end; 206 | end; 207 | end; 208 | 209 | function NeedRestart(): Boolean; 210 | var 211 | taskname: String; 212 | begin 213 | taskname := ModPathName; 214 | if IsTaskSelected(taskname) and not UsingWinNT() then begin 215 | Result := True; 216 | end else begin 217 | Result := False; 218 | end; 219 | end; 220 | -------------------------------------------------------------------------------- /dist/version.hpp.in: -------------------------------------------------------------------------------- 1 | // Released under the terms of the BSD License 2 | // (C) 2016, Analog Devices, Inc. 3 | 4 | /// @file version.hpp 5 | /// @brief Library versioning support. 6 | /// Versioning for libsmu follows the guidelines of semantic versioning for 7 | /// shared libraries (http://semver.org/). 8 | 9 | #pragma once 10 | 11 | /// @brief The major version of the libsmu library (X.0.0). 12 | #define LIBSMU_VERSION_MAJOR @LIBSMU_VERSION_MAJOR@ 13 | 14 | /// @brief The minor version of the libsmu library (0.X.0). 15 | #define LIBSMU_VERSION_MINOR @LIBSMU_VERSION_MINOR@ 16 | 17 | /// @brief The patch version of the libsmu library (0.0.X). 18 | #define LIBSMU_VERSION_PATCH @LIBSMU_VERSION_PATCH@ 19 | 20 | /// @brief A numerical libsmu library version. 21 | #define LIBSMU_VERSION ((LIBSMU_VERSION_MAJOR << 16) |\ 22 | (LIBSMU_VERSION_MINOR << 8) |\ 23 | LIBSMU_VERSION_PATCH) 24 | 25 | /// @brief A string representing the libsmu library version. 26 | #define LIBSMU_VERSION_STR "@LIBSMU_VERSION_STR@" 27 | 28 | /// @private 29 | inline const char* libsmu_version_str() { 30 | return LIBSMU_VERSION_STR; 31 | } 32 | 33 | /// @private 34 | inline uint32_t libsmu_version_major() { 35 | return LIBSMU_VERSION_MAJOR; 36 | } 37 | 38 | /// @private 39 | inline uint32_t libsmu_version_minor() { 40 | return LIBSMU_VERSION_MINOR; 41 | } 42 | 43 | /// @private 44 | inline uint32_t libsmu_version_patch() { 45 | return LIBSMU_VERSION_PATCH; 46 | } 47 | -------------------------------------------------------------------------------- /dist/x86/WdfCoInstaller01011.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analogdevicesinc/libsmu/dbb484f004d9eb5251aa4667f5cf09b3ff5610e2/dist/x86/WdfCoInstaller01011.dll -------------------------------------------------------------------------------- /dist/x86/winusbcoinstaller2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analogdevicesinc/libsmu/dbb484f004d9eb5251aa4667f5cf09b3ff5610e2/dist/x86/winusbcoinstaller2.dll -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1.3) 2 | project(doc) 3 | 4 | find_program(DOXYGEN_PATH doxygen) 5 | if (NOT DOXYGEN_PATH) 6 | message(FATAL_ERROR "Doxygen not found!") 7 | endif() 8 | 9 | 10 | set(SOURCES_DIR ${CMAKE_SOURCE_DIR}/include/libsmu) 11 | set(DOXY_WORKING_DIR ${CMAKE_CURRENT_BINARY_DIR}/doxygen_doc) 12 | 13 | 14 | set(PROJECT_NAME "libsmu") 15 | set(DOCUMENTED_FILES "${SOURCES_DIR}/libsmu.hpp \\ 16 | ${CMAKE_CURRENT_SOURCE_DIR}/mainpage.dox 17 | " 18 | ) 19 | 20 | 21 | configure_file( 22 | Doxyfile.in 23 | ${DOXY_WORKING_DIR}/Doxyfile.doxy 24 | ) 25 | 26 | 27 | add_custom_target(doc 28 | COMMAND ${DOXYGEN_PATH} Doxyfile.doxy 29 | COMMAND ${CMAKE_COMMAND} -E make_directory ${DOXY_WORKING_DIR}/html 30 | COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/img ${DOXY_WORKING_DIR}/html/img 31 | WORKING_DIRECTORY ${DOXY_WORKING_DIR} 32 | ) 33 | -------------------------------------------------------------------------------- /doc/img/reading.xml: -------------------------------------------------------------------------------- 1 | 7Vzdj6M4Ev9rWpd96AjCV/I43TO7d9KeNHej0909tWjiJGgIzvLR6Z6H/du3/FFgG0ggge7e2YmiJBhjG/tXVT9XFblx7vfPv2ThYfdPuibJzcJaP984H28Wi4XlW/DFSl5EiW0vPFGyzeK1LKsLvsTfiCyUF27LeE1yrWJBaVLEB70womlKokIrC7OMHvVqG5rovR7CrexRdsgKvkRhguOYy/Gy8v/G62InypcLvy7/O4m3O+zb9lfizGMYfd1mtExljzcLZ8Nf4vQ+xLZkz/kuXNOjUuR8gqnNKIWW2a/98z1J2PTixOF1xQuO9sa52xX7BA5s+MlP/9xxsbz/0xfD3WUklXcmuutqD+/6KUxKbNDsYRMnyT1NaMYPcTacu7zI6FeinLH469yA5C08kawgEnG8SA7wF0L3pMheoIo868oZQzDKw2O9rLYXyMKdsqYLy5OloYTTtmq7nhL4IWelfYbkfAyYoPt7l8lLywT5/iQTtIA2tRnC+1anqGWCYJQjTJDTMkF+Al3cbSjckjpT/m8lEwt+4jbnWuMDVLDdw3N9En5t2bc9h1MpOcLnF5LnMU1nP2HLMCbRuKjaWBGyBnUgD1Oawtcdm84YNMSv4SNJPtM8LqBFOF3Qg3L2QxJvWekjLQq6hxOJUT0hG9ZjKOtlYjZ7LSgb1Mnl1Jarbb2wsG25ZHOfacwnHYGD647QcFeoGrGRnJZZROR1qqowmmqgrNlUEWZbUpxvypbXyZZupRLCZuhmkxOtBY7BaqZ6wdLthOVjDZuBOPXbcCrB2QnNqljp14BrZUMYfgxt8mFx796vGtpEonpcRWJ7C1xSuTTIBDRoyioqNMdQJNI0n9K0yu2u4wyYgxDKI8nZvB6SME5vaRamIGgj2B1v+YZqNXgt/H4kT3EE8/Xnh69rKLvXBK+Eyinwthglkq4/MLpbl6zDfEdYm2xO+kyPUN8aZoQa1oTqrO1BJR8s5o7jWf7K5S9dG9i+PJ7AUjluMF94y2XguJ4frCx5e9UOxGiwr9VizXqBb6+81TLwPVeaBfOGzliwC+xPk1gvGKHJo1DhMev4CaVqVh7WYQGbJhjMUxgD7wCgwBZMSqeVxHmh8h/l0gnpj4JAAF728j92MHfx8P9waM1XQYAFn0kWwyQRJuOM6HbC9hqM4hZKLuJKMtAJQFnpd8TL0rqUPrmuYU1g0iaCHtKr6W3HP9JDyS7/V0mgp+/AhCwdaw4fK9CAdgA/9DXzVvOV+pIr+Br2xe7BjnoZmKE2BcGkCqwtb7yvxLpOMLdc8L64VsDe2qQucO8+gfx6tjv3Oju225dzqGSf7sQ53cmIUt/GGLv5syDL24wQ2LpcLzeBod3QaXOWKyNJu8oF0eaDOCEcJHmkx091wR0vgBM7msXfQHeF0NPY5EziQpUjOcrehg+Uk/CxyZe8bTnnTptnbCxDeLrrinkPpmeObmCrexhfPvxuR5XCpfJwf0gEC8sY7TrQwwGWHHzQGRAiaC2N6D5Ot/DzN2H0rBBcxVAxCSNeEUZKhRs7fIF22ggbM3C87mO52QBXWlgU3MtyZPWl1mwfsuV/5PwP3AwMPrsQ5NVKy/1DPdQNGEQoo+ljQqOvMDogipfAH2mgCfUu8ljRRJM/ClbZRh6teXUoyKPrVgW9yaNmn67f9LgrXXn5rcprHEEKAl1ylt7FjBKc3oa3pqWxDvkBxcahhdUOrAJA6ToJ62GB3hqHz3EhYLiURwKFgS8PL8LgQLx5FrjZVGUqr0eW6Xtzx7WXC0tsWafb3jjAy3QEuZfDEcIt55p6TTBCzLKBxryMIlCjm5JVnEmlyXbVBkiB9BhO/YzAVohvyQUs5BChtnd3431UIgIRAIWhp4HFfbxec5TzkMJdFWZsC601UVfJlknJqiCqHJ0WhtTQl5EkLOInFaJDwHIr5xOd9qalHsVrHyDzvDyaBPPWslP9NwnXwmyDlyWs7bnwr6h2+mRgia9YpY9YVVRmBiUcHjQdgYVDePFcqNRDwqWqDiy7aoOK4q8sncM8XuGaMZ1qniERgE0pBk2sGZlvZbWHMElmP53mLjQrdnRL0zBRDYcy8SiHMlCXH8II1v1XfvSRRcsVWs/pEpPualXZQtSOLo2pXBXau1L2zNhbteM5o6S7NWtHP4ZrDfupMSBa7DlKzxjliJtdtJkK2lwGI1CGm3hbZmQm6PFDBk7V05B6px7S/u5Qw+OAx5P4Q/W+HLvKtBnsD0VwTL/zs+2mVRHZBNZMbJ1A8Wg6KmaaSeqo71If4SLoyLGMQPx5QLi4q0buis68kRWTu2zvp59iaoxy1Q9rTcKsR6VuDUU+CgWyUcYUtHpMtWVlOlP2/Ry+cB9y9w+Kr4jTkpYMv3tI6fvOlZ6EKm42MLD3CkqvolDDVZ4RTjAbGlPlNdlYl2n8yyrBIWpOh5uD+5TR1Vx7P33VnHH1yhhlXzVnsMCJ1FyTweUvafTDLDfMsne1WfZxRSc2y9jPZWa58ru+T7Pc9G4yi/xDl2p2WVdAljR4Q5Crr6WPOm105Lb30xe5xtWot98ncpvJabFM3MAYVpwC8wNkfeNhqdmabMIyYfMn3DW/A23as6MjgyfGq5RYGWtLXsSY6tk99yug/Gos20b2h7k458G8NNRjI9g9EphXmH5o9NOFukZ9Cd+u+kvcL7XXPyMsjVm4VFg8PcBgOB7GkRU0lIqsQMQVHPcPcfpQQGpxDgFbzlFA+afCo8yV+3++AOIgPJuBo5nhrsx34qyQEmxSGAMuVMWOlUoJbASiyXNEDnw3xlulR/YjZmKHMkthe7ZJ2LNSILGsW82/Hefp39jcfSXkIE6Uh0vyB9+1pF4uihBYm0YUHR2k2E+nKBr1l9IN1imKxn0Y9QeKIl49XBQlGULVOI0sNiN4PneEANZnmDoBzejZEKygAG8DLet8WQXs8joeKnqCsBmna9ycVWkXzJUCyVFCagSTA0Zu5YBziEnXgtwuS40e5WBEl6ZlZQ6cmZ65wW6AgnLIjnHO5JwbXl75GMYsNPX2KcDW3DGyOHj8vM3VM2Ig7KIUxAkcQi46OVHGe+6umskgxk6v0VBfcbzFzTSaxilis5Cr1+ZT0r2OLfjn5jGhgLI3tj5SDnTjk8McF1gjSkLYokVY/DNgt4L+Wh6NnhUy4Gk+R3fwBOY6j7U5MrLyEJedjk2jPuQhDdpMtV99gRQYXuFprFLTDwDAagoCV/fHHUuvNdL1YvYBRiximU0Vc5TcsrIrioiIVMRdmBTsAh8agsL0MWdf3xulw7V0DSekyVSGO8FWPZNUh0qLZz6PcobCNerLfc1lTjO8erC0YEYRUjhJtkYWlqbrgUlKRlmtjBRllrbkhIA48TxY3YnWEItdCAkVqiSJHRUfdr3tSuJ9b9akyEAnH1KwTw8g3UaOIpyW/4eBGYaCMLEHWOp8Q/bQUnvCYYdzbh6AL+2aPFlpjS5JP78yecVZmpKMe57B2eKN/CazpREjaC1P8MHa8q0/4+F13nWNW0gNB8yJTfufXf/65hMdZmS/T9aS8fioNVF2QOVXMzvqDHCZF2C8tG+ik3FjVbj1XSphZGdq+stmA0jmFCUC/1ZKkvqRh3Wcs1xSUKivnD9gaDyWjvKGj5VCQuOUOQUmAN3LM6lcnfVCW9PlFWDAQjXo3QBqmOxNnO2PohbssAoeehBsmOP8r0FlTz7z1o/1QfT8dchs1VFfNgse9GvobHX5O1WlTZfkDz77any2tyo3tWv1H2IDJA6zLjrb6P1gTHM0/fJyzgMUDut/mhPV63/0cz79AQ== -------------------------------------------------------------------------------- /doc/img/writing.xml: -------------------------------------------------------------------------------- 1 | 7Vxbk6M40v01Fet5KAeYi+3Hquqe2YfZ+HqjY2O+faqgQLaJxsjDpVzdD/vbJ1NSghBggwtXX6YdDtsIIQnp5NFRKvGN87B/+S0LDrt/8YglNwsrerlx3t0sFgvLt+ALUz7LFNteeDJlm8WRSqsTPsZfmEpUF27LOGJ5I2PBeVLEh2ZiyNOUhUUjLcgyfmxm2/CkWesh2KoaVYWY8DEMEmrHXLUX0/+Io2In01cLv07/J4u3O6rb9tfyzFMQftpmvExVjTcLZyNe8vQ+oLJUzfkuiPhRS3LeQ9dmnEPJ+Gv/8sAS7F7qOLqu+EytvXHud8U+gQMbforTv/ZcrO7/9MVwdxlL1Z3J6vrKo7t+DpKSCjRr2MRJ8sATnolD6g3nPi8y/olpZyzxOtcgdQvPLCuYQpxIUg38jfE9K7LPkEWddVWPERjV4bEeVttbqsSdNqYLy1OpgYLTtiq77hL4oXqlu4dUf4zooIcHF+2lo4N8/yodtIAyGz1E9613UUcHQSsn6CCno4P8BKq433C4Jb2n/D9LNAtx4jYXrHEHGWz38FKfhF9b/LbncCplR/j8yPI85unsFyoZ2iQLl1lbI8IioAN1mPIUvu6xO2NgiN+DJ5Z84HlcQIlwuuAH7exdEm8x9YkXBd/DicTInrAN1hiofJnszUEDio06OZyN4eoaL0rsGi5V3Acei04n4NC4EzTcNVEjFZLzMguZuk6nCqOoFsraRRVBtmXF+aJsdZ0q6VaREBXDN5ucNUoQGKx6ahAs3V5YPtWwGYlTvwunCpy90KyStXoNuFZzCOLHYJO7xYP7sG6xiUL1tERiewsaUjU0pAQa0FRZdGhOQSRqaj7FtNrtRnEGykEa5ZHl2K+HJIjTW54FKRjaBPOOt/qKtLp8K/y+Y89xCP31/cPXNcjuLcGroHIKvB2TEkujO5S7dUoU5DuGZWKfDOkeSd8NzEgabhjV2bmHSH65mDuOZ/lrV7yabGD76vgKM5XjLucLb7VaOq7nL9eWur1qBWIUOHTWwmK9pW+vvfVq6XuumhbMGzozg10w/7SF9QIFTR4Gmo6J4meyqll5iIICFk3QmOcgBt0BQIElmLJOK4nzQtc/2qVXlD8aAgF42ef/x4O5S4f/hUNrvl4uKeEDy2LoJIY2jkJ3qBjqAG0/RmkJpQZxrRToFUBZ8TvhZWVdKp9c15hNoNOuBD2SV9efO/6vLA4lXv/vkpWI3e9/Elk55ycRAuDUk4g9QAINmkXGThyEGN0IbYXPoVOH6yznlgsuFtda4rvRiwtaoF/BSD3bnXu9Fdvr+Vp/GVY31HxPV+KcrmRC0+6Shf0iWSribcYYrE9ebxpLg8LIM3NWEJMRvcrP0OVoOGEcLHnix/d1wr1IgBM7nsVfgJ4CqGlqBaZwoduRauVgBeZYc+lIUy9126rPnS7311Sz3emqK3k9WoMZlFrdw/T24fd7oxqCabSM6kUTSScTOX2Cq5JWpuaSSqxLcFnz6lAKLtetEiYUXMaUIJimn+3XTS7wO7lgGlwul00grryLVRg4ig0PR0dhPXAEngiwByjbATOA5nkdYAcQ+tfG4UtcSBiu1JFE4dJXh5djUAfcadHvWeCa0rlJQVaNoud7c8e1VwtLLvOutyRwQOY0EeReDkfYojhX1FuCEfb5WmjMyzAED+emxIwz0N3glOe4EjVAChrCcIRnDJYPYhkrYaGaCLm9+xvvneZFDwEoiJ4WFvdxFAmUCzf8fbU117Ud1aNmqk1G1RJ9z7AGqDTDNvoylgRF/NxE7XCw3Kr+JEe3OfFN4ulekpC7fAcGOqNjdfdHBmSRbtELERSB2MOtPBL9az0DF2K8KjbCrERlhr4av804gaSFDblzm4seqRedOCjtVas9Mn5t4Bz0EQVRpPez7PacthmsGZtvVbbHIElmv7SNUZ8xeFbs+JanQaJPG1rHkxWqra38EIQw6r+Lo3e4v6xp5KeEh5/QtqtRxYGoXUMNnVINUYv3W2M2ueWZu1XV8uEMRffzak89hjOK6qkxIEsc2ErPaOWEK0eaMTW0uQgjoMdNvC0zNsuD/SFhjxm4IU9D6lvwKV6CqWrMjOU7HV/Fg9isy7Gr2JTRHkQCx/WXUbbdnlPk/rs12wgNAMTT4KgYmUlx1A/JRzQITeRYxtb1eUC4tEQl5UqesYmJyV111zOMmFqtXA/DWlsuN/dxbg0in0QA2WRjGlo9pLasTGdpuX+UxJYL+MJ9QDwZQhiID+RNyUvE7x6C4H5w0lNQpaUGbYW9AelVEmo85TW9DK2CpqS8thrrmxr/tiQ4huaacHNolTI5zXXXM5TmjKvXRiuH0pyhAq9Ec20Fl39Ow5/Tcmta9l49Lfs0oleelqmey6blyuv6bU7Lbd8mzsg/ubQxLzcJyFIT3hjkNsfSJ06bHLnd9QxFrnE18fa3idx2OBenUIc/VagDIBS0H2DrC8CENCXovR1Ky10APsekcpLpcT4R2wRlgj0tHTv/A4G1x6MjAhm+OTxqYCnJineGlaqLUNNKsxm9gfUGVvJqW7CdptwzB/e8MawMem3tPE9kDGsK+DPq6UNtK7+Cf1/+Fa23uvOfMbZWL1xqbF5ze8JwXExjazTRarYG1gNu/0cwuccConnzDcuEyIHZI5UOaTE7/OcjQM4qdhkL0AIPHJ4uIsuhAjFlk8ESrDJKzYRbxnmrXwawRTinISwNaws/7oRBFrtGdB57OcAWA1412yRc5kDjlr0J6whYGKqZTAViQVnNzWijGewlZAexthS3yI/4I0ZqQE+8uAP4RvPLoEZ4YAqqPpoO+zhP/4EN+MTYQZ4oD73k8WNRyuWcAfuH1+EMp2lNVE8vZxj5V8rf18sZxn0Y+UdyBl09njOU6iMOvw5ptDcqffT4oAWw2VO5Acq4WUA5tdXjQQE+FWCVrvhbdY1zFzEwLpqGhTHre10dFqMlUWViUw09TYIyAKHW7AFOYapIwYP7M4aoJalmy2JN/YDVzMA5JQwIzAuKxVsFIcKyY5zjrQh5ITIfgxi36r5+ELE1d4yYFhFN0OX6unRj8DxrdAU8vsljWS45fYkKBq4226Exxsq3VdBQq701nskyddNEZtv2YMxGumEnnXQUrptzTg59VlCOMAlgCRpS8q+AxQrKkToaB7kBMS8jnu9zmg6spRmGPNXizwjhI5z1Om6N/BBlNWqx2H31Bag2vN7XmYzafg4AVgvXkr6PO4zFtbRtCaHTxI53EGLcViVslfTtEqpSm+6CpMALfCgIEtOnHL9+0MXhrWs4WU2BMt7Jtx4Y0TrWWjzzCZUzyq2VX627LnMK0tWjrYViEUm5KY01sbG0XStoKRnHXBkryiztiHkBc0LAG07CllnsAggY0S1JLtNEs+uFYRLvB6sgzQZ69Y2GfX4A6zYiMOG0+ocMip+UAggfaKmjKfExpu5wyh7n43wJvsJX7hZ2zEYdweuTB+c4K9OSaakzOrS8Fb9lljThDmHHM30wtsIzgbpauBqEvK9xG3LEnFzGf+/865uPf5iRC0OisowHSq0rRT9U+tWsqHcDz7yA9oOHBnIZN1ZtJ3+TJEzqTPdvbzaAZCFRau+18oFFcY7Rs0CobxwfYTAehtu8mvEuf9AUAjavGTNhAtC9PFLMbapeKOt6cRO0IaNP6P0Aak3ZmzjbH2UuWGEV+K3UsMD530PKnnxAbpjqg+iAtxGzVUVD1Sx4+F8jZ6vLv1EqbXsif+rZr6Vn+6ncZNfqX8VGWBxFlfSWMfixn3ZrhsUdnQcoHNb/PSez1//x57z/Cw== -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(CheckFunctionExists) 2 | CHECK_FUNCTION_EXISTS(getopt GETOPT_FOUND) 3 | 4 | if(NOT WIN32) 5 | link_directories(${LINK_DIRECTORIES} ${LIBUSB_LIBRARY_DIRS}) 6 | endif() 7 | include_directories(SYSTEM ${LIBUSB_INCLUDE_DIRS}) 8 | 9 | if(NOT GETOPT_FOUND) 10 | # use internal getopt implementation 11 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src/cli) 12 | set(GETOPT_C_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../src/cli/getopt_internal.c) 13 | endif() 14 | 15 | add_executable(hotplug hotplug.cpp ${GETOPT_C_FILE}) 16 | add_executable(read-write read-write.cpp ${GETOPT_C_FILE}) 17 | add_executable(read-write-continuous read-write-continuous.cpp ${GETOPT_C_FILE}) 18 | add_executable(read read.cpp ${GETOPT_C_FILE}) 19 | add_executable(read-continuous read-continuous.cpp ${GETOPT_C_FILE}) 20 | add_executable(smu_stream_multi_out smu_stream_multi_out.cpp ${GETOPT_C_FILE}) 21 | add_executable(leds leds.cpp ${GETOPT_C_FILE}) 22 | target_link_libraries(hotplug smu) 23 | target_link_libraries(read-write smu) 24 | target_link_libraries(read-write-continuous smu) 25 | target_link_libraries(read smu) 26 | target_link_libraries(read-continuous smu) 27 | target_link_libraries(smu_stream_multi_out smu) 28 | target_link_libraries(leds smu) 29 | -------------------------------------------------------------------------------- /examples/hotplug.cpp: -------------------------------------------------------------------------------- 1 | // Simple example demonstrating hotplug support. 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | using std::cout; 11 | using std::cerr; 12 | using std::endl; 13 | 14 | using namespace smu; 15 | 16 | int main(int argc, char **argv) 17 | { 18 | // Create session object and add all compatible devices them to the 19 | // session. Note that this currently doesn't handle returned errors. 20 | Session* session = new Session(); 21 | std::vector < Device* > last_devices; 22 | 23 | while (true) { 24 | std::this_thread::sleep_for(std::chrono::milliseconds(2000)); 25 | session->scan(); 26 | 27 | std::vector < Device* > available_devices = session->m_available_devices; 28 | 29 | //check if there is any disconnected device 30 | for (auto other_device : last_devices) { 31 | bool found = 0; 32 | 33 | for (auto device : available_devices) { 34 | if (!device->m_serial.compare(other_device->m_serial)) { 35 | found = 1; 36 | break; 37 | } 38 | } 39 | 40 | if (!found) { 41 | cout << "Device detached!\n"; 42 | last_devices.erase(std::remove(last_devices.begin(), last_devices.end(), other_device), last_devices.end()); 43 | } 44 | } 45 | 46 | //check if there is any new connected device 47 | for (auto device : available_devices) { 48 | bool found = 0; 49 | 50 | for (auto other_device : last_devices) { 51 | if (!device->m_serial.compare(other_device->m_serial)) { 52 | found = 1; 53 | break; 54 | } 55 | } 56 | 57 | if (!found) { 58 | cout << "Device attached!\n"; 59 | } 60 | else { 61 | available_devices.erase(std::remove(available_devices.begin(), available_devices.end(), device), available_devices.end()); 62 | delete device; 63 | } 64 | } 65 | 66 | last_devices.insert(last_devices.end(), available_devices.begin(), available_devices.end()); 67 | cout << "Number of available devices: " << last_devices.size() << '\n'; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /examples/leds.cpp: -------------------------------------------------------------------------------- 1 | // Simple example for setting devices leds. 2 | 3 | #ifdef _WIN32 4 | #include 5 | #else 6 | #include 7 | #endif 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #ifndef _WIN32 19 | int (*_isatty)(int) = &isatty; 20 | #endif 21 | 22 | using std::cerr; 23 | using std::endl; 24 | 25 | using namespace smu; 26 | 27 | int main(int argc, char **argv) 28 | { 29 | Session* session = new Session(); 30 | session->add_all(); 31 | 32 | if (session->m_devices.size() == 0) { 33 | cerr << "Plug in a device." << endl; 34 | exit(1); 35 | } 36 | 37 | while(true){ 38 | for(unsigned i=0; i<8;i++){ 39 | for(auto dev : session->m_devices){ 40 | dev->set_led(i); 41 | } 42 | std::this_thread::sleep_for(std::chrono::milliseconds(250)); 43 | } 44 | } 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /examples/read-continuous.cpp: -------------------------------------------------------------------------------- 1 | // Simple example for reading data in a continuous fashion. 2 | // 3 | // When run the example should output from all channels to 4 | // stdout in a continuous fashion. If any sample is dropped the program exits 5 | // with an error code of 1. 6 | // 7 | // Note that slower/older setups and/or slower older terminals can cause sample 8 | // drops, redirecting stdout should eliminate this for most cases. 9 | // 10 | // Sample dropping can be forced by sending the running program SIGQUIT 11 | // (via Ctrl-\), to quit use SIGINT (Ctrl-C). 12 | 13 | #ifdef _WIN32 14 | #include 15 | #else 16 | #include 17 | #endif 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | #ifndef _WIN32 30 | int (*_isatty)(int) = &isatty; 31 | #endif 32 | 33 | using std::cout; 34 | using std::cerr; 35 | using std::endl; 36 | 37 | using namespace smu; 38 | 39 | void signalHandler(int signum) 40 | { 41 | cerr << endl << "sleeping for a bit to cause an overflow exception in continuous mode" << endl; 42 | std::this_thread::sleep_for(std::chrono::milliseconds(250)); 43 | } 44 | 45 | int main(int argc, char **argv) 46 | { 47 | #ifndef _WIN32 48 | // Make SIGQUIT force sample drops. 49 | signal(SIGQUIT, signalHandler); 50 | #endif 51 | 52 | // Create session object and add all compatible devices them to the 53 | // session. Note that this currently doesn't handle returned errors. 54 | Session* session = new Session(); 55 | session->add_all(); 56 | 57 | if (session->m_devices.size() == 0) { 58 | cerr << "Plug in a device." << endl; 59 | exit(1); 60 | } 61 | 62 | // Grab the first device from the session. 63 | auto dev = *(session->m_devices.begin()); 64 | 65 | // Run session in continuous mode. 66 | session->start(0); 67 | 68 | // Data to be read from the device is formatted into a vector of four 69 | // floats in an array, specifically in the format 70 | // . 71 | std::vector> rxbuf; 72 | 73 | while (true) { 74 | try { 75 | // Read 1024 samples at a time from the device. 76 | // Note that the timeout (3rd parameter to read() defaults to 0 77 | // (nonblocking mode) so the number of samples returned won't 78 | // necessarily be 1024. 79 | dev->read(rxbuf, 1024); 80 | } catch (const std::system_error& e) { 81 | if (!_isatty(1)) { 82 | // Exit on dropped samples when stdout isn't a terminal. 83 | cerr << "sample(s) dropped: " << e.what() << endl; 84 | exit(1); 85 | } 86 | } 87 | 88 | for (auto i: rxbuf) { 89 | // Overwrite a singular line if stdout is a terminal, otherwise 90 | // output line by line. 91 | if (_isatty(1)) 92 | printf("\r% 6f % 6f % 6f % 6f", i[0], i[1], i[2], i[3]); 93 | else 94 | printf("% 6f % 6f % 6f % 6f\n", i[0], i[1], i[2], i[3]); 95 | } 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /examples/read-write-continuous.cpp: -------------------------------------------------------------------------------- 1 | // Simple example for reading/writing data in a continuous fashion. 2 | 3 | #ifdef _WIN32 4 | #include 5 | #else 6 | #include 7 | #endif 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #ifndef _WIN32 21 | int (*_isatty)(int) = &isatty; 22 | #endif 23 | 24 | using std::cout; 25 | using std::cerr; 26 | using std::endl; 27 | 28 | using namespace smu; 29 | 30 | void signalHandler(int signum) 31 | { 32 | cerr << endl << "sleeping for a bit to cause an overflow exception in continuous mode" << endl; 33 | std::this_thread::sleep_for(std::chrono::milliseconds(250)); 34 | } 35 | 36 | int main(int argc, char **argv) 37 | { 38 | #ifndef _WIN32 39 | // Make SIGQUIT force sample drops. 40 | signal(SIGQUIT, signalHandler); 41 | #endif 42 | 43 | // Create session object and add all compatible devices them to the 44 | // session. Note that this currently doesn't handle returned errors. 45 | Session* session = new Session(); 46 | session->add_all(); 47 | 48 | if (session->m_devices.size() == 0) { 49 | cerr << "Plug in a device." << endl; 50 | exit(1); 51 | } 52 | 53 | // Grab the first device from the session. 54 | auto dev = *(session->m_devices.begin()); 55 | 56 | // Set device channels to source voltage and measure current. 57 | dev->set_mode(0, SVMI); 58 | dev->set_mode(1, SVMI); 59 | 60 | // Run session in continuous mode. 61 | session->start(0); 62 | 63 | std::vector> rxbuf; 64 | std::vector a_txbuf; 65 | std::vector b_txbuf; 66 | 67 | // refill Tx buffers with data 68 | auto refill_data = [=](std::vector& buf, unsigned size, int voltage) { 69 | buf.clear(); 70 | for (unsigned i = 0; i < size; i++) { 71 | buf.push_back(voltage); 72 | } 73 | }; 74 | 75 | uint64_t i = 0; 76 | uint64_t v = 0; 77 | 78 | while (true) { 79 | // If stdout is a terminal change the written value approximately 80 | // every second, otherwise change it on every iteration. 81 | if (_isatty(1)) 82 | v = i / 100; 83 | else 84 | v = i; 85 | 86 | // Refill outgoing data buffers. 87 | refill_data(a_txbuf, 1000, v % 6); 88 | refill_data(b_txbuf, 1000, v % 6); 89 | i++; 90 | 91 | try { 92 | // Write iterating voltage values to both channels. 93 | dev->write(a_txbuf, 0); 94 | dev->write(b_txbuf, 1); 95 | // Read incoming samples in a non-blocking fashion. 96 | dev->read(rxbuf, 1000); 97 | } catch (const std::system_error& e) { 98 | if (!_isatty(1)) { 99 | // Exit on dropped samples when stdout isn't a terminal. 100 | cerr << "sample(s) dropped: " << e.what() << endl; 101 | exit(1); 102 | } 103 | } 104 | 105 | for (auto i: rxbuf) { 106 | // Overwrite a singular line if stdout is a terminal, otherwise 107 | // output line by line. 108 | if (_isatty(1)) 109 | printf("\r% 6f % 6f % 6f % 6f", i[0], i[1], i[2], i[3]); 110 | else 111 | printf("% 6f % 6f % 6f % 6f\n", i[0], i[1], i[2], i[3]); 112 | } 113 | }; 114 | } 115 | -------------------------------------------------------------------------------- /examples/read-write.cpp: -------------------------------------------------------------------------------- 1 | // Simple example for reading/writing data in a non-continuous fashion. 2 | 3 | #ifdef _WIN32 4 | #include 5 | #else 6 | #include 7 | #endif 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #ifndef _WIN32 20 | int (*_isatty)(int) = &isatty; 21 | #endif 22 | 23 | using std::cout; 24 | using std::cerr; 25 | using std::endl; 26 | 27 | using namespace smu; 28 | 29 | int main(int argc, char **argv) 30 | { 31 | // Create session object and add all compatible devices them to the 32 | // session. Note that this currently doesn't handle returned errors. 33 | Session* session = new Session(); 34 | session->add_all(); 35 | 36 | if (session->m_devices.size() == 0) { 37 | cerr << "Plug in a device." << endl; 38 | exit(1); 39 | } 40 | 41 | // Grab the first device from the session. 42 | auto dev = *(session->m_devices.begin()); 43 | 44 | // Set device channels to source voltage and measure current. 45 | dev->set_mode(0, SVMI); 46 | dev->set_mode(1, SVMI); 47 | 48 | std::vector> rxbuf; 49 | std::vector a_txbuf; 50 | std::vector b_txbuf; 51 | 52 | // refill Tx buffers with data 53 | auto refill_data = [=](std::vector& buf, unsigned size, int voltage) { 54 | buf.clear(); 55 | for (unsigned i = 0; i < size; i++) { 56 | buf.push_back(voltage); 57 | } 58 | }; 59 | 60 | uint64_t i = 0; 61 | uint64_t v = 0; 62 | 63 | while (true) { 64 | // If stdout is a terminal change the written value approximately 65 | // every second, otherwise change it on every iteration. 66 | if (_isatty(1)) 67 | v = i / 100; 68 | else 69 | v = i; 70 | 71 | // Refill outgoing data buffers. 72 | refill_data(a_txbuf, 1024, v % 6); 73 | refill_data(b_txbuf, 1024, v % 6); 74 | i++; 75 | 76 | try { 77 | // Write iterating voltage values to both channels. 78 | dev->write(a_txbuf, 0); 79 | dev->write(b_txbuf, 1); 80 | 81 | // Run the session for 1024 samples. 82 | session->run(1024); 83 | 84 | // Read incoming samples in a blocking fashion. 85 | dev->read(rxbuf, 1024, -1); 86 | } catch (const std::system_error& e) { 87 | if (!_isatty(1)) { 88 | // Exit on dropped samples when stdout isn't a terminal. 89 | cerr << "sample(s) dropped: " << e.what() << endl; 90 | exit(1); 91 | } 92 | } 93 | 94 | for (auto i: rxbuf) { 95 | // Overwrite a singular line if stdout is a terminal, otherwise 96 | // output line by line. 97 | if (_isatty(1)) 98 | printf("\r% 6f % 6f % 6f % 6f", i[0], i[1], i[2], i[3]); 99 | else 100 | printf("% 6f % 6f % 6f % 6f\n", i[0], i[1], i[2], i[3]); 101 | } 102 | }; 103 | } 104 | -------------------------------------------------------------------------------- /examples/read.cpp: -------------------------------------------------------------------------------- 1 | // Simple example for reading data in a non-continuous fashion. 2 | 3 | #ifdef _WIN32 4 | #include 5 | #else 6 | #include 7 | #endif 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #ifndef _WIN32 19 | int (*_isatty)(int) = &isatty; 20 | #endif 21 | 22 | using std::cout; 23 | using std::cerr; 24 | using std::endl; 25 | 26 | using namespace smu; 27 | 28 | int main(int argc, char **argv) 29 | { 30 | // Create session object and add all compatible devices them to the 31 | // session. Note that this currently doesn't handle returned errors. 32 | Session* session = new Session(); 33 | session->add_all(); 34 | 35 | if (session->m_devices.size() == 0) { 36 | cerr << "Plug in a device." << endl; 37 | exit(1); 38 | } 39 | 40 | // Grab the first device from the session. 41 | auto dev = *(session->m_devices.begin()); 42 | 43 | // Data to be read from the device is formatted into a vector of four 44 | // floats in an array, specifically in the format 45 | // . 46 | std::vector> rxbuf; 47 | 48 | while (true) { 49 | // Run the session for 1024 samples. 50 | session->run(1024); 51 | try { 52 | // Read 1024 samples at a time from the device. 53 | // Note that the timeout (3rd parameter to read() defaults to 0 54 | // (nonblocking mode) so the number of samples returned won't 55 | // necessarily be 1024. 56 | dev->read(rxbuf, 1024); 57 | } catch (const std::system_error& e) { 58 | if (!_isatty(1)) { 59 | // Exit on dropped samples when stdout isn't a terminal. 60 | cerr << "sample(s) dropped: " << e.what() << endl; 61 | exit(1); 62 | } 63 | } 64 | 65 | for (auto i: rxbuf) { 66 | // Overwrite a singular line if stdout is a terminal, otherwise 67 | // output line by line. 68 | if (_isatty(1)) 69 | printf("\r% 6f % 6f % 6f % 6f", i[0], i[1], i[2], i[3]); 70 | else 71 | printf("% 6f % 6f % 6f % 6f\n", i[0], i[1], i[2], i[3]); 72 | } 73 | }; 74 | } 75 | -------------------------------------------------------------------------------- /examples/smu_stream_multi_out.cpp: -------------------------------------------------------------------------------- 1 | // Simple test for reading data from multiple devices. 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | using std::cout; 14 | using std::cerr; 15 | using std::endl; 16 | 17 | using namespace smu; 18 | 19 | void signalHandler(int signum) 20 | { 21 | cerr << endl << "sleeping for a bit to cause an overflow exception" << endl; 22 | std::this_thread::sleep_for(std::chrono::milliseconds(250)); 23 | } 24 | 25 | int main(int argc, char **argv) 26 | { 27 | #ifndef _WIN32 28 | // Make SIGQUIT force sample drops. 29 | signal(SIGQUIT, signalHandler); 30 | #endif 31 | 32 | // Create session object and add all compatible devices them to the 33 | // session. Note that this currently doesn't handle returned errors. 34 | Session* session = new Session(); 35 | session->add_all(); 36 | 37 | if (session->m_devices.size() < 2) { 38 | cerr << "This test is for multiple device setups, plug in more devices." << endl; 39 | exit(1); 40 | } 41 | 42 | // Grab the first device from the session. 43 | auto dev = *(session->m_devices.begin()); 44 | 45 | // Run session at the default device rate. 46 | session->configure(dev->get_default_rate()); 47 | 48 | // Run session in continuous mode. 49 | session->start(0); 50 | 51 | // Data to be read from the device is formatted into a vector of four 52 | // floats in an array, specifically in the format 53 | // . 54 | std::vector> buf; 55 | float v; 56 | int i; 57 | 58 | while (true) { 59 | i = 0; 60 | for (auto dev: session->m_devices) { 61 | try { 62 | // Read 1024 samples at a time from the device. 63 | // Note that the timeout (3rd parameter to read() defaults to 0 64 | // (nonblocking mode) so the number of samples returned won't 65 | // necessarily be 1024. 66 | dev->read(buf, 1024); 67 | } catch (const std::system_error& e) { 68 | // Exit on dropped samples. 69 | cerr << "sample(s) dropped!" << endl; 70 | exit(1); 71 | } 72 | 73 | // Iterate over all returned samples, doesn't have to be 1024. 74 | for (auto a: buf) { 75 | v = a[0] + a[1] + a[2] + a[3]; 76 | // Can force samples to drop on slower setups and/or slower 77 | // terminals, redirect stdout to alleviate this. 78 | printf("device %i: %f\n", i, v); 79 | } 80 | i++; 81 | } 82 | }; 83 | } 84 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | option(BUILD_STATIC_LIB "Build static library" OFF) 2 | option(OSX_PACKAGE "Do not create an OSX package by default" OFF) 3 | 4 | # get rid of Visual Studio's default "Debug" and "Release" output directories 5 | if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}) 7 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}) 8 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}) 9 | 10 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}) 11 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}) 12 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}) 13 | 14 | endif() 15 | if(NOT WIN32) 16 | find_library(PTHREAD_LIBRARIES pthread) 17 | mark_as_advanced(PTHREAD_LIBRARIES) 18 | set(LIBS_TO_LINK ${PTHREAD_LIBRARIES}) 19 | endif() 20 | 21 | if(APPLE) 22 | # build universal binaries by default 23 | set(CMAKE_OSX_ARCHITECTURES "x86_64") 24 | endif() 25 | 26 | # use pkg-config for everything that's not Windows 27 | if(NOT WIN32) 28 | include(FindPkgConfig REQUIRED) 29 | pkg_check_modules(LIBUSB REQUIRED libusb-1.0) 30 | link_directories(${LINK_DIRECTORIES} ${LIBUSB_LIBRARY_DIRS}) 31 | else() 32 | find_library(LIBUSB_LIBRARIES usb-1.0) 33 | find_path(LIBUSB_INCLUDE_DIRS libusb-1.0/libusb.h) 34 | endif() 35 | 36 | if(MSVC) 37 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) 38 | endif() 39 | 40 | # Classify libusb headers as system headers to suppress various warnings, 41 | # e.g. http://www.libusb.org/ticket/2. 42 | include_directories(SYSTEM ${LIBUSB_INCLUDE_DIRS}) 43 | 44 | # only boost headers are needed, no runtime libraries 45 | find_package(Boost "1.53" REQUIRED) 46 | include_directories(${Boost_INCLUDE_DIRS}) 47 | 48 | option(USE_OpenMP "Use OpenMP" ON) 49 | if(USE_OpenMP) 50 | find_package(OpenMP) 51 | if(OPENMP_FOUND) 52 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 53 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") 54 | endif() 55 | endif() 56 | 57 | set(LIBS_TO_LINK ${LIBUSB_LIBRARIES}) 58 | if(CMAKE_COMPILER_IS_GNUCXX) 59 | SET(LIBS_TO_LINK ${LIBS_TO_LINK} m) 60 | endif() 61 | file(GLOB LIBSMU_CPPFILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 62 | file(GLOB LIBSMU_HEADERS ${CMAKE_SOURCE_DIR}/include/libsmu/*.hpp) 63 | 64 | add_library(smu SHARED ${LIBSMU_CPPFILES} ${LIBSMU_HEADERS}) 65 | set_target_properties(smu PROPERTIES 66 | VERSION ${LIBSMU_VERSION} 67 | SOVERSION ${LIBSMU_VERSION_MAJOR} 68 | PUBLIC_HEADER "${LIBSMU_HEADERS}" 69 | ) 70 | target_link_libraries(smu ${LIBS_TO_LINK}) 71 | 72 | # build static library if requested 73 | if(BUILD_STATIC_LIB) 74 | add_library(smu-static STATIC ${LIBSMU_CPPFILES} ${LIBSMU_HEADERS}) 75 | set_target_properties(smu-static PROPERTIES OUTPUT_NAME smu) 76 | target_link_libraries(smu-static ${LIBS_TO_LINK}) 77 | install(TARGETS smu-static 78 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") 79 | endif() 80 | 81 | # force outputted library name for Visual Studio 82 | if (MSVC) 83 | set_target_properties(smu PROPERTIES OUTPUT_NAME libsmu) 84 | endif() 85 | if (APPLE) 86 | set_target_properties(smu PROPERTIES FRAMEWORK FALSE) 87 | if(OSX_PACKAGE) 88 | set_target_properties(smu PROPERTIES FRAMEWORK TRUE OUTPUT_NAME libsmu) 89 | set(OSX_INSTALL_FRAMEWORKSDIR "/Library/Frameworks" CACHE STRING "Installation directory for frameworks") 90 | get_filename_component(OSX_INSTALL_FRAMEWORKSDIR "${OSX_INSTALL_FRAMEWORKSDIR}" REALPATH BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") 91 | 92 | set(CMAKE_MACOSX_RPATH OFF) 93 | set(SKIP_INSTALL_ALL ${OSX_PACKAGE}) 94 | endif() 95 | endif() 96 | 97 | install(TARGETS smu 98 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" 99 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 100 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 101 | PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/libsmu" 102 | FRAMEWORK DESTINATION "${OSX_INSTALL_FRAMEWORKSDIR}") 103 | -------------------------------------------------------------------------------- /src/cli/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(..) 2 | 3 | include(CheckFunctionExists) 4 | CHECK_FUNCTION_EXISTS(getopt GETOPT_FOUND) 5 | 6 | if(NOT WIN32) 7 | link_directories(${LINK_DIRECTORIES} ${LIBUSB_LIBRARY_DIRS}) 8 | endif() 9 | 10 | if(GETOPT_FOUND) 11 | add_executable(smu_bin smu.cpp) 12 | else(GETOPT_FOUND) 13 | # use internal getopt implementation 14 | add_executable(smu_bin smu.cpp getopt_internal.c) 15 | add_definitions(-DUSE_CUSTOM_GETOPT=1) 16 | endif(GETOPT_FOUND) 17 | 18 | include_directories(SYSTEM ${LIBUSB_INCLUDE_DIRS}) 19 | set_target_properties(smu_bin PROPERTIES OUTPUT_NAME smu) 20 | target_link_libraries(smu_bin smu) 21 | 22 | if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 23 | install(TARGETS smu_bin RUNTIME DESTINATION ${OSX_INSTALL_FRAMEWORKSDIR}/libsmu.framework/cli) 24 | else() 25 | install(TARGETS smu_bin RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 26 | endif() 27 | 28 | # Make the test targets available to the main CMakeLists.txt 29 | set(SMUCLI_TARGET smu_bin PARENT_SCOPE) -------------------------------------------------------------------------------- /src/cli/getopt_internal.h: -------------------------------------------------------------------------------- 1 | // https://github.com/kimgr/getopt_port 2 | // Copyright (c) 2012, Kim Gräsman 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are met: 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither the name of Kim Gräsman nor the 13 | // names of contributors may be used to endorse or promote products 14 | // derived from this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | // DISCLAIMED. IN NO EVENT SHALL KIM GRÄSMAN BE LIABLE FOR ANY 20 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.#ifndef INCLUDED_GETOPT_PORT_H 26 | 27 | #ifndef INCLUDED_GETOPT_PORT_H 28 | #define INCLUDED_GETOPT_PORT_H 29 | 30 | #if defined(__cplusplus) 31 | extern "C" { 32 | #endif 33 | 34 | extern const int no_argument; 35 | extern const int required_argument; 36 | extern const int optional_argument; 37 | 38 | extern char* optarg; 39 | extern int optind, opterr, optopt; 40 | 41 | struct option { 42 | const char* name; 43 | int has_arg; 44 | int* flag; 45 | int val; 46 | }; 47 | 48 | int getopt(int argc, char* const argv[], const char* optstring); 49 | 50 | int getopt_long(int argc, char* const argv[], 51 | const char* optstring, const struct option* longopts, int* longindex); 52 | 53 | #if defined(__cplusplus) 54 | } 55 | #endif 56 | 57 | #endif // INCLUDED_GETOPT_PORT_H 58 | -------------------------------------------------------------------------------- /src/cli/smu.cpp: -------------------------------------------------------------------------------- 1 | // Released under the terms of the BSD License 2 | // (C) 2014-2016 3 | // Analog Devices, Inc. 4 | // Kevin Mehall 5 | // Ian Daniher 6 | 7 | #ifdef USE_CUSTOM_GETOPT 8 | #include "getopt_internal.h" 9 | #else 10 | #include 11 | #endif 12 | 13 | #if _WIN32 14 | #include 15 | #else 16 | #include 17 | #endif 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | #ifndef _WIN32 30 | int (*_isatty)(int) = &isatty; 31 | #endif 32 | 33 | using std::cout; 34 | using std::cerr; 35 | using std::endl; 36 | 37 | using namespace smu; 38 | 39 | static void list_devices(Session* session) 40 | { 41 | if (session->m_devices.empty()) { 42 | cerr << "smu: no supported devices plugged in" << endl; 43 | } else { 44 | for (auto dev: session->m_devices) { 45 | printf("%s: serial %s : fw %s : hw %s\n", 46 | dev->info()->label, dev->m_serial.c_str(), 47 | dev->m_fwver.c_str(), dev->m_hwver.c_str()); 48 | } 49 | } 50 | } 51 | 52 | static void display_usage(void) 53 | { 54 | printf("smu: utility for managing M1K devices\n" 55 | "\n" 56 | " -h, --help print this help message and exit\n" 57 | " --version show libsmu version\n" 58 | " -l, --list-devices list supported devices currently attached to the system\n" 59 | " -p, --hotplug-devices simple session device hotplug testing\n" 60 | " -s, --stream-samples stream samples to stdout from a single attached device\n" 61 | " -d, --display-calibration display calibration data from all attached devices\n" 62 | " -r, --reset-calibration reset calibration data to the defaults on all attached devices\n" 63 | " -w, --write-calibration write calibration data to a single attached device\n" 64 | " -f, --flash flash firmware image to a single attached device\n"); 65 | } 66 | 67 | static void stream_samples(Session* session) 68 | { 69 | auto dev = *(session->m_devices.begin()); 70 | auto dev_info = dev->info(); 71 | for (unsigned ch_i = 0; ch_i < dev_info->channel_count; ch_i++) { 72 | dev->set_mode(ch_i, HI_Z); 73 | } 74 | 75 | session->configure(dev->get_default_rate()); 76 | session->start(0); 77 | std::vector> buf; 78 | unsigned dev_index; 79 | 80 | while (true) { 81 | dev_index = 0; 82 | for (auto dev: session->m_devices) { 83 | try { 84 | dev->read(buf, 1000, -1); 85 | } catch (const std::runtime_error& e) { 86 | // Only error out if stdout isn't a tty, otherwise it usually 87 | // can't keep up anyway. 88 | if (!_isatty(1)) { 89 | cerr << "smu: stopping stream: " << e.what() << endl; 90 | return; 91 | } 92 | } 93 | 94 | for (auto x: buf) { 95 | printf("dev %u: %f %f %f %f\n", dev_index, x[0], x[1], x[2], x[3]); 96 | } 97 | dev_index++; 98 | } 99 | }; 100 | } 101 | 102 | int write_calibration(Session* session, const char *file) 103 | { 104 | int ret; 105 | auto dev = *(session->m_devices.begin()); 106 | ret = dev->write_calibration(file); 107 | if (ret < 0) { 108 | if (ret == -EINVAL) 109 | cerr << "smu: invalid calibration data format" << endl; 110 | else if (ret == -EPIPE) 111 | cerr << "smu: firmware version doesn't support calibration (update to 2.06 or later)" << endl; 112 | else 113 | perror("smu: failed to write calibration data"); 114 | return 1; 115 | } 116 | return 0; 117 | } 118 | 119 | void display_calibration(Session* session) 120 | { 121 | std::vector> cal; 122 | for (auto dev: session->m_devices) { 123 | printf("%s: serial %s: fw %s: hw %s\n", 124 | dev->info()->label, dev->m_serial.c_str(), 125 | dev->m_fwver.c_str(), dev->m_hwver.c_str()); 126 | dev->calibration(&cal); 127 | for (int i = 0; i < 8; i++) { 128 | switch (i) { 129 | case 0: printf(" Channel A, measure V\n"); break; 130 | case 1: printf(" Channel A, measure I\n"); break; 131 | case 2: printf(" Channel A, source V\n"); break; 132 | case 3: printf(" Channel A, source I\n"); break; 133 | case 4: printf(" Channel B, measure V\n"); break; 134 | case 5: printf(" Channel B, measure I\n"); break; 135 | case 6: printf(" Channel B, source V\n"); break; 136 | case 7: printf(" Channel B, source I\n"); break; 137 | } 138 | printf(" offset: %.4f\n", cal[i][0]); 139 | printf(" p gain: %.4f\n", cal[i][1]); 140 | printf(" n gain: %.4f\n", cal[i][2]); 141 | } 142 | printf("\n"); 143 | } 144 | } 145 | 146 | int reset_calibration(Session* session) 147 | { 148 | int ret; 149 | for (auto dev: session->m_devices) { 150 | ret = dev->write_calibration(NULL); 151 | if (ret < 0) { 152 | if (ret == -EPIPE) 153 | cerr << "smu: firmware version doesn't support calibration (update to 2.06 or later)" << endl; 154 | else 155 | perror("smu: failed to reset calibration data"); 156 | return 1; 157 | } 158 | } 159 | return 0; 160 | } 161 | 162 | int main(int argc, char **argv) 163 | { 164 | int opt; 165 | int option_index = 0; 166 | 167 | // display usage info if no arguments are specified 168 | if (argc == 1) { 169 | display_usage(); 170 | return EXIT_FAILURE; 171 | } 172 | 173 | Session* session = new Session(); 174 | // add all available devices to the session at startup 175 | if (session->add_all() < 0) { 176 | perror("smu: error initializing session"); 177 | return 1; 178 | } 179 | 180 | // map long options to short ones 181 | static struct option long_options[] = { 182 | {"help", no_argument, 0, 'a'}, 183 | {"version", no_argument, 0, 'v'}, 184 | {"list", no_argument, 0, 'l'}, 185 | {"stream", no_argument, 0, 's'}, 186 | {"display-calibration", no_argument, 0, 'd'}, 187 | {"reset-calibration", no_argument, 0, 'r'}, 188 | {"write-calibration", required_argument, 0, 'w'}, 189 | {"flash", required_argument, 0, 'f'}, 190 | {0, 0, 0, 0} 191 | }; 192 | 193 | while ((opt = getopt_long(argc, argv, "hvplsdrw:f:", 194 | long_options, &option_index)) != -1) { 195 | switch (opt) { 196 | case 'l': 197 | // list attached device info 198 | list_devices(session); 199 | break; 200 | case 's': 201 | // stream samples from an attached device to stdout 202 | if (!session->m_devices.empty()) { 203 | stream_samples(session); 204 | } else { 205 | cerr << "smu: no supported devices plugged in" << endl; 206 | return EXIT_FAILURE; 207 | } 208 | break; 209 | case 'd': 210 | // display calibration data from all attached m1k devices 211 | display_calibration(session); 212 | break; 213 | case 'r': 214 | // reset calibration data of all attached m1k devices 215 | if (session->m_devices.empty()) { 216 | cerr << "smu: no supported devices plugged in" << endl; 217 | return EXIT_FAILURE; 218 | } 219 | if (reset_calibration(session)) { 220 | return EXIT_FAILURE; 221 | } 222 | cout << "smu: successfully reset calibration data" << endl; 223 | break; 224 | case 'w': 225 | // write calibration data to a single attached m1k device 226 | if (session->m_devices.empty()) { 227 | cerr << "smu: no supported devices plugged in" << endl; 228 | return EXIT_FAILURE; 229 | } else if (session->m_devices.size() > 1) { 230 | cerr << "smu: multiple devices attached, calibration only works on a single device" << endl; 231 | cerr << "Please detach all devices except the one targeted for calibration." << endl; 232 | return EXIT_FAILURE; 233 | } 234 | if (write_calibration(session, optarg)) 235 | return EXIT_FAILURE; 236 | cout << "smu: successfully updated calibration data" << endl; 237 | break; 238 | case 'f': 239 | // flash firmware image to all attached m1k devices 240 | try { 241 | session->flash_firmware(optarg); 242 | } catch (const std::exception& e) { 243 | cout << "smu: failed updating firmware: " << e.what() << endl; 244 | return EXIT_FAILURE; 245 | } 246 | cout << "smu: successfully updated firmware" << endl; 247 | cout << "Please unplug and replug the device(s) to finish the process." << endl; 248 | break; 249 | case 'h': 250 | display_usage(); 251 | break; 252 | case 'v': 253 | cout << LIBSMU_VERSION_STR << endl; 254 | break; 255 | default: 256 | display_usage(); 257 | return EXIT_FAILURE; 258 | } 259 | } 260 | 261 | delete session; 262 | return EXIT_SUCCESS; 263 | } 264 | -------------------------------------------------------------------------------- /src/debug.hpp: -------------------------------------------------------------------------------- 1 | // Released under the terms of the BSD License 2 | // (C) 2014-2016, Analog Devices, Inc 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #ifdef DEBUG_BUILD 9 | #define DEBUG_TEST 1 10 | #else 11 | #define DEBUG_TEST 0 12 | #endif 13 | 14 | #define DEBUG(...) do { if (DEBUG_TEST) fprintf(stderr, __VA_ARGS__); } while(0); 15 | -------------------------------------------------------------------------------- /src/device.cpp: -------------------------------------------------------------------------------- 1 | // Released under the terms of the BSD License 2 | // (C) 2014-2016 3 | // Analog Devices, Inc. 4 | // Kevin Mehall 5 | // Ian Daniher 6 | 7 | #include 8 | 9 | #include "debug.hpp" 10 | 11 | #include 12 | 13 | using namespace smu; 14 | 15 | Device::Device(Session* s, libusb_device* d, libusb_device_handle *h, 16 | const char* hwver, const char* fwver, const char* serial): 17 | m_hwver(hwver), m_fwver(fwver), m_serial(serial), m_session(s), m_usb_dev(d), m_usb(h) 18 | { 19 | } 20 | 21 | int Device::ctrl_transfer(unsigned bmRequestType, unsigned bRequest, unsigned wValue, unsigned wIndex, unsigned char *data, unsigned wLength, unsigned timeout) 22 | { 23 | return libusb_control_transfer(m_usb, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout); 24 | } 25 | -------------------------------------------------------------------------------- /src/device_m1000.hpp: -------------------------------------------------------------------------------- 1 | // Released under the terms of the BSD License 2 | // (C) 2014-2016 3 | // Analog Devices, Inc. 4 | // Kevin Mehall 5 | // Ian Daniher 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include "debug.hpp" 23 | #include "usb.hpp" 24 | #include 25 | 26 | #define EEPROM_VALID 0x01ee02dd 27 | 28 | static const sl_signal_info m1000_signal_info[2] = { 29 | {"Voltage", 0x7, 0x2, 0.0, 5.0, 5.0/65536}, 30 | {"Current", 0x6, 0x4, -0.2, 0.2, 0.4/65536}, 31 | }; 32 | 33 | // Calibration data format stored in the device's EEPROM. 34 | struct EEPROM_cal { 35 | uint32_t eeprom_valid; 36 | float offset[8]; 37 | float gain_p[8]; 38 | float gain_n[8]; 39 | }; 40 | 41 | namespace smu { 42 | extern "C" void LIBUSB_CALL m1000_in_completion(libusb_transfer *t); 43 | extern "C" void LIBUSB_CALL m1000_out_completion(libusb_transfer *t); 44 | 45 | class M1000_Device: public Device { 46 | public: 47 | // Handle incoming USB transfers. 48 | void in_completion(libusb_transfer *t); 49 | // Handle outgoing USB transfers. 50 | void out_completion(libusb_transfer *t); 51 | 52 | // Override virtual methods of the base Device class. 53 | const sl_device_info* info() const override; 54 | const sl_channel_info* channel_info(unsigned channel) const override; 55 | Signal* signal(unsigned channel, unsigned signal) override; 56 | int set_mode(unsigned channel, unsigned mode, bool restore = true) override; 57 | int get_mode(unsigned channel) override; 58 | int fwver_sem(std::array& components) override; 59 | int set_serial(std::string serial) override; 60 | ssize_t read(std::vector>& buf, size_t samples, int timeout,bool skipsamples) override; 61 | int write(std::vector& buf, unsigned channel, bool cyclic) override; 62 | void flush(int channel, bool read) override; 63 | int sync() override; 64 | int write_calibration(const char* cal_file_name) override; 65 | int read_calibration() override; 66 | void calibration(std::vector>* cal) override; 67 | int samba_mode() override; 68 | int set_led(unsigned leds) override; 69 | int set_adc_mux(unsigned adc_mux); // New function added; 70 | void set_usb_device_addr(std::pair usb_addr); 71 | 72 | protected: 73 | friend class Session; 74 | friend void LIBUSB_CALL m1000_in_completion(libusb_transfer *t); 75 | friend void LIBUSB_CALL m1000_out_completion(libusb_transfer *t); 76 | 77 | Signal m_signals[2][2]; 78 | unsigned m_mode[2]; 79 | 80 | ~M1000_Device(); 81 | 82 | // Queue with ~100ms worth of incoming sample values at the default rate. 83 | // The sample values are formatted in arrays of four values, 84 | // specifically in the following order: . 85 | boost::lockfree::spsc_queue> m_in_samples_q; 86 | 87 | // Number of samples available for reading/writing. 88 | // TODO: Drop this when stable distros contain >= boost-1.57 with 89 | // read_available() and write_available() calls for the spsc queue. 90 | std::atomic m_in_samples_avail; 91 | std::atomic m_out_samples_avail[2] = {}; 92 | 93 | // Queues with ~100ms worth of outgoing sample values for both channels at the default rate. 94 | boost::lockfree::spsc_queue _out_samples_a_q; 95 | boost::lockfree::spsc_queue _out_samples_b_q; 96 | 97 | // Reference the queues above via an array. 98 | boost::lockfree::spsc_queue* m_out_samples_q[2] = {&_out_samples_a_q, &_out_samples_b_q}; 99 | 100 | // Write buffers, one for each channel. 101 | std::vector m_out_samples_buf[2]; 102 | bool m_out_samples_buf_cyclic[2]{false,false}; 103 | std::mutex m_out_samples_mtx[2]; 104 | std::mutex m_out_samples_state_mtx[2]; 105 | std::condition_variable m_out_samples_cv[2]; 106 | 107 | // Used for write thread signaling, initialized to zero. If greater 108 | // than zero the related write thread will stop using its current 109 | // buffer and wait for another to be submitted. If less than zero, the 110 | // write thread will return -- used to signal the thread to exit. 111 | std::atomic m_out_samples_stop[2] = {}; 112 | 113 | // Threads used to write outgoing samples values to the queues above. 114 | std::thread m_out_samples_thr[2]; 115 | 116 | // Used to keep initial USB transfer kickoff thread alive on Windows 117 | // until off() is called. 118 | std::condition_variable_any m_usb_cv; 119 | 120 | M1000_Device(Session* s, libusb_device* d, libusb_device_handle* h, 121 | const char* hw_version, const char* fw_version, const char* serial): 122 | Device(s, d, h, hw_version, fw_version, serial), 123 | m_signals { 124 | {Signal(&m1000_signal_info[0]), Signal(&m1000_signal_info[1])}, 125 | {Signal(&m1000_signal_info[0]), Signal(&m1000_signal_info[1])}, 126 | }, 127 | m_mode{HI_Z,HI_Z}, 128 | m_in_samples_q{s->m_queue_size}, 129 | m_in_samples_avail{0}, 130 | _out_samples_a_q{s->m_queue_size}, 131 | _out_samples_b_q{s->m_queue_size} 132 | {} 133 | 134 | // Reformat received data, performs integer to float conversion. 135 | void handle_in_transfer(libusb_transfer* t); 136 | 137 | // Reformat outgoing data, performs float to integer conversion. 138 | void handle_out_transfer(libusb_transfer* t); 139 | 140 | // Submit data transfers to usb thread, from host to device. 141 | int submit_out_transfer(libusb_transfer* t); 142 | 143 | // Submit data transfers to usb thread, from device to host. 144 | int submit_in_transfer(libusb_transfer* t); 145 | 146 | // Encode output samples. 147 | // @param chan Target channel index. 148 | // @param peek Use the first element from the write queue without discarding it. 149 | uint16_t encode_out(unsigned chan, bool peek = false); 150 | 151 | /// @brief Read ADM1177 status. 152 | /// See http://www.analog.com/media/en/technical-documentation/data-sheets/ADM1177.pdf for technical documentation. 153 | /// @return If an overcurrent event occurred in the most recent data request, 1 is returned. 154 | /// @return If no overcurrent event occurred, 0 is returned. 155 | /// @return On error, a negative integer is returned relating to the error status. 156 | int read_adm1177(); 157 | 158 | // Most recent value written to the output of each channel initialized 159 | // to an invalid value in order to know when data hasn't been written 160 | // to a channel. 161 | float m_previous_output[2] = {std::nanf(""), std::nanf("")}; 162 | 163 | // Next value available for the output of each channel. 164 | float m_next_output[2] = {std::nanf(""), std::nanf("")}; 165 | 166 | // USB start of frame packet number. 167 | uint16_t m_sof_start = 0; 168 | 169 | // clock cycles per sample 170 | int m_sam_per = 0; 171 | // minimum clock cycles per sample (100ksps) 172 | const int m_min_per = 240; 173 | // maximum clock cycles per sample (~1024 samples/s) 174 | const int m_max_per = 24000; 175 | 176 | unsigned m_samples_per_transfer; 177 | unsigned m_packets_per_transfer; 178 | Transfers m_in_transfers; 179 | Transfers m_out_transfers; 180 | 181 | // Device calibration data. 182 | EEPROM_cal m_cal; 183 | 184 | // Number of requested samples. 185 | uint64_t m_sample_count = 0; 186 | 187 | // Override virtual methods of the base Device class. 188 | int get_default_rate() override; 189 | int claim() override; 190 | int release() override; 191 | int configure(unsigned sampleRate) override; 192 | int on() override; 193 | int off() override; 194 | int cancel() override; 195 | int run(uint64_t samples) override; 196 | }; 197 | 198 | } 199 | -------------------------------------------------------------------------------- /src/signal.cpp: -------------------------------------------------------------------------------- 1 | // Released under the terms of the BSD License 2 | // (C) 2014-2017 3 | // Analog Devices, Inc 4 | // Kevin Mehall 5 | // Ian Daniher 6 | 7 | 8 | #include 9 | #include 10 | 11 | #include "debug.hpp" 12 | #include 13 | #include 14 | 15 | const double PI = boost::math::constants::pi(); 16 | 17 | using namespace smu; 18 | 19 | void Signal::constant(std::vector& buf, uint64_t samples, float val) 20 | { 21 | m_src = CONSTANT; 22 | m_src_v1 = val; 23 | 24 | for (unsigned i = 0; i < samples; i++) { 25 | buf.push_back(get_sample()); 26 | } 27 | } 28 | 29 | void Signal::square(std::vector& buf, uint64_t samples, float midpoint, float peak, double period, double phase, double duty) 30 | { 31 | m_src = SQUARE; 32 | m_src_phase = phase; 33 | m_src_period = period; 34 | m_src_v1 = midpoint; 35 | m_src_v2 = peak; 36 | m_src_duty = duty; 37 | 38 | for (unsigned i = 0; i < samples; i++) { 39 | buf.push_back(get_sample()); 40 | } 41 | } 42 | 43 | void Signal::sawtooth(std::vector& buf, uint64_t samples, float midpoint, float peak, double period, double phase) 44 | { 45 | m_src = SAWTOOTH; 46 | m_src_phase = phase; 47 | m_src_period = period; 48 | m_src_v1 = midpoint; 49 | m_src_v2 = peak; 50 | 51 | for (unsigned i = 0; i < samples; i++) { 52 | buf.push_back(get_sample()); 53 | } 54 | } 55 | 56 | void Signal::stairstep(std::vector& buf, uint64_t samples, float midpoint, float peak, double period, double phase) 57 | { 58 | m_src = STAIRSTEP; 59 | m_src_phase = phase; 60 | m_src_period = period; 61 | m_src_v1 = midpoint; 62 | m_src_v2 = peak; 63 | 64 | for (unsigned i = 0; i < samples; i++) { 65 | buf.push_back(get_sample()); 66 | } 67 | } 68 | 69 | void Signal::sine(std::vector& buf, uint64_t samples, float midpoint, float peak, double period, double phase) 70 | { 71 | m_src = SINE; 72 | m_src_phase = phase; 73 | m_src_period = period; 74 | m_src_v1 = midpoint; 75 | m_src_v2 = peak; 76 | 77 | for (unsigned i = 0; i < samples; i++) { 78 | buf.push_back(get_sample()); 79 | } 80 | } 81 | 82 | void Signal::triangle(std::vector& buf, uint64_t samples, float midpoint, float peak, double period, double phase) 83 | { 84 | m_src = TRIANGLE; 85 | m_src_phase = phase; 86 | m_src_period = period; 87 | m_src_v1 = midpoint; 88 | m_src_v2 = peak; 89 | 90 | for (unsigned i = 0; i < samples; i++) { 91 | buf.push_back(get_sample()); 92 | } 93 | } 94 | 95 | // Internal function to generate waveform values. 96 | float Signal::get_sample() 97 | { 98 | switch (m_src) { 99 | case CONSTANT: 100 | return m_src_v1; 101 | 102 | case SQUARE: 103 | case SAWTOOTH: 104 | case SINE: 105 | case STAIRSTEP: 106 | case TRIANGLE: { 107 | 108 | auto peak_to_peak = m_src_v2 - m_src_v1; 109 | auto phase = m_src_phase; 110 | auto norm_phase = phase / m_src_period; 111 | if (norm_phase < 0) 112 | norm_phase += 1; 113 | m_src_phase = fmod(m_src_phase + 1, m_src_period); 114 | 115 | switch (m_src) { 116 | case SQUARE: 117 | return (norm_phase < m_src_duty) ? m_src_v1 : m_src_v2; 118 | 119 | case SAWTOOTH: { 120 | float int_period = truncf(m_src_period); 121 | float int_phase = truncf(phase); 122 | float frac_period = m_src_period - int_period; 123 | float frac_phase = phase - int_phase; 124 | float max_int_phase; 125 | 126 | // Get the integer part of the maximum value phase will be set at. 127 | // For example: 128 | // - If m_src_period = 100.6, phase first value = 0.3 then 129 | // phase will take values: 0.3, 1.3, ..., 98.3, 99.3, 100.3 130 | // - If m_src_period = 100.6, phase first value = 0.7 then 131 | // phase will take values: 0.7, 1.7, ..., 98.7, 99.7 132 | if (frac_period <= frac_phase) 133 | max_int_phase = int_period - 1; 134 | else 135 | max_int_phase = int_period; 136 | auto nphase = int_phase / max_int_phase; 137 | if(nphase < 0) 138 | nphase += 1; 139 | return m_src_v2 - nphase * peak_to_peak; 140 | } 141 | 142 | case STAIRSTEP: 143 | return m_src_v2 - floorf(norm_phase * 10) * peak_to_peak / 9; 144 | 145 | case SINE: 146 | return m_src_v1 + (1 + cos(norm_phase * 2 * PI)) * peak_to_peak / 2; 147 | 148 | case TRIANGLE: 149 | return m_src_v1 + fabs(1 - norm_phase * 2) * peak_to_peak; 150 | default: 151 | throw std::runtime_error("unknown waveform"); 152 | } 153 | } 154 | } 155 | throw std::runtime_error("unknown waveform"); 156 | } 157 | -------------------------------------------------------------------------------- /src/usb.cpp: -------------------------------------------------------------------------------- 1 | // Released under the terms of the BSD License 2 | // (C) 2014-2016 3 | // Analog Devices, Inc 4 | // Kevin Mehall 5 | // Ian Daniher 6 | 7 | #include "usb.hpp" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #include "debug.hpp" 19 | 20 | // Mapping of libusb error codes to system errnos. 21 | static std::map libusb_to_errno_map = { 22 | {LIBUSB_ERROR_INVALID_PARAM, EINVAL}, 23 | {LIBUSB_ERROR_ACCESS, EACCES}, 24 | {LIBUSB_ERROR_NO_DEVICE, ENODEV}, 25 | {LIBUSB_ERROR_NOT_FOUND, ENXIO}, 26 | {LIBUSB_ERROR_BUSY, EBUSY}, 27 | {LIBUSB_ERROR_TIMEOUT, ETIMEDOUT}, 28 | {LIBUSB_ERROR_OVERFLOW, EIO}, 29 | {LIBUSB_ERROR_PIPE, EPIPE}, 30 | {LIBUSB_ERROR_INTERRUPTED, EINTR}, 31 | {LIBUSB_ERROR_NO_MEM, ENOMEM}, 32 | {LIBUSB_ERROR_NOT_SUPPORTED, ENOSYS}, 33 | }; 34 | 35 | unsigned int libusb_to_errno(int libusb_err) 36 | { 37 | // All libusb errors that require mapping are negative. All non-negative 38 | // values either define success (0) or relate to a value that should be 39 | // returned as is. 40 | if (libusb_err >= 0) 41 | return libusb_err; 42 | 43 | auto sys_err = libusb_to_errno_map.find(libusb_err); 44 | if (sys_err != libusb_to_errno_map.end()) 45 | return sys_err->second; 46 | else 47 | return EIO; 48 | } 49 | 50 | int libusb_errno_or_zero(int ret) 51 | { 52 | if (ret < 0) 53 | return -libusb_to_errno(ret); 54 | else 55 | return 0; 56 | } 57 | 58 | int Transfers::alloc(unsigned count, libusb_device_handle* handle, 59 | unsigned char endpoint, unsigned char type, size_t buf_size, 60 | unsigned timeout, libusb_transfer_cb_fn callback, void* user_data) { 61 | clear(); 62 | m_transfers.resize(count, NULL); 63 | for (size_t i = 0; i < count; i++) { 64 | auto t = m_transfers[i] = libusb_alloc_transfer(0); 65 | if (!t) 66 | return -ENOMEM; 67 | t->dev_handle = handle; 68 | t->flags = LIBUSB_TRANSFER_FREE_BUFFER; 69 | t->endpoint = endpoint; 70 | t->type = type; 71 | t->timeout = timeout; 72 | t->length = buf_size; 73 | t->callback = callback; 74 | t->user_data = user_data; 75 | t->buffer = (uint8_t*) malloc(buf_size); 76 | if (!t->buffer) 77 | return -ENOMEM; 78 | } 79 | return 0; 80 | } 81 | 82 | void Transfers::failed(libusb_transfer* t) 83 | { 84 | for (int i = m_transfers.size(); i == 0; i--) { 85 | if (m_transfers[i] == t) { 86 | libusb_free_transfer(t); 87 | m_transfers.erase(m_transfers.begin()+i); 88 | } 89 | } 90 | } 91 | 92 | void Transfers::clear() 93 | { 94 | for (auto i: m_transfers) { 95 | libusb_free_transfer(i); 96 | } 97 | if (num_active != 0) 98 | DEBUG("%s: num_active after free: %i\n", __func__, num_active); 99 | m_transfers.clear(); 100 | } 101 | 102 | int Transfers::cancel() 103 | { 104 | int ret = 0; 105 | for (auto i: m_transfers) { 106 | if (num_active > 1) { 107 | ret = libusb_cancel_transfer(i); 108 | if (ret != 0 && ret != LIBUSB_ERROR_NOT_FOUND) { 109 | // abort if a transfer is not successfully cancelled 110 | DEBUG("%s: usb transfer cancelled with status: %s\n", __func__, libusb_error_name(ret)); 111 | return -libusb_to_errno(ret); 112 | } 113 | } 114 | } 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /src/usb.hpp: -------------------------------------------------------------------------------- 1 | // Released under the terms of the BSD License 2 | // (C) 2014-2016 3 | // Analog Devices, Inc 4 | // Kevin Mehall 5 | // Ian Daniher 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | // Map libusb error codes to system errnos. 17 | // If there is no match, EIO is returned. 18 | unsigned int libusb_to_errno(int libusb_err); 19 | 20 | // Map libusb error codes to negative system errnos and positive return values 21 | // (relating to the byte count of successful calls) to zero. 22 | int libusb_errno_or_zero(int libusb_err); 23 | 24 | // Wrapper for a collection of libusb transfers. 25 | class Transfers { 26 | public: 27 | ~Transfers() { clear(); } 28 | 29 | // Currently running usb tranfers. 30 | std::vector m_transfers; 31 | 32 | // Allocate a new collection of libusb transfers. 33 | // @return 0 if transfer allocation successful. 34 | // @return 1 if transfer allocation failed. 35 | int alloc(unsigned count, libusb_device_handle* handle, 36 | unsigned char endpoint, unsigned char type, size_t buf_size, 37 | unsigned timeout, libusb_transfer_cb_fn callback, void* user_data); 38 | 39 | // Remove a transfer that was not successfully submitted from the 40 | // collection of pending transfers. 41 | void failed(libusb_transfer* t); 42 | 43 | // Free and clear collection of libusb transfers. 44 | void clear(); 45 | 46 | // Signal cleanup - stop streaming and cleanup libusb state. 47 | // Loop over pending transfers, canceling each remaining transfer that 48 | // hasn't already been canceled. Returns an error code if one of the 49 | // transfers doesn't complete, or zero for success. 50 | int cancel(); 51 | 52 | // Number of current usb transfers. 53 | size_t size() { return m_transfers.size(); } 54 | 55 | // Treat m_transfers like an iterator. 56 | typedef std::vector::iterator iterator; 57 | typedef std::vector::const_iterator const_iterator; 58 | iterator begin() { return m_transfers.begin(); } 59 | const_iterator begin() const { return m_transfers.begin(); } 60 | iterator end() { return m_transfers.end(); } 61 | const_iterator end() const { return m_transfers.end(); } 62 | 63 | // Current number of pending transfers. 64 | int32_t num_active = 0; 65 | }; 66 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | enable_testing() 2 | include(CTest) 3 | 4 | if(WIN32 OR APPLE) 5 | # download and build gtest on Windows and OS X 6 | set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules) 7 | include(DownloadProject) 8 | download_project( 9 | PROJ googletest 10 | GIT_REPOSITORY https://github.com/google/googletest.git 11 | GIT_TAG v1.10.x 12 | ${UPDATE_DISCONNECTED_IF_AVAILABLE} 13 | ) 14 | 15 | # Prevent gtest from overriding our compiler/linker options 16 | # when building with Visual Studio 17 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 18 | 19 | add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) 20 | 21 | # When using CMake 2.8.11 or later, header path dependencies 22 | # are automatically added to the gtest and gmock targets. 23 | # For earlier CMake versions, we have to explicitly add the 24 | # required directories to the header search path ourselves. 25 | if (CMAKE_VERSION VERSION_LESS 2.8.11) 26 | include_directories( 27 | "${gtest_SOURCE_DIR}/include" 28 | "${gmock_SOURCE_DIR}/include") 29 | endif() 30 | 31 | set(GTEST_LIBS gtest gtest_main) 32 | else() 33 | find_package(GTest REQUIRED) 34 | include_directories(${GTEST_INCLUDE_DIRS}) 35 | set(GTEST_LIBS ${GTEST_BOTH_LIBRARIES}) 36 | endif() 37 | 38 | if(NOT WIN32) 39 | link_directories(${LINK_DIRECTORIES} ${LIBUSB_LIBRARY_DIRS}) 40 | endif() 41 | include_directories(SYSTEM ${LIBUSB_INCLUDE_DIRS}) 42 | 43 | # determine all tests from existing sources 44 | file(GLOB TEST_SRCS "test-*.cpp") 45 | foreach(TEST_SRC ${TEST_SRCS}) 46 | # pull the test name from the .cpp file name without the extension 47 | get_filename_component(TEST "${TEST_SRC}" NAME_WE) 48 | 49 | # create the test application 50 | add_executable(${TEST} ${TEST}.cpp) 51 | target_link_libraries(${TEST} smu ${GTEST_LIBS}) 52 | add_test(NAME ${TEST} COMMAND ${TEST}) 53 | 54 | # add test to the test list 55 | list(APPEND TESTS ${TEST}) 56 | endforeach(TEST_SRC) 57 | 58 | # don't run any tests in parallel 59 | set_tests_properties(${TESTS} PROPERTIES RUN_SERIAL TRUE) 60 | 61 | # add support for `make check` to build/run tests 62 | add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} -V DEPENDS ${TESTS}) 63 | -------------------------------------------------------------------------------- /tests/CMakeModules/DownloadProject.CMakeLists.cmake.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.2) 2 | 3 | project(${DL_ARGS_PROJ}-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(${DL_ARGS_PROJ}-download 7 | ${DL_ARGS_UNPARSED_ARGUMENTS} 8 | SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" 9 | BINARY_DIR "${DL_ARGS_BINARY_DIR}" 10 | CONFIGURE_COMMAND "" 11 | BUILD_COMMAND "" 12 | INSTALL_COMMAND "" 13 | TEST_COMMAND "" 14 | ) 15 | -------------------------------------------------------------------------------- /tests/CMakeModules/DownloadProject.cmake: -------------------------------------------------------------------------------- 1 | # MODULE: DownloadProject 2 | # 3 | # PROVIDES: 4 | # download_project( PROJ projectName 5 | # [PREFIX prefixDir] 6 | # [DOWNLOAD_DIR downloadDir] 7 | # [SOURCE_DIR srcDir] 8 | # [BINARY_DIR binDir] 9 | # [QUIET] 10 | # ... 11 | # ) 12 | # 13 | # Provides the ability to download and unpack a tarball, zip file, git repository, 14 | # etc. at configure time (i.e. when the cmake command is run). How the downloaded 15 | # and unpacked contents are used is up to the caller, but the motivating case is 16 | # to download source code which can then be included directly in the build with 17 | # add_subdirectory() after the call to download_project(). Source and build 18 | # directories are set up with this in mind. 19 | # 20 | # The PROJ argument is required. The projectName value will be used to construct 21 | # the following variables upon exit (obviously replace projectName with its actual 22 | # value): 23 | # 24 | # projectName_SOURCE_DIR 25 | # projectName_BINARY_DIR 26 | # 27 | # The SOURCE_DIR and BINARY_DIR arguments are optional and would not typically 28 | # need to be provided. They can be specified if you want the downloaded source 29 | # and build directories to be located in a specific place. The contents of 30 | # projectName_SOURCE_DIR and projectName_BINARY_DIR will be populated with the 31 | # locations used whether you provide SOURCE_DIR/BINARY_DIR or not. 32 | # 33 | # The DOWNLOAD_DIR argument does not normally need to be set. It controls the 34 | # location of the temporary CMake build used to perform the download. 35 | # 36 | # The PREFIX argument can be provided to change the base location of the default 37 | # values of DOWNLOAD_DIR, SOURCE_DIR and BINARY_DIR. If all of those three arguments 38 | # are provided, then PREFIX will have no effect. The default value for PREFIX is 39 | # CMAKE_BINARY_DIR. 40 | # 41 | # The QUIET option can be given if you do not want to show the output associated 42 | # with downloading the specified project. 43 | # 44 | # In addition to the above, any other options are passed through unmodified to 45 | # ExternalProject_Add() to perform the actual download, patch and update steps. 46 | # The following ExternalProject_Add() options are explicitly prohibited (they 47 | # are reserved for use by the download_project() command): 48 | # 49 | # CONFIGURE_COMMAND 50 | # BUILD_COMMAND 51 | # INSTALL_COMMAND 52 | # TEST_COMMAND 53 | # 54 | # Only those ExternalProject_Add() arguments which relate to downloading, patching 55 | # and updating of the project sources are intended to be used. Also note that at 56 | # least one set of download-related arguments are required. 57 | # 58 | # If using CMake 3.2 or later, the UPDATE_DISCONNECTED option can be used to 59 | # prevent a check at the remote end for changes every time CMake is run 60 | # after the first successful download. See the documentation of the ExternalProject 61 | # module for more information. It is likely you will want to use this option if it 62 | # is available to you. Note, however, that the ExternalProject implementation contains 63 | # bugs which result in incorrect handling of the UPDATE_DISCONNECTED option when 64 | # using the URL download method or when specifying a SOURCE_DIR with no download 65 | # method. Fixes for these have been created, the last of which is scheduled for 66 | # inclusion in CMake 3.8.0. Details can be found here: 67 | # 68 | # https://gitlab.kitware.com/cmake/cmake/commit/bdca68388bd57f8302d3c1d83d691034b7ffa70c 69 | # https://gitlab.kitware.com/cmake/cmake/issues/16428 70 | # 71 | # If you experience build errors related to the update step, consider avoiding 72 | # the use of UPDATE_DISCONNECTED. 73 | # 74 | # EXAMPLE USAGE: 75 | # 76 | # include(download_project.cmake) 77 | # download_project(PROJ googletest 78 | # GIT_REPOSITORY https://github.com/google/googletest.git 79 | # GIT_TAG master 80 | # UPDATE_DISCONNECTED 1 81 | # QUIET 82 | # ) 83 | # 84 | # add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) 85 | # 86 | #======================================================================================== 87 | 88 | 89 | set(_DownloadProjectDir "${CMAKE_CURRENT_LIST_DIR}") 90 | 91 | include(CMakeParseArguments) 92 | 93 | function(download_project) 94 | 95 | set(options QUIET) 96 | set(oneValueArgs 97 | PROJ 98 | PREFIX 99 | DOWNLOAD_DIR 100 | SOURCE_DIR 101 | BINARY_DIR 102 | # Prevent the following from being passed through 103 | CONFIGURE_COMMAND 104 | BUILD_COMMAND 105 | INSTALL_COMMAND 106 | TEST_COMMAND 107 | ) 108 | set(multiValueArgs "") 109 | 110 | cmake_parse_arguments(DL_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 111 | 112 | # Hide output if requested 113 | if (DL_ARGS_QUIET) 114 | set(OUTPUT_QUIET "OUTPUT_QUIET") 115 | else() 116 | unset(OUTPUT_QUIET) 117 | message(STATUS "Downloading/updating ${DL_ARGS_PROJ}") 118 | endif() 119 | 120 | # Set up where we will put our temporary CMakeLists.txt file and also 121 | # the base point below which the default source and binary dirs will be 122 | if (NOT DL_ARGS_PREFIX) 123 | set(DL_ARGS_PREFIX "${CMAKE_BINARY_DIR}") 124 | endif() 125 | if (NOT DL_ARGS_DOWNLOAD_DIR) 126 | set(DL_ARGS_DOWNLOAD_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-download") 127 | endif() 128 | 129 | # Ensure the caller can know where to find the source and build directories 130 | if (NOT DL_ARGS_SOURCE_DIR) 131 | set(DL_ARGS_SOURCE_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-src") 132 | endif() 133 | if (NOT DL_ARGS_BINARY_DIR) 134 | set(DL_ARGS_BINARY_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-build") 135 | endif() 136 | set(${DL_ARGS_PROJ}_SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" PARENT_SCOPE) 137 | set(${DL_ARGS_PROJ}_BINARY_DIR "${DL_ARGS_BINARY_DIR}" PARENT_SCOPE) 138 | 139 | # Create and build a separate CMake project to carry out the download. 140 | # If we've already previously done these steps, they will not cause 141 | # anything to be updated, so extra rebuilds of the project won't occur. 142 | configure_file("${_DownloadProjectDir}/DownloadProject.CMakeLists.cmake.in" 143 | "${DL_ARGS_DOWNLOAD_DIR}/CMakeLists.txt") 144 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . 145 | RESULT_VARIABLE result 146 | ${OUTPUT_QUIET} 147 | WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}" 148 | ) 149 | if(result) 150 | message(FATAL_ERROR "CMake step for ${DL_ARGS_PROJ} failed: ${result}") 151 | endif() 152 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 153 | RESULT_VARIABLE result 154 | ${OUTPUT_QUIET} 155 | WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}" 156 | ) 157 | if(result) 158 | message(FATAL_ERROR "Build step for ${DL_ARGS_PROJ} failed: ${result}") 159 | endif() 160 | 161 | endfunction() 162 | -------------------------------------------------------------------------------- /tests/CMakeModules/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Crascit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /tests/fixtures.hpp: -------------------------------------------------------------------------------- 1 | // Test fixtures. 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | using namespace smu; 10 | 11 | // Create a session. 12 | class SessionFixture : public testing::Test { 13 | protected: 14 | Session* m_session; 15 | 16 | // SetUp() is run immediately before a test starts. 17 | virtual void SetUp() { 18 | m_session = new Session(); 19 | } 20 | 21 | // TearDown() is invoked immediately after a test finishes. 22 | virtual void TearDown() { 23 | delete m_session; 24 | } 25 | }; 26 | 27 | // Require at least one device to exist. 28 | class SingleDeviceFixture : public SessionFixture { 29 | protected: 30 | Device* m_dev; 31 | 32 | virtual void SetUp() { 33 | SessionFixture::SetUp(); 34 | 35 | int ret = m_session->scan(); 36 | 37 | if (ret < 0) 38 | FAIL() << "failed scanning for devices"; 39 | else if (ret == 0) 40 | FAIL() << "no devices plugged in"; 41 | 42 | if (m_session->add(m_session->m_available_devices[0])) 43 | FAIL() << "failed adding device"; 44 | 45 | m_dev = *(m_session->m_devices.begin()); 46 | } 47 | }; 48 | 49 | // Require at least two devices to exist. 50 | class MultiDeviceFixture : public SessionFixture { 51 | protected: 52 | std::set m_devices; 53 | 54 | virtual void SetUp() { 55 | SessionFixture::SetUp(); 56 | 57 | // requires at least one device plugged in 58 | if (m_session->add_all() < 2) 59 | FAIL() << "multiple devices are required"; 60 | 61 | m_devices = m_session->m_devices; 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /tests/gtest-output.hpp: -------------------------------------------------------------------------------- 1 | // Custom output for the google test framework. 2 | // 3 | // Use PRINTF() where printf() would be used and TEST_COUT where cout would be 4 | // used in order to match regular gtest output for custom status messages. 5 | 6 | #include 7 | 8 | #include 9 | 10 | enum GTestColor { 11 | COLOR_DEFAULT, 12 | COLOR_RED, 13 | COLOR_GREEN, 14 | COLOR_YELLOW 15 | }; 16 | 17 | const char* GetAnsiColorCode(GTestColor color) { 18 | switch (color) { 19 | case COLOR_RED: return "1"; 20 | case COLOR_GREEN: return "2"; 21 | case COLOR_YELLOW: return "3"; 22 | default: return NULL; 23 | }; 24 | } 25 | 26 | void ColoredPrintf(GTestColor color, const char* fmt, ...) { 27 | va_list args; 28 | va_start(args, fmt); 29 | 30 | static const bool in_color_mode = true; 31 | const bool use_color = in_color_mode && (color != COLOR_DEFAULT); 32 | 33 | if (!use_color) { 34 | vprintf(fmt, args); 35 | va_end(args); 36 | return; 37 | } 38 | 39 | printf("\033[0;3%sm", GetAnsiColorCode(color)); 40 | vprintf(fmt, args); 41 | printf("\033[m"); // Resets the terminal to default. 42 | va_end(args); 43 | } 44 | 45 | #define PRINTF(...) do { ColoredPrintf(COLOR_GREEN, "[**********] "); ColoredPrintf(COLOR_YELLOW, __VA_ARGS__); } while(0) 46 | 47 | // C++ stream interface 48 | class TestCout : public std::stringstream { 49 | public: 50 | ~TestCout() { 51 | PRINTF("%s",str().c_str()); 52 | } 53 | }; 54 | 55 | #define TEST_COUT TestCout() 56 | -------------------------------------------------------------------------------- /tests/run-tests.bat: -------------------------------------------------------------------------------- 1 | rem Run test suite on Windows. 2 | rem 3 | rem Executes all test applications within the current directory in alphabetical 4 | rem order. If a test fails the process waits on a keypress to continue. 5 | rem 6 | rem Note that all test executables must be named "test-testname.exe" in order 7 | rem to be included in the test run. 8 | 9 | @echo off 10 | for %%F in ("test-*.exe") do ( 11 | "%%F" || pause 12 | ) 13 | pause 14 | -------------------------------------------------------------------------------- /tests/test-device.cpp: -------------------------------------------------------------------------------- 1 | // Tests for device functionality. 2 | 3 | #include 4 | 5 | #include "fixtures.hpp" 6 | #include 7 | 8 | using namespace smu; 9 | 10 | class DeviceTest : public SingleDeviceFixture {}; 11 | 12 | // Verify device doesn't have any empty serial number. 13 | TEST_F(DeviceTest, serial) { 14 | ASSERT_NE(m_dev->m_serial, ""); 15 | } 16 | 17 | // Verify device doesn't have any empty firmware version. 18 | TEST_F(DeviceTest, fwver) { 19 | ASSERT_NE(m_dev->m_fwver, ""); 20 | } 21 | 22 | // Verify device doesn't have any empty hardware version. 23 | TEST_F(DeviceTest, hwver) { 24 | ASSERT_NE(m_dev->m_hwver, ""); 25 | } 26 | 27 | int main(int argc, char **argv) { 28 | ::testing::InitGoogleTest(&argc, argv); 29 | return RUN_ALL_TESTS(); 30 | } 31 | -------------------------------------------------------------------------------- /tests/test-multi-read.cpp: -------------------------------------------------------------------------------- 1 | // Tests for read functionality. 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "fixtures.hpp" 11 | #include 12 | 13 | using namespace smu; 14 | 15 | class MultiReadTest : public MultiDeviceFixture {}; 16 | 17 | TEST_F(MultiReadTest, non_continuous) { 18 | std::vector> rxbuf; 19 | 20 | // Run session in non-continuous mode. 21 | m_session->run(1000); 22 | 23 | for (auto dev: m_devices) { 24 | // Grab 1000 samples in a blocking fashion in HI-Z mode. 25 | dev->read(rxbuf, 1000, -1); 26 | 27 | // We should have gotten 1000 samples. 28 | EXPECT_EQ(rxbuf.size(), 1000); 29 | 30 | // Which all should be near 0. 31 | for (auto x: rxbuf) { 32 | EXPECT_EQ(0, std::fabs(std::round(x[0]))); 33 | EXPECT_EQ(0, std::fabs(std::round(x[1]))); 34 | EXPECT_EQ(0, std::fabs(std::round(x[2]))); 35 | EXPECT_EQ(0, std::fabs(std::round(x[3]))); 36 | } 37 | } 38 | } 39 | 40 | TEST_F(MultiReadTest, continuous) { 41 | std::vector> rxbuf; 42 | 43 | // Run session in continuous mode. 44 | m_session->start(0); 45 | 46 | for (auto dev: m_devices) { 47 | // Grab 1000 samples in a nonblocking fashion in HI-Z mode. 48 | dev->read(rxbuf, 1000); 49 | 50 | // We should have gotten between 0 and 1000 samples. 51 | EXPECT_LE(rxbuf.size(), 1000); 52 | EXPECT_GE(rxbuf.size(), 0); 53 | rxbuf.clear(); 54 | 55 | // Grab 1000 samples with a timeout of 150ms. 56 | dev->read(rxbuf, 1000, 150); 57 | // Which should be long enough to get all 1000 samples. 58 | EXPECT_EQ(rxbuf.size(), 1000); 59 | rxbuf.clear(); 60 | 61 | // Grab 1000 more samples in a blocking fashion. 62 | dev->read(rxbuf, 1000, -1); 63 | EXPECT_EQ(rxbuf.size(), 1000); 64 | rxbuf.clear(); 65 | 66 | // Sleeping for a bit to cause an overflow exception. 67 | std::this_thread::sleep_for(std::chrono::milliseconds(250)); 68 | 69 | // Trying to read should now throw a buffer overflow exception. 70 | ASSERT_THROW(dev->read(rxbuf, 1000), std::system_error); 71 | } 72 | } 73 | 74 | 75 | int main(int argc, char **argv) { 76 | ::testing::InitGoogleTest(&argc, argv); 77 | return RUN_ALL_TESTS(); 78 | } 79 | -------------------------------------------------------------------------------- /tests/test-read-write.cpp: -------------------------------------------------------------------------------- 1 | // Tests for read/write functionality. 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "fixtures.hpp" 12 | #include 13 | 14 | using namespace smu; 15 | 16 | // Read/write fixture initializing generic variables used by all read/write tests. 17 | class ReadWriteTest : public SingleDeviceFixture { 18 | protected: 19 | std::vector> rxbuf; 20 | std::vector a_txbuf; 21 | std::vector b_txbuf; 22 | uint64_t sample_count = 0; 23 | 24 | // TearDown() is invoked immediately after a test finishes. 25 | virtual void TearDown() { 26 | SingleDeviceFixture::TearDown(); 27 | rxbuf.clear(); 28 | a_txbuf.clear(); 29 | b_txbuf.clear(); 30 | sample_count = 0; 31 | } 32 | }; 33 | 34 | // refill Tx buffers with data 35 | static void refill_data(std::vector& buf, unsigned size, int voltage) { 36 | buf.clear(); 37 | for (unsigned i = 0; i < size; i++) { 38 | buf.push_back(voltage); 39 | } 40 | } 41 | 42 | TEST_F(ReadWriteTest, non_continuous_fallback_values) { 43 | // Set device channels to source voltage and measure current. 44 | m_dev->set_mode(0, SVMI); 45 | m_dev->set_mode(1, SVMI); 46 | 47 | 48 | // Fill Tx buffers with 1000 samples and request the maximum amount of 49 | // samples (queue size) back. The remaining 24 samples should have the same 50 | // output values since the most recently written value to the channel will 51 | // be used to complete output packets. 52 | refill_data(a_txbuf, 1000, 2); 53 | refill_data(b_txbuf, 1000, 4); 54 | m_dev->write(a_txbuf, 0); 55 | m_dev->write(b_txbuf, 1); 56 | m_session->run(m_session->m_queue_size); 57 | m_dev->read(rxbuf, m_session->m_queue_size, -1); 58 | 59 | // Verify all samples are what we expect. 60 | for (unsigned i = 0; i < rxbuf.size(); i++) { 61 | sample_count++; 62 | EXPECT_EQ(2, std::fabs(std::round(rxbuf[i][0]))) << "failed at sample: " << sample_count; 63 | EXPECT_EQ(4, std::fabs(std::round(rxbuf[i][2]))) << "failed at sample: " << sample_count; 64 | } 65 | } 66 | 67 | TEST_F(ReadWriteTest, non_continuous) { 68 | // Set device channels to source voltage and measure current. 69 | m_dev->set_mode(0, SVMI); 70 | m_dev->set_mode(1, SVMI); 71 | 72 | // Verify read/write data for 10 seconds. 73 | unsigned voltage = 0; 74 | auto clk_start = std::chrono::high_resolution_clock::now(); 75 | while (true) { 76 | auto clk_end = std::chrono::high_resolution_clock::now(); 77 | auto clk_diff = std::chrono::duration_cast(clk_end - clk_start); 78 | if (clk_diff.count() > 10) 79 | break; 80 | 81 | // Refill outgoing data buffers. 82 | refill_data(a_txbuf, 1000, voltage % 6); 83 | refill_data(b_txbuf, 1000, voltage % 6); 84 | 85 | // Write iterating voltage values to both channels. 86 | m_dev->write(a_txbuf, 0); 87 | m_dev->write(b_txbuf, 1); 88 | 89 | // Run the session for 1000 samples. 90 | m_session->run(1000); 91 | 92 | // Read incoming samples in a blocking fashion. 93 | m_dev->read(rxbuf, 1000, -1); 94 | 95 | // Validate received values. 96 | EXPECT_EQ(rxbuf.size(), 1000); 97 | for (unsigned i = 0; i < rxbuf.size(); i++) { 98 | sample_count++; 99 | EXPECT_EQ(voltage % 6, std::fabs(std::round(rxbuf[i][0]))) << "failed at sample: " << sample_count; 100 | EXPECT_EQ(voltage % 6, std::fabs(std::round(rxbuf[i][2]))) << "failed at sample: " << sample_count; 101 | 102 | // show output progress per second 103 | if (sample_count % m_session->m_sample_rate == 0) { 104 | std::cout << "*" << std::flush; 105 | } 106 | } 107 | voltage++; 108 | } 109 | std::cout << std::endl; 110 | } 111 | 112 | TEST_F(ReadWriteTest, continuous) { 113 | // Set device channels to source voltage and measure current. 114 | m_dev->set_mode(0, SVMI); 115 | m_dev->set_mode(1, SVMI); 116 | 117 | // Run session in continuous mode. 118 | m_session->start(0); 119 | 120 | // Verify read/write data for 10 seconds. 121 | unsigned voltage = 0; 122 | auto clk_start = std::chrono::high_resolution_clock::now(); 123 | while (true) { 124 | auto clk_end = std::chrono::high_resolution_clock::now(); 125 | auto clk_diff = std::chrono::duration_cast(clk_end - clk_start); 126 | if (clk_diff.count() > 10) 127 | break; 128 | 129 | // Refill outgoing data buffers. 130 | refill_data(a_txbuf, 1000, voltage % 6); 131 | refill_data(b_txbuf, 1000, voltage % 6); 132 | 133 | try { 134 | // Write iterating voltage values to both channels. 135 | m_dev->write(a_txbuf, 0); 136 | m_dev->write(b_txbuf, 1); 137 | 138 | // Read incoming samples in a non-blocking fashion. 139 | m_dev->read(rxbuf, 1000); 140 | } catch (const std::runtime_error&) { 141 | // ignore sample drops 142 | } 143 | 144 | // Validate received values. 145 | for (unsigned i = 0; i < rxbuf.size(); i++) { 146 | sample_count++; 147 | EXPECT_EQ(voltage % 6, std::fabs(std::round(rxbuf[i][0]))) << "value: " << rxbuf[i][0] << " ,failed at sample: " << sample_count; 148 | EXPECT_EQ(voltage % 6, std::fabs(std::round(rxbuf[i][2]))) << "value: " << rxbuf[i][2] << " ,failed at sample: " << sample_count; 149 | 150 | // show output progress per second 151 | if (sample_count % m_session->m_sample_rate == 0) { 152 | std::cout << "*" << std::flush; 153 | } 154 | } 155 | 156 | if (sample_count && sample_count % 1000 == 0) 157 | voltage++; 158 | } 159 | std::cout << std::endl; 160 | 161 | // Verify we're running at 100+ ksps. 162 | EXPECT_GE(sample_count, 100000*10); 163 | } 164 | 165 | int main(int argc, char **argv) { 166 | ::testing::InitGoogleTest(&argc, argv); 167 | return RUN_ALL_TESTS(); 168 | } 169 | -------------------------------------------------------------------------------- /tests/test-session.cpp: -------------------------------------------------------------------------------- 1 | // Tests for session functionality. 2 | 3 | #include 4 | 5 | #include "fixtures.hpp" 6 | #include 7 | 8 | using namespace smu; 9 | 10 | class SessionTest : public SessionFixture {}; 11 | 12 | 13 | TEST_F(SessionTest, empty) { 14 | // No devices on session init. 15 | EXPECT_EQ(m_session->m_devices.size(), 0); 16 | } 17 | 18 | int main(int argc, char **argv) { 19 | ::testing::InitGoogleTest(&argc, argv); 20 | return RUN_ALL_TESTS(); 21 | } 22 | --------------------------------------------------------------------------------