diff --git a/.deploy_to_sourceforge.py b/.deploy_to_sourceforge.py index 49d520bd2..2dbe10192 100644 --- a/.deploy_to_sourceforge.py +++ b/.deploy_to_sourceforge.py @@ -7,14 +7,15 @@ if len(sys.argv) < 2: print("Usage: python .deploy_to_sourceforge.py file1 [file2] ...") - pw = os.environ['PYPI_PASSWORD'] + pw = os.environ['PYPI_PSW'] ssh = sshshell.SshShell(hostname='frs.sourceforge.net', user='lneuhaus', password=pw, shell=False) for filename in sys.argv[1:]: - for destpath in ['/home/frs/project/pyrpl/%s/' % __version__, - '/home/frs/project/pyrpl/']: + for destpath in ['/home/frs/project/pyrpl/', + '/home/frs/project/pyrpl/%s/' % __version__ + ]: print("Uploading file '%s' to '%s' on sourceforge..." % (filename, destpath)) try: ssh.scp.put(filename, destpath) diff --git a/.docker/all_python_build.sh b/.docker/all_python_build.sh new file mode 100755 index 000000000..f0bde8c52 --- /dev/null +++ b/.docker/all_python_build.sh @@ -0,0 +1,7 @@ +docker system prune -a +docker build --build-arg PYTHON_VERSION=3.7 -t python-37 ../. +docker build --build-arg PYTHON_VERSION=3.6 -t python-36 ../. +docker build --build-arg PYTHON_VERSION=3.5 -t python-35 ../. +docker build --build-arg PYTHON_VERSION=2.7 -t python-27 ../. +docker build --build-arg PYTHON_VERSION=3 -t python-3 ../. +docker build -t python ../. diff --git a/.docker/build.sh b/.docker/build.sh new file mode 100755 index 000000000..3713631b4 --- /dev/null +++ b/.docker/build.sh @@ -0,0 +1 @@ +docker build -t pyrpl ../. diff --git a/.docker/run-27.sh b/.docker/run-27.sh new file mode 100755 index 000000000..1101f2468 --- /dev/null +++ b/.docker/run-27.sh @@ -0,0 +1,7 @@ +docker run -ti --rm \ + -e DISPLAY \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + -v /home/leo/github/pyrpl:/home/pyrpl \ + -v /home/leo/github/pyrpl-copy:/home/pyrpl-copy \ + --net=host \ + python-27 diff --git a/.docker/run-3.sh b/.docker/run-3.sh new file mode 100755 index 000000000..f1ddfaba8 --- /dev/null +++ b/.docker/run-3.sh @@ -0,0 +1,7 @@ +docker run -ti --rm \ + -e DISPLAY \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + -v /home/leo/github/pyrpl:/home/pyrpl \ + -v /home/leo/github/pyrpl-copy:/home/pyrpl-copy \ + --net=host \ + python-3 diff --git a/.docker/run-35.sh b/.docker/run-35.sh new file mode 100755 index 000000000..b73eba384 --- /dev/null +++ b/.docker/run-35.sh @@ -0,0 +1,7 @@ +docker run -ti --rm \ + -e DISPLAY \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + -v /home/leo/github/pyrpl:/home/pyrpl \ + -v /home/leo/github/pyrpl-copy:/home/pyrpl-copy \ + --net=host \ + python-35 diff --git a/.docker/run-36.sh b/.docker/run-36.sh new file mode 100755 index 000000000..84a0770c1 --- /dev/null +++ b/.docker/run-36.sh @@ -0,0 +1,7 @@ +docker run -ti --rm \ + -e DISPLAY \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + -v /home/leo/github/pyrpl:/home/pyrpl \ + -v /home/leo/github/pyrpl-copy:/home/pyrpl-copy \ + --net=host \ + python-36 diff --git a/.docker/run.sh b/.docker/run.sh new file mode 100755 index 000000000..35db139c6 --- /dev/null +++ b/.docker/run.sh @@ -0,0 +1,7 @@ +docker run -ti --rm \ + -e DISPLAY \ + -v /tmp/.X11-unix:/tmp/.X11-unix \ + -v /home/leo/github/pyrpl:/home/pyrpl \ + -v /home/leo/github/pyrpl-copy:/home/pyrpl-copy \ + --net=host \ + python-37 diff --git a/.docker/setup_jenkins.sh b/.docker/setup_jenkins.sh new file mode 100755 index 000000000..9423bb43b --- /dev/null +++ b/.docker/setup_jenkins.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo 'JAVA_ARGS="-Dhudson.tasks.MailSender.SEND_TO_UNKNOWN_USERS=true"' >> /etc/default/jenkins +xhost +local:docker + diff --git a/.docker/test/Dockerfile b/.docker/test/Dockerfile new file mode 100644 index 000000000..bec6f3c01 --- /dev/null +++ b/.docker/test/Dockerfile @@ -0,0 +1,56 @@ +# define base image +FROM ubuntu:latest +# FROM node:7-onbuild + +# set maintainer +LABEL maintainer "pyrpl.readthedocs.io@gmail.com" + +USER root + +ARG CONDA_DIR="/opt/conda" +ARG PYTHON_VERSION="3" + +# setup ubuntu with gui support +RUN apt update --yes +RUN apt upgrade --yes +RUN apt update --yes +RUN apt-get install --yes systemd wget sloccount qt5-default +# sets up keyboard support in GUI +ENV QT_XKB_CONFIG_ROOT /usr/share/X11/xkb + +# install miniconda +RUN mkdir /tmp/miniconda +WORKDIR /tmp/miniconda +RUN if [ "$PYTHON_VERSION" = "2" ] ; then wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O Miniconda.sh; else wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O Miniconda.sh; fi +RUN chmod +x Miniconda.sh +RUN ./Miniconda.sh -b -p $CONDA_DIR + +# set path environment variable to refer to conda bin dir (we are working in the (base) conda environment +ENV PATH="$CONDA_DIR/bin:$PATH" +ENV LD_LIBRARY_PATH="$CONDA_DIR/lib:$LD_LIBRARY_PATH" + +RUN python -V + +# install desired python version and additional packages +RUN conda install --yes python=$PYTHON_VERSION numpy scipy paramiko pandas jupyter nose pip pyqt qtpy nbconvert coverage twine matplotlib + +RUN python -V + +RUN pip install radon +RUN conda install --yes nb_conda nb_conda_kernels nb_anacondacloud + +RUN python -V + +# Clean up miniconda installation files +WORKDIR / +RUN rm -rf /tmp/miniconda + +# auxiliary environment variable +ENV PYTHON_VERSION=$PYTHON_VERSION + +# print a message +RUN echo "Docker image is up and running...." +RUN echo $PATH + +# print some python diagnostics information +RUN python -V diff --git a/.gitignore b/.gitignore index 770314460..003a23f97 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,11 @@ pyrpl/fpga/red_pitaya.prm pyrpl/fpga/*.dmp pyrpl/.idea +# Vivado project-related stuff +pyrpl/fpga/project/.* +pyrpl/fpga/project/ +*.jou + #fpga-related fpga/out/ fpga/sdk/ @@ -63,12 +68,14 @@ htmlcov/ .tox/ .coverage .coverage.* +cover/* .cache nosetests.xml coverage.xml *,cover .hypothesis/ xunit.xml +unit_tests.xml # Translations *.mo @@ -123,3 +130,4 @@ scripts/build/ scripts/__init__.py scripts/run_pyrpl.spec scripts/pyrpl.spec +cover/index.html diff --git a/.travis.yml b/.travis.yml old mode 100644 new mode 100755 index e479624dd..85bdeb9af --- a/.travis.yml +++ b/.travis.yml @@ -4,29 +4,36 @@ # lots of stuff here comes from https://gist.github.com/dan-blanchard/7045057 -env: -- AUTOCORRETPEP8=0 DISPLAY=:99.0 # REDPITAYA variables are defined in travis account (encrypted) -language: python -# nosetests is only executed for 2.7, 3.5 and 3.6, but we still test the -# installation for other python versions (3.4) -python: - - "2.7" - - "3.6" - - "3.5" - - "3.4" - notifications: - email: false + email: + recipients: + - pyrpl.readthedocs.io@gmail.com + +language: generic + +env: + - TRAVIS_PYTHON_VERSION=3.7 +os: + - osx + - linux before_install: - - sudo apt-get update + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + sudo apt-get update; + fi # We do this conditionally because it saves us some downloading. - - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then + - if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh; - else + elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; + elif [[ "$TRAVIS_OS_NAME" == "osx" && "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then + wget https://repo.continuum.io/miniconda/Miniconda2-latest-MacOSX-x86_64.sh -O miniconda.sh; + elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + wget https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O miniconda.sh; + else + echo "Invalid combination of OS ($TRAVIS_OS_NAME) and Python version ($TRAVIS_PYTHON_VERSION)"; fi - chmod +x miniconda.sh - bash miniconda.sh -b -p $HOME/miniconda @@ -37,56 +44,62 @@ before_install: # Useful for debugging any issues with conda - conda info -a # The next lines fix a crash with multiprocessing on Travis and are not specific to using Miniconda - - sudo rm -rf /dev/shm - - sudo ln -s /run/shm /dev/shm + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + sudo rm -rf /dev/shm; sudo ln -s /run/shm /dev/shm; + fi # starts gui support, see https://docs.travis-ci.com/user/gui-and-headless-browsers/ - - sh -e /etc/init.d/xvfb start + # and https://github.com/travis-ci/travis-ci/issues/7313#issuecomment-279914149 (for MacOSX) + # formerly "sh -e sudo Xvfb :99 -ac -screen 0 1024x768x8"; + - export DISPLAY=":99.0" + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + sh -e /etc/init.d/xvfb start; + fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + Xvfb :98 -ac -screen 0 1024x768x8; + fi & # give it some time to start - sleep 3 install: # avoid to get cancelled because of very long tests # we get issues with building numpy etc if we do not include those in the next line - - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy scipy paramiko pandas nose pip pyqt qtpy + - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy scipy paramiko pandas nose pip pyqt qtpy nbconvert - source activate test-environment - - if [[ "$TRAVIS_PYTHON_VERSION" != "3.3" ]]; then - conda install --yes -c conda-forge coveralls; - fi - # overwrite default global config file with a custom one for travis (allows slower communication time) - - \cp ./travis_global_config.yml ./pyrpl/config/global_config.yml + # convert readme file to rst for PyPI + - conda install pandoc + - pandoc --from=markdown --to=rst --output=README.rst README.md + # install pyinstaller + - cd .. + - git clone https://www.github.com/lneuhaus/pyinstaller.git -b develop --depth=1 + - cd pyinstaller + - git status + - python setup.py develop + - cd .. + - cd pyrpl + # install pyrpl - python setup.py install - # packages for coverage reports - - pip install coverage codecov -# Run test +# create, test and upload binary for mac os script: - - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]] || [[ "$TRAVIS_PYTHON_VERSION" == "3.4" ]] || [[ "$TRAVIS_PYTHON_VERSION" == "3.6" ]]; then - nosetests; - fi + - export QT_QPA_PLATFORM_PLUGIN_PATH=$HOME/miniconda/bin/envs/test-environment/Library/plugins/platforms + - pyinstaller pyrpl.spec + - mv dist/pyrpl ./pyrpl-mac-develop + - chmod 755 pyrpl-mac-develop + - (./pyrpl-mac-develop config=test_osx hostname=_FAKE_ &) + - PYRPL_PID=$! + - sleep 30 + - killall pyrpl-mac-develop + - python .deploy_to_sourceforge.py pyrpl-mac-develop -after_script: - - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]] || [[ "$TRAVIS_PYTHON_VERSION" == "3.4" ]] || [[ "$TRAVIS_PYTHON_VERSION" == "3.6" ]]; then - codecov; - fi - -# automatic release when a new tag is created needs a few preliminary steps... -# create a Readme.rst for PyPI -# make an executable for linux and upload to sourceforge +# automatic release when a new tag is created: before_deploy, deploy, and after_deploy before_deploy: - - if [[ "$TRAVIS_PYTHON_VERSION" == "3.4" ]]; then - source activate test-environment; - conda install pandoc; - pip install pyinstaller; - pandoc --from=markdown --to=rst --output=README.rst README.md; - pyinstaller pyrpl.spec; - mv dist/pyrpl ./pyrpl-linux; - python .deploy_to_sourceforge.py pyrpl-linux; - fi + - echo Deploy + - source activate test-environment deploy: provider: pypi user: lneuhaus - password: $PYPI_PASSWORD + password: $PYPI_PSW skip_cleanup: true on: tags: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..84bbcb6f2 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# PyRPL changelog + + +## 0.9.5 (November 17, 2020) + +- merges the "0.9.3-develop" branch with accumulated upgrades from over 2 years +- last version to support Python 2.7 (though not running tests any more) +- tested on Python 3.6 and 3.7 +- significant improvements to IIR filter module \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..7b5f8630c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,48 @@ +# define base image +FROM ubuntu:latest +# FROM node:7-onbuild + +# set maintainer +LABEL maintainer "pyrpl.readthedocs.io@gmail.com" + +USER root + +ARG CONDA_DIR="/opt/conda" +ARG PYTHON_VERSION="3" + +# setup ubuntu with gui support +RUN apt update --yes +RUN apt upgrade --yes +RUN apt update --yes +RUN apt-get install --yes systemd wget sloccount qt5-default binutils +# sets up keyboard support in GUI +ENV QT_XKB_CONFIG_ROOT /usr/share/X11/xkb + +# install miniconda +RUN mkdir /tmp/miniconda +WORKDIR /tmp/miniconda +RUN if [ "$PYTHON_VERSION" = "2" ] ; then wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O Miniconda.sh; else wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O Miniconda.sh; fi +RUN chmod +x Miniconda.sh +RUN ./Miniconda.sh -b -p $CONDA_DIR + +# set path environment variable to refer to conda bin dir (we are working in the (base) conda environment +ENV PATH="$CONDA_DIR/bin:$PATH" +# set library path until pyinstaller issue is fixed +ENV LD_LIBRARY_PATH="$CONDA_DIR/lib:$LD_LIBRARY_PATH" + +# install desired python version and additional packages +RUN conda install --yes python=$PYTHON_VERSION numpy scipy paramiko pandas jupyter nose pip pyqt qtpy nbconvert coverage twine matplotlib nb_conda_kernels + +# Clean up miniconda installation files +WORKDIR / +RUN rm -rf /tmp/miniconda + +# auxiliary environment variable +ENV PYTHON_VERSION=$PYTHON_VERSION + +# print a message +RUN echo "Docker image is up and running...." +RUN echo $PATH + +# print some python diagnostics information +RUN python -V diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 000000000..9c60d714b --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,175 @@ +#!groovy + + + +def getRepoURL() { + sh "git config --get remote.origin.url > .git/remote-url" + return readFile(".git/remote-url").trim() +} + +def getCommitSha() { + sh "git rev-parse HEAD > .git/current-commit" + return readFile(".git/current-commit").trim() +} + +void setBuildStatus(String message, String state) { + step([ + $class: "GitHubCommitStatusSetter", + reposSource: [$class: "ManuallyEnteredRepositorySource", url: getRepoURL()], + contextSource: [$class: "ManuallyEnteredCommitContextSource", context: "ci/jenkins/build-status"], + errorHandlers: [[$class: "ChangingBuildStatusErrorHandler", result: "UNSTABLE"]], + statusResultSource: [ $class: "ConditionalStatusResultSource", results: [[$class: "AnyBuildResult", message: message, state: state]] ] + ]); +} + + +pipeline { + triggers { pollSCM('*/1 * * * *') } + + options { + // skipDefaultCheckout(true) // rather do the checkout in all stages + // Keep the 10 most recent builds + buildDiscarder(logRotator(numToKeepStr: '10')) + timestamps() + } + + + environment { + REDPITAYA_HOSTNAME = "192.168.178.26" + //REDPITAYA_HOSTNAME = "rp-f03f3a" + //REDPITAYA_HOSTNAME = "nobody.justdied.com" + DOCKER_ARGS = '-u root -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=:0 --net=host' + //NOSETESTS_COMMAND = 'nosetests pyrpl/test/test_ipython_notebook/test_ipython_kernel.py' + NOSETESTS_COMMAND = 'nosetests' + PYPI = credentials('f63335ce-493d-4caf-8ebe-d7e2629f79f3') + REDPITAYA = credentials('2bf38f88-833a-4624-9682-3a6f0a145d30') + REDPITAYA_USER = "$REDPITAYA_USR" + REDPITAYA_PASSWORD = "$REDPITAYA_PSW" + + } + + agent any + + stages { + stage('Notify github that a build was started') { + agent any + steps { setBuildStatus("Jenkins build started...", "PENDING") }} + stage('Unit tests') { parallel { + stage('Python 3.7') { + agent { dockerfile { args "$DOCKER_ARGS" + additionalBuildArgs '--build-arg PYTHON_VERSION=3.7' }} + steps { lock('redpitaya') { + sh ''' which python + python -V + echo $PYTHON_VERSION + conda list + # use a custom global configfile adapted to the hardware for unit tests + cp ./jenkins_global_config.yml ./pyrpl/config/global_config.yml + python setup.py install + ''' + sh "$NOSETESTS_COMMAND" }} + post { always { junit allowEmptyResults: true, testResults: 'unit_test_results.xml' }}} + stage('Python 3.6') { + agent { dockerfile { args "$DOCKER_ARGS" + additionalBuildArgs '--build-arg PYTHON_VERSION=3.6' }} + steps { lock('redpitaya') { + sh ''' which python + python -V + echo $PYTHON_VERSION + conda list + # use a custom global configfile adapted to the hardware for unit tests + cp ./jenkins_global_config.yml ./pyrpl/config/global_config.yml + python setup.py install + ''' + sh "$NOSETESTS_COMMAND"}} + post { always { junit allowEmptyResults: true, testResults: 'unit_test_results.xml' }}} + /*stage('Python 3.5') { + agent { dockerfile { args "$DOCKER_ARGS" + additionalBuildArgs '--build-arg PYTHON_VERSION=3.5' }} + steps { + sh ''' which python + python -V + echo $PYTHON_VERSION + conda list + # use a custom global configfile adapted to the hardware for unit tests + cp ./jenkins_global_config.yml ./pyrpl/config/global_config.yml + python setup.py install + ''' + sh "$NOSETESTS_COMMAND"} + post { always { junit allowEmptyResults: true, testResults: 'unit_test_results.xml' }}}*/ + stage('Python 2.7') { + agent { dockerfile { args "$DOCKER_ARGS" + additionalBuildArgs '--build-arg PYTHON_VERSION=2.7' }} + steps { lock('redpitaya') { + sh ''' which python + python -V + echo $PYTHON_VERSION + conda list + # use a custom global configfile adapted to the hardware for unit tests + cp ./jenkins_global_config.yml ./pyrpl/config/global_config.yml + python setup.py install + ''' + sh "$NOSETESTS_COMMAND"}} + post { always { junit allowEmptyResults: true, testResults: 'unit_test_results.xml' }}} + stage('Linux binary') { + agent { dockerfile { args '-u root -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=:0 --net=host' + additionalBuildArgs '--build-arg PYTHON_VERSION=3.7' }} + steps { lock('fake_redpitaya') { + sh ''' apt-get install psmisc + python setup.py install + pip install https://github.com/lneuhaus/pyinstaller/tarball/develop + pyinstaller pyrpl.spec + mv dist/pyrpl ./pyrpl-linux-develop + python .deploy_to_sourceforge.py pyrpl-linux-develop + chmod 755 pyrpl-linux-develop + (./pyrpl-linux-develop config=test_linux hostname=_FAKE_ &) + PYRPL_PID=$! + sleep 30 + killall -9 pyrpl-linux-develop + ''' + //sh 'python .deploy_to_sourceforge.py pyrpl-linux-develop' + }} + post { always { archiveArtifacts allowEmptyArchive: true, artifacts: 'pyrpl-linux-develop', fingerprint: true }}} + stage('pip wheel') { + agent { dockerfile { args '-u root -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=:0 --net=host' + additionalBuildArgs '--build-arg PYTHON_VERSION=3.7' }} + steps { lock('fake_redpitaya') { + sh ''' python setup.py install + # convert readme file to rst for PyPI + conda install pandoc + pandoc --from=markdown --to=rst --output=README.rst README.md + # make distributions for PyPI + python setup.py sdist + python setup.py bdist_wheel --universal + # upload to PyPI + # twine upload dist/**/*.* + '''}} + post { always { archiveArtifacts allowEmptyArchive: true, artifacts: 'dist/**/*.*', fingerprint: true}}} + }} + stage('Deploy') { + agent { dockerfile { args '-u root -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=:0 --net=host' + additionalBuildArgs '--build-arg PYTHON_VERSION=3.7' }} + when { + expression { currentBuild.result == null || currentBuild.result == 'SUCCESS'}} + steps { + sh ''' python setup.py install + '''}} + } + post { + failure { + emailext ( + attachLog: true, + subject: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'", + body: """

FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]':

+

Check console output at ${env.JOB_NAME} [${env.BUILD_NUMBER}]

""", + compressLog: false, + recipientProviders: [requestor(), developers(), brokenTestsSuspects(), brokenBuildSuspects(), upstreamDevelopers(), culprits()], + replyTo: 'pyrpl.readthedocs.io@gmail.com', + to: 'pyrpl.readthedocs.io@gmail.com') + setBuildStatus("Build failed!", "FAILURE") + } + success { setBuildStatus("Build successful!", "SUCCESS") } + unstable { setBuildStatus("Build erroneous!", "ERROR") } + } +} + diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..42781a9c3 --- /dev/null +++ b/Makefile @@ -0,0 +1,158 @@ +SRC_DIR=. + +all: clean sloc test flakes lint clone + +# almost direct translation of travis script +before_install: + apt-get update; + if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then + wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh; + elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; + elif [[ "$TRAVIS_OS_NAME" == "osx" && "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then + wget https://repo.continuum.io/miniconda/Miniconda2-latest-MacOSX-x86_64.sh -O miniconda.sh; + elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + wget https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O miniconda.sh; + else + echo "Invalid combination of OS ($TRAVIS_OS_NAME) and Python version ($TRAVIS_PYTHON_VERSION)"; + fi + chmod +x miniconda.sh + bash miniconda.sh -b -p $HOME/miniconda + export PATH="$HOME/miniconda/bin:$PATH" + hash -r + conda config --set always_yes yes --set changeps1 no + conda update -q conda + # Useful for debugging any issues with conda + conda info -a + # The next lines fix a crash with multiprocessing on Travis and are not specific to using Miniconda + sudo rm -rf /dev/shm + sudo ln -s /run/shm /dev/shm + # starts gui support, see https://docs.travis-ci.com/user/gui-and-headless-browsers/ + # and https://github.com/travis-ci/travis-ci/issues/7313#issuecomment-279914149 (for MacOSX) + # formerly "sh -e sudo Xvfb :99 -ac -screen 0 1024x768x8"; + export DISPLAY=":99.0" + if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + sh -e /etc/init.d/xvfb start; + fi + if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + Xvfb :98 -ac -screen 0 1024x768x8; + fi & + # give it some time to start + sleep 3 + conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy scipy paramiko pandas nose pip pyqt qtpy + source activate test-environment + # convert readme file to rst for PyPI + conda install pandoc + pandoc --from=markdown --to=rst --output=README.rst README.md + # overwrite default global config file with a custom one for travis (allows slower communication time) + \cp ./travis_global_config.yml ./pyrpl/config/global_config.yml + # packages for coverage reports + conda install --yes -c conda-forge coveralls + pip install coverage codecov + # install pyrpl + python setup.py install + +script: + if [[ "$TRAVIS_PYTHON_VERSION" == "3.5" && "$TRAVIS_OS_NAME" == "osx" ]]; then + cd ..; + git clone https://www.github.com/lneuhaus/pyinstaller.git -b develop; + cd pyinstaller; + git status; + python setup.py develop; + cd ..; + cd pyrpl; + pyinstaller pyrpl.spec; + mv dist/pyrpl ./pyrpl-mac-develop; + python .deploy_to_sourceforge.py pyrpl-mac-develop; + chmod 755 pyrpl-mac-develop; + (./pyrpl-mac-develop config=test_osx hostname=_FAKE_ &); + PYRPL_PID=$!; + sleep 30; + killall -9 pyrpl-mac-develop; + fi + if [[ "$TRAVIS_PYTHON_VERSION" == "3.4" && "$TRAVIS_OS_NAME" == "linux" ]]; then + cd ..; + git clone https://www.github.com/lneuhaus/pyinstaller.git -b develop; + cd pyinstaller; + git status; + python setup.py develop; + cd ..; + cd pyrpl; + pyinstaller pyrpl.spec; + mv dist/pyrpl ./pyrpl-linux-develop; + python .deploy_to_sourceforge.py pyrpl-linux-develop; + chmod 755 pyrpl-linux-develop; + (./pyrpl-linux-develop config=test_linux hostname=_FAKE_ &); + PYRPL_PID=$!; + sleep 30; + killall pyrpl-linux-develop; + fi + - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" && "$TRAVIS_OS_NAME" == "linux" ]] || [[ "$TRAVIS_PYTHON_VERSION" == "3.5" && "$TRAVIS_OS_NAME" == "osx" ]] || [[ "$TRAVIS_PYTHON_VERSION" == "3.6" && "$TRAVIS_OS_NAME" == "linux" ]]; then + echo "NOSETESTS ARE ENABLED"; + nosetests; + fi + +after_script: + if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" && "$TRAVIS_OS_NAME" == "linux" ]] || [[ "$TRAVIS_PYTHON_VERSION" == "3.5" && "$TRAVIS_OS_NAME" == "linux" ]] || [[ "$TRAVIS_PYTHON_VERSION" == "3.6" && "$TRAVIS_OS_NAME" == "osx" ]]; then + codecov; + fi + +# automatic release when a new tag is created: before_deploy, deploy, and after_deploy +before_deploy: + echo Deploy + source activate test-environment + +deploy: + provider: pypi + user: lneuhaus + password: $PYPI_PASSWORD + skip_cleanup: true + on: + tags: true + +distributions: "sdist bdist_wheel --universal" + +# make an executable for linux and upload to sourceforge in python 3.4 +# same for macos +# put windows executable into the right directory (at last such that "latest" points to windows .exe) +after_deploy: + source activate test-environment + if [[ "$TRAVIS_PYTHON_VERSION" == "3.5" && "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_TEST_RESULT" == 0 ]]; then + pip install https://github.com/pyinstaller/pyinstaller/archive/develop.zip; + export QT_QPA_PLATFORM_PLUGIN_PATH=$HOME/miniconda/envs/test-environment/plugins/platforms; + pyinstaller pyrpl.spec; + mv dist/pyrpl ./pyrpl-linux; + python .deploy_to_sourceforge.py pyrpl-linux; + fi + if [[ "$TRAVIS_PYTHON_VERSION" == "3.5" && "$TRAVIS_OS_NAME" == "osx" && "$TRAVIS_TEST_RESULT" == 0 ]]; then + pip install https://github.com/pyinstaller/pyinstaller/archive/develop.zip; + export QT_QPA_PLATFORM_PLUGIN_PATH=$HOME/miniconda/envs/test-environment/plugins/platforms; + pyinstaller pyrpl.spec; + mv dist/pyrpl ./pyrpl-mac; + python .deploy_to_sourceforge.py pyrpl-mac; + fi + wget wget https://sourceforge.net/projects/pyrpl/files/pyrpl-windows.exe -O pyrpl-windows.exe + python .deploy_to_sourceforge.py pyrpl-windows.exe + +sloc: + sloccount --duplicates --wide --details $(SRC_DIR) | fgrep -v .git > sloccount.sc || : + +test: + cd $(SRC_DIR) && nosetests --verbose --with-xunit --xunit-file=../xunit.xml --with-xcoverage --xcoverage-file=../coverage.xml || : + +flakes: + find $(SRC_DIR) -name *.py|egrep -v '^./tests/'|xargs pyflakes > pyflakes.log || : + +lint: + find $(SRC_DIR) -name *.py|egrep -v '^./tests/' | xargs pylint --output-format=parseable --reports=y > pylint.log || : + +clone: + clonedigger --cpd-output $(SRC_DIR) || : + +clean: + rm -f pyflakes.log + rm -f pylint.log + rm -f sloccount.sc + rm -f output.xml + rm -f coverage.xml + rm -f xunit.xml diff --git a/README.md b/README.md index 73b236877..9c7ff0357 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ -[PyRPL](http://lneuhaus.github.io/pyrpl/) +[PyRPL](http://www.pyrpl.org/) [![travis status](https://travis-ci.org/lneuhaus/pyrpl.svg?branch=master "Travisstatus")](https://travis-ci.org/lneuhaus/pyrpl) +[![appveyor status](https://ci.appveyor.com/api/projects/status/wv2acmg869acg5yy?svg=true)](https://ci.appveyor.com/project/lneuhaus/pyrpl) [![code coverage](https://codecov.io/github/lneuhaus/pyrpl/coverage.svg?branch=master "Code coverage")](https://codecov.io/gh/lneuhaus/pyrpl) [![Python versions on PyPI](https://img.shields.io/pypi/pyversions/pyrpl.svg)](https://pypi.python.org/pypi/pyrpl/) [![PyRPL version on PyPI](https://img.shields.io/pypi/v/pyrpl.svg "PyRPL on PyPI")](https://pypi.python.org/pypi/pyrpl/) -[![Download pyrpl](https://img.shields.io/sourceforge/dt/pyrpl.svg)](https://sourceforge.net/projects/pyrpl/files/latest/download) -[![Documentation Status](https://readthedocs.org/projects/pyrpl/badge/?version=latest)](http://pyrpl.readthedocs.io/en/latest/?badge=latest) +[![Download pyrpl](https://img.shields.io/sourceforge/dt/pyrpl.svg)](https://sourceforge.net/projects/pyrpl/files/) +[![Documentation Status](https://readthedocs.org/projects/pyrpl/badge/?version=latest)](http://pyrpl.readthedocs.io/en/latest/) [![join chat on gitter](https://badges.gitter.im/JoinChat.svg "Join chat on gitter")](https://gitter.im/lneuhaus/pyrpl) [![License](https://img.shields.io/pypi/l/pyrpl.svg)](https://github.com/lneuhaus/pyrpl/blob/master/LICENSE) @@ -15,7 +16,7 @@ PyRPL (Python RedPitaya Lockbox) turns your RedPitaya into a powerful DSP device, especially suitable as a digital lockbox and measurement device in quantum optics experiments. ## Website -The official PyRPL website address is [http://lneuhaus.github.io/pyrpl/](http://lneuhaus.github.io/pyrpl/). +The official PyRPL website address is [http://pyrpl.readthedocs.io/](http://pyrpl.readthedocs.io). The information on the website is more up-to-date than in this readme. ## Installation The easiest and fastest way to get PyRPL is to download and execute the [precompiled executable for windows](https://sourceforge.net/projects/pyrpl/files/latest/download). This option requires no extra programs to be installed on the computer. @@ -23,11 +24,11 @@ The easiest and fastest way to get PyRPL is to download and execute the [precomp If instead you would like to use and/or modify the source code, make sure you have an installation of Python (2.7, 3.4, 3.5, or 3.6). If you are new to Python or unexperienced with fighting installation issues, it is recommended to install the [Anaconda](https://www.continuum.io/downloads) Python distribution, which allows to install all PyRPL dependencies via ``` -conda install numpy scipy paramiko pandas nose pip pyqt qtpy pyqtgraph pyyaml +conda install numpy scipy paramiko pandas nose pip pyqt qtpy pyqtgraph pyyaml nbconvert ``` Check [this documentation section](http://pyrpl.readthedocs.io/en/latest/user_guide/installation/common_problems.html#anaconda-problems) for hints if you are unable to execute conda in a terminal. Alternatively, if you prefer creating a virtual environment for pyrpl, do so with the following two commands ``` -conda create -y -n pyrpl-env numpy scipy paramiko pandas nose pip pyqt qtpy pyqtgraph pyyaml +conda create -y -n pyrpl-env numpy scipy paramiko pandas nose pip pyqt qtpy pyqtgraph pyyaml nbconvert activate pyrpl-env ``` If you are not using Anaconda, you must manually install the python package [PyQt5](https://pypi.python.org/pypi/PyQt5) or [PyQt4](https://pypi.python.org/pypi/PyQt4), which requires a working C compiler installation on the system. diff --git a/README.rst b/README.rst deleted file mode 100644 index 3bb01abed..000000000 --- a/README.rst +++ /dev/null @@ -1,185 +0,0 @@ -` `__ -======================================= - -|travis status| |code coverage| |Python versions on PyPI| |PyrRPL version on PyPI| |Documentation Status| |join chat on gitter| |License| - -|Download PyRPL| |LGPLv3| - -PyRPL (Python RedPitaya Lockbox) turns your RedPitaya into a powerful DSP device, especially suitable as a digital lockbox and measurement device in quantum optics experiments. - -Website -------- - -Get started by checking out the `official PyRPL website `__. - -Installation ------------- - -The easiest and fastest way to get PyRPL is to download and execute the `precompiled executable for windows `__. This option requires no extra programs to be installed on the computer. - -If instead you would like to use and/or modify the source code, make sure you have an installation of Python (2.7, 3.4, 3.5, or 3.6). If you are new to Python or unexperienced with fighting installation issues, it is recommended to install the `Anaconda `__ Python distribution, which allows to install all PyRPL dependencies via - -:: - - conda install numpy scipy paramiko pandas nose pip pyqt qtpy pyqtgraph pyyaml - -Check `this wiki page `__ -for hints if you cannot execute conda in a terminal. Alternatively, if -you prefer creating a virtual environment for pyrpl, do so with the -following two commands - -:: - - conda create -y -n pyrpl-env numpy scipy paramiko pandas nose pip pyqt qtpy pyqtgraph pyyaml - activate pyrpl-env - -If you are not using Anaconda, you must manually install the python -package `PyQt5 `__ or -`PyQt4 `__, which requires a working -C compiler installation on the system. - -Next, clone (if you have a `git -client `__ installed - recommended -option) the pyrpl repository to your computer with - -:: - - git clone https://github.com/lneuhaus/pyrpl.git - -or `download and -extract `__ (if -you do not want to install git on your computer) the repository. - -Install PyRPL by navigating with the command line terminal (the one -where the pyrpl-env environment is active in case you are using -anaconda) into the pyrpl root directory and typing - -:: - - python setup.py develop - -Quick start ------------ - -First, hook up your Red Pitaya / STEMlab to a LAN accessible from your -computer (follow the instructions for this on redpitya.com and make sure -you can access your Red Pitaya with a web browser by typing its -ip-address / hostname into the address bar). In an IPython console or -JuPyter notebook, type - -:: - - from pyrpl import Pyrpl - p = Pyrpl(config='your_configuration_name', hostname='your_redpitaya_ip_address') - -The GUI should open and you can start playing around with it. By calling -pyrpl with different strings for 'your\_configuration\_name', your -settings for a given configuration will be automatically remembered by -PyRPL. You can drop the hostname argument after the first call of a -given configuration. Different RedPitayas with different configuration -names can be run simultaneously. - -Issues ------- - -We collect a list of common problems in a `dedicated wiki -page `__. -If you do not find your problem listed there, please report all problems -or wishes as new issues on `this -page `__, so we can fix it and -improve the future user experience. - -Unit test ---------- - -If you want to check whether PyRPL works correctly on your machine, -navigate with a command line terminal into the pyrpl root directory and -type the following commands (by substituting the ip-address / hostname -of your Red Pitaya, of course) - -:: - - set REDPITAYA_HOSTNAME=your_redpitaya_ip_address - nosetests - -All tests should take about 3 minutes and finish without failures or -errors. If there are errors, please report the console output as an -issue (see the section "Issues" below for detailed explanations). - -Next steps / documentation --------------------------- - -The full PyRPL documentation is hosted at `http://pyrpl.readthedocs/io`_. -We are still in the process of creating an up-to-date version of the -documentation of the current code. If the current documentation is wrong -or insufficient, please post an -`issue `__ and we will -prioritize documenting the part of code you need. You can find all -documentation in the subfolder -`"doc" `__. Get -started by reading our paper on PyRPL, reading the official `html -documentation `__, -or going through the -`tutorial.ipynb `__ -notebook. - -Updates -------- - -Since PyRPL is continuously improved, you should install upgrades if you -expect bugfixes by navigating into the pyrpl root directory on your -local harddisk computer and typing - -:: - - git pull - -FPGA bitfile generation (only for developers) ---------------------------------------------- - -In case you would like to modify the logic running on the FPGA, you -should make sure that you are able to generate a working bitfile on your -machine. To do so, you must install Vivado 2015.4 `(64-bit -windows `__](https://www.xilinx.com/member/forms/download/xef.html?filename=Xilinx\_Vivado\_SDK\_2015.4\_1118\_2\_Lin64.bin&akdm=1) -and `together with a working -license `__. -Next, with a terminal in the pyrpl root directory, type - -:: - - cd pyrpl/fpga - make - -Compilation should take between 10 and 30 minutes, depending on your -machine. If there are no errors during compilation, the new bitfile -(pyrpl/fpga/red\_pitaya.bin) will be automatically used at the next -restart of PyRPL. The best way to getting started is to skim through the -very short Makefile in the fpga directory and to continue by reading the -files mentioned in the makefile and the refences therein. All verilog -source code is located in the subdirectory pyrpl/fpga/rtl/. - -License -------- - -Please read our license file -`LICENSE `__ for -more information. - -.. |travis status| image:: https://travis-ci.org/lneuhaus/pyrpl.svg?branch=master - :target: https://travis-ci.org/lneuhaus/pyrpl -.. |code coverage| image:: https://codecov.io/github/lneuhaus/pyrpl/coverage.svg?branch=master - :target: https://codecov.io/gh/lneuhaus/pyrpl -.. |Python versions on PyPI| image:: https://img.shields.io/pypi/pyversions/pyrpl.svg - :target: https://pypi.python.org/pypi/pyrpl/ -.. |PyrRPL version on PyPI| image:: https://img.shields.io/pypi/v/pyrpl.svg - :target: https://pypi.python.org/pypi/pyrpl/ -.. |Documentation Status| image:: https://readthedocs.org/projects/pyrpl/badge/?version=latest - :target: http://pyrpl.readthedocs.io/en/latest/?badge=latest -.. |join chat on gitter| image:: https://badges.gitter.im/JoinChat.svg - :target: https://gitter.im/lneuhaus/pyrpl -.. |License| image:: https://img.shields.io/pypi/l/pyrpl.svg - :target: https://github.com/lneuhaus/pyrpl/blob/master/LICENSE -.. |Download PyRPL| image:: https://a.fsdn.com/con/app/sf-download-button - :target: https://sourceforge.net/projects/pyrpl/files/ -.. |LGPLv3| image:: https://www.gnu.org/graphics/gplv3-88x31.png - :target: https://www.gnu.org/licenses/gpl.html diff --git a/THANKS.txt b/THANKS.txt deleted file mode 100644 index fba3a307b..000000000 --- a/THANKS.txt +++ /dev/null @@ -1,21 +0,0 @@ -PyRPL is open source software that allows to use FPGA boards with -analog interfaces for measurement and control of real-world devices in -physics and engineering, notably experiments in quantum optics. It was -started in 2014 by Leonhard Neuhaus for controlling experiments in physics at -the Laboratoire Kastler Brossel in Paris, France. Its was initially based on -the open-source code for the Red Pitaya (www.redpitaya.com) and gradually -diverged away from it. In 2016, large parts of the graphical user interface -were added to the project by Samuel Deleglise. A number of additional people -have since contributed to PyRPL by discussing with us, testing, bug reporting, -or small changes to the code or documentation. Below is a partial list. If -you've been left off, please change this file by yourself or send an email -to the maintainer (currently neuhaus@lkb.upmc.fr). - -Jonas Neergard-Nielsen -Xueshi Guo -Jerome Degallaix -Pierre Clade -Matthew Winchester -Remi Metzdorff -Kevin Makles -Clement Chardin diff --git a/_config.yml b/_config.yml deleted file mode 100644 index c4192631f..000000000 --- a/_config.yml +++ /dev/null @@ -1 +0,0 @@ -theme: jekyll-theme-cayman \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..64903a123 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,126 @@ +# AppVeyor.com is a Continuous Integration service to build and run tests under Windows +# see https://github.com/ogrisel/python-appveyor-demo/blob/master/appveyor.yml + +# this script is largely inspired by https://github.com/obspy/obspy/blob/master/appveyor.yml +clone_depth: 50 + +environment: + global: + # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the + # /E:ON and /V:ON options are not enabled in the batch script intepreter + # See: http://stackoverflow.com/a/13751649/163740 + CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\misc\\appveyor\\run_with_env.cmd" + CI_URL: "--ci-url https://ci.appveyor.com/project/%APPVEYOR_REPO_NAME%/build/1.0.%APPVEYOR_BUILD_NUMBER%-%APPVEYOR_REPO_BRANCH%" + PR_URL: "--pr-url https://github.com/%APPVEYOR_REPO_NAME%/pull/%APPVEYOR_PULL_REQUEST_NUMBER%" + + matrix: + # Pre-installed Python versions, which Appveyor may upgrade to + # a later point release. + # See: http://www.appveyor.com/docs/installed-software#python + # only use python 3.5 for building .exe (for auto-upload of binaries) + - PYTHON: "C:\\Miniconda35-x64" + PYTHON_VERSION: "3.5" + PYTHON_ARCH: "64" + +init: + # If there is a newer build queued for the same PR, cancel this one. + # The AppVeyor 'rollout builds' option is supposed to serve the same + # purpose but it is problematic because it tends to cancel builds pushed + # directly to master instead of just PR builds (or the converse). + # credits: JuliaLang developers. + - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` + https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` + Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` + throw "There are newer queued builds for this pull request, failing early." } + - ps: | + If (($env:SKIP_NOTAG -eq "true") -and ($env:APPVEYOR_REPO_TAG -ne "true")) { + throw "Skipping Python version, not a tag." + } +install: + # - ECHO "Filesystem root:" + # - ps: "ls \"C:/\"" + + # - ECHO "Installed SDKs:" + # - ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\"" + + # Prepend Python to the PATH of this build (this cannot be + # done from inside the powershell script as it would require to restart + # the parent CMD process). + - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" + + # Check that we have the expected version and architecture for Python + - "python -c \"import sys; print(sys.version)\"" + + # Upgrade to the latest version of pip to avoid it displaying warnings + # about it being out of date. + - "pip install --disable-pip-version-check --user --upgrade pip" + + # Install the build and runtime dependencies of the project. + - "conda update -q --yes conda" + - "conda config --add channels conda-forge" + # add channel twice to move it to the top of the channel priority list + - "conda config --add channels conda-forge" + - "conda config --set always_yes yes --set changeps1 no" + # Create a conda environment using the required packages. + - "conda create -q --yes -n test-environment python=%PYTHON_VERSION% numpy scipy paramiko pandas nose pip pyqt qtpy" + - "activate test-environment" + # additional dependencies + - "conda install --yes -c conda-forge coveralls" + - "pip install coverage codecov" + # Useful for debugging any issues with conda + - "conda info -a" + # list package versions + - "conda list" + # installation + - "python setup.py install" + # pyinstaller installation + # - "pip install https://github.com/lneuhaus/pyinstaller/archive/develop.zip" + # - "pip install pyinstaller==3.2" + - pip install pypiwin32 + - cd .. + # - pip install https://github.com/pyinstaller/pyinstaller/tarball/develop + - git clone --branch develop --depth=1 https://www.github.com/lneuhaus/pyinstaller.git + - cd pyinstaller + #- git fetch origin pull/2991/head:pr2991 + #- git checkout pr2991 + - git status + - python setup.py develop + - cd .. + - cd pyrpl + # set hostname to fake in case pyrpl is executed + - "SET REDPITAYA_HOSTNAME=_FAKE_" + # fix a strange bug with pyinstaller packaging + # - "copy %PYTHON%\\envs\\test-environment\\qt.conf %PYTHON%\\envs\\test-environment\\ScriptS\\qt.conf" + - "SET QT_QPA_PLATFORM_PLUGIN_PATH=%PYTHON%\\envs\\test-environment\\Library\\plugins\\platforms" + +# Not a .NET project, we build in the install step instead +build: false + +# the actual tes is whether pyinstaller manages to install pyrpl +test_script: + - "pyinstaller pyrpl.spec" + - "mv dist/pyrpl.exe ./pyrpl-windows.exe" +# optional tests: + - ps: $MyProcess = Start-Process -NoNewWindow -FilePath "C:\projects\pyrpl\pyrpl-windows.exe" -ArgumentList "config=test_windows hostname=_FAKE_" -RedirectStandardOutput out.txt -RedirectStandardError err.txt + - timeout 20 +# print output to screen + - type out.txt + - type err.txt +# - ps: "Stop-Process -Id $MyProcess.Id" +# - ps: Stop-Process -Name "pyrpl-windows.exe" +# - "nosetests" + +# deploy to sourceforge if build was successful and commit has a tag +on_success: + - ps: | + If ($env:APPVEYOR_REPO_TAG -eq "true") { + python .deploy_to_sourceforge.py pyrpl-windows.exe; + } + - ps: | + If ($env:APPVEYOR_REPO_TAG -ne "true") { + mv ./pyrpl-windows.exe ./pyrpl-windows-develop.exe; + python .deploy_to_sourceforge.py pyrpl-windows-develop.exe; + } + +# Do not build feature branch with open Pull Requests +skip_branch_with_pr: true diff --git a/docs/1.3 b/docs/1.3 deleted file mode 100644 index 4024553c0..000000000 --- a/docs/1.3 +++ /dev/null @@ -1,6 +0,0 @@ -Requirement already satisfied: sphinx in c:\python27\lib\site-packages -Requirement already satisfied: Pygments>=1.2 in c:\python27\lib\site-packages (from sphinx) -Requirement already satisfied: docutils>=0.7 in c:\python27\lib\site-packages (from sphinx) -Requirement already satisfied: pillow>=2.0.0 in c:\python27\lib\site-packages (from sphinx) -Requirement already satisfied: Jinja2>=2.3 in c:\python27\lib\site-packages (from sphinx) -Requirement already satisfied: markupsafe in c:\python27\lib\site-packages (from Jinja2>=2.3->sphinx) diff --git a/docs/example-notebooks/asg-synchronization-example.ipynb b/docs/example-notebooks/asg-synchronization-example.ipynb new file mode 100644 index 000000000..d2332b2b0 --- /dev/null +++ b/docs/example-notebooks/asg-synchronization-example.ipynb @@ -0,0 +1,121 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "#define hostname\n", + "HOSTNAME = '192.168.1.100'" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pyrpl.redpitaya:Successfully connected to Redpitaya with hostname 192.168.178.26.\n" + ] + } + ], + "source": [ + "import pyrpl\n", + "p = pyrpl.Pyrpl(config=\"\", # do not use a config file \n", + " hostname=HOSTNAME)\n", + "rp = p.redpitaya # shortcut for the the redpitaya handler" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "# setup both asgs\n", + "for asg in [rp.asg0, rp.asg1]:\n", + " asg.setup(waveform='square', \n", + " amplitude=0.5, \n", + " offset=0, \n", + " frequency=1e6, \n", + " output_direct='off',\n", + " trigger_source='immediately',\n", + " )\n", + "\n", + "# make the assertion that signals are not synhronized\n", + "ch1, ch2 = rp.scope.curve()\n", + "assert ((ch1-ch2)!=0).any(), 'asg channel outputs are identical'\n", + "\n", + "# manually check on the scope that signals are not synchronized\n", + "rp.scope.setup(input1='asg0',\n", + " input2='asg1',\n", + " duration=1e-6,\n", + " average=False,\n", + " trigger_source='asg0',\n", + " rolling_mode=False,\n", + " running_state='running_continuous'\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "# setup the trigger pin\n", + "rp.hk.expansion_P0_output = True\n", + "rp.hk.expansion_P0 = False\n", + "\n", + "# setup asg trigger\n", + "for asg in [rp.asg0, rp.asg1]:\n", + " asg.trigger_source = \"ext_positive_edge\"\n", + " \n", + "# launch the trigger by creating a 0-to-1 transition on trigger pin\n", + "rp.hk.expansion_P0 = True\n", + "rp.hk.expansion_P0 = False\n", + "\n", + "# make the assertion that signals are synhronized\n", + "ch1, ch2 = rp.scope.curve()\n", + "assert ((ch1-ch2)==0).all(), 'asg channel outputs are not identical'\n", + "\n", + "# now check that the asg signals are synchronized on the scope...\n", + "rp.scope.setup(running_state='running_continuous')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/example-notebooks/async_acquisition.ipynb b/docs/example-notebooks/async_acquisition.ipynb new file mode 100644 index 000000000..36db0c684 --- /dev/null +++ b/docs/example-notebooks/async_acquisition.ipynb @@ -0,0 +1,110 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8) Using asynchronous functions with python 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Pyrpl uses the Qt eventloop to perform asynchronous tasks, but it has been set as the default loop of `asyncio`, such that you only need to learn how to use the standard python module [`asyncio`](https://docs.python.org/3/library/asyncio.html), and you don't need to know anything about Qt. To give you a quick overview of what can be done, we present in the following block an exemple of 2 tasks running in parrallele. The first one mimicks a temperature control loop, measuring periodically a signal every 1 s, and changing the offset of an `asg` based on the measured value (we realize this way a slow and rudimentary software pid). In parrallele, another task consists in repeatedly shifting the frequency of an asg, and measuring an averaged spectrum on the spectrum analyzer.\n", + "\n", + "Both tasks are defined by coroutines (a python function that is preceded by the keyword `async`, and that can contain the keyword `await`). Basically, the execution of each coroutine is interrupted whenever the keyword `await` is encountered, giving the chance to other tasks to be executed. It will only be resumed once the underlying coroutine's value becomes ready.\n", + "\n", + "Finally to execute the cocroutines, it is not enough to call `my_coroutine()`, since we need to send the task to the event loop. For that, we use the function `ensure_future` from pyrpl.async_utils module. This function immediately returns an object that is not the result of the task (not the object that is behind `return` inside the coroutine), but rather a Future object, that can be used to retrieve the actual result once it is ready (this is done by calling `future.result()` latter on).\n", + "\n", + "If you are executing the code inside the ipython notebook, then, this is all you have to do, since an event loop is already running in the back (a qt eventloop if you are using the option %pylab qt). Otherwise, you have to use one of the functions (`LOOP.run_forever()`, `LOOP.run_until_complete()`, or `LOOP.run_in_executor()`) to launch the eventloop." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "#define hostname\n", + "HOSTNAME = '192.169.1.100'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pyrpl import Pyrpl\n", + "p = Pyrpl(config='', # do not use a config file here \n", + " hostname=HOSTNAME)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#no-test # Will be tested only on branch python3-only\n", + "async def run_temperature_lock(setpoint=0.1): # coroutines can receive arguments\n", + " with p.asgs.pop(\"temperature\") as asg: # use the context manager \"with\" to \n", + " # make sure the asg will be freed after the acquisition\n", + " asg.setup(frequency=0, amplitue=0, offset=0) # Use the asg as a dummy \n", + " while IS_TEMP_LOCK_ACTIVE: # The loop will run untill this flag is manually changed to False\n", + " await asyncio.sleep(1) # Give way to other coroutines for 1 s\n", + " measured_temp = asg.offset # Dummy \"temperature\" measurment\n", + " asg.offset+= (setpoint - measured_temp)*0.1 # feedback with an integral gain\n", + " print(\"measured temp: \", measured_temp) # print the measured value to see how the execution flow works\n", + " \n", + "async def run_n_fits(n): # a coroutine to launch n acquisitions\n", + " sa = p.spectrumanalyzer\n", + " with p.asgs.pop(\"fit_spectra\") as asg: # use contextmanager again\n", + " asg.setup(output_direct='out1',\n", + " trigger_source='immediately')\n", + " freqs = [] # variables stay available all along the coroutine's execution\n", + " for i in range(n): # The coroutine qill be executed several times on the await statement inside this loop\n", + " asg.setup(frequency=1000*i) # Move the asg frequency\n", + " sa.setup(input=asg, avg=10, span=100e3, baseband=True) # setup the sa for the acquisition\n", + " spectrum = await sa.single_async() # wait for 10 averages to be ready\n", + " freq = sa.data_x[spectrum.argmax()] # take the max of the spectrum\n", + " freqs.append(freq) # append it to the result\n", + " print(\"measured peak frequency: \", freq) # print to show how the execution goes\n", + " return freqs # Once the execution is over, the Future will be filled with the result...\n", + "\n", + "from pyrpl.async_utils import ensure_future, sleep, wait\n", + "IS_TEMP_LOCK_ACTIVE = True\n", + "\n", + "temp_future = ensure_future(run_temperature_lock(0.5)) # send temperature control task to the eventloop\n", + "fits_future = ensure_future(run_n_fits(50)) # send spectrum measurement task to the eventloop \n", + "sleep(5)\n", + "IS_TEMP_LOCK_ACTIVE = False\n", + "print(wait(fits_future))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/example-notebooks/iq-accumulator-example.ipynb b/docs/example-notebooks/iq-accumulator-example.ipynb new file mode 100644 index 000000000..f392d8fc8 --- /dev/null +++ b/docs/example-notebooks/iq-accumulator-example.ipynb @@ -0,0 +1,167 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "#define hostname\n", + "HOSTNAME = '192.168.1.100'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pyrpl import Pyrpl\n", + "p = Pyrpl(config=\"\", # do not use a configfile\n", + " hostname=HOSTNAME)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "p.rp.iq2.setup?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "iq = p.rp.iq2\n", + "print('Retrieved iq module \"%s\"' % iq.name)\n", + "\n", + "# setup the iq module iq2 so that both demodulation quadratures are visible on the scope\n", + "iq.setup(input='in1',\n", + " amplitude=0.5,\n", + " output_direct='out1',\n", + " output_signal='quadrature',\n", + " frequency=2**14, # set the frequency to half the demodulation\n", + " phase=0, #tune the phase as necessary\n", + " modulation_at_2f='off',\n", + " demodulation_at_2f='on',\n", + " acbandwidth=500)\n", + "\n", + "scope = p.rp.scope\n", + "scope.setup(input1='iq2', input2='iq2_2',\n", + " duration=8,\n", + " average=True,\n", + " trigger_source='immediately',\n", + " rolling_mode=True)\n", + "\n", + "# now you can view the measurement on the scope" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# possibly tune the demodulation phase or quadrature_factor, or frequency\n", + "iq.phase = 90\n", + "iq.quadrature_factor = 100\n", + "iq.frequency -= 0.1\n", + "print(iq.frequency)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Continuous low-level readout of the IQ accumulator magnitude and phase" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# How it works:\n", + "# When iq.frequency is written, the accumulator waits for iq._na_sleepcycles clock cycles (a 8 ns)\n", + "# and then averages over iq._na_averages clock cycles, the resulting sum being ready in iq._nadata_total\n", + "print(iq._na_sleepcycles, iq._na_averages, iq._nadata_total)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Example\n", + "from pyrpl.async_utils import sleep\n", + "import numpy as np\n", + "\n", + "\n", + "iq._na_sleepcycles = 0 # we need no sleep cycles, since there is no transient behaviour in our setup\n", + "\n", + "avg_duration = 0.1 # average over 0.1 s\n", + "iq._na_averages = avg_duration / 8e-9 # set number of averages of accumulator\n", + "\n", + "print(\"Sleep for\", iq._na_sleepcycles, \"cycles\")\n", + "print(\"Average for\", iq._na_averages, \"cycles\\n\")\n", + "\n", + "# make a local storage of iq.frquency, amplitude, _na_averages to save the \n", + "# comminucation time needed to read those values all over again\n", + "frequency = iq.frequency\n", + "na_averages = iq._na_averages\n", + "amplitude = iq.amplitude\n", + "\n", + "# Acquisition\n", + "print(\"Magnitude\\t phase\")\n", + "for i in range(5): # acquisition loop, take 5 samples\n", + " iq.frequency = frequency # writing to the frequency register triggers the next averaging run \n", + " sleep(na_averages*8e-9) # wait for averaging to finish\n", + " signal = iq._nadata_total/na_averages # result is stored in iq._nadata\n", + " dB = 20*np.log10(signal/amplitude) # conversion in dB w.r.t. drive amplitude\n", + " mag = np.abs(signal) # magnitude\n", + " phase = np.angle(signal, deg=True)%360 # phase in degrees\n", + " print(\"%.2e\\t %.1f\" %(mag, phase))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/example-notebooks/pwm-example.ipynb b/docs/example-notebooks/pwm-example.ipynb new file mode 100644 index 000000000..44da1ede8 --- /dev/null +++ b/docs/example-notebooks/pwm-example.ipynb @@ -0,0 +1,85 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "#define hostname\n", + "HOSTNAME = '192.168.1.100'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pyrpl\n", + "p = pyrpl.Pyrpl(config=\"\", # do not use a config file \n", + " hostname=HOSTNAME)\n", + "rp = p.redpitaya # shortcut for the the redpitaya handler\n", + "\n", + "print(rp.pwm0.__doc__) # print the help, same is possible with \"rp.pwm0?\" in interactive python console\n", + "# last line of help text is outdated and means to say: \"Currently, only pwm0 and pwm1 are available.\"\n", + "\n", + "print(\"Default inputs for pwm0/1: %s/%s\\n\"%(rp.pwm0.input, rp.pwm1.input))\n", + "print(\"Input options for pwm0/1: \\n%s\"%rp.pwm0.input_options)\n", + "rp.pwm0.input = 'pid0' # connect the output_signal of pid0 to pwm0 pin\n", + "rp.pwm1.input = rp.asg0 # connect the output_signal of asg0 to pwm0 pin\n", + "print(\"Current inputs for pwm0/1: %s/%s\\n\"%(rp.pwm0.input, rp.pwm1.input))\n", + "\n", + "# the pwm-module maps from the DSP signal voltages between +/-1V to the output voltages of the PWM pins from 0 to 1.8V.\n", + "# we have a direct read-access to the current output voltages for all four PWM pins:\n", + "print(\"Current output voltages at the four PWM pins: \", rp.ams.dac0, rp.ams.dac1, rp.ams.dac2, rp.ams.dac3)\n", + "\n", + "# we can set the static output voltage directly for the remaining pins dac2 and dac3 (those are not interfaced)\n", + "rp.ams.dac2 = 0 # minimum possible value, everything smaller is clipped to 0\n", + "rp.ams.dac3 = 1.8 # maximumum possible value, everything larger is clipped to 1.8\n", + "print(\"Current output voltages at the four PWM pins: \", rp.ams.dac0, rp.ams.dac1, rp.ams.dac2, rp.ams.dac3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/example-notebooks/tutorial.ipynb b/docs/example-notebooks/tutorial.ipynb new file mode 100644 index 000000000..dd6aff2a4 --- /dev/null +++ b/docs/example-notebooks/tutorial.ipynb @@ -0,0 +1,1607 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Introduction to pyrpl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1) Introduction" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The RedPitaya is an affordable FPGA board with fast analog inputs and outputs. This makes it interesting also for quantum optics experiments. The software package PyRPL (Python RedPitaya Lockbox) is an implementation of many devices that are needed for optics experiments every day. The user interface and all high-level functionality is written in python, but an essential part of the software is hidden in a custom FPGA design (based on the official RedPitaya software version 0.95). While most users probably never want to touch the FPGA design, the Verilog source code is provided together with this package and may be modified to customize the software to your needs. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 2) Table of contents" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this document, you will find the following sections:\n", + "1. Introduction\n", + "2. ToC\n", + "3. Installation\n", + "4. First steps\n", + "5. RedPitaya Modules\n", + "6. The Pyrpl class\n", + "7. The Graphical User Interface\n", + "\n", + "If you are using Pyrpl for the first time, you should read sections 1-4. This will take about 15 minutes and should leave you able to communicate with your RedPitaya via python.\n", + "\n", + "If you plan to use Pyrpl for a project that is not related to quantum optics, you probably want to go to section 5 then and omit section 6 altogether. Inversely, if you are only interested in a powerful tool for quantum optics and dont care about the details of the implementation, go to section 6. If you plan to contribute to the repository, you should definitely read section 5 to get an idea of what this software package realy does, and where help is needed. Finaly, Pyrpl also comes with a Graphical User Interface (GUI) to interactively control the modules described in section 5. Please, read section 7 for a quick description of the GUI." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 3) Installation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Option 3: Simple clone from GitHub (developers)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If instead you plan to synchronize with github on a regular basis, you can also leave the downloaded code where it is and add the parent directory of the pyrpl folder to the PYTHONPATH environment variable as described in this thread: http://stackoverflow.com/questions/3402168/permanently-add-a-directory-to-pythonpath. For all beta-testers and developers, this is the preferred option. So the typical PYTHONPATH environment variable should look somewhat like this:\n", + "$\\texttt{PYTHONPATH=C:\\OTHER_MODULE;C:\\GITHUB\\PYRPL}$\n", + "\n", + "If you are experiencing problems with the dependencies on other python packages, executing the following command in the pyrpl directory might help:\n", + "\n", + "$\\texttt{python setup.py install develop}$\n", + "\n", + "If at a later point, you have the impression that updates from github are not reflected in the program's behavior, try this: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pyrpl\n", + "print(pyrpl.__file__)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Should the directory not be the one of your local github installation, you might have an older version of pyrpl installed. Just delete any such directories other than your principal github clone and everything should work. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Option 2: from GitHub using setuptools (beta version)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Download the code manually from https://github.com/lneuhaus/pyrpl/archive/master.zip and unzip it or get it directly from git by typing \n", + "\n", + "$\\texttt{git clone https://github.com/lneuhaus/pyrpl.git YOUR_DESTINATIONFOLDER}$\n", + "\n", + "In a command line shell, navigate into your new local pyrplockbox directory and execute\n", + "\n", + "$\\texttt{python setup.py install}$\n", + "\n", + "This copies the files into the side-package directory of python. The setup should make sure that you have the python libraries paramiko (http://www.paramiko.org/installing.html) and scp (https://pypi.python.org/pypi/scp) installed. If this is not the case you will get a corresponding error message in a later step of this tutorial. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Option 1: with pip (coming soon)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you have pip correctly installed, executing the following line in a command line should install pyrplockbox and all dependencies: \n", + "\n", + "$\\texttt{pip install pyrpl}$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "#no-test\n", + "!pip install pyrpl #if you look at this file in ipython notebook, just execute this cell to install pyrplockbox" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Compiling the server application (optional)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The software comes with a precompiled version of the server application (written in C) that runs on the RedPitaya. This application is uploaded automatically when you start the connection. If you made changes to this file, you can recompile it by typing\n", + "\n", + "$\\texttt{python setup.py compile_server}$\n", + "\n", + "For this to work, you must have gcc and the cross-compiling libraries installed. Basically, if you can compile any of the official RedPitaya software written in C, then this should work, too. \n", + "If you do not have a working cross-compiler installed on your UserPC, you can also compile directly on the RedPitaya (tested with ecosystem v0.95). To do so, you must upload the directory pyrpl/monitor_server on the redpitaya, and launch the compilation with the command\n", + "$\\texttt{make CROSS_COMPILE=}$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Compiling the FPGA bitfile (optional)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you would like to modify the FPGA code or just make sure that it can be compiled, you should have a working installation of Vivado 2015.4. For windows users it is recommended to set up a virtual machine with Ubuntu on which the compiler can be run in order to avoid any compatibility problems. For the FPGA part, you only need the /fpga subdirectory of this software. Make sure it is somewhere in the file system of the machine with the vivado installation. Then type the following commands. You should adapt the path in the first and second commands to the locations of the Vivado installation / the fpga directory in your filesystem: \n", + "\n", + "$\\texttt{source /opt/Xilinx/Vivado/2015.4/settings64.sh}$\n", + "\n", + "$\\texttt{cd /home/myusername/fpga}$\n", + "\n", + "$\\texttt{make}$\n", + "\n", + "The compilation should take between 15 and 30 minutes. The result will be the file $\\texttt{fpga/red_pitaya.bin}$. To test the new FPGA design, make sure that this file in the fpga subdirectory of your pyrpl code directory. That is, if you used a virtual machine for the compilation, you must copy the file back to the original machine on which you run pyrpl." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Unitary tests (optional)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order to make sure that any recent changes do not affect prior functionality, a large number of automated tests have been implemented. Every push to the github repository is automatically installed tested on an empty virtual linux system. However, the testing server has currently no RedPitaya available to run tests directly on the FPGA. Therefore it is also useful to run these tests on your local machine in case you modified the code. \n", + "\n", + "Currently, the tests confirm that \n", + "- all pyrpl modules can be loaded in python\n", + "- all designated registers can be read and written\n", + "- future: functionality of all major submodules against reference benchmarks\n", + "\n", + "To run the test, navigate in command line into the pyrpl directory and type\n", + "\n", + "$\\texttt{set REDPITAYA=192.168.1.100}$ (in windows) or \n", + "\n", + "$\\texttt{export REDPITAYA=192.168.1.100}$ (in linux)\n", + "\n", + "$\\texttt{python setup.py nosetests}$\n", + "\n", + "The first command tells the test at which IP address it can find a RedPitaya. The last command runs the actual test. After a few seconds, there should be some output saying that the software has passed more than 140 tests. \n", + "\n", + "After you have implemented additional features, you are encouraged to add unitary tests to consolidate the changes. If you immediately validate your changes with unitary tests, this will result in a huge productivity improvement for you. You can find all test files in the folder $\\texttt{pyrpl/pyrpl/test}$, and the existing examples (notably $\\texttt{test_example.py}$) should give you a good point to start. As long as you add a function starting with 'test_' in one of these files, your test should automatically run along with the others. As you add more tests, you will see the number of total tests increase when you run the test launcher. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Workflow to submit code changes (for developers)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As soon as the code will have reached version 0.9.0.3 (high-level unitary tests implemented and passing, approx. end of May 2016), we will consider the master branch of the github repository as the stable pre-release version. The goal is that the master branch will guarantee functionality at all times. \n", + "\n", + "Any changes to the code, if they do not pass the unitary tests or have not been tested, are to be submitted as pull-requests in order not to endanger the stability of the master branch. We will briefly desribe how to properly submit your changes in that scenario. \n", + "\n", + "Let's say you already changed the code of your local clone of pyrpl. Instead of directly committing the change to the master branch, you should create your own branch. In the windows application of github, when you are looking at the pyrpl repository, there is a small symbol looking like a steet bifurcation in the upper left corner, that says \"Create new branch\" when you hold the cursor over it. Click it and enter the name of your branch \"leos development branch\" or similar. The program will automatically switch to that branch. Now you can commit your changes, and then hit the \"publish\" or \"sync\" button in the upper right. That will upload your changes so everyone can see and test them. \n", + "\n", + "You can continue working on your branch, add more commits and sync them with the online repository until your change is working. If the master branch has changed in the meantime, just click 'sync' to download them, and then the button \"update from master\" (upper left corner of the window) that will insert the most recent changes of the master branch into your branch. If the button doesn't work, that means that there are no changes available. This way you can benefit from the updates of the stable pre-release version, as long as they don't conflict with the changes you have been working on. If there are conflicts, github will wait for you to resolve them. In case you have been recompiling the fpga, there will always be a conflict w.r.t. the file 'red_pitaya.bin' (since it is a binary file, github cannot simply merge the differences you implemented). The best way to deal with this problem is to recompile the fpga bitfile after the 'update from master'. This way the binary file in your repository will correspond to the fpga code of the merged verilog files, and github will understand from the most recent modification date of the file that your local version of red_pitaya.bin is the one to keep. \n", + "\n", + "At some point, you might want to insert your changes into the master branch, because they have been well-tested and are going to be useful for everyone else, too. To do so, after having committed and synced all recent changes to your branch, click on \"Pull request\" in the upper right corner, enter a title and description concerning the changes you have made, and click \"Send pull request\". Now your job is done. I will review and test the modifications of your code once again, possibly fix incompatibility issues, and merge it into the master branch once all is well. After the merge, you can delete your development branch. If you plan to continue working on related changes, you can also keep the branch and send pull requests later on. If you plan to work on a different feature, I recommend you create a new branch with a name related to the new feature, since this will make the evolution history of the feature more understandable for others. Or, if you would like to go back to following the master branch, click on the little downward arrow besides the name of your branch close to the street bifurcation symbol in the upper left of the github window. You will be able to choose which branch to work on, and to select master. \n", + "\n", + "Let's all try to stick to this protocol. It might seem a little complicated at first, but you will quikly appreciate the fact that other people's mistakes won't be able to endanger your working code, and that by following the commits of the master branch alone, you will realize if an update is incompatible with your work." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4) First steps" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If the installation went well, you should now be able to load the package in python. If that works you can pass directly to the next section 'Connecting to the RedPitaya'." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from pyrpl import Pyrpl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Sometimes, python has problems finding the path to pyrplockbox. In that case you should add the pyrplockbox directory to your pythonpath environment variable (http://stackoverflow.com/questions/3402168/permanently-add-a-directory-to-pythonpath). If you do not know how to do that, just manually navigate the ipython console to the directory, for example: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#no-test\n", + "cd c:\\lneuhaus\\github\\pyrpl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now retry to load the module. It should really work now. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from pyrpl import Pyrpl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Connecting to the RedPitaya" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You should have a working SD card (any version of the SD card content is okay) in your RedPitaya (for instructions see http://redpitaya.com/quick-start/). The RedPitaya should be connected via ethernet to your computer. To set this up, there is plenty of instructions on the RedPitaya website (http://redpitaya.com/quick-start/). If you type the ip address of your module in a browser, you should be able to start the different apps from the manufacturer. The default address is http://192.168.1.100.\n", + "If this works, we can load the python interface of pyrplockbox by specifying the RedPitaya's ip address. If you leave the HOSTNAME blanck, a popup window will open up to let you choose among the various connected Redpitayas on your local network." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "#define hostname\n", + "HOSTNAME = \"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pyrpl import Pyrpl\n", + "p = Pyrpl(config='', # do not use a config file\n", + " #config='tutorial', # this would continuously save the current redpitaya state to a file \"tutorial.yml\" \n", + " hostname=HOSTNAME)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you see at least one '>' symbol, your computer has successfully connected to your RedPitaya via SSH. This means that your connection works. The message 'Server application started on port 2222' means that your computer has sucessfully installed and started a server application on your RedPitaya. Once you get 'Client started with success', your python session has successfully connected to that server and all things are in place to get started." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Basic communication with your RedPitaya" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#check the value of input1\n", + "print(p.rp.scope.voltage_in1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With the last command, you have successfully retrieved a value from an FPGA register. This operation takes about 300 µs on my computer. So there is enough time to repeat the reading n times." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#see how the adc reading fluctuates over time\n", + "import time\n", + "from matplotlib import pyplot as plt\n", + "times,data = [],[]\n", + "t0 = time.time()\n", + "n = 3000\n", + "for i in range(n):\n", + " times.append(time.time()-t0)\n", + " data.append(p.rp.scope.voltage_in1)\n", + "print(\"Rough time to read one FPGA register: \", (time.time()-t0)/n*1e6, \"µs\")\n", + "%matplotlib inline\n", + "f, axarr = plt.subplots(1,2, sharey=True)\n", + "axarr[0].plot(times, data, \"+\");\n", + "axarr[0].set_title(\"ADC voltage vs time\");\n", + "axarr[1].hist(data, bins=10,normed=True, orientation=\"horizontal\");\n", + "axarr[1].set_title(\"ADC voltage histogram\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You see that the input values are not exactly zero. This is normal with all RedPitayas as some offsets are hard to keep zero when the environment changes (temperature etc.). So we will have to compensate for the offsets with our software. Another thing is that you see quite a bit of scatter beetween the points - almost as much that you do not see that the datapoints are quantized. The conclusion here is that the input noise is typically not totally negligible. Therefore we will need to use every trick at hand to get optimal noise performance. \n", + "\n", + "After reading from the RedPitaya, let's now try to write to the register controlling the first 8 yellow LED's on the board. The number written to the LED register is displayed on the LED array in binary representation. You should see some fast flashing of the yellow leds for a few seconds when you execute the next block." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "#blink some leds for 5 seconds\n", + "from time import sleep\n", + "for i in range(1025):\n", + " p.rp.hk.led=i\n", + " sleep(0.005)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# now feel free to play around a little to get familiar with binary representation by looking at the leds.\n", + "from time import sleep\n", + "p.rp.hk.led = 0b00000001\n", + "for i in range(10):\n", + " p.rp.hk.led = ~p.rp.hk.led>>1\n", + " sleep(0.2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import random\n", + "for i in range(100):\n", + " p.rp.hk.led = random.randint(0,255)\n", + " sleep(0.02)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5) RedPitaya modules" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's now look a bit closer at the class RedPitaya. Besides managing the communication with your board, it contains different modules that represent the different sections of the FPGA. You already encountered two of them in the example above: \"hk\" and \"scope\". Here is the full list of modules:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "r = p.rp #redpitaya object\n", + "r.hk #\"housekeeping\" = LEDs and digital inputs/outputs\n", + "r.ams #\"analog mixed signals\" = auxiliary ADCs and DACs.\n", + "\n", + "r.scope #oscilloscope interface\n", + "\n", + "r.asg0 #\"arbitrary signal generator\" channel 1\n", + "r.asg1 #\"arbitrary signal generator\" channel 2\n", + "\n", + "r.pid0 #first of four PID modules\n", + "r.pid1\n", + "r.pid2\n", + "\n", + "r.iq0 #first of three I+Q quadrature demodulation/modulation modules\n", + "r.iq1\n", + "r.iq2\n", + "\n", + "r.iir #\"infinite impules response\" filter module that can realize complex transfer functions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ASG and Scope module" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Arbitrary Signal Generator" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are two Arbitrary Signal Generator modules: asg1 and asg2. For these modules, any waveform composed of $2^{14}$ programmable points is sent to the output with arbitrary frequency and start phase upon a trigger event. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "asg = r.asg0 # make a shortcut\n", + "print(\"Trigger sources:\", asg.trigger_sources)\n", + "print(\"Output options: \", asg.output_directs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's set up the ASG to output a sawtooth signal of amplitude 0.8 V (peak-to-peak 1.6 V) at 1 MHz on output 2:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "asg.output_direct = 'out2'\n", + "asg.setup(waveform='halframp', frequency=20e4, amplitude=0.8, offset=0, trigger_source='immediately')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Oscilloscope" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The scope works similar to the ASG but in reverse: Two channels are available. A table of $2^{14}$ datapoints for each channel is filled with the time series of incoming data. Downloading a full trace takes about 10 ms over standard ethernet. The rate at which the memory is filled is the sampling rate (125 MHz) divided by the value of 'decimation'. The property 'average' decides whether each datapoint is a single sample or the average of all samples over the decimation interval. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s = p.rp.scope # shortcut\n", + "print(\"Available decimation factors:\", s.decimations)\n", + "print(\"Trigger sources:\", s.trigger_sources)\n", + "print(\"Available inputs: \", s.inputs)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s.inputs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's have a look at a signal generated by asg1. Later we will use convenience functions to reduce the amount of code necessary to set up the scope:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pyrpl.async_utils import sleep\n", + "from pyrpl import RedPitaya\n", + "\n", + "#reload everything\n", + "r = p.rp #redpitaya object\n", + "asg = r.asg1\n", + "s = r.scope\n", + "\n", + "# turn off asg so the scope has a chance to measure its \"off-state\" as well \n", + "asg.output_direct = \"off\"\n", + "\n", + "# setup scope\n", + "s.input1 = 'asg1'\n", + "\n", + "# pass asg signal through pid0 with a simple integrator - just for fun (detailed explanations for pid will follow)\n", + "r.pid0.input = 'asg1' \n", + "r.pid0.ival = 0 # reset the integrator to zero\n", + "r.pid0.i = 1000 # unity gain frequency of 1000 hz\n", + "r.pid0.p = 1.0 # proportional gain of 1.0\n", + "r.pid0.inputfilter = [0,0,0,0] # leave input filter disabled for now\n", + "\n", + "# show pid output on channel2\n", + "s.input2 = 'pid0'\n", + "\n", + "# trig at zero volt crossing\n", + "s.threshold_ch1 = 0 \n", + "\n", + "# positive/negative slope is detected by waiting for input to \n", + "# sweept through hysteresis around the trigger threshold in \n", + "# the right direction \n", + "s.hysteresis_ch1 = 0.01\n", + "\n", + "# trigger on the input signal positive slope\n", + "s.trigger_source = 'ch1_positive_edge'\n", + "\n", + "# take data symetrically around the trigger event\n", + "s.trigger_delay = 0\n", + "\n", + "# set decimation factor to 64 -> full scope trace is 8ns * 2^14 * decimation = 8.3 ms long\n", + "s.decimation = 64\n", + "\n", + "# only 1 trace average\n", + "s.trace_average = 1\n", + "\n", + "# setup the scope for an acquisition\n", + "curve = s.single_async()\n", + "sleep(0.001)\n", + "print(\"\\nBefore turning on asg:\")\n", + "print(\"Curve ready:\", s.curve_ready()) # trigger should still be armed\n", + "\n", + "# turn on asg and leave enough time for the scope to record the data\n", + "asg.setup(frequency=1e3, amplitude=0.3, start_phase=90, waveform='halframp', trigger_source='immediately')\n", + "sleep(0.010)\n", + "\n", + "# check that the trigger has been disarmed\n", + "print(\"\\nAfter turning on asg:\")\n", + "print(\"Curve ready:\", s.curve_ready())\n", + "print(\"Trigger event age [ms]:\",8e-9*((s.current_timestamp&0xFFFFFFFFFFFFFFFF) - s.trigger_timestamp)*1000)\n", + "\n", + "# plot the data\n", + "%matplotlib inline\n", + "curve = curve.result()\n", + "plt.plot(s.times*1e3, curve[0], s.times*1e3, curve[1]);\n", + "plt.xlabel(\"Time [ms]\");\n", + "plt.ylabel(\"Voltage\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What do we see? The blue trace for channel 1 shows just the output signal of the asg. The time=0 corresponds to the trigger event. One can see that the trigger was not activated by the constant signal of 0 at the beginning, since it did not cross the hysteresis interval. One can also see a 'bug': After setting up the asg, it outputs the first value of its data table until its waveform output is triggered. For the halframp signal, as it is implemented in pyrpl, this is the maximally negative value. However, we passed the argument start_phase=90 to the asg.setup function, which shifts the first point by a quarter period. Can you guess what happens when we set start_phase=180? You should try it out!\n", + "\n", + "In green, we see the same signal, filtered through the pid module. The nonzero proportional gain leads to instant jumps along with the asg signal. The integrator is responsible for the constant decrease rate at the beginning, and the low-pass that smoothens the asg waveform a little. One can also foresee that, if we are not paying attention, too large an integrator gain will quickly saturate the outputs. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# useful functions for scope diagnostics\n", + "print(\"Curve ready:\", s.curve_ready())\n", + "print(\"Trigger source:\",s.trigger_source)\n", + "print(\"Trigger threshold [V]:\",s.threshold_ch1)\n", + "print(\"Averaging:\",s.average)\n", + "print(\"Trigger delay [s]:\",s.trigger_delay)\n", + "print(\"Trace duration [s]: \",s.duration)\n", + "print(\"Trigger hysteresis [V]\", s.hysteresis_ch1)\n", + "print(\"Current scope time [cycles]:\",hex(s.current_timestamp))\n", + "print(\"Trigger time [cycles]:\",hex(s.trigger_timestamp))\n", + "print(\"Current voltage on channel 1 [V]:\", r.scope.voltage_in1)\n", + "print(\"First point in data buffer 1 [V]:\", s.ch1_firstpoint)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### PID module" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have already seen some use of the pid module above. There are four PID modules available: pid0 to pid3. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(r.pid0.help())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Proportional and integral gain" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#make shortcut\n", + "pid = r.pid0\n", + "\n", + "#turn off by setting gains to zero\n", + "pid.p,pid.i = 0,0\n", + "print(\"P/I gain when turned off:\", pid.i,pid.p)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# small nonzero numbers set gain to minimum value - avoids rounding off to zero gain\n", + "pid.p = 1e-100\n", + "pid.i = 1e-100\n", + "print(\"Minimum proportional gain: \",pid.p)\n", + "print(\"Minimum integral unity-gain frequency [Hz]: \",pid.i)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# saturation at maximum values\n", + "pid.p = 1e100\n", + "pid.i = 1e100\n", + "print(\"Maximum proportional gain: \",pid.p)\n", + "print(\"Maximum integral unity-gain frequency [Hz]: \",pid.i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Control with the integral value register" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "#make shortcut\n", + "pid = r.pid0\n", + "\n", + "# set input to asg1\n", + "pid.input = \"asg1\"\n", + "\n", + "# set asg to constant 0.1 Volts\n", + "r.asg1.setup(waveform=\"dc\", offset = 0.1)\n", + "\n", + "# set scope ch1 to pid0\n", + "r.scope.input1 = 'pid0'\n", + "\n", + "#turn off the gains for now\n", + "pid.p,pid.i = 0, 0\n", + "\n", + "#set integral value to zero\n", + "pid.ival = 0\n", + "\n", + "#prepare data recording\n", + "from time import time\n", + "times, ivals, outputs = [], [], []\n", + "\n", + "# turn on integrator to whatever negative gain\n", + "pid.i = -10\n", + "\n", + "# set integral value above the maximum positive voltage\n", + "pid.ival = 1.5 \n", + "\n", + "#take 1000 points - jitter of the ethernet delay will add a noise here but we dont care\n", + "for n in range(1000):\n", + " times.append(time())\n", + " ivals.append(pid.ival)\n", + " outputs.append(r.scope.voltage_in1)\n", + "\n", + "#plot\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "times = np.array(times)-min(times)\n", + "plt.plot(times,ivals,times,outputs);\n", + "plt.xlabel(\"Time [s]\");\n", + "plt.ylabel(\"Voltage\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Again, what do we see? We set up the pid module with a constant (positive) input from the ASG. We then turned on the integrator (with negative gain), which will inevitably lead to a slow drift of the output towards negative voltages (blue trace). We had set the integral value above the positive saturation voltage, such that it takes longer until it reaches the negative saturation voltage. The output of the pid module is bound to saturate at +- 1 Volts, which is clearly visible in the green trace. The value of the integral is internally represented by a 32 bit number, so it can practically take arbitrarily large values compared to the 14 bit output. You can set it within the range from +4 to -4V, for example if you want to exloit the delay, or even if you want to compensate it with proportional gain. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Input filters" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The pid module has one more feature: A bank of 4 input filters in series. These filters can be either off (bandwidth=0), lowpass (bandwidth positive) or highpass (bandwidth negative). The way these filters were implemented demands that the filter bandwidths can only take values that scale as the powers of 2. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# off by default\n", + "r.pid0.inputfilter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# minimum cutoff frequency is 2 Hz, maximum 77 kHz (for now)\n", + "r.pid0.inputfilter = [1,1e10,-1,-1e10]\n", + "print(r.pid0.inputfilter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# not setting a coefficient turns that filter off\n", + "r.pid0.inputfilter = [0,4,8]\n", + "print(r.pid0.inputfilter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# setting without list also works\n", + "r.pid0.inputfilter = -2000\n", + "print(r.pid0.inputfilter)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# turn off again\n", + "r.pid0.inputfilter = []\n", + "print(r.pid0.inputfilter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You should now go back to the Scope and ASG example above and play around with the setting of these filters to convince yourself that they do what they are supposed to. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### IQ module" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Demodulation of a signal means convolving it with a sine and cosine at the 'carrier frequency'. The two resulting signals are usually low-pass filtered and called 'quadrature I' and and 'quadrature Q'. Based on this simple idea, the IQ module of pyrpl can implement several functionalities, depending on the particular setting of the various registers. In most cases, the configuration can be completely carried out through the setup function of the module. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lock-in detection / PDH / synchronous detection" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#reload to make sure settings are default ones\n", + "#from pyrpl import Pyrpl\n", + "#r = Pyrpl(hostname=HOSTNAME, config='tutorial').rp\n", + "\n", + "#shortcut\n", + "iq = r.iq0\n", + "\n", + "# modulation/demodulation frequency 25 MHz\n", + "# two lowpass filters with 10 and 20 kHz bandwidth\n", + "# input signal is analog input 1\n", + "# input AC-coupled with cutoff frequency near 50 kHz\n", + "# modulation amplitude 0.1 V\n", + "# modulation goes to out1\n", + "# output_signal is the demodulated quadrature 1\n", + "# quadrature_1 is amplified by 10\n", + "iq.setup(frequency=25e6, bandwidth=[10e3,20e3], gain=0.0, \n", + " phase=0, acbandwidth=50000, amplitude=0.5, \n", + " input='in1', output_direct='out1', \n", + " output_signal='quadrature', quadrature_factor=10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After this setup, the demodulated quadrature is available as the output_signal of iq0, and can serve for example as the input of a PID module to stabilize the frequency of a laser to a reference cavity. The module was tested and is in daily use in our lab. Frequencies as low as 20 Hz and as high as 50 MHz have been used for this technique. At the present time, the functionality of a PDH-like detection as the one set up above cannot be conveniently tested internally. We plan to upgrade the IQ-module to VCO functionality in the near future, which will also enable testing the PDH functionality. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Network analyzer" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When implementing complex functionality in the RedPitaya, the network analyzer module is by far the most useful tool for diagnostics. The network analyzer is able to probe the transfer function of any other module or external device by exciting the device with a sine of variable frequency and analyzing the resulting output from that device. This is done by demodulating the device output (=network analyzer input) with the same sine that was used for the excitation and a corresponding cosine, lowpass-filtering, and averaging the two quadratures for a well-defined number of cycles. From the two quadratures, one can extract the magnitude and phase shift of the device's transfer function at the probed frequencies. Let's illustrate the behaviour. For this example, you should connect output 1 to input 1 of your RedPitaya, such that we can compare the analog transfer function to a reference. Make sure you put a 50 Ohm terminator in parallel with input 1. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# shortcut for na\n", + "na = p.networkanalyzer\n", + "na.iq_name = 'iq1'\n", + "\n", + "#take transfer functions. first: iq1 -> iq1, second iq1->out1->(your cable)->adc1\n", + "na.setup(start=1e3,stop=62.5e6,points=1001,rbw=1000,amplitude=0.2,input='iq1',output_direct='off', acbandwidth=0, trace_average=1)\n", + "iq1 = na.single()\n", + "na.setup(start=1e3,stop=62.5e6,points=1001,rbw=1000,amplitude=0.2,input='in1',output_direct='out1', acbandwidth=0, trace_average=1)\n", + "adc1 = na.single()\n", + "f = na.data_x\n", + "#plot\n", + "from pyrpl.hardware_modules.iir.iir_theory import bodeplot\n", + "%matplotlib inline\n", + "bodeplot([(f, iq1, \"iq1->iq1\"), (f, adc1, \"iq1->out1->in1->iq1\")], xlog=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If your cable is properly connected, you will see that both magnitudes are near 0 dB over most of the frequency range. Near the Nyquist frequency (62.5 MHz), one can see that the internal signal remains flat while the analog signal is strongly attenuated, as it should be to avoid aliasing. One can also see that the delay (phase lag) of the internal signal is much less than the one through the analog signal path. \n", + "\n", + "If you have executed the last example (PDH detection) in this python session, iq0 should still send a modulation to out1, which is added to the signal of the network analyzer, and sampled by input1. In this case, you should see a little peak near the PDH modulation frequency, which was 25 MHz in the example above. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Lorentzian bandpass filter" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The iq module can also be used as a bandpass filter with continuously tunable phase. Let's measure the transfer function of such a bandpass with the network analyzer:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# shortcut for na and bpf (bandpass filter)\n", + "na = p.networkanalyzer\n", + "na.iq_name = 'iq1'\n", + "bpf = r.iq2\n", + "\n", + "# setup bandpass\n", + "bpf.setup(frequency = 2.5e6, #center frequency\n", + " Q=10.0, # the filter quality factor\n", + " acbandwidth = 10e5, # ac filter to remove pot. input offsets\n", + " phase=0, # nominal phase at center frequency (propagation phase lags not accounted for)\n", + " gain=2.0, # peak gain = +6 dB \n", + " output_direct='off', \n", + " output_signal='output_direct', \n", + " input='iq1')\n", + "\n", + "# take transfer function\n", + "na.setup(start=1e5, stop=4e6, points=201, rbw=100, avg=3, \n", + " amplitude=0.2, input='iq2',output_direct='off', trace_average=1)\n", + "tf1 = na.single()\n", + "\n", + "# add a phase advance of 82.3 degrees and measure transfer function\n", + "bpf.phase = 82.3\n", + "na.setup(start=1e5, stop=4e6, points=201, rbw=100, avg=3, \n", + " amplitude=0.2, input='iq2',output_direct='off', trace_average=1)\n", + "tf2 = na.single()\n", + "f = na.data_x\n", + "\n", + "#plot\n", + "from pyrpl.hardware_modules.iir.iir_theory import bodeplot\n", + "%matplotlib inline\n", + "bodeplot([(f, tf1, \"phase = 0.0\"), (f, tf2, \"phase = %.1f\"%bpf.phase)])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Frequency comparator module" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To lock the frequency of a VCO (Voltage controlled oscillator) to a frequency reference defined by the RedPitaya, the IQ module contains the frequency comparator block. This is how you set it up. You have to feed the output of this module through a PID block to send it to the analog output. As you will see, if your feedback is not already enabled when you turn on the module, its integrator will rapidly saturate (-585 is the maximum value here, while a value of the order of 1e-3 indicates a reasonable frequency lock). " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "iq = r.iq0\n", + "\n", + "# turn off pfd module for settings\n", + "iq.pfd_on = False\n", + "\n", + "# local oscillator frequency\n", + "iq.frequency = 33.7e6\n", + "\n", + "# local oscillator phase\n", + "iq.phase = 0\n", + "iq.input = 'in1' \n", + "iq.output_direct = 'off'\n", + "iq.output_signal = 'pfd'\n", + "\n", + "print(\"Before turning on:\")\n", + "print(\"Frequency difference error integral\", iq.pfd_integral)\n", + "\n", + "print(\"After turning on:\")\n", + "iq.pfd_on = True\n", + "for i in range(10):\n", + " print(\"Frequency difference error integral\", iq.pfd_integral)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### IIR module" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Sometimes it is interesting to realize even more complicated filters. This is the case, for example, when a piezo resonance limits the maximum gain of a feedback loop. For these situations, the IIR module can implement filters with 'Infinite Impulse Response' (https://en.wikipedia.org/wiki/Infinite_impulse_response). It is the your task to choose the filter to be implemented by specifying the complex values of the poles and zeros of the filter. In the current version of pyrpl, the IIR module can implement IIR filters with the following properties:\n", + "- strictly proper transfer function (number of poles > number of zeros)\n", + "- poles (zeros) either real or complex-conjugate pairs\n", + "- no three or more identical real poles (zeros)\n", + "- no two or more identical pairs of complex conjugate poles (zeros)\n", + "- pole and zero frequencies should be larger than $\\frac{f_\\rm{nyquist}}{1000}$ (but you can optimize the nyquist frequency of your filter by tuning the 'loops' parameter)\n", + "- the DC-gain of the filter must be 1.0. Despite the FPGA implemention being more flexible, we found this constraint rather practical. If you need different behavior, pass the IIR signal through a PID module and use its input filter and proportional gain. If you still need different behaviour, the file iir.py is a good starting point. \n", + "- total filter order <= 16 (realizable with 8 parallel biquads)\n", + "- a remaining bug limits the dynamic range to about 30 dB before internal saturation interferes with filter performance\n", + "\n", + "Filters whose poles have a positive real part are unstable by design. Zeros with positive real part lead to non-minimum phase lag. Nevertheless, the IIR module will let you implement these filters. \n", + "\n", + "In general the IIR module is still fragile in the sense that you should verify the correct implementation of each filter you design. Usually you can trust the simulated transfer function. It is nevertheless a good idea to use the internal network analyzer module to actually measure the IIR transfer function with an amplitude comparable to the signal you expect to go through the filter, as to verify that no saturation of internal filter signals limits its performance. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#shortcut\n", + "iir = r.iir\n", + "\n", + "#print docstring of the setup function\n", + "print(iir.setup.__doc__)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#prepare plot parameters\n", + "%matplotlib inline\n", + "import matplotlib\n", + "matplotlib.rcParams['figure.figsize'] = (10, 6)\n", + "\n", + "#setup a complicated transfer function\n", + "zeros = [ +4e4j-300,-2e5j-1000]\n", + "#[ -4e4j-300, +4e4j-300,-2e5j-1000, +2e5j-1000, -2e6j-3000, +2e6j-3000]\n", + "poles = [ -1e6, +5e4j-300]\n", + "#[ -1e6, -5e4j-300, +5e4j-300, -1e5j-3000, +1e5j-3000, -1e6j-30000, +1e6j-30000]\n", + "designdata = iir.setup(zeros=zeros, poles=poles, loops=None, plot=True);\n", + "print(\"Filter sampling frequency: \", 125./iir.loops,\"MHz\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you try changing a few coefficients, you will see that your design filter is not always properly realized. The bottleneck here is the conversion from the analytical expression (poles and zeros) to the filter coefficients, not the FPGA performance. This conversion is (among other things) limited by floating point precision. We hope to provide a more robust algorithm in future versions. If you can obtain filter coefficients by another, preferrably analytical method, this might lead to better results than our generic algorithm. \n", + "\n", + "Let's check if the filter is really working as it is supposed:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# first thing to check if the filter is not ok\n", + "print(\"IIR overflows before:\", bool(iir.overflow))\n", + "\n", + "# measure tf of iir filter\n", + "p.rp.iir.input = 'iq1'\n", + "p.networkanalyzer.setup(iq_name='iq1', start=1e4, stop=3e6, points = 301, rbw=100, trace_average=1, \n", + " amplitude=0.1, input='iir', output_direct='off', logscale=True)\n", + "tf = p.networkanalyzer.single()\n", + "f = p.networkanalyzer.data_x\n", + "\n", + "# first thing to check if the filter is not ok\n", + "print(\"IIR overflows after:\", bool(iir.overflow))\n", + "\n", + "#plot with design data\n", + "%matplotlib inline\n", + "import matplotlib\n", + "matplotlib.rcParams['figure.figsize'] = (10, 6)\n", + "from pyrpl.hardware_modules.iir.iir_theory import bodeplot\n", + "bodeplot([(f, iir.transfer_function(f),\"designed system\")] + [(f,tf,\"measured system\")],xlog=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, the filter has trouble to realize large dynamic ranges. With the current standard design software, it takes some 'practice' to design transfer functions which are properly implemented by the code. While most zeros are properly realized by the filter, you see that the first two poles suffer from some kind of saturation. We are working on an automatic rescaling of the coefficients to allow for optimum dynamic range. From the overflow register printed above the plot, you can also see that the network analyzer scan caused an internal overflow in the filter. All these are signs that different parameters should be tried. \n", + "\n", + "A straightforward way to impove filter performance is to adjust the DC-gain and compensate it later with the gain of a subsequent PID module. See for yourself what the parameter g=0.1 (instead of the default value g=1.0) does here:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#rescale the filter by 20fold reduction of DC gain\n", + "iir.setup(zeros=zeros, poles=poles, g=0.1,loops=None,plot=False);\n", + "\n", + "# first thing to check if the filter is not ok\n", + "print(\"IIR overflows before:\", bool(iir.overflow))\n", + "\n", + "# measure tf of iir filter\n", + "p.rp.iir.input = 'networkanalyzer'\n", + "p.networkanalyzer.setup(start=1e4, stop=3e6, points= 301, rbw=100, trace_average=1, \n", + " amplitude=0.1, input='iir', output_direct='off', logscale=True)\n", + "tf = p.networkanalyzer.single()\n", + "f = p.networkanalyzer.data_x\n", + "# first thing to check if the filter is not ok\n", + "print(\"IIR overflows after:\", bool(iir.overflow))\n", + "\n", + "#plot with design data\n", + "%matplotlib inline\n", + "import pylab\n", + "pylab.rcParams['figure.figsize'] = (10, 6)\n", + "from pyrpl.hardware_modules.iir.iir_theory import bodeplot\n", + "bodeplot([(f, p.rp.iir.transfer_function(f), \"design\")]+[(f,tf,\"measured system\")],xlog=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You see that we have improved the second peak (and avoided internal overflows) at the cost of increased nosie in other regions. Of course this noise can be reduced by increasing the NA averaging time. But maybe it will be detrimental to your application? After all, IIR filter design is far from trivial, but this tutorial should have given you enough information to get started and maybe to improve the way we have implemented the filter in pyrpl (e.g. by implementing automated filter coefficient scaling).\n", + "\n", + "If you plan to play more with the filter, these are the remaining internal iir registers:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "iir = p.rp.iir\n", + "\n", + "# useful diagnostic functions\n", + "print(\"IIR on:\", iir.on)\n", + "#print(\"IIR bypassed:\", iir.shortcut)\n", + "#print(\"IIR copydata:\", iir.copydata)\n", + "print(\"IIR loops:\", iir.loops)\n", + "print(\"IIR overflows:\", iir.overflow)\n", + "print(\"\\nCoefficients (6 per biquad):\")\n", + "print(iir.coefficients)\n", + "\n", + "# set the unity transfer function to the filter\n", + "iir._setup_unity()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6) The Pyrpl class" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The RedPitayas in our lab are mostly used to stabilize one item or another in quantum optics experiments. To do so, the experimenter usually does not want to bother with the detailed implementation on the RedPitaya while trying to understand the physics going on in her/his experiment. For this situation, we have developed the Pyrpl class, which provides an API with high-level functions such as:\n", + " \n", + " # optimial pdh-lock with setpoint 0.1 cavity bandwidth away from resonance\n", + " cavity.lock(method='pdh',detuning=0.1)\n", + " \n", + " # unlock the cavity\n", + " cavity.unlock()\n", + " \n", + " # calibrate the fringe height of an interferometer, and lock it at local oscillator phase 45 degrees\n", + " interferometer.lock(phase=45.0) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### First attempts at locking" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "SECTION NOT READY YET, BECAUSE CODE NOT CLEANED YET" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now lets go for a first attempt to lock something. Say you connect the error signal (transmission or reflection) of your setup to input 1. Make sure that the peak-to-peak of the error signal coincides with the maximum voltages the RedPitaya can handle (-1 to +1 V if the jumpers are set to LV). This is important for getting optimal noise performance. If your signal is too low, amplify it. If it is too high, you should build a voltage divider with 2 resistors of the order of a few kOhm (that way, the input impedance of the RedPitaya of 1 MOhm does not interfere). \n", + "\n", + "Next, connect output 1 to the standard actuator at your hand, e.g. a piezo. Again, you should try to exploit the full -1 to +1 V output range. If the voltage at the actuator must be kept below 0.5V for example, you should make another voltage divider for this. Make sure that you take the input impedance of your actuator into consideration here. If you output needs to be amplified, it is best practice to put the voltage divider after the amplifier as to also attenuate the noise added by the amplifier. Hovever, when this poses a problem (limited bandwidth because of capacity of the actuator), you have to put the voltage divider before the amplifier. Also, this is the moment when you should think about low-pass filtering the actuator voltage. Because of DAC noise, analog low-pass filters are usually more effective than digital ones. A 3dB bandwidth of the order of 100 Hz is a good starting point for most piezos. \n", + "\n", + "You often need two actuators to control your cavity. This is because the output resolution of 14 bits can only realize 16384 different values. This would mean that with a finesse of 15000, you would only be able to set it to resonance or a linewidth away from it, but nothing in between. To solve this, use a coarse actuator to cover at least one free spectral range which brings you near the resonance, and a fine one whose range is 1000 or 10000 times smaller and who gives you lots of graduation around the resonance. The coarse actuator should be strongly low-pass filtered (typical bandwidth of 1Hz or even less), the fine actuator can have 100 Hz or even higher bandwidth. Do not get confused here: the unity-gain frequency of your final lock can be 10- or even 100-fold above the 3dB bandwidth of the analog filter at the output - it suffices to increase the proportional gain of the RedPitaya Lockbox. \n", + "\n", + "Once everything is connected, let's grab a PID module, make a shortcut to it and print its helpstring. All modules have a metho help() which prints all available registers and their description:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pid = p.rp.pid0\n", + "print(pid.help())\n", + "pid.ival #bug: help forgets about pid.ival: current integrator value [volts]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We need to inform our RedPitaya about which connections we want to make. The cabling discussed above translates into:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pid.input = 'in1'\n", + "pid.output_direct = 'out1'\n", + "\n", + "#see other available options just for curiosity:\n", + "print(pid.inputs)\n", + "print(pid.output_directs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, we need to define a setpoint. Lets first measure the offset when the laser is away from the resonance, and then measure or estimate how much light gets through on resonance. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# turn on the laser\n", + "offresonant = p.rp.scope.voltage_in1 #volts at analog input 1 with the unlocked cavity" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# make a guess of what voltage you will measure at an optical resonance\n", + "resonant = 0.5 #Volts at analog input 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# set the setpoint at relative reflection of 0.75 / rel. transmission of 0.25\n", + "pid.setpoint = 0.75*offresonant + 0.25*resonant" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now lets start to approach the resonance. We need to figure out from which side we are coming. The choice is made such that a simple integrator will naturally drift into the resonance and stay there:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "pid.i = 0 # make sure gain is off\n", + "pid.p = 0\n", + "#errorsignal = adc1 - setpoint \n", + "if resonant > offresonant: # when we are away from resonance, error is negative. \n", + " slopesign = 1.0 # therefore, near resonance, the slope is positive as the error crosses zero. \n", + "else:\n", + " slopesign = -1.0\n", + "gainsign = -slopesign #the gain must be the opposite to stabilize\n", + "# the effectove gain will in any case slopesign*gainsign = -1. \n", + "\n", + "#Therefore we must start at the maximum positive voltage, so the negative effective gain leads to a decreasing output\n", + "pid.ival = 1.0 #sets the integrator value = output voltage to maximum\n", + "\n", + "from time import sleep\n", + "sleep(1.0) #wait for the voltage to stabilize (adjust for a few times the lowpass filter bandwidth)\n", + "\n", + "#finally, turn on the integrator\n", + "pid.i = gainsign * 0.1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#no-test\n", + "#with a bit of luck, this should work\n", + "from time import time\n", + "t0 = time()\n", + "while True:\n", + " relative_error = abs((p.rp.scope.voltage_in1-pid.setpoint)/(offresonant-resonant))\n", + " if time()-t0 > 2: #diagnostics every 2 seconds\n", + " print(\"relative error:\",relative_error)\n", + " t0 = time()\n", + " if relative_error < 0.1:\n", + " break\n", + " sleep(0.01)\n", + " if pid.ival <= -1:\n", + " print(\"Resonance missed. Trying again slower..\")\n", + " pid.ival = 1.2 #overshoot a little\n", + " pid.i /= 2\n", + "print(\"Resonance approch successful\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " Questions to users: what parameters do you know?\n", + " finesse of the cavity? 1000\n", + " length? 1.57m\n", + " what error signals are available? transmission direct, reflection AC -> directement pdh analogique\n", + " \n", + " are modulators available n/a\n", + " \n", + " what cavity length / laser frequency actuators are available? PZT mephisto DC - 10kHz, 48MHz opt./V, V_rp apmplifie x20\n", + " temperature du laser <1 Hz 2.5~GHz/V, apres AOM\n", + " \n", + " what is known about them (displacement, bandwidth, amplifiers)?\n", + " \n", + " what analog filters are present? YAG PZT a 10kHz\n", + " \n", + " imposer le design des sorties\n", + " \n", + " \n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "More to come" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "#shortcut\n", + "iq = p.rp.iq0\n", + "\n", + "iq.setup(frequency=1000e3, bandwidth=[10e3,20e3], gain=0.0, \n", + " phase=0, acbandwidth=50000, amplitude=0.4, \n", + " input='in1', output_direct='out1', \n", + " output_signal='output_direct', quadrature_factor=0)\n", + "iq.frequency=10\n", + "p.rp.scope.input1='in1'\n", + "\n", + "# shortcut for na\n", + "na = p.networkanalyzer\n", + "na.iq_name = \"iq1\"\n", + "\n", + "# pid1 will be our device under test\n", + "pid = p.rp.pid0\n", + "pid.input = 'iq1'\n", + "pid.i = 0\n", + "pid.ival = 0\n", + "pid.p = 1.0\n", + "pid.setpoint = 0\n", + "pid.inputfilter = []#[-1e3, 5e3, 20e3, 80e3]\n", + "\n", + "# take the transfer function through pid1, this will take a few seconds...\n", + "na.setup(start=0,stop=200e3,points=101,rbw=100,avg=1,amplitude=0.5,input='iq1',output_direct='off', acbandwidth=0)\n", + "y = na.single()\n", + "x = na.data_x\n", + "#plot\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "import numpy as np\n", + "plt.plot(x*1e-3,np.abs(y)**2);\n", + "plt.xlabel(\"Frequency [kHz]\");\n", + "plt.ylabel(\"|S21|\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7) The Graphical User Interface" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Most of the modules described in section 5 can be controlled via a graphical user interface. The graphical window can be displayed with the following:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "WARNING: For the GUI to work fine within an ipython session, the option --gui=qt has to be given to the command launching ipython. This makes sure that an event loop is running." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#no-test\n", + "from pyrpl import Pyrpl\n", + "p = Pyrpl(hostname=HOSTNAME, config='tutorial')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following window should open itself. Feel free to play with the button and tabs to start and stop the scope acquisition...\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The window is composed of several tabs, each corresponding to a particular module. Since they generate a graphical output, the scope, network analyzer, and spectrum analyzer modules are very pleasant to use in GUI mode. For instance, the scope tab can be used to display in real-time the waveforms acquired by the redpitaya scope. Since the refresh rate is quite good, the scope tab can be used to perform optical alignements or to monitor transient signals as one would do it with a standalone scope." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Pyrpl uses the Qt eventloop to perform asynchronous tasks, but it has been set as the default loop of `asyncio`, such that you only need to learn how to use the standard python module [`asyncio`](https://docs.python.org/3/library/asyncio.html), and you don't need to know anything about Qt. To give you a quick overview of what can be done, we present in the following block an exemple of 2 tasks running in parrallele. The first one mimicks a temperature control loop, measuring periodically a signal every 1 s, and changing the offset of an `asg` based on the measured value (we realize this way a slow and rudimentary software pid). In parrallele, another task consists in repeatedly shifting the frequency of an asg, and measuring an averaged spectrum on the spectrum analyzer.\n", + "\n", + "Both tasks are defined by coroutines (a python function that is preceded by the keyword `async`, and that can contain the keyword `await`). Basically, the execution of each coroutine is interrupted whenever the keyword `await` is encountered, giving the chance to other tasks to be executed. It will only be resumed once the underlying coroutine's value becomes ready.\n", + "\n", + "Finally to execute the cocroutines, it is not enough to call `my_coroutine()`, since we need to send the task to the event loop. For that, we use the function `ensure_future` from the asyncio module. This function immediately returns an object that is not the result of the task (not the object that is behind `return` inside the coroutine), but rather a Future object, that can be used to retrieve the actual result once it is ready (this is done by calling `future.result()` latter on).\n", + "\n", + "If you are executing the code inside the ipython notebook, then, this is all you have to do, since an event loop is already running in the back (a qt eventloop if you are using the option %pylab qt). Otherwise, you have to use one of the functions (`LOOP.run_forever()`, `LOOP.run_until_complete()`, or `LOOP.run_in_executor()`) to launch the eventloop." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + }, + "name": "" + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index deaf50a3d..000000000 --- a/docs/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/docs/makefile b/docs/makefile index de6164dd1..9533688e9 100644 --- a/docs/makefile +++ b/docs/makefile @@ -49,41 +49,42 @@ help: clean: rm -rf $(BUILDDIR)/* -# Preliminaries to a real build - convert tutorial to readable format -ipython nbconvert --to html source/user_guide/tutorial/tutorial.ipynb +common: + # Preliminaries to a real build - convert tutorial to readable format + ipython nbconvert --to html source/user_guide/tutorial/tutorial.ipynb -html: +html: common $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." -dirhtml: +dirhtml: common $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." -singlehtml: +singlehtml: common $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." -pickle: +pickle: common $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." -json: +json: common $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." -htmlhelp: +htmlhelp: common $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." -qthelp: +qthelp: common $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ @@ -92,7 +93,7 @@ qthelp: @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyrpl.qhc" -devhelp: +devhelp: common $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @@ -101,80 +102,80 @@ devhelp: @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pyrpl" @echo "# devhelp" -epub: +epub: common $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." -latex: +latex: common $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." -latexpdf: +latexpdf: common $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." -latexpdfja: +latexpdfja: common $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." -text: +text: common $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." -man: +man: common $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." -texinfo: +texinfo: common $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." -info: +info: common $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." -gettext: +gettext: common $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." -changes: +changes: common $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." -linkcheck: +linkcheck: common $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." -doctest: +doctest: common $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." -xml: +xml: common $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." -pseudoxml: +pseudoxml: common $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." \ No newline at end of file diff --git a/docs/old_files/benchmarks/asyncio_no_correction.png b/docs/old_files/benchmarks/asyncio_no_correction.png deleted file mode 100644 index 5f2a89318..000000000 Binary files a/docs/old_files/benchmarks/asyncio_no_correction.png and /dev/null differ diff --git a/docs/old_files/benchmarks/images/with_timer/asyncio.png b/docs/old_files/benchmarks/images/with_timer/asyncio.png deleted file mode 100644 index b2699f35e..000000000 Binary files a/docs/old_files/benchmarks/images/with_timer/asyncio.png and /dev/null differ diff --git a/docs/old_files/benchmarks/images/with_timer/asyncio_no_correction.png b/docs/old_files/benchmarks/images/with_timer/asyncio_no_correction.png deleted file mode 100644 index 5f2a89318..000000000 Binary files a/docs/old_files/benchmarks/images/with_timer/asyncio_no_correction.png and /dev/null differ diff --git a/docs/old_files/benchmarks/images/with_timer/my_sleep.png b/docs/old_files/benchmarks/images/with_timer/my_sleep.png deleted file mode 100644 index de4ccb115..000000000 Binary files a/docs/old_files/benchmarks/images/with_timer/my_sleep.png and /dev/null differ diff --git a/docs/old_files/benchmarks/images/with_timer/processEvents.png b/docs/old_files/benchmarks/images/with_timer/processEvents.png deleted file mode 100644 index 2f397a803..000000000 Binary files a/docs/old_files/benchmarks/images/with_timer/processEvents.png and /dev/null differ diff --git a/docs/old_files/benchmarks/images/with_timer/qeventloop.png b/docs/old_files/benchmarks/images/with_timer/qeventloop.png deleted file mode 100644 index 5bd8eff96..000000000 Binary files a/docs/old_files/benchmarks/images/with_timer/qeventloop.png and /dev/null differ diff --git a/docs/old_files/benchmarks/images/with_timer/time.sleep.png b/docs/old_files/benchmarks/images/with_timer/time.sleep.png deleted file mode 100644 index a64cae9d7..000000000 Binary files a/docs/old_files/benchmarks/images/with_timer/time.sleep.png and /dev/null differ diff --git a/docs/old_files/benchmarks/images/without_timer/asyncio.png b/docs/old_files/benchmarks/images/without_timer/asyncio.png deleted file mode 100644 index 3eed67466..000000000 Binary files a/docs/old_files/benchmarks/images/without_timer/asyncio.png and /dev/null differ diff --git a/docs/old_files/benchmarks/images/without_timer/my_sleep.png b/docs/old_files/benchmarks/images/without_timer/my_sleep.png deleted file mode 100644 index b7596bb58..000000000 Binary files a/docs/old_files/benchmarks/images/without_timer/my_sleep.png and /dev/null differ diff --git a/docs/old_files/benchmarks/images/without_timer/processEvents.png b/docs/old_files/benchmarks/images/without_timer/processEvents.png deleted file mode 100644 index 6c58e272e..000000000 Binary files a/docs/old_files/benchmarks/images/without_timer/processEvents.png and /dev/null differ diff --git a/docs/old_files/benchmarks/images/without_timer/qeventloop.png b/docs/old_files/benchmarks/images/without_timer/qeventloop.png deleted file mode 100644 index 6424ae825..000000000 Binary files a/docs/old_files/benchmarks/images/without_timer/qeventloop.png and /dev/null differ diff --git a/docs/old_files/benchmarks/images/without_timer/time.sleep.png b/docs/old_files/benchmarks/images/without_timer/time.sleep.png deleted file mode 100644 index 621fac05b..000000000 Binary files a/docs/old_files/benchmarks/images/without_timer/time.sleep.png and /dev/null differ diff --git a/docs/old_files/benchmarks/timers.ipynb b/docs/old_files/benchmarks/timers.ipynb deleted file mode 100644 index 3679c8089..000000000 --- a/docs/old_files/benchmarks/timers.ipynb +++ /dev/null @@ -1,855 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%pylab qt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "from PyQt4 import QtCore, QtGui\n", - "n_calc = [0]\n", - "def calculate():\n", - " sin(rand(1000))\n", - " n_calc[0]+=1\n", - " timer.start()\n", - "\n", - "timer = QtCore.QTimer()\n", - "timer.setInterval(0)\n", - "timer.setSingleShot(True)\n", - "timer.timeout.connect(calculate)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "from PyQt4 import QtCore, QtGui\n", - "from psutil import cpu_percent\n", - "from time import sleep\n", - "from timeit import default_timer\n", - "APP = QtGui.QApplication.instance()\n", - "\n", - "close('all')\n", - "\n", - "n_calc[0] = 0\n", - "\n", - "from time import time\n", - "def sleep_loop(delay):\n", - " loop = QtCore.QEventLoop()\n", - " timer = QtCore.QTimer()\n", - " timer.setInterval(delay*1000)\n", - " timer.setSingleShot(True)\n", - " timer.timeout.connect(loop.quit)\n", - " timer.start()\n", - " loop.exec() # la loop prend en charge elle-même l'évenement du timer qui va la faire mourir après delay.\n", - "\n", - "def sleep_pe(delay):\n", - " tic = default_timer()\n", - " while(default_timer()1e-3:\n", - " sleep_loop(delay - 1e-3)\n", - " while(default_timer()0.001:\n", - " #await asyncio.sleep(delay - 0.001)\n", - " #while default_timer() < tic + delay:\n", - " # APP.processEvents()\n", - "\n", - "timer.start()\n", - "\n", - "label = \"asyncio\"\n", - "async def run():\n", - " fig = figure()\n", - " times_measured = []\n", - " times_asked = []\n", - "\n", - " n_calc_before = n_calc[0]\n", - " tic_for_func = default_timer()\n", - " cpu_percent() # call starts the cpu_percent averaging\n", - " for time_asked in linspace(0., MAX_TIME, 200):\n", - " #if int(time_asked*10000)%10==0:\n", - " # print(time_asked)\n", - " for i in range(10):\n", - " tic = default_timer()\n", - " await sleep_coroutine(time_asked)\n", - " toc = default_timer()\n", - " times_measured.append(toc - tic)\n", - " times_asked.append(time_asked)\n", - " cpu_percents = cpu_percent()\n", - " toc_for_func = default_timer()\n", - " calc_done = (n_calc[0] - n_calc_before)/(toc_for_func - tic_for_func)\n", - " plot(times_asked, times_measured, '.')\n", - " plot([0, MAX_TIME], [0, MAX_TIME])\n", - " xlabel(\"time asked\")\n", - " ylabel(\"time measured\")\n", - " xlim(0, MAX_TIME)\n", - " ylim(0, MAX_TIME)\n", - " title(label + ' cpu_percent=' + str(cpu_percents) + ' calc/s=' + str(int(calc_done)))\n", - " timer.stop()\n", - " fig.savefig('asyncio_no_correction.png')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "asyncio.ensure_future(run())\n", - "#loop.run_forever() # this would be to launch the " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%pylab qt\n", - "import asyncio\n", - "import scipy.fftpack\n", - "import quamash\n", - "from PyQt4 import QtCore, QtGui\n", - "import asyncio\n", - "loop = quamash.QEventLoop()\n", - "asyncio.set_event_loop(loop) \n", - "\n", - "\n", - "class Scope(object):\n", - " async def run_single(self, avg):\n", - " y_avg = zeros(100)\n", - " for i in range(avg):\n", - " await asyncio.sleep(1)\n", - " y_avg+=rand(100)\n", - " return y_avg\n", - "\n", - "class SpecAn(object):\n", - " scope = Scope()\n", - " \n", - " async def run_single(self, avg):\n", - " y = zeros(100, dtype=complex)\n", - " for i in range(avg):\n", - " trace = await self.scope.run_single(1)\n", - " y+= scipy.fftpack.fft(trace)\n", - " return y\n", - " \n", - "sa = SpecAn()\n", - "scope = Scope()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "v = asyncio.ensure_future(sa.run_single(10))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "v.result()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "async def my_loop():\n", - " for i in range(10):\n", - " " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%pylab qt\n", - "import asyncio\n", - "import scipy.fftpack\n", - "import quamash\n", - "from PyQt4 import QtCore, QtGui\n", - "APP = QtGui.QApplication.instance()\n", - "import asyncio\n", - "from promise import Promise\n", - "loop = quamash.QEventLoop()\n", - "asyncio.set_event_loop(loop) \n", - "\n", - "\n", - "class MyPromise(Promise):\n", - " def get(self):\n", - " while self.is_pending:\n", - " APP.processEvents()\n", - " return super(MyPromise, self).get()\n", - "\n", - "\n", - "class Scope(object):\n", - " def __init__(self):\n", - " self.timer = QtCore.QTimer()\n", - " self.timer.setSingleShot(True)\n", - " self.timer.setInterval(1000)\n", - " self.timer.timeout.connect(self.check_for_curve)\n", - "\n", - " def run_single(self, avg):\n", - " self.current_avg = 0\n", - " self.avg = avg\n", - " self.y_avg = zeros(100)\n", - " self.p = MyPromise()\n", - " self.timer.start()\n", - " return self.p\n", - "\n", - " def check_for_curve(self):\n", - " if self.current_avg=self.avg:\n", - " self.p.fulfill(self.y_avg)\n", - " else:\n", - " p = self.scope.run_single(1)\n", - " p.then(self.average_one_curve)\n", - "\n", - "sa = SpecAn()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p = sa.run_single(3)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p.is_fulfilled" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p.get()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "sa.avg" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "s" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%pylab qt\n", - "import asyncio\n", - "import scipy.fftpack\n", - "import quamash\n", - "from PyQt4 import QtCore, QtGui\n", - "import asyncio\n", - "loop = quamash.QEventLoop()\n", - "asyncio.set_event_loop(loop) \n", - "\n", - "\n", - "class Scope(object):\n", - " async def run_single(self, avg):\n", - " y_avg = zeros(100)\n", - " for i in range(avg):\n", - " await asyncio.sleep(1)\n", - " y_avg+=rand(100)\n", - " return y_avg\n", - "\n", - "\n", - "class Na(object):\n", - " async def run_single(self, avg):\n", - " y_avg = zeros(100)\n", - " for i in range(avg):\n", - " await asyncio.sleep(1)\n", - " y_avg+=rand(100)\n", - " return y_avg\n", - "\n", - "scope = Scope()\n", - "na = Na()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "async def my_coroutine(n):\n", - " c1 = zeros(100)\n", - " c2 = zeros(100)\n", - "\n", - " for i in range(n):\n", - " print(\"launching f\")\n", - " f = asyncio.ensure_future(scope.run_single(1))\n", - " print(\"launching g\")\n", - " g = asyncio.ensure_future(na.run_single(1))\n", - " print(\"=======\")\n", - " c1+= await f\n", - " c2+= await g\n", - " print(\"f returned\")\n", - " print(\"g returned\")\n", - "\n", - " return c1 + c2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p = asyncio.ensure_future(my_coroutine(3))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p.result()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p.result?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "my_coroutine" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "asyncio.ensure_future(my_coroutine(1))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "obj = my_coroutine(1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "obj.__await__" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "class MyAwaitable(object):\n", - " def __await__(self):\n", - " return ((x for x in range(5)))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "m = MyAwaitable()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p = asyncio.ensure_future(m)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p.result()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "m = MyFuture()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p = asyncio.ensure_future(m)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p.done()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "m.set_result(5)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p.result()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "from asyncio.futures import Future\n", - "\n", - "class MyFuture(Future):\n", - " def __await__(self):\n", - " self.timer = QtCore.QTimer()\n", - " self.timer.timeout.connect(lambda : self.set_result(rand(100)))\n", - " self.timer.setSingleShot(True)\n", - " self.timer.setInterval(1000)\n", - " self.timer.start()\n", - " print('awaiting')\n", - " \n", - "class AsyncScope(object):\n", - " def run_single(self, avg):\n", - " self.f = MyFuture()\n", - " return self.f\n", - " \n", - "a = AsyncScope()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "f" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "f = a.run_single(3)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "a.f.set_result(6)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "f" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p = asyncio.ensure_future(a.run_single(3))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p.done()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p.__await__()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "p.result()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.5.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/source/DSP.jpg b/docs/source/DSP.jpg new file mode 100644 index 000000000..665e07b78 Binary files /dev/null and b/docs/source/DSP.jpg differ diff --git a/0.7 b/docs/source/_templates/globaltoc.html similarity index 100% rename from 0.7 rename to docs/source/_templates/globaltoc.html diff --git a/docs/source/_templates/layout.html b/docs/source/_templates/layout.html new file mode 100644 index 000000000..936dcab04 --- /dev/null +++ b/docs/source/_templates/layout.html @@ -0,0 +1,30 @@ +{% extends "!layout.html" %} + +{% block header %} + {{ super() }} + + + Fork PyRPL on GitHub + + +{% endblock %} + +{% block body %} + {{ super() }} +{% endblock %} diff --git a/docs/source/api.rst b/docs/source/api.rst new file mode 100644 index 000000000..e842bd97a --- /dev/null +++ b/docs/source/api.rst @@ -0,0 +1,235 @@ +API manual +************** + +This manual will guide you step-by-step through the programming interface of each PyRPL modules. + +1 First steps +================= + +If the installation went well, you should now be able to load the +package in python. If that works you can pass directly to the next +section 'Connecting to the RedPitaya'. + +.. code:: python + + from pyrpl import Pyrpl + +Sometimes, python has problems finding the path to pyrpl. In that +case you should add the pyrplockbox directory to your pythonpath +environment variable +(http://stackoverflow.com/questions/3402168/permanently-add-a-directory-to-pythonpath). +If you do not know how to do that, just manually navigate the ipython +console to the directory, for example: + +.. code:: python + + cd c:\lneuhaus\github\pyrpl + +Now retry to load the module. It should really work now. + +.. code:: python + + from pyrpl import Pyrpl + +Connecting to the RedPitaya +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You should have a working SD card (any version of the SD card content is +okay) in your RedPitaya (for instructions see +http://redpitaya.com/quick-start/). The RedPitaya should be connected +via ethernet to your computer. To set this up, there is plenty of +instructions on the RedPitaya website +(http://redpitaya.com/quick-start/). If you type the ip address of your +module in a browser, you should be able to start the different apps from +the manufacturer. The default address is http://192.168.1.100. If this +works, we can load the python interface of pyrplockbox by specifying the +RedPitaya's ip address. + +.. code:: python + + HOSTNAME = "192.168.1.100" + +.. code:: python + + from pyrpl import Pyrpl + p = Pyrpl(hostname=HOSTNAME) + +If you see at least one '>' symbol, your computer has successfully +connected to your RedPitaya via SSH. This means that your connection +works. The message 'Server application started on port 2222' means that +your computer has sucessfully installed and started a server application +on your RedPitaya. Once you get 'Client started with success', your +python session has successfully connected to that server and all things +are in place to get started. + + +Basic communication with your RedPitaya +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + # Access the RedPitaya object in charge of communicating with the board + r = p.rp + + #check the value of input1 + print r.scope.voltage1 + +With the last command, you have successfully retrieved a value from an +FPGA register. This operation takes about 300 µs on my computer. So +there is enough time to repeat the reading n times. + +.. code:: python + + #see how the adc reading fluctuates over time + import time + from matplotlib import pyplot as plt + times, data = [],[] + t0 = time.time() + n = 3000 + for i in range(n): + times.append(time.time()-t0) + data.append(r.scope.voltage_in1) + print("Rough time to read one FPGA register: ", (time.time()-t0)/n*1e6, "µs") + %matplotlib inline + f, axarr = plt.subplots(1,2, sharey=True) + axarr[0].plot(times, data, "+") + axarr[0].set_title("ADC voltage vs time") + axarr[1].hist(data, bins=10,normed=True, orientation="horizontal") + axarr[1].set_title("ADC voltage histogram") + +You see that the input values are not exactly zero. This is normal with +all RedPitayas as some offsets are hard to keep zero when the +environment changes (temperature etc.). So we will have to compensate +for the offsets with our software. Another thing is that you see quite a +bit of scatter beetween the points - almost as much that you do not see +that the datapoints are quantized. The conclusion here is that the input +noise is typically not totally negligible. Therefore we will need to use +every trick at hand to get optimal noise performance. + +After reading from the RedPitaya, let's now try to write to the register +controlling the first 8 yellow LED's on the board. The number written to +the LED register is displayed on the LED array in binary representation. +You should see some fast flashing of the yellow leds for a few seconds +when you execute the next block. + +.. code:: python + + #blink some leds for 5 seconds + from time import sleep + for i in range(1025): + r.hk.led=i + sleep(0.005) + +.. code:: python + + # now feel free to play around a little to get familiar with binary representation by looking at the leds. + from time import sleep + r.hk.led = 0b00000001 + for i in range(10): + r.hk.led = ~r.hk.led>>1 + sleep(0.2) + +.. code:: python + + import random + for i in range(100): + r.hk.led = random.randint(0,255) + sleep(0.02) + +2 RedPitaya (or Hardware) modules +================================== + +Let's now look a bit closer at the class RedPitaya. Besides managing the +communication with your board, it contains different modules that +represent the different sections of the FPGA. You already encountered +two of them in the example above: "hk" and "scope". Here is the full +list of modules: + +.. code:: python + + r.hk #"housekeeping" = LEDs and digital inputs/outputs + r.ams #"analog mixed signals" = auxiliary ADCs and DACs. + + r.scope #oscilloscope interface + + r.asg0 #"arbitrary signal generator" channel 0 + r.asg1 #"arbitrary signal generator" channel 1 + + r.pid0 #first of three PID modules + r.pid1 + r.pid2 + + r.iq0 #first of three I+Q quadrature demodulation/modulation modules + r.iq1 + r.iq2 + + r.iir #"infinite impulse response" filter module that can realize complex transfer functions + + +ASG and Scope module +~~~~~~~~~~~~~~~~~~~~~~ + +Arbitrary Signal Generator +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: pyrpl.hardware_modules.asg + +Oscilloscope +~~~~~~~~~~~~ + +.. automodule:: pyrpl.hardware_modules.scope + + +PID module +~~~~~~~~~~ + +.. automodule:: pyrpl.hardware_modules.pid + +IQ module +~~~~~~~~~~ + +.. automodule:: pyrpl.hardware_modules.iq + +IIR module +~~~~~~~~~~ + +.. automodule:: pyrpl.hardware_modules.iir + + +3 Pyrpl (or Software) modules +================================== + +Software modules are modules that don't have an FPGA counterpart. They are directly accessible at the root pyrpl object +(no need to go through the redpitaya object). We have already encountered a software module above. Remember how we accessed the +network analyzer module: + +.. code:: python + + HOSTNAME = "192.168.1.100" + from pyrpl import Pyrpl + p = Pyrpl(hostname=HOSTNAME) + + # hardware modules are members of the redpitaya object + p.rp.iq0 + + # software modules are members of the root pyrpl object + p.networkanalyzer + +Software modules usually perform higher-level tasks than hardware modules. Moreover, accessing a hardware module without +care could be harmful to some acquisition already running on the redpitaya. For this reason, it is advisable to access hardware modules +via module managers only. + +Using Module Managers +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: pyrpl.software_modules.module_managers + +Spectrum Analyzer +~~~~~~~~~~~~~~~~~~~ + +.. automodule:: pyrpl.software_modules.spectrum_analyzer + +Lockbox +~~~~~~~~~ + +Coming soon \ No newline at end of file diff --git a/docs/source/basics.rst b/docs/source/basics.rst new file mode 100644 index 000000000..841c0e1be --- /dev/null +++ b/docs/source/basics.rst @@ -0,0 +1,318 @@ +Basics of the PyRPL Architecture +********************************** + +This section presents the basic architecture of PyRPL. The main goal here is to quickly give a broad overview of PyRPL's internal logic +without distracting the reader with too many technical details. For a more detailled description of the individual components described in this page, please, refer +to the corresponding section :doc:`developer_guide/index`. + +Motivation +=========== + +Available hardware boards featuring FPGAs, CPUs and analog in- and outputs makes it possible to use digital signal processing (DSP) +to control quantum optics experiments. Running open-source software on this hardware has many advantages: + +- Lab space: small size, less different devices +- Money: cheap hardware, free software +- Time: connect cables once, re-wire digitally automate experiments work from home +- Automated measurements incite to take more data-points perform experiments more reproducibly + record additional, auxiliary data +- Functionality beyond analog electronics +- Modify or customize instrument functionality + +However, learning all the subtelties of FPGA programming, compiling and debugging FPGA code can be extremely time consumming. +Hence, PyRPL aims at providing a large panel of functionalities on a precompiled FPGA bitfile. These FPGA modules are highly customizable by changing +register values without the need to recompile the FPGA code written in Hardware Description Language. High-level functionalities are implemented by a python +package running remotely and controlling the FPGA registers. + + + +Hardware Platform - Red Pitaya +=============================== + +At the moment, Red Pitaya is the only hardware platform supported by PyRPL. + +.. image:: redpitaya.jpg + :scale: 100 % + :alt: The redpitaya board + :align: center + +The RedPitaya board is an affordable FPGA + CPU board running a Linux operating system. The FPGA is running at a clock rate of 125 MSps and +it is interfaced with 2 analog inputs and 2 analog outputs (14 bits, 125 MSps). The minimum input-output latency is of the order of 200 ns and +the effective resolution is 12 bits for inputs and 13 bits for outputs. 4 slow analog inputs and outputs and 16 I/O ports are also available. +Visit the The Red Pitaya homepage (http://www.redpitaya.com) for more details on the platform. + +Software Infrastructure +======================= + +The FPGA functionalities of PyRPL are organized in various DSP modules. These modules can be configured and arbitrarily connected together +using a python package running on a client computer. This design offers a lot of flexibility in the design and control of various experimental +setups without having to recompile the FPGA code each time a different fonctionality is needed. A fast ethernet interface maps all FPGA registers +to Python variables. The read/write time is around 250 microseconds for a typical LAN connection. High-level functionalities are achieved by +performing successive operations on the FPGA registers using the Python API. A Graphical User Interface is also provided to easily visualize and +modify the different FPGA registers. We provide a description of the different software components below. + +.. image:: software_architecture.jpg + :scale: 100 % + :alt: PyRPL software architecture + :align: center + +FPGA modules +------------ + +At the moment, the FPGA code provided with PyRPL implements various Digital Signal Processing modules: + ++--------------+------------+--------------------------------------------------------+ +| Module name |# available | Short description | ++==============+============+========================================================+ +| Scope | 1 | A 16384 points, 2 channels oscilloscope | +| | | capable of monitoring internal or external signals | ++--------------+------------+--------------------------------------------------------+ +| ASG | 2 | An arbitrary signal generator capable of generating | +| | | various waveforms, and even gaussian white noise | ++--------------+------------+--------------------------------------------------------+ +| IQ modulator/| 3 | An internal frequency reference is used to digitally | +| demodulator | | demodulate a given input signal. The frequency | +| | | reference can be outputed to serve as a driving signal.| +| | | The slowly varying quadratures can also be used to | +| | | remodulate the 2 phase-shifted internal references, | +| | | turning the module | +| | | into a very narrow bandpass filter. See the page | +| | | :ref:`iq-widget-label` for more details | ++--------------+------------+--------------------------------------------------------+ +| PID | 3 | Proportional/Integrator/Differential feedback modules | +| | | (In the current version, the differential gain is | +| | | disabled). The gain of each parameter can be set | +| | | independently and each module is also equiped with a | +| | | 4th order linear filter (applied before the PID | +| | | correction) | ++--------------+------------+--------------------------------------------------------+ +| IIR | 1 | An Infinite Impulse Response filter that can be used to| +| | | realize real-time filters with comlex | +| | | transfer-functions | ++--------------+------------+--------------------------------------------------------+ +| Trigger | 1 | A module to detect a transition on an analog signal. | ++--------------+------------+--------------------------------------------------------+ +| Sampler | 1 | A module to sample each external or external signal | ++--------------+------------+--------------------------------------------------------+ +| Pwm | 4 | Modules to control the pulse width modulation pins of | +| | | the redpitaya | ++--------------+------------+--------------------------------------------------------+ +| Hk | 1 | House keeping module to monitor redpitaya constants and| +| | | control the LED status | ++--------------+------------+--------------------------------------------------------+ + +Modules can be connected to each other arbitrarily. For this purpose, the modules contain a generic register **input_select** (except for ASG). +Connecting the **output_signal** of submodule **i** to the **input_signal** of submodule **j** is done by setting the register **input_select[j]** to **i**; + +Similarly, a second, possibly different output is allowed for each module (except for scope and trigger): **output_direct**. +This output is added to the analog output 1 and/or 2 depending on the value of the register **output_select**. + +The routing of digital signals within the different FPGA modules is handled by a DSP multiplexer coded in VHDL in the file `red_pitaya_dsp.v `_. +An illustration of the DSP module's principle is provided below: + +.. image:: DSP.jpg + :scale: 100 % + :alt: DSP Signal routing in PyRPL + :align: center + +Monitor Server +--------------- + +The monitor server is a lightweight application written in C (the source code is in the file `monitor_server.c `_) and running on the redpitaya OS to allow remote writing and monitoring of FPGA registers. + +The program is launched on the redpitaya with (automatically done at startup):: + + ./monitor-server PORT-NUMBER, where the default port number is 2222. + +We allow for bidirectional data transfer. The client (python program) connects to the server, which in return accepts the connection. +The client sends 8 bytes of data: + +- Byte 1 is interpreted as a character: 'r' for read and 'w' for write, and 'c' for close. All other messages are ignored. +- Byte 2 is reserved. +- Bytes 3+4 are interpreted as unsigned int. This number n is the amount of 4-byte-units to be read or written. Maximum is 2^16. +- Bytes 5-8 are the start address to be written to. + +If the command is read, the server will then send the requested 4*n bytes to the client. +If the command is write, the server will wait for 4*n bytes of data from the server and write them to the designated FPGA address space. +If the command is close, or if the connection is broken, the server program will terminate. + +After this, the server will wait for the next command. + + +Python package PyRPL +----------------------- + +The python package PyRPL defines all the necessary tools to abstract the communication layer between the client-computer and the redpitaya. +In this way, it is possible to manipulate FPGA registers transparently, as if they were simple attributes of local python objects. +We give here a brief overview of the main python objects in PyRPL. + + + +The Module class ++++++++++++++++++ + +Each FPGA module has a python counterpart: an instance of the class HardwareModule. The inheritance diagram of all HardwareModules is represented below: + +.. inheritance-diagram:: pyrpl.hardware_modules.scope.Scope pyrpl.hardware_modules.iq.Iq pyrpl.hardware_modules.pid.Pid pyrpl.hardware_modules.iir.IIR pyrpl.hardware_modules.trig.Trig pyrpl.hardware_modules.sampler.Sampler pyrpl.hardware_modules.pwm.Pwm + :parts: 1 + +For more complex functionalities, such as those involving the concurrent use of several FPGA modules, +purely software modules can be created. Those modules only inherit from the base class Module and they don't have an FPGA counterpart. Below, the inheritance diagram of all software modules: + +.. inheritance-diagram:: pyrpl.software_modules.Lockbox pyrpl.software_modules.NetworkAnalyzer pyrpl.software_modules.SpectrumAnalyzer pyrpl.software_modules.SoftwarePidLoop pyrpl.software_modules.CurveViewer pyrpl.software_modules.Loop pyrpl.software_modules.PyrplConfig pyrpl.software_modules.Iqs pyrpl.software_modules.Asgs pyrpl.software_modules.Scopes pyrpl.software_modules.Iirs pyrpl.software_modules.Trigs + :parts: 1 + +In addition, to prevent a hardware resource from being used twice, HardwareModules should be accessed via the ModuleManagers which takes care of reserving them for a specific user or Module. For example: + +.. code-block:: python + + # import pyrpl library + from pyrpl import Pyrpl + + # create an interface to the Red Pitaya + pyrpl = Pyrpl() + + # reserve the scope for user 'username' + with pyrpl.scopes.pop('username') as mod: + curve = mod.single() # acquire a curve + # The scope is freed for latter use at this point + + +The Proprety descriptors ++++++++++++++++++++++++++ + +HardwareModules are essentially a list of FPGA registers that can be accessed transparently such as on the following example: + +.. code-block:: python + + # import pyrpl library + import pyrpl + + # create an interface to the Red Pitaya + r = pyrpl.Pyrpl().redpitaya + + print(r.hk.led) # print the current led pattern + + r.hk.led = 0b10101010 # change led pattern + +Changing a register's value should trigger the following actions: + +- communicating the new value to the monitor_server for the FPGA update via a TCP-IP socket. +- the new value should be saved on-disk to restore the system in the same state at the next startup. +- in case a Graphical User Interface is running, the displayed value should be updated. + +To make sure all these actions are triggered by the simple python affectation, we use a `descriptor `_ pattern. The idea is to define +setter and getter functions inside an auxilary "descriptor" class. The diagram below shows the inheritance diagram for the most common attribute descriptor types. + +.. inheritance-diagram:: pyrpl.attributes.IntRegister pyrpl.attributes.SelectRegister pyrpl.attributes.FilterRegister pyrpl.attributes.BoolRegister pyrpl.attributes.FloatRegister + :parts: 1 + +As for the distinction between software modules and hardware modules above, the properties that inherit from BaseRegister are directly mapping an FPGA register. +On the other hand, software modules are using properties that are not in direct correspondance with an FPGA register. However, since they inherit from BaseAttribute, +the load/save and GUI update mechanism is still implemented. + + +Module states +++++++++++++++ + +An important member of the Module class is the list **_setup_attributes**. This is a list of attribute names forming a subset of all module attributes. The value of the attributes in +**_setup_attributes** constitutes the current state of the module. When the PyRPL instance has been created with a configuration file, the current state of each module is kept in-sync +with the configuration file. This is particularly useful for GUI users who would like to keep the previous settings of all modules from one session to the next. + +.. warning:: The config file is *not* kept in-sync with modules that are reserved by a user or another module. It is the responsibility of the user-script or owner module to keep track of the slave module state. Moreover, the slave-module is restored to the last current state whenever it becomes free. + +The state of a module can be saved for latter use in a separate section of the config file. The following example shows the basic use of the load/save API: + +.. code-block:: python + + # import pyrpl library + from pyrpl import Pyrpl + + # create an interface to the Red Pitaya + scope = Pyrpl('new_config_file').redpitaya.scope + + scope.duration = 1. # set curve duration to 1s + scope.save_state('slow_scan') # save state with label 'slow_scan' + scope.duration = 0.01 # set curve duration to 0.01s + scope.save_state('fast_scan') # save state with label 'fast_scan' + scope.load_state('slow_scan') # load state 'slow_scan' + scope.single() # acquire curve with a 1s duration + + +Automatic GUI creation ++++++++++++++++++++++++++ + +Designing Graphical User Interface can be a tedious work. However, since module attributes are defined in a uniform fashion across the project, +most of the GUI creation can be handled automatically. Our GUI is based on the very popular and cross platform library `PyQt `_ +in conjonction with the `qtpy `_ abstraction layer to make PyRPL compatible with PyQt4, PyQt5 and PySide APIs. + +Each PyRPL module is represented by a widget in the Main PyRPL window. The list of attributes to display in the GUI is defined in the Module class by the class member **_gui_attributes**. +When the module widget is created, sub-widgets are automatically created to manipulate the value of each attribute listed in **_gui_attributes**. + + +Example: definition of the Pid class +++++++++++++++++++++++++++++++++++++++ + +The following is extracted from `pid.py `_ + +.. code-block:: python + + class Pid(FilterModule): + # Type of widget to use for this Module class + # should derive from ModuleWidget + _widget_class = PidWidget + + # QObject used to communicate with the widget + _signal_launcher = SignalLauncherPid + + # List of attributes forming the module state + _setup_attributes = ["input", # defined in base class FilterModule + "output_direct", # defined in base class FilterModule + "setpoint", + "p", + "i", + #"d", # Not implemented in the current version of PyRPL + "inputfilter", + "max_voltage", + "min_voltage"] + + # list of attribtue to display in the GUI + _gui_attributes = _setup_attributes + ["ival"] + + # Actions to perform immediately after a state has been loaded + def _setup(self): + """ + sets up the pid (just setting the attributes is OK). + """ + pass + + # Below are the different attributes of a PID module (mostly registers) + + ival = IValAttribute(min=-4, max=4, increment= 8. / 2**16, doc="Current " + "value of the integrator memory (i.e. pid output voltage offset)") + + setpoint = FloatRegister(0x104, bits=14, norm= 2 **13, + doc="pid setpoint [volts]") + + min_voltage = FloatRegister(0x124, bits=14, norm= 2 **13, + doc="minimum output signal [volts]") + max_voltage = FloatRegister(0x128, bits=14, norm= 2 **13, + doc="maximum output signal [volts]") + + p = GainRegister(0x108, bits=_GAINBITS, norm= 2 **_PSR, + doc="pid proportional gain [1]") + i = GainRegister(0x10C, bits=_GAINBITS, norm= 2 **_ISR * 2.0 * np.pi * + 8e-9, + doc="pid integral unity-gain frequency [Hz]") + (...) + + +The generated widget is represented below: + + +.. image:: pid_example.jpg + :scale: 100 % + :alt: The PID widget + :align: center + + diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst new file mode 100644 index 000000000..aec895e3a --- /dev/null +++ b/docs/source/changelog.rst @@ -0,0 +1,18 @@ +Releases +************** + + +Version 0.9.4.0 +===================== + +* Smoother transitions of output voltages during stage transitions in lockbox. +* Automatic Red Pitaya device search extended to multiple network adapters and most recent STEMLab OS v0.98. +* Improved documentation hosted on `www.pyrpl.org `_ and `video tutorial on youtube `_. +* Binaries for Windows, Linux and Mac OSX automatically generated for new releases and `available on sourceforge `_. + + +Version 0.9.3.X +===================== + +There are no release notes for PyRPL versions prior to version 0.9.4. + diff --git a/docs/source/conf.py b/docs/source/conf.py index 5b2311580..9cb959844 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -14,6 +14,7 @@ import sys import os +import sphinx_bootstrap_theme # http://read-the-docs.readthedocs.org/en/latest/faq.html#i-get-import-errors-on-libraries-that-depend-on-c-modules @@ -32,18 +33,19 @@ # the following code fixes various errors with mocked objects from qtpy import QtWidgets, QtCore import asyncio - import logging mod_cls_list = [(QtWidgets, 'QWidget'), - (QtWidgets, 'QLabel'), - (QtWidgets, 'QGroupBox'), - (QtWidgets, 'QLabel'), - (QtCore, 'QObject'), - (asyncio, 'Future')] + (QtWidgets, 'QLabel'), + (QtWidgets, 'QGroupBox'), + (QtWidgets, 'QLabel'), + (QtCore, 'QObject'), + (asyncio, 'Future')] for module, cls_name in mod_cls_list: # set the problematic class to a dummy class setattr(module, cls_name, type(cls_name, (object,), {})) # make sure the class appears to be in the containing module setattr(getattr(module, cls_name), '__module__', module) + import numpy + numpy.pi = 3.141 # If extensions (or modules to document with autodoc) are in another directory, @@ -64,9 +66,12 @@ extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.napoleon', # supports google- or numpy-style dosctrings - 'sphinx.ext.imgmath', # supports things like :math:`a^2 + b^2 = c^2` + #'sphinx.ext.imgmath', # imgmath is not working on my local Ubuntu (LN) + 'sphinx.ext.mathjax', # supports things like :math:`a^2 + b^2 = c^2` 'sphinx.ext.todo', 'sphinx.ext.viewcode', + 'sphinx.ext.inheritance_diagram', + 'sphinx.ext.graphviz' ] # Include todo directives. @@ -86,7 +91,7 @@ # General information about the project. project = u'pyrpl' -copyright = u'2017, Leonhard Neuhaus' +copyright = u'2014-2017, Leonhard Neuhaus, Samuel Deléglise' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -117,7 +122,7 @@ #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). @@ -142,15 +147,82 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. #html_theme = 'default' # 'nature' -html_theme = 'nature' +html_theme = 'bootstrap' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -html_theme_options = {} +html_theme_options = { + # Navigation bar title. (Default: ``project`` value) + 'navbar_title': "PyRPL", + + # Tab name for entire site. (Default: "Site") + 'navbar_site_name': "Navigation", + + # A list of tuples containing pages or urls to link to. + # Valid tuples should be in the following forms: + # (name, page) # a link to a page + # (name, "/aa/bb", 1) # a link to an arbitrary relative url + # (name, "http://example.com", True) # arbitrary absolute url + # Note the "1" or "True" value above as the third argument to indicate + # an arbitrary url. + 'navbar_links': [ + ("Home", "index"), + #("Gallery", "gallery/index"), + ("Installation", "installation"), + ("Graphical user interface", "gui"), + ("API", "api"), + ("How PyRPL works", "basics"), + ("Infos for Developers", "developer_guide/index"), + ], + + # Render the next and previous page links in navbar. (Default: true) + 'navbar_sidebarrel': False, + + # Render the current pages TOC in the navbar. (Default: true) + 'navbar_pagenav': False, + + # Tab name for the current pages TOC. (Default: "Page") + 'navbar_pagenav_name': "Page sections", + + # Global TOC depth for "site" navbar tab. (Default: 1) + # Switching to -1 shows all levels. + 'globaltoc_depth': 2, + + # Include hidden TOCs in Site navbar? + # + # Note: If this is "false", you cannot have mixed ``:hidden:`` and + # non-hidden ``toctree`` directives in the same page, or else the build + # will break. + # + # Values: "true" (default) or "false" + 'globaltoc_includehidden': "true", + + # HTML navbar class (Default: "navbar") to attach to
element. + # For black navbar, do "navbar navbar-inverse" + 'navbar_class': "navbar navbar-inverse", + + # Fix navigation bar to top of page? + # Values: "true" (default) or "false" + 'navbar_fixed_top': "true", + + # Location of link to source. + # Options are "nav" (default), "footer" or anything else to exclude. + 'source_link_position': "footer", + + # Bootswatch (http://bootswatch.com/) theme. + # + # Options are nothing (default) or the name of a valid theme + # such as "cosmo" or "sandstone". "united", + #'bootswatch_theme': "united", # + + # Choose Bootstrap version. + # Values: "3" (default) or "2" (in quotes) + 'bootstrap_version': "3", +} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". @@ -167,7 +239,7 @@ # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +html_favicon = 'icon.ico' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -189,6 +261,7 @@ # Custom sidebar templates, maps document names to template names. #html_sidebars = {} +html_sidebars = {'**': ['localtoc.html']} # Additional templates that should be rendered to pages, maps page names to # template names. @@ -210,7 +283,7 @@ #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the diff --git a/docs/source/contents.rst b/docs/source/contents.rst new file mode 100644 index 000000000..3f732b8d5 --- /dev/null +++ b/docs/source/contents.rst @@ -0,0 +1,13 @@ + +Full documentation structure +****************************** + +.. toctree:: + :maxdepth: -1 + :numbered: + + installation + gui + api + basics + developer_guide/index diff --git a/docs/source/developer_guide/api/spectrum.rst b/docs/source/developer_guide/api/spectrum.rst index 54072c111..90d2f780d 100644 --- a/docs/source/developer_guide/api/spectrum.rst +++ b/docs/source/developer_guide/api/spectrum.rst @@ -14,7 +14,9 @@ which consists in the following steps: 1. Each segment is multiplied by a symmetric window function of the same size. -2. the DFT of individual segments is performed. +2. The DFT of individual segments is performed. The segment is padded + before the FFT by a number of 0s to provide more points in the estimated + spectrum than in the original time segment. 3. The square modulus of the resulting periodograms are averaged to give the estimate of the spectrum, with the same size as the initial time-segments. @@ -45,105 +47,111 @@ In the following, we discuss the normalization of windowing functions, and then, the basic principle of operation of the two modes "iq" and "baseband". -Normalization of windowing functions ------------------------------------- -The Fourier transform of the series a\_n is defined by -A\_m = 1/N sum(a\_k exp(-2 i pi m k/N)) [1] +Definitions +----------- -With this convention, we need to pay attention that the DC-component is -for m=0, and the "negative frequencies" are actually located in the -second half of the interval [N/2, N]. Indeed, we can show that because -of the discretization, A\_{N - m} = A\_{-m} ++----------------------------------------+----------------------------------------------------+ +|name | definition | ++========================================+====================================================+ +| Original time series | x[k], 0<=k = delta(k-k'). -rbw = sum(f\_n^2)/sum(f\_n) [9] +We would like the total spectrum in units of Vrms^2/Hz, integrated from 0 to Nyquist frequency +to yield the same variance of 1. This is ensured by the Equivalent noise bandwidth of the filter window. +To convert from V_pk^2 to V_rms^2/Hz, the spectrum is divided by the residual bandwidth of the filter window. -With this choice, the correct results are retrieved if we make all -calculations in Vpk^2, and divide the results by the rbw to convert them -in Vpk^2/Hz. +Let's calculate: -For this reason, the rbw is not exactly the width at 3 dB of the filter -spectrum, but actually depends on the precise shape of the window over -the whole frequency range via eq [9]. +sum_r <|Y[r]|^2> = (...) = N sum_k w[k]^2 <|x[k]|^2> + +If we remind that x[k] is a white noise following <|x[k]|^2> = 1, we get: + +sum_r <|Y[r]|^2> = N sum_k w[k]^2 + +So, since we want: + +sum_r <|Z[r]|^2> df = 2, (indeed, we want to work with single-sided spectra, such that integrating over positive frequencies is enough) + +with df the frequency step in the FFT, we need to choose: + +rbw = N sum_k w[k]^2 df /4 + +In order to use dimensionless parameters for the filter windows, we can introduce the equivalent noise bandwidth: + +ENBW = sum_k w[k]^2/(sum_k w[k])^2 = 1/4 sum_k w[k]^2 + +Finally, we get the expression of the rbw: + +rbw = sample_rate ENBW IQ mode ------- diff --git a/docs/source/developer_guide/contributing.rst b/docs/source/developer_guide/contributing.rst new file mode 100644 index 000000000..e3bbcfbc9 --- /dev/null +++ b/docs/source/developer_guide/contributing.rst @@ -0,0 +1,19 @@ +Contributing to PyRPL +************************ + + +Contributions to the PyRPL are welcome. To submit your changes for inclusion, please follow this procedure: + +1. Fork this repository to your own github account using the fork button in the upper right corner on ``_. + +2. Clone (download) the fork to a local computer using git clone. + +3. Modify anything you find useful, from the Python source code to the FPGA design. If you modify the FPGA, make sure to include the bitfile (see :doc:`fpga_compilation`). + +4. Modify the documentation in the docs-subfolder if necessary. + +5. Use git add-->git commit-->git push to add changes to your fork. + +6. Then submit a pull request by clicking the pull request button on your github repo. + +Check the `guide to git `_ for more information. \ No newline at end of file diff --git a/docs/source/developer_guide/fpga_compilation.rst b/docs/source/developer_guide/fpga_compilation.rst index f5fbd6e79..24edef218 100644 --- a/docs/source/developer_guide/fpga_compilation.rst +++ b/docs/source/developer_guide/fpga_compilation.rst @@ -1,3 +1,4 @@ +.. _building_fpga: Building the FPGA firmware **************************** diff --git a/docs/source/developer_guide/index.rst b/docs/source/developer_guide/index.rst index ee997b4f1..c262e827c 100644 --- a/docs/source/developer_guide/index.rst +++ b/docs/source/developer_guide/index.rst @@ -1,12 +1,16 @@ -Developer's guide +Notes for developers ************************ .. toctree:: :maxdepth: 2 + contributing fpga_compilation unittests codingstyle codingworkflow api/index dist/index + sdcard + +* :doc:`../contents` \ No newline at end of file diff --git a/docs/source/developer_guide/sdcard.rst b/docs/source/developer_guide/sdcard.rst new file mode 100644 index 000000000..6dfff42fb --- /dev/null +++ b/docs/source/developer_guide/sdcard.rst @@ -0,0 +1,36 @@ + +SD card preparation +=================== + +Option 0: +Download and unzip the `Red Pitaya OS Version 0.92 image `_. Flash this image on a >= 4 GB SD card using a tool like `Win32DiskImager `_, and insert the card into your Red Pitaya. + + +Option 1: flash the full image at once +-------------------------------------- + +For the SD card to be bootable by the redpitaya, several things need to be ensured (Fat32 formatting, boot flag on the right partition...), such that simply copying all required files onto the SD card is not enough to make it bootable. +The simplest method is to copy bit by bit the content of an image file onto the sd card (including partition table and flags). On windows, this can be done with the software `Win32DiskImager `_. +The next section provides a detailed procedure to make the SD card bootable starting from the list of files to be copied. + + +Option 2: Format and copy a list of files on the SD card +---------------------------------------------------------- + +The previous method can be problematic, for instance, if the capacity of the SD card is too small for the provided image file (Indeed, even empty space in the original 4 GB card has been included in the image file). +Hence, it can be advantageous to copy the files individually on the SD card, however, we need to pay attention to make the SD-card bootable. For this we need a Linux system. The following procedure assumes an `Ubuntu `_ system installed on a `virtualbox `_: + +#. Open the ubuntu virtualbox on a computer equipped with a SD card reader. +#. To make sure the SD card will be visible in the virtualbox, we need to go to configuration/usb and enable the sd card reader. +#. Open the ubuntu virtual machine and install gparted and dosfstools with the commands:: + + sudo apt-get install gparted + sudo apt-get install dosfstools + +#. Insert the sd card in the reader and launch gparted on the corresponding device (/dev/sdb in this case but the correct value can be found with "dmesg | tail"):: + + sudo gparted /dev/sdb + +#. In the gparted interface, delete all existing partitions, create a partition map if there is not already one, then create 1 fat32 partition with the maximum space available. To execute these operations, it is necessary to unmount the corresponding partitions (can be done within gparted). +#. Once formatted, right click to set the flag "boot" to that partition. +#. Close gparted, remount the sd card (by simply unplugging/replugging it), and copy all files at the root of the sd card (normally mounted somewhere in /media/xxxx) diff --git a/docs/source/gallery/index.rst b/docs/source/gallery/index.rst index 05e51eb66..b8c3c413e 100644 --- a/docs/source/gallery/index.rst +++ b/docs/source/gallery/index.rst @@ -1,6 +1,6 @@ Gallery of PyRPL usage examples ******************************** -We should add a gallery with use cases of Pyrpl to attract potential users. +We should add a gallery with use cases of Pyrpl to show potential users what PyRPL looks before they install it. This can be essentially a copy-paste from the PyRPL poster presented at CLEO Europe 2017 and the publication. diff --git a/docs/source/gui.rst b/docs/source/gui.rst new file mode 100644 index 000000000..15b32f63c --- /dev/null +++ b/docs/source/gui.rst @@ -0,0 +1,172 @@ +GUI instruments manual +************************* + +In this section, we show how to control the main modules of Pyrpl with the Graphical User Interface (GUI). + + +Video tutorial +================ + +Get started by watching the video tutorial below on locking a Michelson +interferometer with PyRPL. The video covers: + +- how to set up the hardware for a typical Red Pitaya use case (interferometer locking) +- how to get started by :ref:`start_gui-label` +- how to use the :ref:`scope-widget-label` and Arbitrary Signal Generator GUI +- how to set up and configure the :ref:`lockbox-widget-label` +- how to measure a transfer function with the :ref:`na-widget-label` + +.. raw:: html + + + + +.. _start_gui-label: + +Starting the GUI +================ + +If you use the `windows or linux binary files `_, +just launch the executable and the GUI should start. Passing the command-line +argument :code:`--help` to the executable shows a list of optional +command-line arguments. + +If instead you have :ref:`a source code installation <_installation_from_source>`, +then you can either launch PyRPL from a terminal with + +.. code-block:: bash + + python -m pyrpl example_filename + + +or execute the following code block in Python: + +.. code-block:: python + + # import pyrpl library + import pyrpl + + # create a Pyrpl object and store the configuration in a file 'example_filename.yml' + # by default, the parameter 'gui' is set to True + p = pyrpl.Pyrpl(config='example_filename') + + +If you are using the file 'example_filename.yml' for the first time, a screen will pop-up +asking you to choose among the different RedPitayas connected to your local network. After that, +the main Pyrpl widget should appear: + +.. image:: pyrpl_widget.jpg + :width: 60 % + :alt: The main pyrpl widget + :align: center + +The main pyrpl widget is initially empty, however, you can use the "modules" menu to populate it +with module widgets. The module widgets can be closed or reopened at any time, docked/undocked +from the main module window by drag-and-drop on their sidebar, and their position on screen will be +saved in the config file for the next startup. + +We explain the operation of the most useful module widgets in the following sections. + + +.. _typical-widget-label: + +A typical module widget: PID module +==================================== + +The image below shows a typical module widget, here for the PID modules. + +.. image:: pid_widget.jpg + :width: 100 % + :alt: A typical module widget + :align: center + +.. automodule:: pyrpl.widgets.module_widgets.base_module_widget + + +.. _acquisition-widget-label: + +Acquisition Module Widgets +============================= + +.. automodule:: pyrpl.widgets.module_widgets.acquisition_module_widget + + +.. _scope-widget-label: + +Scope Widget +--------------- + +The scope widget is represented in the image below. + +.. image:: scope_widget.jpg + :width: 100 % + :alt: scope module widget + :align: center + +.. automodule:: pyrpl.widgets.module_widgets.scope_widget + + +.. _na-widget-label: + +Network Analyzer Widget +-------------------------- + +The network analyzer widget is represented in the image below. + +.. image:: network_analyzer_widget.jpg + :width: 100 % + :alt: scope module widget + :align: center + +.. automodule:: pyrpl.widgets.module_widgets.na_widget + + +.. _specan-widget-label: + +Spectrum Analyzer Widget +--------------------------- + +The spectrum analyzer widget is represented in the image below. + +.. image:: spectrum_analyzer_widget.jpg + :width: 100 % + :alt: scope module widget + :align: center + +.. automodule:: pyrpl.widgets.module_widgets.spec_an_widget + +.. warning:: Because the spectrum analyzer uses the data sampled by the scope to perform measurements, + it is not possible to use both instruments simultaneaously. When the spectrum-analyzer is running, + the scope-widget appears greyed-out to show that it is not available. + + + +.. _iq-widget-label: + +Iq Widget +=================== + +The iq widget is represented in the image below. A schematic of the internal connection of the IQ-module can be +shown or hidden with the arrow button. + +.. image:: iq_widget.gif + :width: 100% + :alt: scope module widget + :align: center + +.. automodule:: pyrpl.widgets.module_widgets.iq_widget + + +.. _lockbox-widget-label: + +Lockbox Widget +=================== + +The lockbox widget is represented in the image below. + +.. image:: lockbox_widget.jpg + :width: 100 % + :alt: lockbox module widget + :align: center + +.. automodule:: pyrpl.widgets.module_widgets.lockbox_widget diff --git a/docs/source/icon.ico b/docs/source/icon.ico new file mode 100644 index 000000000..fa75edce4 Binary files /dev/null and b/docs/source/icon.ico differ diff --git a/docs/source/index.rst b/docs/source/index.rst index 35a1b5e07..5532f45d5 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,21 +3,225 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to pyrpl's documentation! -********************************* +******************* +What is PyRPL? +******************* -The `Red Pitaya (a.k.a. STEM Lab) `_ `(see full documentation) `_ is an affordable FPGA board with fast analog inputs and outputs. This makes it interesting also for quantum optics experiments. -The software package PyRPL (Python RedPitaya Lockbox) is an implementation of many devices that are needed for optics experiments every day. -Its user interface and all high-level functionality is written in python, but an essential part of the software is hidden in a custom FPGA design (based on the official RedPitaya software version 0.95). -While most users probably never want to touch the FPGA design, the Verilog source code is provided together with this package and may be modified to customize the software to your needs. +.. admonition:: PyRPL is an open-source software package providing many instruments on cheap FPGA hardware boards, e.g.: + + * oscilloscopes, + * network analyzers, + * lock-in amplifiers, + * multiple automatic feedback controllers, + * digital filters of very high order (24), + * and much more. + + +.. admonition:: PyRPL currently runs exclusively on the Red Pitaya. + + The Red Pitaya (a.k.a. STEM Lab) (http://www.redpitaya.com, `see full documentation `_) is an affordable (ca. 260 Euros) FPGA board with fast (125 MHz) analog inputs and outputs. + + +.. admonition:: PyRPL comes with a graphical user interface (GUI). + + See our :doc:`GUI manual ` or the `video tutorial on youtube `_. + + +.. admonition:: PyRPL has a convenient Python API. + + See :ref:`high_level_example` or :ref:`low_level_example`, and the :doc:`full API documentation ` . + + +.. admonition:: PyRPL `binary executables `__ for `Windows, `__ `Linux, `__ or `Mac OS X `__ + + can be easily :ref:`downloaded ` and run without any installation work. + + +.. admonition:: PyRPL's code is entirely public `on github `_ and can be customized, + + including the `Verilog source code for the FPGA `_ which is based on the official Red Pitaya software version 0.95. + + +.. admonition:: PyRPL is already used in many research groups all over the world. + + See for yourself the :ref:`user_feedback`. + + +.. admonition:: PyRPL is free software and comes with the `GNU General Public License v3.0 `_. + + Read the `license `_ for more details! + + + + +.. _manual: + +Manual +******************* .. toctree:: - :maxdepth: 2 + :maxdepth: 1 + :titlesonly: + :hidden: - gallery/index - user_guide/index - reference_guide/index + installation + gui + api + basics developer_guide/index - indices_and_tables/index + contents + +* :doc:`installation` +* :doc:`gui` +* :doc:`api` +* :doc:`basics` +* :doc:`developer_guide/index` +* :doc:`contents` + + +.. _low_level_example: + +Low-level API example +************************ + +.. code-block:: python + + # import pyrpl library + import pyrpl + + # create an interface to the Red Pitaya + r = pyrpl.Pyrpl().redpitaya + + r.hk.led = 0b10101010 # change led pattern + + # measure a few signal values + print("Voltage at analog input1: %.3f" % r.sampler.in1) + print("Voltage at analog output2: %.3f" % r.sampler.out2) + print("Voltage at the digital filter's output: %.3f" % r.sampler.iir) + + # output a function U(t) = 0.5 V * sin(2 pi * 10 MHz * t) to output2 + r.asg0.setup(waveform='sin', + amplitude=0.5, + frequency=10e6, + output_direct='out2') + + # demodulate the output signal from the arbitrary signal generator + r.iq0.setup(input='asg0', # demodulate the signal from asg0 + frequency=10e6, # demodulaltion at 10 MHz + bandwidth=1e5) # demodulation bandwidth of 100 kHz + + # set up a PID controller on the demodulated signal and add result to out2 + r.pid0.setup(input='iq0', + output_direct='out2', # add pid signal to output 2 + setpoint=0.05, # pid setpoint of 50 mV + p=0.1, # proportional gain factor of 0.1 + i=100, # integrator unity-gain-frequency of 100 Hz + input_filter = [3e3, 10e3]) # add 2 low-passes (3 and 10 kHz) + + # modify some parameters in real-time + r.iq0.frequency += 2.3 # add 2.3 Hz to demodulation frequency + r.pid0.i *= 2 # double the integrator unity-gain-frequency + + # take oscilloscope traces of the demodulated and pid signal + data = r.scope.curve(input1='iq0', input2='pid0', + duration=1.0, trigger_source='immediately') + + +.. _high_level_example: + +High-level API example +************************* + +.. code-block:: python + + # import pyrpl library + import pyrpl + + # create a Pyrpl object and store the configuration in a file 'filter-cavity.yml' + p = pyrpl.Pyrpl(config='filter-cavity') + + # ... connect hardware (a Fabry-Perot cavity in this example) and + # configure its paramters with the PyRPL GUI that shows up + + # sweep the cavity length + p.lockbox.sweep() + + # calibrate the cavity parameters + p.lockbox.calibrate() + + # lock to the resonance with a predefined sequence + p.lockbox.lock() + + # launch two different measurements simultaneously + transfer_function = p.network_analyzer.single_async( + input='lockbox.reflection', output='out2', + start=1e3, stop=1e6, points=10000, rbw=1000) + spectrum = p.spectrum_analyzer.single_async( + input='in2', span=1e5, trace_averages=10) + + # wait for measurements to finish + while not transfer_function.done() and not spectrum.done(): + # check whether lock was lost + if not p.lockbox.is_locked(): + # re-lock the cavity + p.lockbox.relock() + # re-start measurements + transfer_function = p.network_analyzer.single_async() + spectrum = p.spectrum_analyzer.single_async() + + # display a measurement result in the curve browser + p.curve_viewer.curve = transfer_function.result() + + +.. include:: user_feedback.rst + + +.. include:: publications.rst + + +.. include:: thanks.rst + + +.. |travis status| image:: https://travis-ci.org/lneuhaus/pyrpl.svg?branch=master + :target: https://travis-ci.org/lneuhaus/pyrpl +.. |appveyor status| image:: https://ci.appveyor.com/api/projects/status/wv2acmg869acg5yy?svg=true + :target: https://ci.appveyor.com/project/lneuhaus/pyrpl +.. |code coverage| image:: https://codecov.io/github/lneuhaus/pyrpl/coverage.svg?branch=master + :target: https://codecov.io/gh/lneuhaus/pyrpl +.. |Python versions on PyPI| image:: https://img.shields.io/pypi/pyversions/pyrpl.svg + :target: https://pypi.python.org/pypi/pyrpl/ +.. |PyRPL version on PyPI| image:: https://img.shields.io/pypi/v/pyrpl.svg + :target: https://pypi.python.org/pypi/pyrpl/ +.. |Download pyrpl| image:: https://img.shields.io/sourceforge/dt/pyrpl.svg + :target: https://sourceforge.net/projects/pyrpl/files/ +.. |Documentation Status| image:: https://readthedocs.org/projects/pyrpl/badge/?version=latest + :target: http://pyrpl.readthedocs.io/en/latest/ +.. |join chat on gitter| image:: https://badges.gitter.im/JoinChat.svg + :target: https://gitter.im/lneuhaus/pyrpl +.. |License| image:: https://img.shields.io/pypi/l/pyrpl.svg + :target: https://github.com/lneuhaus/pyrpl/blob/master/LICENSE + + +Old documentation sections +********************************************************** + +The old documentation is obsolete and will soon be deleted. Please refer to the more recent documentation in the :ref:`manual` section. + +* :doc:`gallery/index` +* :doc:`user_guide/index` +* :doc:`reference_guide/index` +* :doc:`developer_guide/index` +* :doc:`indices_and_tables/index` +* :doc:`contents` + + +Current build status +*********************** + +|travis status| |appveyor status| |code coverage| |Python versions on PyPI| |PyRPL version on PyPI| + +|Download pyrpl| |Documentation Status| |join chat on gitter| |License| + +.. include:: changelog.rst \ No newline at end of file diff --git a/docs/source/indices_and_tables/pyrpl.hardware_modules.iir.rst b/docs/source/indices_and_tables/pyrpl.hardware_modules.iir.rst index 9d8959f04..c3c3bc020 100644 --- a/docs/source/indices_and_tables/pyrpl.hardware_modules.iir.rst +++ b/docs/source/indices_and_tables/pyrpl.hardware_modules.iir.rst @@ -4,13 +4,6 @@ pyrpl\.hardware\_modules\.iir package Submodules ---------- -pyrpl\.hardware\_modules\.iir\.bodefit module ---------------------------------------------- - -.. automodule:: pyrpl.hardware_modules.iir.bodefit - :members: - :undoc-members: - :show-inheritance: pyrpl\.hardware\_modules\.iir\.iir module ----------------------------------------- diff --git a/docs/source/installation.rst b/docs/source/installation.rst new file mode 100644 index 000000000..7cf6ae497 --- /dev/null +++ b/docs/source/installation.rst @@ -0,0 +1,33 @@ +Installation +************* + +Preparing the hardware +========================= + +For PyRPL to work, you must have a working `Red Pitaya / StemLab `_ (`official documentation `_) connected to the same local area network (LAN) as the computer PyRPL is running on. PyRPL is compatible with all operating system versions of the Red Pitaya and does not require any customization of the Red Pitaya. If you have not already set up your Red Pitaya: + +* download and unzip the `Red Pitaya OS Version 0.92 image `_, +* flash this image on 4 GB (or larger) micro SD card using `Win32DiskImager `_ (see a `step-by-step guide for all operating systems `_), and insert the card into your Red Pitaya, and +* connect the Red Pitaya to your LAN and connect its power supply. + +:doc:`user_guide/installation/hardware_installation` gives more detailed instructions in case you are experiencing any trouble. + + +.. _installing_pyrpl: + +Installing PyRPL +================= + +The easiest and fastest way to get PyRPL running is to download and execute the latest precompiled executable for + +* **windows**: `pyrpl-windows.exe `__, +* **linux**: `pyrpl-linux `__, or +* **Mac OS X**: `pyrpl-mac `__. + +If you prefer an installation from source code, go to :ref:`installation_from_source`. + + +Compiling the FPGA code (optional) +=================================== + +A ready-to-use FPGA bitfile comes with PyRPL. If you want to build your own, possibly customized bitfile, go to :doc:`developer_guide/fpga_compilation`. \ No newline at end of file diff --git a/docs/source/iq_widget.gif b/docs/source/iq_widget.gif new file mode 100644 index 000000000..2cee1ebbb Binary files /dev/null and b/docs/source/iq_widget.gif differ diff --git a/docs/source/lockbox_widget.jpg b/docs/source/lockbox_widget.jpg new file mode 100644 index 000000000..d66a72dfb Binary files /dev/null and b/docs/source/lockbox_widget.jpg differ diff --git a/docs/source/logo.png b/docs/source/logo.png index f91208a11..111628ac0 100644 Binary files a/docs/source/logo.png and b/docs/source/logo.png differ diff --git a/docs/source/logos/ANR.png b/docs/source/logos/ANR.png new file mode 100644 index 000000000..332329454 Binary files /dev/null and b/docs/source/logos/ANR.png differ diff --git a/docs/source/logos/CNRS.png b/docs/source/logos/CNRS.png new file mode 100644 index 000000000..c11ea0420 Binary files /dev/null and b/docs/source/logos/CNRS.png differ diff --git a/docs/source/logos/CQOM.png b/docs/source/logos/CQOM.png new file mode 100644 index 000000000..415623615 Binary files /dev/null and b/docs/source/logos/CQOM.png differ diff --git a/docs/source/logos/ENS.png b/docs/source/logos/ENS.png new file mode 100644 index 000000000..59d2db4b3 Binary files /dev/null and b/docs/source/logos/ENS.png differ diff --git a/docs/source/logos/LKB.png b/docs/source/logos/LKB.png new file mode 100644 index 000000000..b114cfcd2 Binary files /dev/null and b/docs/source/logos/LKB.png differ diff --git a/docs/source/logos/OMQ-2.png b/docs/source/logos/OMQ-2.png new file mode 100755 index 000000000..d43804d04 Binary files /dev/null and b/docs/source/logos/OMQ-2.png differ diff --git a/docs/source/logos/OMQ.png b/docs/source/logos/OMQ.png new file mode 100755 index 000000000..18e7f5330 Binary files /dev/null and b/docs/source/logos/OMQ.png differ diff --git a/docs/source/logos/UPMC.png b/docs/source/logos/UPMC.png new file mode 100644 index 000000000..6e9adc54d Binary files /dev/null and b/docs/source/logos/UPMC.png differ diff --git a/docs/source/network_analyzer_widget.jpg b/docs/source/network_analyzer_widget.jpg new file mode 100644 index 000000000..782746ca9 Binary files /dev/null and b/docs/source/network_analyzer_widget.jpg differ diff --git a/docs/source/oldicon.ico b/docs/source/oldicon.ico new file mode 100644 index 000000000..5d1b097af Binary files /dev/null and b/docs/source/oldicon.ico differ diff --git a/docs/source/oldlogo.png b/docs/source/oldlogo.png new file mode 100644 index 000000000..f91208a11 Binary files /dev/null and b/docs/source/oldlogo.png differ diff --git a/docs/source/pid_example.jpg b/docs/source/pid_example.jpg new file mode 100644 index 000000000..791923552 Binary files /dev/null and b/docs/source/pid_example.jpg differ diff --git a/docs/source/pid_widget.jpg b/docs/source/pid_widget.jpg new file mode 100644 index 000000000..57f6f3b9d Binary files /dev/null and b/docs/source/pid_widget.jpg differ diff --git a/docs/source/publications.rst b/docs/source/publications.rst new file mode 100644 index 000000000..48cfdf32f --- /dev/null +++ b/docs/source/publications.rst @@ -0,0 +1,7 @@ +Publications about PyRPL +*************************************** + + +1. `L. Neuhaus, R. Metzdorff, S. Chua, T. Jacqmin, T. Briant, A. Heidmann, P.-F. Cohadon, S. Deléglise, "PyRPL (Python Red Pitaya Lockbox) — An open-source software package for FPGA-controlled quantum optics experiments", 2017 Conference on Lasers and Electro-Optics Europe & European Quantum Electronics Conference (CLEO/Europe-EQEC), Munich, Germany, 2017. `_ +2. `L. Neuhaus, "Red Pitaya DAC performance', blog post, 2016. URL https://ln1985blog.wordpress.com/2016/02/07/red-pitaya-dac-performance/. `_ +3. `L. Neuhaus, "Adding voltage regulators for the RedPitaya output stage", blog post, 2016. URL https://ln1985blog.wordpress.com/2016/02/07/adding-voltage-regulators-for-the-redpitaya-output-stage/. `_ diff --git a/docs/source/pyrpl_widget.jpg b/docs/source/pyrpl_widget.jpg new file mode 100644 index 000000000..4462443fc Binary files /dev/null and b/docs/source/pyrpl_widget.jpg differ diff --git a/docs/source/redpitaya.jpg b/docs/source/redpitaya.jpg new file mode 100644 index 000000000..7704fff03 Binary files /dev/null and b/docs/source/redpitaya.jpg differ diff --git a/docs/source/redpitaya.png b/docs/source/redpitaya.png new file mode 100644 index 000000000..75dd1bf49 Binary files /dev/null and b/docs/source/redpitaya.png differ diff --git a/docs/source/scope_widget.jpg b/docs/source/scope_widget.jpg new file mode 100644 index 000000000..62a46bb77 Binary files /dev/null and b/docs/source/scope_widget.jpg differ diff --git a/docs/source/software_architecture.jpg b/docs/source/software_architecture.jpg new file mode 100644 index 000000000..677c0d757 Binary files /dev/null and b/docs/source/software_architecture.jpg differ diff --git a/docs/source/spectrum_analyzer_widget.jpg b/docs/source/spectrum_analyzer_widget.jpg new file mode 100644 index 000000000..b3e292f9e Binary files /dev/null and b/docs/source/spectrum_analyzer_widget.jpg differ diff --git a/docs/source/thanks.rst b/docs/source/thanks.rst new file mode 100644 index 000000000..e931f1d09 --- /dev/null +++ b/docs/source/thanks.rst @@ -0,0 +1,63 @@ +Contributors +************** + +Below is partial list of PyRPL contributors. We do our best to keep this list updated. +If you've been left off, please change this file by yourself or send an email to the +maintainer (currently neuhaus@lkb.upmc.fr). + +* Leonhard Neuhaus +* Samuel Deléglise +* Jonas Neergard-Nielsen +* Xueshi Guo +* Jerome Degallaix +* Pierre Clade +* Matthew Winchester +* Remi Metzdorff +* Kevin Makles +* Clement Chardin + + +Funding and support +********************** + +Work on PyRPL was partially funded and/or supported by the following organizations: + +.. image:: logos/OMQ-2.png + :target: http://www.lkb.upmc.fr/optomecanics/ + :width: 16% +.. image:: logos/LKB.png + :target: http://www.lkb.upmc.fr/ + :width: 16% +.. image:: logos/ANR.png + :target: http://www.agence-nationale-recherche.fr/ + :width: 16% +.. image:: logos/CQOM.png + :target: http://www.cqom-itn.net/ + :width: 16% +.. image:: logos/CNRS.png + :target: http://www.cnrs.fr/ + :width: 16% +.. image:: logos/UPMC.png + :target: http://www.upmc.fr/en/ + :width: 16% + +* `Optomechanics and Quantum Measurement Group `_ at the `Laboratoire Kastler Brossel in Paris `_ +* `Agence nationale de recherche (ANR) `_ +* `Marie Curie Initial Training Network (ITN) "Cavity Quantum OptoMechanics" (CQOM) `_ +* `Centre National de la Recherche Scientifique (CNRS) `_ +* `Universite Pierre et Marie Curie (UPMC) `_ + + +About +********* + +PyRPL is open source software that allows to use FPGA boards with +analog interfaces for measurement and control of real-world devices in +physics and engineering, notably experiments in quantum optics. It was +started in 2014 by Leonhard Neuhaus for controlling experiments in the field +of quantum physics at the Laboratoire Kastler Brossel in Paris, France. +Its was initially based on the open-source code for the `Red Pitaya `_ +and gradually diverged away from it. In 2016, large parts of the graphical +user interface were added to the project by Samuel Deleglise. PyRPL was finally +published as an open-source project under the GNU General Public License, Version 3 +and has been online since July 2017. diff --git a/docs/source/user_feedback.rst b/docs/source/user_feedback.rst new file mode 100644 index 000000000..801f1f3b8 --- /dev/null +++ b/docs/source/user_feedback.rst @@ -0,0 +1,41 @@ +.. _user_feedback: + +Feedback by PyRPL users +********************************* + + +.. admonition:: PyRPL was developed for and is one of the core components of three ongoing experiments in Quantum Cavity Optomechanics + + in the `Optomechanics and Quantum Measurement Group `_ at `Laboratoire Kastler Brossel in Paris `_. + + +.. admonition:: "Brilliant toolbox with an impressive number of functionalities. After quickly tuning a few parameters, we were able to lock our Fabry-Perot cavity with almost no effort." + + Dr. Jérôme Degallaix from the `Laboratoire des Matériaux Avancés in Lyon `_. + + +.. admonition:: "I am trying PyRPL to see if we can replace the really expensive ZI-lockings to generate parametric feedback at low frequencies." + + Dr. Pau Mestres from `Rainer Blatt's Quantum Optics and Spectroscopy Group `_ at `University of Innsbruck `_. + + +.. admonition:: "IT WORKS! Thanks for your wonderful project :)" + + Dr. Kun Huang from the `State Key Laboratory of Precision Spectroscopy `_ at `East China Normal University `_. + + +.. admonition:: "Thank you very much for your amazing PyRPL module! I have been using it continuously since last week, and it has saved us a lot of trouble already!" + + Ivan Galinsky from the `Quantum Membranes Lab, QUANTOP, Niels Bohr Institute `_, `University of Copenhagen `_. + + +.. admonition:: PyRPL is furthermore used by + + * Team of Dr. Jonas Schou Neergaard-Nielsen at the `Quantum Physics and Information Technology division `_ of the `Technical University of Denmark `_, + * Development team at `Sacher Lasertechnik `_, + * Team of Dr. Pierre Verlot in the `Luminescence Group `_ at `ILM `_, `University Claude Bernard Lyon 1 `_, + * Dr. Gordon A. Shaw, `Mass and Force Group `_ at the `Quantum Metrology division `_ of the `National Institute of Standards and Technology (NIST) `_, + * Team of Prof. Dr. Jean-Philippe Brantut at the `Laboratory for quantum gases `_ of the `École Polytechnique Fédérale de Lausanne (EPFL) `_. + + +If you are using PyRPL and would like to help to promote the project, please send your feedback to `pyrpl.readthedocs.io@gmail.com `_ and we will include your voice on this page. diff --git a/docs/source/user_guide/installation/hardware_installation.rst b/docs/source/user_guide/installation/hardware_installation.rst index bba2d6026..1cd4f3063 100644 --- a/docs/source/user_guide/installation/hardware_installation.rst +++ b/docs/source/user_guide/installation/hardware_installation.rst @@ -1,42 +1,32 @@ Hardware installation for PyRPL ********************************* -The `RedPitaya `_ is an affordable FPGA board with fast analog inputs and outputs. -Before starting, we need to prepare a bootable SD card for the RedPitaya. In principle, PyRPL is compatible with the lattest version of the RedPitaya OS, however, -to prevent any compatibility issues, we provide here the version of the Redipatay OS against which PyRPL has been tested. - -SD card preparation -=================== - -Option 0: -Download abd unzip the `Red Pitaya OS Version 0.92 image `_. Flash this image on a >= 4 GB SD card using a tool like `Win32DiskImager `_, and insert the card into your Red Pitaya. - -Option 1: flash the full image at once --------------------------------------- -For the SD card to be bootable by the redpitaya, several things need to be ensured (Fat32 formatting, boot flag on the right partition...), such that simply copying all required files onto the SD card is not enough to make it bootable. -The simplest method is to copy bit by bit the content of an image file onto the sd card (including partition table and flags). On windows, this can be done with the software `Win32DiskImager `_. -The next section provides a detailed procedure to make the SD card bootable starting from the list of files to be copied. - - -Option 2: Format and copy a list of files on the SD card ----------------------------------------------------------- -The previous method can be problematic, for instance, if the capacity of the SD card is too small for the provided image file (Indeed, even empty space in the original 4 GB card has been included in the image file). -Hence, it can be advantageous to copy the files individually on the SD card, however, we need to pay attention to make the SD-card bootable. For this we need a Linux system. The following procedure assumes an `Ubuntu `_ system installed on a `virtualbox `_: - #. Open the ubuntu virtualbox on a computer equipped with a SD card reader. - #. To make sure the SD card will be visible in the virtualbox, we need to go to configuration/usb and enable the sd card reader. - #. Open the ubuntu virtual machine and install gparted and dosfstools with the commands:: - sudo apt-get install gparted - sudo apt-get install dosfstools - #. Insert the sd card in the reader and launch gparted on the corresponding device (/dev/sdb in this case but the correct value can be found with "dmesg | tail"):: - sudo gparted /dev/sdb - #. In the gparted interface, delete all existing partitions, create a partition map if there is not already one, then create 1 fat32 partition with the maximum space available. To execute these operations, it is necessary to unmount the corresponding partitions (can be done within gparted). - #. Once formatted, right click to set the flag "boot" to that partition. - #. Close gparted, remount the sd card (by simply unplugging/replugging it), and copy all files at the root of the sd card (normally mounted somewhere in /media/xxxx) - - -Communication with the Redpitaya +The `RedPitaya `_ is an affordable FPGA board with fast analog inputs and outputs. PyRPL implements a large number of measurement and real-time feedback instruments for the Red Pitaya. For PyRPL to work, you must have a working `Red Pitaya / StemLab `_ connected to the same local area network (LAN) as the computer PyRPL is running on. PyRPL is compatible with all operating system versions of the Red Pitaya and does not require any customization of the Red Pitaya. If you have not already set up your Red Pitaya, you have two options: :ref:`sdcard_092` or :ref:`sdcard_quick`. You should :ref:`sdcard_check` at the end. + + +.. _sdcard_092: + +Install a minimum-weight SD card (recommended) +================================================ + +Just download and unzip the `Red Pitaya OS Version 0.92 image `_. Flash this image on a >= 4 GB SD card using a tool like `Win32DiskImager `_, and insert the card into your Red Pitaya. Hook up the Red Pitaya to the LAN, connect its power supply and continue with :ref:`installing_pyrpl`. + + +.. _sdcard_official: + +Follow the official documentation +================================== +Follow the quick start steps of the `official Red Pitaya documentation `_ until you can somehow access the Red Pitaya from your computer. You are then ready for :ref:`installing_pyrpl`. + + + +.. _sdcard_check: + +Check that it is working ================================ +The fastest way to check that the Red Pitaya is working is simply to launch PyRPL and let it automatically discover the Red Pitaya device. If this does not work, you may want to ensure that the Red Pitaya and SD card are actually working together. + To make sure the SD card is bootable, insert it into the slot of the Redpitaya and plug the power supply. Connect the redpitaya to your local network with an ethernet cable and enter the IP-adress of the repitaya into an internet browser. The redpitaya welcome screen should show-up! @@ -46,24 +36,3 @@ The redpitaya welcome screen should show-up! This is the Redpitaya welcome screen. - -Quick start -================= - -First, hook up your Red Pitaya / STEMlab to a LAN accessible from your -computer (follow the instructions for this on redpitya.com and make sure -you can access your Red Pitaya with a web browser by typing its -ip-address / hostname into the address bar). In an IPython console or -JuPyter notebook, type - -:: - - from pyrpl import Pyrpl - p = Pyrpl(config='your_configuration_name', hostname='your_redpitaya_ip_address') - -The GUI should open and you can start playing around with it. By calling -pyrpl with different strings for 'your\_configuration\_name', your -settings for a given configuration will be automatically remembered by -PyRPL. You can drop the hostname argument after the first call of a -given configuration. Different RedPitayas with different configuration -names can be run simultaneously. diff --git a/docs/source/user_guide/installation/pyrpl_installation.rst b/docs/source/user_guide/installation/pyrpl_installation.rst index 8fa4c7b5b..f29975475 100644 --- a/docs/source/user_guide/installation/pyrpl_installation.rst +++ b/docs/source/user_guide/installation/pyrpl_installation.rst @@ -2,15 +2,17 @@ Installing PyRPL ********************************* - -The Fastest way: Running from binary files +Running from binary files (fastest) ==================================== -The easiest and fastest way to get PyRPL running is to download and execute the `precompiled executable for windows or linux `__. This option requires no extra programs to be installed on the computer. If you want the Pyrpl binaries for a Mac, please let us know `by creating a new issue `_ and we will prepare them for you. +The easiest and fastest way to get PyRPL running is to download and execute the `precompiled executable for windows "pyrpl-windows.exe" `__ or `linux "pyrpl-linux" `__. This option requires no extra programs to be installed on the computer. If you want the Pyrpl binaries for a Mac, please let us know `by creating a new issue `_ and we will prepare them for you. + + +.. _installation_from_source: -The Hacker's way: Running the Python source code -================================================ +Running the Python source code +=================================== If you would like to use and/or modify the source code, make sure you have an installation of Python (2.7, 3.4, 3.5, or 3.6). @@ -27,11 +29,11 @@ Option 1: Installation from Anaconda If you are new to Python or unexperienced with fighting installation issues, it is recommended to install the `Anaconda `__ Python distribution, which allows to install all PyRPL dependencies via:: - conda install numpy scipy paramiko pandas nose pip pyqt qtpy pyqtgraph pyyaml + conda install numpy scipy paramiko pandas nose pip pyqt qtpy pyqtgraph pyyaml nbconvert Check :ref:`anaconda_problems` for hints if you cannot execute conda in a terminal. Alternatively, if you prefer creating a virtual environment for pyrpl, do so with the following two commands:: - conda create -y -n pyrpl-env numpy scipy paramiko pandas nose pip pyqt qtpy pyqtgraph pyyaml + conda create -y -n pyrpl-env numpy scipy paramiko pandas nose pip pyqt qtpy pyqtgraph pyyaml nbconvert activate pyrpl-env @@ -54,8 +56,9 @@ Downloading and installing PyRPL from source Various channels are available to obtain the PyRPL source code. + Option 1: Installation with pip (recommended, for standard users) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you have pip correctly installed, executing the following line in a command line should install pyrpl and all missing dependencies:: diff --git a/docs/source/user_guide/tutorial/index.rst b/docs/source/user_guide/tutorial/index.rst index fd2d9b500..afbb37300 100644 --- a/docs/source/user_guide/tutorial/index.rst +++ b/docs/source/user_guide/tutorial/index.rst @@ -1,7 +1,7 @@ Quickstart Tutorial for PyRPL ******************************** -You can download the tutorial in form of a :download:`Jupyter notebook file ` or in :download:`HTML-form `. +You can download the API tutorial in form of a :download:`Jupyter notebook file ` or in :download:`HTML-form `. .. raw:: html diff --git a/docs/source/user_guide/tutorial/tutorial.html b/docs/source/user_guide/tutorial/tutorial.html index 669354840..aa4608166 100644 --- a/docs/source/user_guide/tutorial/tutorial.html +++ b/docs/source/user_guide/tutorial/tutorial.html @@ -11726,7 +11726,9 @@ div#notebook { overflow: visible; border-top: none; -}@media print { +} + +@media print { div.cell { display: block; page-break-inside: avoid; @@ -11747,7 +11749,7 @@ - +