├── .coveragerc ├── .github └── workflows │ ├── CI.yml │ ├── nightly.yml │ ├── python-publish.yml │ └── test-python-publish.yml ├── .gitignore ├── .pre-commit-config.yaml ├── LICENSE.txt ├── MANIFEST.in ├── Makefile ├── README.md ├── cflib ├── __init__.py ├── bootloader │ ├── __init__.py │ ├── boottypes.py │ └── cloader.py ├── cpx │ ├── __init__.py │ └── transports.py ├── crazyflie │ ├── __init__.py │ ├── appchannel.py │ ├── commander.py │ ├── console.py │ ├── extpos.py │ ├── high_level_commander.py │ ├── link_statistics.py │ ├── localization.py │ ├── log.py │ ├── mem │ │ ├── __init__.py │ │ ├── deck_memory.py │ │ ├── i2c_element.py │ │ ├── led_driver_memory.py │ │ ├── led_timings_driver_memory.py │ │ ├── lighthouse_memory.py │ │ ├── loco_memory.py │ │ ├── loco_memory_2.py │ │ ├── memory_element.py │ │ ├── memory_tester.py │ │ ├── multiranger_memory.py │ │ ├── ow_element.py │ │ ├── paa3905_memory.py │ │ └── trajectory_memory.py │ ├── param.py │ ├── platformservice.py │ ├── swarm.py │ ├── syncCrazyflie.py │ ├── syncLogger.py │ ├── toc.py │ └── toccache.py ├── crtp │ ├── __init__.py │ ├── cflinkcppdriver.py │ ├── crtpdriver.py │ ├── crtpstack.py │ ├── exceptions.py │ ├── pcap.py │ ├── prrtdriver.py │ ├── radio_link_statistics.py │ ├── radiodriver.py │ ├── serialdriver.py │ ├── tcpdriver.py │ ├── udpdriver.py │ └── usbdriver.py ├── drivers │ ├── __init__.py │ ├── cfusb.py │ └── crazyradio.py ├── localization │ ├── __init__.py │ ├── _ippe.py │ ├── ippe_cf.py │ ├── lighthouse_bs_geo.py │ ├── lighthouse_bs_vector.py │ ├── lighthouse_config_manager.py │ ├── lighthouse_geometry_solver.py │ ├── lighthouse_initial_estimator.py │ ├── lighthouse_sample_matcher.py │ ├── lighthouse_sweep_angle_reader.py │ ├── lighthouse_system_aligner.py │ ├── lighthouse_system_scaler.py │ ├── lighthouse_types.py │ └── param_io.py ├── positioning │ ├── __init__.py │ ├── motion_commander.py │ └── position_hl_commander.py ├── resources │ └── binaries │ │ └── nrf51-s110-and-bl.bin └── utils │ ├── __init__.py │ ├── callbacks.py │ ├── encoding.py │ ├── multiranger.py │ ├── param_file_helper.py │ ├── power_switch.py │ ├── reset_estimator.py │ └── uri_helper.py ├── docs ├── api │ ├── cflib │ │ └── index.md │ ├── index.md │ └── template │ │ └── text.mako ├── development │ ├── eeprom.md │ ├── index.md │ ├── matlab.md │ ├── uart_communication.md │ └── wireshark.md ├── functional-areas │ ├── crazyradio_lib.md │ └── index.md ├── images │ └── wireshark-dissector.png ├── index.md ├── installation │ ├── index.md │ ├── install.md │ └── usb_permissions.md └── user-guides │ ├── index.md │ ├── python_api.md │ ├── sbs_connect_log_param.md │ ├── sbs_motion_commander.md │ └── sbs_swarm_interface.md ├── examples ├── aideck │ └── fpv.py ├── autonomy │ ├── autonomousSequence.py │ ├── autonomous_sequence_high_level.py │ ├── autonomous_sequence_high_level_compressed.py │ ├── circling_square_demo.py │ ├── full_state_setpoint_demo.py │ ├── motion_commander_demo.py │ └── position_commander_demo.py ├── cache │ └── .gitkeep ├── cfbridge.py ├── console │ └── console.py ├── led-ring │ ├── led_mem_set.py │ ├── led_param_set.py │ └── led_timing.py ├── lighthouse │ ├── lighthouse_openvr_grab.py │ ├── lighthouse_openvr_grab_color.py │ ├── lighthouse_openvr_multigrab.py │ ├── multi_bs_geometry_estimation.py │ ├── read_lighthouse_mem.py │ └── write_lighthouse_mem.py ├── link_quality │ └── latency.py ├── loco_nodes │ └── lps_reboot_to_bootloader.py ├── logging │ ├── basiclog.py │ └── basiclogSync.py ├── memory │ ├── erase-ow.py │ ├── flash-memory.py │ ├── read-eeprom.py │ ├── read-l5.py │ ├── read-ow.py │ ├── read-paa3905.py │ ├── read_deck_mem.py │ ├── write-eeprom.py │ └── write-ow.py ├── mocap │ ├── mocap_hl_commander.py │ └── qualisys_hl_commander.py ├── motors │ ├── multiramp.py │ └── ramp.py ├── multiranger │ ├── multiranger_pointcloud.py │ ├── multiranger_push.py │ ├── multiranger_wall_following.py │ └── wall_following │ │ └── wall_following.py ├── parameters │ ├── basicparam.py │ ├── persistent_params.py │ └── persistent_params_from_file.py ├── positioning │ ├── flowsequenceSync.py │ ├── initial_position.py │ ├── matrix_light_printer.py │ └── monalisa.png ├── radio │ ├── radio-test.py │ └── scan.py ├── step-by-step │ ├── sbs_connect_log_param.py │ ├── sbs_motion_commander.py │ └── sbs_swarm.py └── swarm │ ├── asynchronizedSwarm.py │ ├── hl-commander-swarm.py │ ├── leader-follower.py │ ├── swarmSequence.py │ ├── swarmSequenceCircle.py │ └── synchronizedSequence.py ├── lpslib ├── __init__.py └── lopoanchor.py ├── module.json ├── pyproject.toml ├── setup_linux.sh ├── sys_test ├── __init__.py ├── single_cf_grounded │ ├── README.md │ ├── single_cf_grounded.py │ ├── test_bootloader.py │ ├── test_link.py │ └── test_power_switch.py └── swarm_test_rig │ ├── README.md │ ├── __init__.py │ ├── cload_all.sh │ ├── rig_support.py │ ├── test_connection.py │ ├── test_logging.py │ ├── test_memory_map.py │ └── test_response_time.py ├── test ├── __init__.py ├── crazyflie │ ├── __init__.py │ ├── mem │ │ ├── __init__.py │ │ └── test_lighthouse_memory.py │ ├── test_localization.py │ ├── test_swarm.py │ ├── test_syncCrazyflie.py │ └── test_syncLogger.py ├── crtp │ ├── __init__.py │ └── test_crtpstack.py ├── localization │ ├── __init__.py │ ├── fixtures │ │ ├── parameters.yaml │ │ └── system_config.yaml │ ├── lighthouse_fixtures.py │ ├── lighthouse_test_base.py │ ├── test_ippe_cf.py │ ├── test_lighthouse_bs_geo.py │ ├── test_lighthouse_bs_vector.py │ ├── test_lighthouse_config_manager.py │ ├── test_lighthouse_geometry_solver.py │ ├── test_lighthouse_initial_estimator.py │ ├── test_lighthouse_sample_matcher.py │ ├── test_lighthouse_system_aligner.py │ ├── test_lighthouse_types.py │ └── test_param_io.py ├── positioning │ ├── __init__.py │ ├── test_motion_commander.py │ └── test_position_hl_commander.py ├── support │ ├── __init__.py │ └── asyncCallbackCaller.py └── utils │ ├── __init__.py │ ├── fixtures │ ├── five_params.yaml │ └── single_param.yaml │ ├── test_callbacks.py │ ├── test_encoding.py │ ├── test_multiranger.py │ └── test_param_file_helper.py ├── tools ├── build-docs │ └── build-docs ├── build │ ├── bdist │ ├── build │ ├── sys-test │ ├── test │ └── verify └── crtp-dissector.lua └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | source = 4 | . 5 | omit = 6 | .tox/* 7 | /usr/* 8 | */tmp* 9 | setup.py 10 | # Don't complain if non-runnable code isn't run 11 | */__main__.py 12 | 13 | [report] 14 | exclude_lines = 15 | # Have to re-enable the standard pragma 16 | \#\s*pragma: no cover 17 | 18 | # Don't complain if tests don't hit defensive assertion code: 19 | ^\s*raise AssertionError\b 20 | ^\s*raise NotImplementedError\b 21 | ^\s*return NotImplemented\b 22 | ^\s*raise$ 23 | 24 | # Don't complain if non-runnable code isn't run: 25 | ^if __name__ == ['"]__main__['"]:$ 26 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | # Run check and build of the lib using the Bitcraze builder docker image 2 | name: CI 3 | 4 | on: 5 | push: 6 | branches: [ master ] 7 | pull_request: 8 | branches: [ master ] 9 | schedule: 10 | # Weekly build to make sure dependencies are OK 11 | - cron: '30 16 * * 6' 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repo 19 | uses: actions/checkout@v4 20 | 21 | - name: Build 22 | run: docker run --rm -v ${PWD}:/module bitcraze/builder ./tools/build/build 23 | 24 | - name: Build docs 25 | run: docker run --rm -v ${PWD}:/module bitcraze/web-builder ./tools/build-docs/build-docs 26 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | # Run check and build of the lib using the Bitcraze builder docker image 2 | name: Nightly Build 3 | 4 | on: 5 | workflow_dispatch: 6 | schedule: 7 | - cron: '0 2 * * *' 8 | 9 | jobs: 10 | build-and-test: 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: [ubuntu-latest, lab-mac, windows-latest] 15 | python-version: ["3.10", "3.11", "3.12", "3.13"] 16 | exclude: 17 | - os: lab-mac 18 | python-version: "3.13" 19 | 20 | runs-on: ${{ matrix.os }} 21 | 22 | steps: 23 | - name: Checkout repo 24 | uses: actions/checkout@v4 25 | 26 | - name: Set up Python ${{ matrix.python-version }} 27 | if: runner.os == 'Linux' || runner.os == 'Windows' 28 | uses: actions/setup-python@v5 29 | with: 30 | python-version: ${{ matrix.python-version }} 31 | 32 | - name: Set up Python ${{ matrix.python-version }} and venv on macOS 33 | if: runner.os == 'macOS' 34 | run: | 35 | brew install python@${{ matrix.python-version }} 36 | $(brew --prefix)/bin/python${{ matrix.python-version }} -m venv venv 37 | echo "PATH=$(pwd)/venv/bin:$PATH" >> $GITHUB_ENV 38 | 39 | - name: Set up MSVC environment (Windows) 40 | uses: ilammy/msvc-dev-cmd@v1 41 | with: 42 | arch: x64 43 | if: runner.os == 'Windows' 44 | 45 | - name: Install dependencies 46 | run: | 47 | python3 -m pip install --upgrade pip build setuptools 48 | python3 -m pip install pre-commit 49 | 50 | - name: Code quality checks 51 | run: pre-commit run --all-files 52 | 53 | - name: Build wheel 54 | run: python3 -m build --wheel 55 | 56 | - name: Install the built wheel 57 | run: | 58 | # Find the built wheel 59 | WHEEL_FILE=$(ls dist/*.whl | head -n 1) 60 | echo "Installing wheel: $WHEEL_FILE" 61 | pip install "$WHEEL_FILE" 62 | shell: bash 63 | if: runner.os != 'Windows' 64 | 65 | - name: Install the built wheel (Windows) 66 | run: | 67 | for /f %%i in ('dir /b dist\*.whl') do set WHEEL_FILE=dist\%%i 68 | echo Installing wheel: %WHEEL_FILE% 69 | pip install %WHEEL_FILE% 70 | shell: cmd 71 | if: runner.os == 'Windows' 72 | 73 | - name: Test 74 | run: python3 -m unittest discover test/ 75 | 76 | build-docs: 77 | runs-on: ubuntu-latest 78 | steps: 79 | - name: Checkout repo 80 | uses: actions/checkout@v4 81 | - name: Setup Python 82 | uses: actions/setup-python@v5 83 | with: 84 | python-version: '3.x' 85 | - name: Install dependencies 86 | run: | 87 | python3 -m pip install --upgrade pip setuptools 88 | python3 -m pip install pdoc3 pyyaml 89 | - name: Build docs 90 | run: ./tools/build-docs/build-docs 91 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Release 10 | 11 | on: 12 | workflow_dispatch: 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | deploy: 19 | 20 | runs-on: ubuntu-latest 21 | environment: 22 | name: pypi 23 | url: https://pypi.org/p/cflib 24 | permissions: 25 | id-token: write 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | with: 30 | fetch-depth: 0 31 | - name: Set up Python 32 | uses: actions/setup-python@v4 33 | with: 34 | python-version: '3.x' 35 | - name: Install dependencies 36 | run: | 37 | python -m pip install --upgrade pip 38 | pip install build 39 | - name: Build package 40 | run: python -m build 41 | - name: Publish package 42 | uses: pypa/gh-action-pypi-publish@release/v1 43 | with: 44 | verbose: true 45 | -------------------------------------------------------------------------------- /.github/workflows/test-python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Test Release 10 | 11 | on: 12 | workflow_dispatch: 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | deploy: 19 | 20 | runs-on: ubuntu-latest 21 | environment: 22 | name: pypi-test 23 | url: https://pypi.org/p/cflib 24 | permissions: 25 | id-token: write 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | with: 30 | fetch-depth: 0 31 | - name: Set up Python 32 | uses: actions/setup-python@v4 33 | with: 34 | python-version: '3.x' 35 | - name: Install dependencies 36 | run: | 37 | python -m pip install --upgrade pip 38 | pip install build 39 | - name: Build package 40 | run: python -m build 41 | - name: Publish package to TestPyPI 42 | uses: pypa/gh-action-pypi-publish@release/v1 43 | with: 44 | repository-url: https://test.pypi.org/legacy/ 45 | verbose: true 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | build/* 3 | dist/* 4 | *.egg-info/ 5 | .coverage 6 | .venv* 7 | .tox* 8 | venv* 9 | **/cache/*.json 10 | 11 | #eclipse generated files 12 | .project 13 | .pydevproject 14 | .settings/* 15 | 16 | #jekyll generated pages 17 | docs/.jekyll-metadata 18 | docs/api/cflib/* 19 | 20 | # Generated documentation 21 | tools/build-docs/.local 22 | tools/build-docs/.cache 23 | docs/api/cflib 24 | !docs/api/cflib/index.md 25 | 26 | #vscode config folder 27 | .vscode/ 28 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: 7192665e31cea6ace58a71e086c7248d7e5610c2 4 | hooks: 5 | - id: autopep8-wrapper 6 | - id: check-added-large-files 7 | - id: check-case-conflict 8 | - id: check-docstring-first 9 | - id: check-json 10 | - id: check-merge-conflict 11 | - id: check-xml 12 | - id: check-yaml 13 | - id: debug-statements 14 | - id: detect-private-key 15 | - id: double-quote-string-fixer 16 | - id: end-of-file-fixer 17 | - id: requirements-txt-fixer 18 | - repo: https://github.com/PyCQA/flake8 19 | rev: 16f5f28a384f0781bebb37a08aa45e65b9526c50 20 | hooks: 21 | - id: flake8 22 | - repo: https://github.com/asottile/reorder_python_imports 23 | rev: ab609b9b982729dfc287b4e75963c0c4de254a31 24 | hooks: 25 | - id: reorder-python-imports 26 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include cflib/resources/binaries *.bin 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REBUILD_FLAG = 2 | 3 | .PHONY: all 4 | all: venv test 5 | 6 | .PHONY: venv 7 | venv: .venv.touch 8 | tox -e venv $(REBUILD_FLAG) 9 | 10 | .PHONY: tests test 11 | tests: test 12 | test: .venv.touch 13 | tox $(REBUILD_FLAG) 14 | 15 | 16 | .venv.touch: setup.py requirements-dev.txt 17 | $(eval REBUILD_FLAG := --recreate) 18 | touch .venv.touch 19 | 20 | 21 | .PHONY: clean 22 | clean: 23 | find . -iname '*.pyc' | xargs rm -f 24 | rm -rf .tox 25 | rm -rf ./venv-* 26 | rm -f .venv.touch 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cflib: Crazyflie python library [![CI](https://github.com/bitcraze/crazyflie-lib-python/workflows/CI/badge.svg)](https://github.com/bitcraze/crazyflie-lib-python/actions) 2 | 3 | cflib is an API written in Python that is used to communicate with the Crazyflie 4 | and Crazyflie 2.0 quadcopters. It is intended to be used by client software to 5 | communicate with and control a Crazyflie quadcopter. For instance the [Crazyflie PC client](https://www.github.com/bitcraze/crazyflie-clients-python) uses the cflib. 6 | 7 | See [below](#platform-notes) for platform specific instruction. 8 | For more info see our [documentation](https://www.bitcraze.io/documentation/repository/crazyflie-lib-python/master/). 9 | 10 | ## Installation 11 | See the [installation instructions](docs/installation/install.md) in the github docs folder. 12 | 13 | ## Official Documentation 14 | 15 | Check out the [Bitcraze crazyflie-lib-python documentation](https://www.bitcraze.io/documentation/repository/crazyflie-lib-python/master/) on our website. 16 | 17 | ## Contribute 18 | Go to the [contribute page](https://www.bitcraze.io/contribute/) on our website to learn more. 19 | 20 | ### Test code for contribution 21 | Run the automated build locally to test your code 22 | 23 | python3 tools/build/build 24 | -------------------------------------------------------------------------------- /cflib/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # || ____ _ __ 5 | # +------+ / __ )(_) /_______________ _____ ___ 6 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 7 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 8 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 9 | # 10 | # Copyright (C) 2011-2013 Bitcraze AB 11 | # 12 | # Crazyflie Nano Quadcopter Client 13 | # 14 | # This program is free software; you can redistribute it and/or 15 | # modify it under the terms of the GNU General Public License 16 | # as published by the Free Software Foundation; either version 2 17 | # of the License, or (at your option) any later version. 18 | # 19 | # This program is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU General Public License for more details. 23 | # You should have received a copy of the GNU General Public License 24 | # along with this program. If not, see . 25 | """ 26 | The Crazyflie Micro Quadcopter library API used to communicate with the 27 | Crazyflie Micro Quadcopter via a communication link. 28 | 29 | The API takes care of scanning, opening and closing the communication link 30 | as well as sending/receiving data from the Crazyflie. 31 | 32 | A link is described using an URI of the following format: 33 | ://. 34 | See each link for the data that can be included in the URI for that interface. 35 | 36 | The two main uses-cases are scanning for Crazyflies available on a 37 | communication link and opening a communication link to a Crazyflie. 38 | 39 | Example of scanning for available Crazyflies on all communication links: 40 | ```python 41 | cflib.crtp.init_drivers() 42 | available = cflib.crtp.scan_interfaces() 43 | for i in available: 44 | print "Found Crazyflie on URI [%s] with comment [%s]" 45 | % (available[0], available[1]) 46 | ``` 47 | 48 | Example of connecting to a Crazyflie with known URI (radio dongle 0 and 49 | radio channel 125): 50 | ```python 51 | cf = Crazyflie() 52 | cf.open_link("radio://0/125") 53 | ... 54 | cf.close_link() 55 | ``` 56 | """ 57 | import warnings 58 | 59 | warnings.simplefilter('always', DeprecationWarning) # Enbable DeprecationWarnings 60 | 61 | __pdoc__ = {} 62 | __pdoc__['cflib.crtp.cflinkcppdriver'] = False 63 | __pdoc__['cflib.cpx.transports'] = False 64 | -------------------------------------------------------------------------------- /cflib/bootloader/boottypes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # || ____ _ __ 5 | # +------+ / __ )(_) /_______________ _____ ___ 6 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 7 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 8 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 9 | # 10 | # Copyright (C) 2011-2013 Bitcraze AB 11 | # 12 | # Crazyflie Nano Quadcopter Client 13 | # 14 | # This program is free software; you can redistribute it and/or 15 | # modify it under the terms of the GNU General Public License 16 | # as published by the Free Software Foundation; either version 2 17 | # of the License, or (at your option) any later version. 18 | # 19 | # This program is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU General Public License for more details. 23 | # You should have received a copy of the GNU General Public License 24 | # along with this program. If not, see . 25 | """ 26 | Bootloading utilities for the Crazyflie. 27 | """ 28 | 29 | __author__ = 'Bitcraze AB' 30 | __all__ = ['BootVersion', 'TargetTypes', 'Target'] 31 | 32 | 33 | class BootVersion: 34 | CF1_PROTO_VER_0 = 0x00 35 | CF1_PROTO_VER_1 = 0x01 36 | CF2_PROTO_VER = 0x10 37 | 38 | @staticmethod 39 | def to_ver_string(ver): 40 | if (ver == BootVersion.CF1_PROTO_VER_0 or ver == BootVersion. 41 | CF1_PROTO_VER_1): 42 | return 'Crazyflie Nano Quadcopter (1.0)' 43 | if ver == BootVersion.CF2_PROTO_VER: 44 | return 'Crazyflie 2.0' 45 | return 'Unknown' 46 | 47 | @staticmethod 48 | def is_cf2(ver): 49 | return ver == BootVersion.CF2_PROTO_VER 50 | 51 | 52 | class TargetTypes: 53 | STM32 = 0xFF 54 | NRF51 = 0xFE 55 | 56 | @staticmethod 57 | def to_string(target): 58 | if target == TargetTypes.STM32: 59 | return 'stm32' 60 | if target == TargetTypes.NRF51: 61 | return 'nrf51' 62 | return 'Unknown' 63 | 64 | @staticmethod 65 | def from_string(name): 66 | if name == 'stm32': 67 | return TargetTypes.STM32 68 | if name == 'nrf51': 69 | return TargetTypes.NRF51 70 | return 0 71 | 72 | 73 | class Target: 74 | 75 | def __init__(self, id): 76 | self.id = id 77 | self.protocol_version = 0xFF 78 | self.page_size = 0 79 | self.buffer_pages = 0 80 | self.flash_pages = 0 81 | self.start_page = 0 82 | self.version = None 83 | self.cpuid = '' 84 | self.data = None 85 | 86 | def __str__(self): 87 | ret = '' 88 | ret += 'Target info: {} (0x{:X})\n'.format( 89 | TargetTypes.to_string(self.id), self.id) 90 | ret += 'Flash pages: {} | Page size: {} | Buffer pages: {} |' \ 91 | ' Start page: {} | Version: {} \n'.format(self.flash_pages, self.page_size, 92 | self.buffer_pages, self.start_page, self.version) 93 | ret += '%d KBytes of flash available for firmware image.' % ( 94 | (self.flash_pages - self.start_page) * self.page_size / 1024) 95 | return ret 96 | -------------------------------------------------------------------------------- /cflib/crazyflie/appchannel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2020 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | """ 23 | Data channel to communicate with an application running in the Crazyflie 24 | """ 25 | import logging 26 | 27 | import cflib.crazyflie.platformservice 28 | from cflib.crtp.crtpstack import CRTPPacket 29 | from cflib.crtp.crtpstack import CRTPPort 30 | from cflib.utils.callbacks import Caller 31 | # from . import Crazyflie 32 | 33 | __author__ = 'Bitcraze AB' 34 | __all__ = ['Appchannel'] 35 | 36 | logger = logging.getLogger(__name__) 37 | 38 | 39 | class Appchannel: 40 | def __init__(self, crazyflie): 41 | self._cf = crazyflie 42 | 43 | self.packet_received = Caller() 44 | 45 | self._cf.add_port_callback(CRTPPort.PLATFORM, 46 | self._incoming) 47 | 48 | def send_packet(self, data): 49 | packet = CRTPPacket() 50 | packet.port = CRTPPort.PLATFORM 51 | packet.channel = cflib.crazyflie.platformservice.APP_CHANNEL 52 | packet.data = data 53 | self._cf.send_packet(packet) 54 | 55 | def _incoming(self, packet: CRTPPacket): 56 | if packet.channel == cflib.crazyflie.platformservice.APP_CHANNEL: 57 | self.packet_received.call(packet.data) 58 | -------------------------------------------------------------------------------- /cflib/crazyflie/console.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # || ____ _ __ 5 | # +------+ / __ )(_) /_______________ _____ ___ 6 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 7 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 8 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 9 | # 10 | # Copyright (C) 2011-2013 Bitcraze AB 11 | # 12 | # Crazyflie Nano Quadcopter Client 13 | # 14 | # This program is free software; you can redistribute it and/or 15 | # modify it under the terms of the GNU General Public License 16 | # as published by the Free Software Foundation; either version 2 17 | # of the License, or (at your option) any later version. 18 | # 19 | # This program is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU General Public License for more details. 23 | # You should have received a copy of the GNU General Public License 24 | # along with this program. If not, see . 25 | """ 26 | Crazyflie console is used to receive characters printed using printf 27 | from the firmware. 28 | """ 29 | from cflib.crtp.crtpstack import CRTPPort 30 | from cflib.utils.callbacks import Caller 31 | 32 | __author__ = 'Bitcraze AB' 33 | __all__ = ['Console'] 34 | 35 | 36 | class Console: 37 | """ 38 | Crazyflie console is used to receive characters printed using printf 39 | from the firmware. 40 | """ 41 | 42 | def __init__(self, crazyflie): 43 | """ 44 | Initialize the console and register it to receive data from the copter. 45 | """ 46 | 47 | self.receivedChar = Caller() 48 | """ 49 | This member variable is used to setup a callback that will be called 50 | when text is received from the CONSOLE port of CRTP (0). 51 | 52 | Example: 53 | ```python 54 | [...] 55 | 56 | def log_console(self, text): 57 | self.log_file.write(text) 58 | 59 | [...] 60 | 61 | self.cf.console.receivedChar.add_callback(self.log_console) 62 | ``` 63 | """ 64 | 65 | self.cf = crazyflie 66 | self.cf.add_port_callback(CRTPPort.CONSOLE, self._incoming) 67 | 68 | def _incoming(self, packet): 69 | """ 70 | Callback for data received from the copter. 71 | """ 72 | # This might be done prettier ;-) 73 | console_text = packet.data.decode('UTF-8') 74 | 75 | self.receivedChar.call(console_text) 76 | -------------------------------------------------------------------------------- /cflib/crazyflie/extpos.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # || ____ _ __ 5 | # +------+ / __ )(_) /_______________ _____ ___ 6 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 7 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 8 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 9 | # 10 | # Copyright (C) 2011-2020 Bitcraze AB 11 | # 12 | # Crazyflie Nano Quadcopter Client 13 | # 14 | # This program is free software; you can redistribute it and/or 15 | # modify it under the terms of the GNU General Public License 16 | # as published by the Free Software Foundation; either version 2 17 | # of the License, or (at your option) any later version. 18 | # 19 | # This program is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU General Public License for more details. 23 | # You should have received a copy of the GNU General Public License 24 | # along with this program. If not, see . 25 | """ 26 | Used for sending external position to the Crazyflie 27 | """ 28 | 29 | __author__ = 'Bitcraze AB' 30 | __all__ = ['Extpos'] 31 | 32 | 33 | class Extpos(): 34 | """ 35 | Used for sending its position to the Crazyflie 36 | """ 37 | 38 | def __init__(self, crazyflie=None): 39 | """ 40 | Initialize the Extpos object. 41 | """ 42 | self._cf = crazyflie 43 | 44 | def send_extpos(self, x, y, z): 45 | """ 46 | Send the current Crazyflie X, Y, Z position. This is going to be 47 | forwarded to the Crazyflie's position estimator. 48 | """ 49 | 50 | self._cf.loc.send_extpos([x, y, z]) 51 | 52 | def send_extpose(self, x, y, z, qx, qy, qz, qw): 53 | """ 54 | Send the current Crazyflie X, Y, Z position and attitude as a 55 | normalized quaternion. This is going to be forwarded to the 56 | Crazyflie's position estimator. 57 | """ 58 | self._cf.loc.send_extpose([x, y, z], [qx, qy, qz, qw]) 59 | -------------------------------------------------------------------------------- /cflib/crazyflie/mem/led_timings_driver_memory.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ,---------, ____ _ __ 4 | # | ,-^-, | / __ )(_) /_______________ _____ ___ 5 | # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 - 2020 Bitcraze AB 10 | # 11 | # This program is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, in version 3. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import logging 23 | 24 | from .memory_element import MemoryElement 25 | 26 | logger = logging.getLogger(__name__) 27 | 28 | 29 | class LEDTimingsDriverMemory(MemoryElement): 30 | """Memory interface for using the LED-ring mapped memory for setting RGB 31 | values over time. To upload and run a show sequence of 32 | the LEDs in the ring""" 33 | 34 | def __init__(self, id, type, size, mem_handler): 35 | super(LEDTimingsDriverMemory, self).__init__(id=id, 36 | type=type, 37 | size=size, 38 | mem_handler=mem_handler) 39 | self._update_finished_cb = None 40 | self._write_finished_cb = None 41 | 42 | self.timings = [] 43 | 44 | def add(self, time, rgb, leds=0, fade=False, rotate=0): 45 | self.timings.append({ 46 | 'time': time, 47 | 'rgb': rgb, 48 | 'leds': leds, 49 | 'fade': fade, 50 | 'rotate': rotate 51 | }) 52 | 53 | def write_data(self, write_finished_cb): 54 | if write_finished_cb is not None: 55 | self._write_finished_cb = write_finished_cb 56 | 57 | data = [] 58 | for timing in self.timings: 59 | # In order to fit all the LEDs in one radio packet RGB565 is used 60 | # to compress the colors. The calculations below converts 3 bytes 61 | # RGB into 2 bytes RGB565. Then shifts the value of each color to 62 | # LSB, applies the intensity and shifts them back for correct 63 | # alignment on 2 bytes. 64 | R5 = ((int)((((int(timing['rgb']['r']) & 0xFF) * 249 + 1014) >> 11) 65 | & 0x1F)) 66 | G6 = ((int)((((int(timing['rgb']['g']) & 0xFF) * 253 + 505) >> 10) 67 | & 0x3F)) 68 | B5 = ((int)((((int(timing['rgb']['b']) & 0xFF) * 249 + 1014) >> 11) 69 | & 0x1F)) 70 | led = (int(R5) << 11) | (int(G6) << 5) | (int(B5) << 0) 71 | extra = ((timing['leds']) & 0x0F) | ( 72 | (timing['fade'] << 4) & 0x10) | ( 73 | (timing['rotate'] << 5) & 0xE0) 74 | 75 | if (timing['time'] & 0xFF) != 0 or led != 0 or extra != 0: 76 | data += [timing['time'] & 0xFF, led >> 8, led & 0xFF, extra] 77 | 78 | data += [0, 0, 0, 0] 79 | self.mem_handler.write(self, 0x00, bytearray(data), flush_queue=True) 80 | 81 | def write_done(self, mem, addr): 82 | if mem.id == self.id and self._write_finished_cb: 83 | self._write_finished_cb(self, addr) 84 | self._write_finished_cb = None 85 | 86 | def disconnect(self): 87 | self._update_finished_cb = None 88 | self._write_finished_cb = None 89 | -------------------------------------------------------------------------------- /cflib/crazyflie/mem/memory_element.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ,---------, ____ _ __ 4 | # | ,-^-, | / __ )(_) /_______________ _____ ___ 5 | # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 - 2020 Bitcraze AB 10 | # 11 | # This program is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, in version 3. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import logging 23 | 24 | logger = logging.getLogger(__name__) 25 | 26 | 27 | class MemoryElement(object): 28 | """A memory """ 29 | 30 | TYPE_I2C = 0 31 | TYPE_1W = 1 32 | TYPE_DRIVER_LED = 0x10 33 | TYPE_LOCO = 0x11 34 | TYPE_TRAJ = 0x12 35 | TYPE_LOCO2 = 0x13 36 | TYPE_LH = 0x14 37 | TYPE_MEMORY_TESTER = 0x15 38 | TYPE_DRIVER_LEDTIMING = 0x17 39 | TYPE_APP = 0x18 40 | TYPE_DECK_MEMORY = 0x19 41 | TYPE_DECK_MULTIRANGER = 0x1A 42 | TYPE_DECK_PAA3905 = 0x1B 43 | 44 | def __init__(self, id, type, size, mem_handler): 45 | """Initialize the element with default values""" 46 | self.id = id 47 | self.type = type 48 | self.size = size 49 | self.mem_handler = mem_handler 50 | 51 | @staticmethod 52 | def type_to_string(t): 53 | """Get string representation of memory type""" 54 | if t == MemoryElement.TYPE_I2C: 55 | return 'I2C' 56 | if t == MemoryElement.TYPE_1W: 57 | return '1-wire' 58 | if t == MemoryElement.TYPE_DRIVER_LEDTIMING: 59 | return 'LED memory driver' 60 | if t == MemoryElement.TYPE_DRIVER_LED: 61 | return 'LED driver' 62 | if t == MemoryElement.TYPE_LOCO: 63 | return 'Loco Positioning' 64 | if t == MemoryElement.TYPE_TRAJ: 65 | return 'Trajectory' 66 | if t == MemoryElement.TYPE_LOCO2: 67 | return 'Loco Positioning 2' 68 | if t == MemoryElement.TYPE_LH: 69 | return 'Lighthouse positioning' 70 | if t == MemoryElement.TYPE_MEMORY_TESTER: 71 | return 'Memory tester' 72 | return 'Unknown' 73 | 74 | def new_data(self, mem, addr, data): 75 | logger.debug('New data, but not OW mem') 76 | 77 | def __str__(self): 78 | """Generate debug string for memory""" 79 | return ('Memory: id={}, type={}, size={}'.format( 80 | self.id, MemoryElement.type_to_string(self.type), self.size)) 81 | -------------------------------------------------------------------------------- /cflib/crazyflie/mem/memory_tester.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ,---------, ____ _ __ 4 | # | ,-^-, | / __ )(_) /_______________ _____ ___ 5 | # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 - 2020 Bitcraze AB 10 | # 11 | # This program is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, in version 3. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import logging 23 | import struct 24 | 25 | from .memory_element import MemoryElement 26 | 27 | logger = logging.getLogger(__name__) 28 | 29 | 30 | class MemoryTester(MemoryElement): 31 | """ 32 | Memory interface for testing the memory sub system, end to end. 33 | 34 | Usage 35 | 1. To verify reading: 36 | * Call read_data() 37 | * Wait for the callback to be called 38 | * Verify that readValidationSucess is True 39 | 40 | 2. To verify writing: 41 | * Set the parameter 'memTst.resetW' in the CF 42 | * call write_data() 43 | * Wait for the callback 44 | * Read the log var 'memTst.errCntW' from the CF and validate that it 45 | is 0 46 | """ 47 | 48 | def __init__(self, id, type, size, mem_handler): 49 | """Initialize Memory tester""" 50 | super(MemoryTester, self).__init__(id=id, type=type, size=size, 51 | mem_handler=mem_handler) 52 | 53 | self._update_finished_cb = None 54 | self._write_finished_cb = None 55 | 56 | self.readValidationSucess = True 57 | 58 | def new_data(self, mem, start_address, data): 59 | """Callback for when new memory data has been fetched""" 60 | if mem.id == self.id: 61 | for i in range(len(data)): 62 | actualValue = struct.unpack('. 22 | import logging 23 | import struct 24 | 25 | from .memory_element import MemoryElement 26 | from cflib.utils.callbacks import Syncer 27 | 28 | logger = logging.getLogger(__name__) 29 | 30 | 31 | class MultirangerMemory(MemoryElement): 32 | """Memory interface for reading the multiranger values""" 33 | 34 | def __init__(self, id, type, size, mem_handler): 35 | super(MultirangerMemory, self).__init__(id=id, type=type, size=size, 36 | mem_handler=mem_handler) 37 | self._read_finished_cb = None 38 | 39 | def new_data(self, mem, addr, data): 40 | """Callback for when new memory data has been fetched""" 41 | if mem.id == self.id and self._read_finished_cb: 42 | unpacked_data = struct.unpack('<'+'H'*int(len(data) / 2), data) 43 | zone_matrix = [] 44 | for i in range(8): 45 | zone_matrix.append(unpacked_data[i*8:i*8+8]) 46 | 47 | self._read_finished_cb(addr, zone_matrix) 48 | 49 | def read_data(self, read_finished_cb): 50 | """Write the saved LED-ring data to the Crazyflie""" 51 | self._read_finished_cb = read_finished_cb 52 | self.mem_handler.read(self, 0, 128) 53 | 54 | def read_data_sync(self): 55 | """Write the saved LED-ring data to the Crazyflie""" 56 | syncer = Syncer() 57 | self.read_data(syncer.success_cb) 58 | syncer.wait() 59 | if syncer.is_success: 60 | return syncer.success_args[1] 61 | else: 62 | return None 63 | 64 | def read_failed(self, mem, addr): 65 | if mem.id == self.id: 66 | logger.debug('Read failed') 67 | 68 | def disconnect(self): 69 | self._read_finished_cb = None 70 | -------------------------------------------------------------------------------- /cflib/crazyflie/mem/paa3905_memory.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ,---------, ____ _ __ 4 | # | ,-^-, | / __ )(_) /_______________ _____ ___ 5 | # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 - 2020 Bitcraze AB 10 | # 11 | # This program is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, in version 3. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import logging 23 | 24 | from .memory_element import MemoryElement 25 | from cflib.utils.callbacks import Syncer 26 | 27 | logger = logging.getLogger(__name__) 28 | 29 | 30 | class PAA3905Memory(MemoryElement): 31 | """Memory interface for reading the multiranger values""" 32 | 33 | def __init__(self, id, type, size, mem_handler): 34 | super(PAA3905Memory, self).__init__(id=id, type=type, size=size, 35 | mem_handler=mem_handler) 36 | self._read_finished_cb = None 37 | 38 | def new_data(self, mem, addr, data): 39 | """Callback for when new memory data has been fetched""" 40 | if mem.id == self.id and self._read_finished_cb: 41 | print(len(data)) 42 | image_matrix = [] 43 | for i in range(35): 44 | image_matrix.append(data[i*35:i*35+35]) 45 | 46 | self._read_finished_cb(addr, image_matrix) 47 | 48 | def read_data(self, read_finished_cb): 49 | """Read image data from PAA3905""" 50 | self._read_finished_cb = read_finished_cb 51 | self.mem_handler.read(self, 0, 1225) 52 | 53 | def read_data_sync(self): 54 | """Write the saved LED-ring data to the Crazyflie""" 55 | syncer = Syncer() 56 | self.read_data(syncer.success_cb) 57 | syncer.wait() 58 | if syncer.is_success: 59 | return syncer.success_args[1] 60 | else: 61 | return None 62 | 63 | def read_failed(self, mem, addr): 64 | if mem.id == self.id: 65 | logger.debug('Read failed') 66 | 67 | def disconnect(self): 68 | self._read_finished_cb = None 69 | -------------------------------------------------------------------------------- /cflib/crazyflie/syncLogger.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2016-2020 Bitcraze AB 10 | # 11 | # Crazyflie Nano Quadcopter Client 12 | # 13 | # This program is free software; you can redistribute it and/or 14 | # modify it under the terms of the GNU General Public License 15 | # as published by the Free Software Foundation; either version 2 16 | # of the License, or (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | """ 25 | This class provides synchronous access to log data from the Crazyflie. 26 | 27 | It acts as an iterator and returns the next value on each iteration. 28 | If no value is available it blocks until log data is received again. 29 | """ 30 | from queue import Queue 31 | 32 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 33 | 34 | 35 | class SyncLogger: 36 | DISCONNECT_EVENT = 'DISCONNECT_EVENT' 37 | 38 | def __init__(self, crazyflie, log_config): 39 | """ 40 | Construct an instance of a SyncLogger 41 | 42 | Takes an Crazyflie or SyncCrazyflie instance and one log configuration 43 | or an array of log configurations 44 | """ 45 | if isinstance(crazyflie, SyncCrazyflie): 46 | self._cf = crazyflie.cf 47 | else: 48 | self._cf = crazyflie 49 | 50 | if isinstance(log_config, list): 51 | self._log_config = log_config 52 | else: 53 | self._log_config = [log_config] 54 | 55 | self._queue = Queue() 56 | 57 | self._is_connected = False 58 | 59 | def connect(self): 60 | if self._is_connected: 61 | raise Exception('Already connected') 62 | 63 | self._cf.disconnected.add_callback(self._disconnected) 64 | for config in self._log_config: 65 | self._cf.log.add_config(config) 66 | config.data_received_cb.add_callback(self._log_callback) 67 | config.start() 68 | 69 | self._is_connected = True 70 | 71 | def disconnect(self): 72 | if self._is_connected: 73 | for config in self._log_config: 74 | config.stop() 75 | config.delete() 76 | 77 | config.data_received_cb.remove_callback( 78 | self._log_callback) 79 | 80 | self._cf.disconnected.remove_callback(self._disconnected) 81 | 82 | self._is_connected = False 83 | 84 | def is_connected(self): 85 | return self._is_connected 86 | 87 | def __iter__(self): 88 | return self 89 | 90 | def next(self): 91 | return self.__next__() 92 | 93 | def __next__(self): 94 | if not self._is_connected: 95 | raise StopIteration 96 | 97 | data = self._queue.get() 98 | 99 | if data == self.DISCONNECT_EVENT: 100 | self._queue.empty() 101 | raise StopIteration 102 | 103 | return data 104 | 105 | def __enter__(self): 106 | self.connect() 107 | return self 108 | 109 | def __exit__(self, exc_type, exc_val, exc_tb): 110 | self.disconnect() 111 | self._queue.empty() 112 | 113 | def _log_callback(self, ts, data, logblock): 114 | self._queue.put((ts, data, logblock)) 115 | 116 | def _disconnected(self, link_uri): 117 | self.disconnect() 118 | self._queue.put(self.DISCONNECT_EVENT) 119 | -------------------------------------------------------------------------------- /cflib/crtp/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # || ____ _ __ 5 | # +------+ / __ )(_) /_______________ _____ ___ 6 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 7 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 8 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 9 | # 10 | # Copyright (C) 2011-2013 Bitcraze AB 11 | # 12 | # Crazyflie Nano Quadcopter Client 13 | # 14 | # This program is free software; you can redistribute it and/or 15 | # modify it under the terms of the GNU General Public License 16 | # as published by the Free Software Foundation; either version 2 17 | # of the License, or (at your option) any later version. 18 | # 19 | # This program is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU General Public License for more details. 23 | # You should have received a copy of the GNU General Public License 24 | # along with this program. If not, see . 25 | """Scans and creates communication interfaces.""" 26 | import logging 27 | import os 28 | 29 | from .exceptions import WrongUriType 30 | from .prrtdriver import PrrtDriver 31 | from .radiodriver import RadioDriver 32 | from .serialdriver import SerialDriver 33 | from .tcpdriver import TcpDriver 34 | from .udpdriver import UdpDriver 35 | from .usbdriver import UsbDriver 36 | 37 | __author__ = 'Bitcraze AB' 38 | __all__ = [] 39 | 40 | logger = logging.getLogger(__name__) 41 | 42 | 43 | CLASSES = [] 44 | 45 | 46 | def init_drivers(enable_debug_driver=False, enable_serial_driver=False): 47 | """Initialize all the drivers.""" 48 | 49 | env = os.getenv('USE_CFLINK') 50 | if env is not None and env == 'cpp': 51 | from .cflinkcppdriver import CfLinkCppDriver 52 | CLASSES.append(CfLinkCppDriver) 53 | else: 54 | CLASSES.extend([RadioDriver, UsbDriver]) 55 | 56 | if enable_debug_driver: 57 | logger.warn('The debug driver is no longer supported!') 58 | 59 | if enable_serial_driver: 60 | CLASSES.append(SerialDriver) 61 | 62 | CLASSES.extend([UdpDriver, PrrtDriver, TcpDriver]) 63 | 64 | 65 | def scan_interfaces(address=None): 66 | """ Scan all the interfaces for available Crazyflies """ 67 | available = [] 68 | found = [] 69 | for driverClass in CLASSES: 70 | try: 71 | logger.debug('Scanning: %s', driverClass) 72 | instance = driverClass() 73 | found = instance.scan_interface(address) 74 | available += found 75 | except Exception: 76 | raise 77 | return available 78 | 79 | 80 | def get_interfaces_status(): 81 | """Get the status of all the interfaces""" 82 | status = {} 83 | for driverClass in CLASSES: 84 | try: 85 | instance = driverClass() 86 | status[instance.get_name()] = instance.get_status() 87 | except Exception: 88 | raise 89 | return status 90 | 91 | 92 | def get_link_driver(uri, radio_link_statistics_callback=None, link_error_callback=None): 93 | """Return the link driver for the given URI. Returns None if no driver 94 | was found for the URI or the URI was not well formatted for the matching 95 | driver.""" 96 | for driverClass in CLASSES: 97 | try: 98 | instance = driverClass() 99 | instance.connect(uri, radio_link_statistics_callback, link_error_callback) 100 | return instance 101 | except WrongUriType: 102 | continue 103 | 104 | return None 105 | -------------------------------------------------------------------------------- /cflib/crtp/crtpdriver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # || ____ _ __ 5 | # +------+ / __ )(_) /_______________ _____ ___ 6 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 7 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 8 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 9 | # 10 | # Copyright (C) 2011-2013 Bitcraze AB 11 | # 12 | # Crazyflie Nano Quadcopter Client 13 | # 14 | # This program is free software; you can redistribute it and/or 15 | # modify it under the terms of the GNU General Public License 16 | # as published by the Free Software Foundation; either version 2 17 | # of the License, or (at your option) any later version. 18 | # 19 | # This program is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU General Public License for more details. 23 | # You should have received a copy of the GNU General Public License 24 | # along with this program. If not, see . 25 | """ 26 | CRTP Driver main class. 27 | """ 28 | 29 | __author__ = 'Bitcraze AB' 30 | __all__ = ['CRTPDriver'] 31 | 32 | 33 | class CRTPDriver: 34 | """ CTRP Driver main class 35 | 36 | This class in inherited by all the CRTP link drivers. 37 | """ 38 | 39 | def __init__(self): 40 | """Driver constructor. Throw an exception if the driver is unable to 41 | open the URI 42 | """ 43 | self.needs_resending = True 44 | 45 | def connect(self, uri, radio_link_statistics_callback, link_error_callback): 46 | """Connect the driver to a specified URI 47 | 48 | @param uri Uri of the link to open 49 | @param radio_link_statistics_callback Callback to report radio link statistics 50 | @param link_error_callback Callback to report errors (will result in 51 | disconnection) 52 | """ 53 | 54 | def send_packet(self, pk): 55 | """Send a CRTP packet""" 56 | 57 | def receive_packet(self, wait=0): 58 | """Receive a CRTP packet. 59 | 60 | @param wait The time to wait for a packet in second. -1 means forever 61 | 62 | @return One CRTP packet or None if no packet has been received. 63 | """ 64 | 65 | def get_status(self): 66 | """ 67 | Return a status string from the interface. 68 | """ 69 | 70 | def get_name(self): 71 | """ 72 | Return a human readable name of the interface. 73 | """ 74 | 75 | def scan_interface(self, address=None): 76 | """ 77 | Scan interface for available Crazyflie quadcopters and return a list 78 | with them. 79 | """ 80 | 81 | def enum(self): 82 | """Enumerate, and return a list, of the available link URI on this 83 | system 84 | """ 85 | 86 | def get_help(self): 87 | """return the help message on how to form the URI for this driver 88 | None means no help 89 | """ 90 | 91 | def close(self): 92 | """Close the link""" 93 | -------------------------------------------------------------------------------- /cflib/crtp/exceptions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # || ____ _ __ 5 | # +------+ / __ )(_) /_______________ _____ ___ 6 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 7 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 8 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 9 | # 10 | # Copyright (C) 2011-2013 Bitcraze AB 11 | # 12 | # Crazyflie Nano Quadcopter Client 13 | # 14 | # This program is free software; you can redistribute it and/or 15 | # modify it under the terms of the GNU General Public License 16 | # as published by the Free Software Foundation; either version 2 17 | # of the License, or (at your option) any later version. 18 | # 19 | # This program is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU General Public License for more details. 23 | # You should have received a copy of the GNU General Public License 24 | # along with this program. If not, see . 25 | """ 26 | Exception used when the URI is not for the current driver 27 | (ie. radio:// for the serial driver ...) 28 | It basically means that an other driver could do the job 29 | It does NOT means that the URI is good or bad 30 | """ 31 | 32 | __author__ = 'Bitcraze AB' 33 | __all__ = ['WrongUriType', 'CommunicationException'] 34 | 35 | 36 | class WrongUriType(Exception): 37 | """ Wrong type of URI for this interface """ 38 | pass 39 | 40 | 41 | class CommunicationException(Exception): 42 | """ Communication problem when communicating with a Crazyflie """ 43 | pass 44 | -------------------------------------------------------------------------------- /cflib/crtp/prrtdriver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import datetime 4 | import logging 5 | import re 6 | 7 | from .crtpstack import CRTPPacket 8 | from .exceptions import WrongUriType 9 | from cflib.crtp.crtpdriver import CRTPDriver 10 | 11 | prrt_installed = True 12 | try: 13 | import prrt 14 | except ImportError: 15 | prrt_installed = False 16 | 17 | __author__ = 'Bitcraze AB' 18 | __all__ = ['PrrtDriver'] 19 | 20 | logger = logging.getLogger(__name__) 21 | 22 | MAX_PAYLOAD = 32 23 | DEFAULT_TARGET_DELAY = 0.05 # unit: s 24 | PRRT_LOCAL_PORT = 5000 25 | 26 | 27 | class PrrtDriver(CRTPDriver): 28 | 29 | def __init__(self): 30 | CRTPDriver.__init__(self) 31 | self.prrt_socket = None 32 | self.uri = '' 33 | self.link_error_callback = None 34 | self.packet_log = None 35 | self.log_index = 0 36 | logger.info('Initialized PRRT driver.') 37 | 38 | def connect(self, uri, linkQualityCallback, linkErrorCallback): 39 | # check if the URI is a PRRT URI 40 | if not re.search('^prrt://', uri): 41 | raise WrongUriType('Not a prrt URI') 42 | 43 | # Check if it is a valid PRRT URI 44 | uri_match = re.search(r'^prrt://((?:[\d]{1,3})\.(?:[\d]{1,3})\.' 45 | r'(?:[\d]{1,3})\.(?:[\d]{1,3})):([\d]{1,5})' 46 | r'(?:/([\d]{1,6}))?$', uri) 47 | if not uri_match: 48 | raise Exception('Invalid PRRT URI') 49 | 50 | if not prrt_installed: 51 | raise Exception('PRRT is missing') 52 | 53 | self.uri = uri 54 | 55 | self.link_error_callback = linkErrorCallback 56 | 57 | address = uri_match.group(1) 58 | port = int(uri_match.group(2)) 59 | target_delay_s = DEFAULT_TARGET_DELAY 60 | if uri_match.group(3): 61 | target_delay_s = int(uri_match.group(3)) / 1000 62 | 63 | self.prrt_socket = prrt.PrrtSocket(('0.0.0.0', PRRT_LOCAL_PORT), 64 | maximum_payload_size=MAX_PAYLOAD, 65 | target_delay=target_delay_s) 66 | self.prrt_socket.connect((address, port)) 67 | 68 | def send_packet(self, pk): 69 | pk_bytes = bytearray([pk.get_header()]) + pk.data 70 | self.prrt_socket.send(pk_bytes) 71 | 72 | def receive_packet(self, wait=0): 73 | try: 74 | if wait == 0: 75 | pk_bytes, _ = self.prrt_socket.receive_asap() 76 | elif wait < 0: 77 | pk_bytes, _ = self.prrt_socket.receive_asap_wait() 78 | else: 79 | deadline = datetime.datetime.utcnow() + datetime.timedelta( 80 | seconds=wait) 81 | pk_bytes, _ = self.prrt_socket.receive_asap_timedwait(deadline) 82 | except prrt.TimeoutException: 83 | return None 84 | 85 | if len(pk_bytes) <= 0: 86 | return None 87 | 88 | pk = CRTPPacket(pk_bytes[0], pk_bytes[1:]) 89 | return pk 90 | 91 | def get_status(self): 92 | return 'No information available' 93 | 94 | def get_name(self): 95 | return 'prrt' 96 | 97 | def scan_interface(self, address): 98 | default_uri = 'prrt://10.8.0.208:5000' 99 | if prrt_installed: 100 | return [[default_uri, ''], ] 101 | return [] 102 | 103 | def close(self): 104 | return 105 | -------------------------------------------------------------------------------- /cflib/crtp/udpdriver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # || ____ _ __ 5 | # +------+ / __ )(_) /_______________ _____ ___ 6 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 7 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 8 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 9 | # 10 | # Copyright (C) 2011-2013 Bitcraze AB 11 | # 12 | # Crazyflie Nano Quadcopter Client 13 | # 14 | # This program is free software; you can redistribute it and/or 15 | # modify it under the terms of the GNU General Public License 16 | # as published by the Free Software Foundation; either version 2 17 | # of the License, or (at your option) any later version. 18 | # 19 | # This program is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU General Public License for more details. 23 | # You should have received a copy of the GNU General Public License 24 | # along with this program. If not, see . 25 | """ CRTP UDP Driver. Work either with the UDP server or with an UDP device 26 | See udpserver.py for the protocol""" 27 | import queue 28 | import re 29 | import socket 30 | import struct 31 | from urllib.parse import urlparse 32 | 33 | from .crtpdriver import CRTPDriver 34 | from .crtpstack import CRTPPacket 35 | from .exceptions import WrongUriType 36 | 37 | __author__ = 'Bitcraze AB' 38 | __all__ = ['UdpDriver'] 39 | 40 | 41 | class UdpDriver(CRTPDriver): 42 | 43 | def __init__(self): 44 | None 45 | 46 | def connect(self, uri, linkQualityCallback, linkErrorCallback): 47 | if not re.search('^udp://', uri): 48 | raise WrongUriType('Not an UDP URI') 49 | 50 | parse = urlparse(uri) 51 | 52 | self.queue = queue.Queue() 53 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 54 | self.addr = (parse.hostname, parse.port) 55 | self.socket.connect(self.addr) 56 | 57 | self.socket.sendto('\xFF\x01\x01\x01'.encode(), self.addr) 58 | 59 | def receive_packet(self, time=0): 60 | data, addr = self.socket.recvfrom(1024) 61 | 62 | if data: 63 | data = struct.unpack('B' * (len(data) - 1), data[0:len(data) - 1]) 64 | pk = CRTPPacket() 65 | pk.port = data[0] 66 | pk.data = data[1:] 67 | return pk 68 | 69 | try: 70 | if time == 0: 71 | return self.rxqueue.get(False) 72 | elif time < 0: 73 | while True: 74 | return self.rxqueue.get(True, 10) 75 | else: 76 | return self.rxqueue.get(True, time) 77 | except queue.Empty: 78 | return None 79 | 80 | def send_packet(self, pk): 81 | raw = (pk.port,) + struct.unpack('B' * len(pk.data), pk.data) 82 | 83 | cksum = 0 84 | for i in raw: 85 | cksum += i 86 | 87 | cksum %= 256 88 | 89 | data = ''.join(chr(v) for v in (raw + (cksum,))) 90 | 91 | # print tuple(data) 92 | self.socket.sendto(data.encode(), self.addr) 93 | 94 | def close(self): 95 | # Remove this from the server clients list 96 | self.socket.sendto('\xFF\x01\x02\x02'.encode(), self.addr) 97 | 98 | def get_name(self): 99 | return 'udp' 100 | 101 | def scan_interface(self, address): 102 | return [] 103 | -------------------------------------------------------------------------------- /cflib/drivers/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # || ____ _ __ 5 | # +------+ / __ )(_) /_______________ _____ ___ 6 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 7 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 8 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 9 | # 10 | # Copyright (C) 2011-2013 Bitcraze AB 11 | # 12 | # Crazyflie Nano Quadcopter Client 13 | # 14 | # This program is free software; you can redistribute it and/or 15 | # modify it under the terms of the GNU General Public License 16 | # as published by the Free Software Foundation; either version 2 17 | # of the License, or (at your option) any later version. 18 | # 19 | # This program is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU General Public License for more details. 23 | # You should have received a copy of the GNU General Public License 24 | # along with this program. If not, see . 25 | """ 26 | Drivers for the link interfaces that can be used by CRTP. 27 | """ 28 | -------------------------------------------------------------------------------- /cflib/localization/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2017 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | from .lighthouse_bs_geo import LighthouseBsGeoEstimator 23 | from .lighthouse_bs_vector import LighthouseBsVector 24 | from .lighthouse_config_manager import LighthouseConfigFileManager 25 | from .lighthouse_config_manager import LighthouseConfigWriter 26 | from .lighthouse_sweep_angle_reader import LighthouseSweepAngleAverageReader 27 | from .lighthouse_sweep_angle_reader import LighthouseSweepAngleReader 28 | from .param_io import ParamFileManager 29 | 30 | __all__ = [ 31 | 'LighthouseBsGeoEstimator', 32 | 'LighthouseBsVector', 33 | 'LighthouseSweepAngleAverageReader', 34 | 'LighthouseSweepAngleReader', 35 | 'LighthouseConfigFileManager', 36 | 'LighthouseConfigWriter', 37 | 'ParamFileManager'] 38 | -------------------------------------------------------------------------------- /cflib/localization/lighthouse_sample_matcher.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ,---------, ____ _ __ 4 | # | ,-^-, | / __ )(_) /_______________ _____ ___ 5 | # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2022 Bitcraze AB 10 | # 11 | # This program is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, in version 3. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | from __future__ import annotations 23 | 24 | from cflib.localization.lighthouse_types import LhCfPoseSample 25 | from cflib.localization.lighthouse_types import LhMeasurement 26 | 27 | 28 | class LighthouseSampleMatcher: 29 | """Utility class to match samples of measurements from multiple lighthouse base stations. 30 | 31 | Assuming that the Crazyflie was moving when the measurements were recorded, 32 | samples that were measured approximately at the same position are aggregated into 33 | a list of LhCfPoseSample. Matching is done using the timestamp and a maximum time span. 34 | """ 35 | 36 | @classmethod 37 | def match(cls, samples: list[LhMeasurement], max_time_diff: float = 0.020, 38 | min_nr_of_bs_in_match: int = 0) -> list[LhCfPoseSample]: 39 | """ 40 | Aggregate samples close in time into lists 41 | """ 42 | 43 | result = [] 44 | current: LhCfPoseSample = None 45 | 46 | for sample in samples: 47 | ts = sample.timestamp 48 | 49 | if current is None: 50 | current = LhCfPoseSample(timestamp=ts) 51 | 52 | if ts > (current.timestamp + max_time_diff): 53 | cls._append_result(current, result, min_nr_of_bs_in_match) 54 | current = LhCfPoseSample(timestamp=ts) 55 | 56 | current.angles_calibrated[sample.base_station_id] = sample.angles 57 | 58 | cls._append_result(current, result, min_nr_of_bs_in_match) 59 | return result 60 | 61 | @classmethod 62 | def _append_result(cls, current: LhCfPoseSample, result: list[LhCfPoseSample], min_nr_of_bs_in_match: int): 63 | if current is not None and len(current.angles_calibrated) >= min_nr_of_bs_in_match: 64 | result.append(current) 65 | -------------------------------------------------------------------------------- /cflib/localization/param_io.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ,---------, ____ _ __ 4 | # | ,-^-, | / __ )(_) /_______________ _____ ___ 5 | # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2024 Bitcraze AB 10 | # 11 | # This program is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, in version 3. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import yaml 23 | 24 | from cflib.crazyflie.param import PersistentParamState 25 | 26 | 27 | class ParamFileManager(): 28 | """Reads and writes parameter configurations from file""" 29 | TYPE_ID = 'type' 30 | TYPE = 'persistent_param_state' 31 | VERSION_ID = 'version' 32 | VERSION = '1' 33 | PARAMS_ID = 'params' 34 | 35 | @staticmethod 36 | def write(file_name, params={}): 37 | file = open(file_name, 'w') 38 | with file: 39 | file_params = {} 40 | for id, param in params.items(): 41 | assert isinstance(param, PersistentParamState) 42 | if isinstance(param, PersistentParamState): 43 | file_params[id] = {'is_stored': param.is_stored, 44 | 'default_value': param.default_value, 'stored_value': param.stored_value} 45 | 46 | data = { 47 | ParamFileManager.TYPE_ID: ParamFileManager.TYPE, 48 | ParamFileManager.VERSION_ID: ParamFileManager.VERSION, 49 | ParamFileManager.PARAMS_ID: file_params 50 | } 51 | 52 | yaml.dump(data, file) 53 | 54 | @staticmethod 55 | def read(file_name): 56 | file = open(file_name, 'r') 57 | with file: 58 | data = None 59 | try: 60 | data = yaml.safe_load(file) 61 | except yaml.YAMLError as exc: 62 | print(exc) 63 | 64 | if ParamFileManager.TYPE_ID not in data: 65 | raise Exception('Type field missing') 66 | 67 | if data[ParamFileManager.TYPE_ID] != ParamFileManager.TYPE: 68 | raise Exception('Unsupported file type') 69 | 70 | if ParamFileManager.VERSION_ID not in data: 71 | raise Exception('Version field missing') 72 | 73 | if data[ParamFileManager.VERSION_ID] != ParamFileManager.VERSION: 74 | raise Exception('Unsupported file version') 75 | 76 | def get_data(input_data): 77 | persistent_params = {} 78 | for id, param in input_data.items(): 79 | persistent_params[id] = PersistentParamState( 80 | param['is_stored'], param['default_value'], param['stored_value']) 81 | return persistent_params 82 | 83 | if ParamFileManager.PARAMS_ID in data: 84 | return get_data(data[ParamFileManager.PARAMS_ID]) 85 | else: 86 | return {} 87 | -------------------------------------------------------------------------------- /cflib/positioning/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2017 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | -------------------------------------------------------------------------------- /cflib/resources/binaries/nrf51-s110-and-bl.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcraze/crazyflie-lib-python/6a427318a0a2a2c3f231861a087d49d04db211b2/cflib/resources/binaries/nrf51-s110-and-bl.bin -------------------------------------------------------------------------------- /cflib/utils/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # || ____ _ __ 5 | # +------+ / __ )(_) /_______________ _____ ___ 6 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 7 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 8 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 9 | # 10 | # Copyright (C) 2011-2013 Bitcraze AB 11 | # 12 | # Crazyflie Nano Quadcopter Client 13 | # 14 | # This program is free software; you can redistribute it and/or 15 | # modify it under the terms of the GNU General Public License 16 | # as published by the Free Software Foundation; either version 2 17 | # of the License, or (at your option) any later version. 18 | # 19 | # This program is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU General Public License for more details. 23 | # You should have received a copy of the GNU General Public License 24 | # along with this program. If not, see . 25 | """ 26 | Various utilities that is needed by the cflib. 27 | """ 28 | -------------------------------------------------------------------------------- /cflib/utils/callbacks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # || ____ _ __ 5 | # +------+ / __ )(_) /_______________ _____ ___ 6 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 7 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 8 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 9 | # 10 | # Copyright (C) 2011-2013 Bitcraze AB 11 | # 12 | # Crazyflie Nano Quadcopter Client 13 | # 14 | # This program is free software; you can redistribute it and/or 15 | # modify it under the terms of the GNU General Public License 16 | # as published by the Free Software Foundation; either version 2 17 | # of the License, or (at your option) any later version. 18 | # 19 | # This program is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU General Public License for more details. 23 | # You should have received a copy of the GNU General Public License 24 | # along with this program. If not, see . 25 | """ 26 | Callback objects used in the Crazyflie library 27 | """ 28 | from threading import Event 29 | 30 | __author__ = 'Bitcraze AB' 31 | __all__ = ['Caller'] 32 | 33 | 34 | class Caller(): 35 | """ An object were callbacks can be registered and called """ 36 | 37 | def __init__(self): 38 | """ Create the object """ 39 | self.callbacks = [] 40 | 41 | def add_callback(self, cb): 42 | """ Register cb as a new callback. Will not register duplicates. """ 43 | if ((cb in self.callbacks) is False): 44 | self.callbacks.append(cb) 45 | 46 | def remove_callback(self, cb): 47 | """ Un-register cb from the callbacks """ 48 | self.callbacks.remove(cb) 49 | 50 | def call(self, *args): 51 | """ Call the callbacks registered with the arguments args """ 52 | copy_of_callbacks = list(self.callbacks) 53 | for cb in copy_of_callbacks: 54 | cb(*args) 55 | 56 | 57 | class Syncer: 58 | """A class to create synchronous behavior for methods using callbacks""" 59 | 60 | def __init__(self): 61 | self._event = Event() 62 | self.success_args = None 63 | self.failure_args = None 64 | self.is_success = False 65 | 66 | def success_cb(self, *args): 67 | self.success_args = args 68 | self.is_success = True 69 | self._event.set() 70 | 71 | def failure_cb(self, *args): 72 | self.failure_args = args 73 | self.is_success = False 74 | self._event.set() 75 | 76 | def wait(self): 77 | self._event.wait() 78 | 79 | def clear(self): 80 | self._event.clear() 81 | -------------------------------------------------------------------------------- /cflib/utils/encoding.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # || ____ _ __ 5 | # +------+ / __ )(_) /_______________ _____ ___ 6 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 7 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 8 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 9 | # 10 | # Copyright (C) 2023 Bitcraze AB 11 | # 12 | # This program is free software; you can redistribute it and/or 13 | # modify it under the terms of the GNU General Public License 14 | # as published by the Free Software Foundation; either version 2 15 | # of the License, or (at your option) any later version. 16 | # 17 | # This program is distributed in the hope that it will be useful, 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | # GNU General Public License for more details. 21 | # You should have received a copy of the GNU General Public License 22 | # along with this program. If not, see . 23 | import struct 24 | 25 | import numpy as np 26 | 27 | 28 | # Code from davidejones at https://gamedev.stackexchange.com/a/28756 29 | def fp16_to_float(float16): 30 | s = int((float16 >> 15) & 0x00000001) # sign 31 | e = int((float16 >> 10) & 0x0000001f) # exponent 32 | f = int(float16 & 0x000003ff) # fraction 33 | 34 | if e == 0: 35 | if f == 0: 36 | return int(s << 31) 37 | else: 38 | while not (f & 0x00000400): 39 | f <<= 1 40 | e -= 1 41 | e += 1 42 | f &= ~0x00000400 43 | # print(s,e,f) 44 | elif e == 31: 45 | if f == 0: 46 | return int((s << 31) | 0x7f800000) 47 | else: 48 | return int((s << 31) | 0x7f800000 | (f << 13)) 49 | 50 | e += 127 - 15 51 | f <<= 13 52 | result = int((s << 31) | (e << 23) | f) 53 | return struct.unpack('f', struct.pack('I', result))[0] 54 | 55 | 56 | def decompress_quaternion(comp): 57 | """Decompress a quaternion 58 | 59 | see quatcompress.h in the firmware for definitions 60 | 61 | Args: 62 | comp int: A 32-bit number 63 | 64 | Returns: 65 | np array: q = [x, y, z, w] 66 | """ 67 | q = np.zeros(4) 68 | mask = (1 << 9) - 1 69 | i_largest = comp >> 30 70 | sum_squares = 0 71 | for i in range(3, -1, -1): 72 | if i != i_largest: 73 | mag = comp & mask 74 | negbit = (comp >> 9) & 0x1 75 | comp = comp >> 10 76 | q[i] = mag / mask / np.sqrt(2) 77 | if negbit == 1: 78 | q[i] = -q[i] 79 | sum_squares += q[i] * q[i] 80 | q[i_largest] = np.sqrt(1.0 - sum_squares) 81 | return q 82 | 83 | 84 | def compress_quaternion(quat): 85 | """Compress a quaternion. 86 | assumes input quaternion is normalized. will fail if not. 87 | 88 | see quatcompress.h in firmware the for definitions 89 | 90 | Args: 91 | quat : An array of floats representing a quaternion [x, y, z, w] 92 | 93 | Returns: 32-bit integer 94 | """ 95 | # Normalize the quaternion 96 | quat_n = np.array(quat) / np.linalg.norm(quat) 97 | 98 | i_largest = 0 99 | for i in range(1, 4): 100 | if abs(quat_n[i]) > abs(quat_n[i_largest]): 101 | i_largest = i 102 | negate = quat_n[i_largest] < 0 103 | 104 | M_SQRT1_2 = 1.0 / np.sqrt(2) 105 | 106 | comp = i_largest 107 | for i in range(4): 108 | if i != i_largest: 109 | negbit = int((quat_n[i] < 0) ^ negate) 110 | mag = int(((1 << 9) - 1) * (abs(quat_n[i]) / M_SQRT1_2) + 0.5) 111 | comp = (comp << 10) | (negbit << 9) | mag 112 | 113 | return comp 114 | -------------------------------------------------------------------------------- /cflib/utils/multiranger.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2018 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | from cflib.crazyflie.log import LogConfig 23 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 24 | 25 | 26 | class Multiranger: 27 | FRONT = 'range.front' 28 | BACK = 'range.back' 29 | LEFT = 'range.left' 30 | RIGHT = 'range.right' 31 | UP = 'range.up' 32 | DOWN = 'range.zrange' 33 | 34 | def __init__(self, crazyflie, rate_ms=100, zranger=False): 35 | if isinstance(crazyflie, SyncCrazyflie): 36 | self._cf = crazyflie.cf 37 | else: 38 | self._cf = crazyflie 39 | self._log_config = self._create_log_config(rate_ms) 40 | 41 | self._up_distance = None 42 | self._front_distance = None 43 | self._back_distance = None 44 | self._left_distance = None 45 | self._right_distance = None 46 | self._down_distance = None 47 | 48 | def _create_log_config(self, rate_ms): 49 | log_config = LogConfig('multiranger', rate_ms) 50 | log_config.add_variable(self.FRONT) 51 | log_config.add_variable(self.BACK) 52 | log_config.add_variable(self.LEFT) 53 | log_config.add_variable(self.RIGHT) 54 | log_config.add_variable(self.UP) 55 | log_config.add_variable(self.DOWN) 56 | 57 | log_config.data_received_cb.add_callback(self._data_received) 58 | 59 | return log_config 60 | 61 | def start(self): 62 | self._cf.log.add_config(self._log_config) 63 | self._log_config.start() 64 | 65 | def _convert_log_to_distance(self, data): 66 | if data >= 8000: 67 | return None 68 | else: 69 | return data / 1000.0 70 | 71 | def _data_received(self, timestamp, data, logconf): 72 | self._up_distance = self._convert_log_to_distance(data[self.UP]) 73 | self._front_distance = self._convert_log_to_distance(data[self.FRONT]) 74 | self._back_distance = self._convert_log_to_distance(data[self.BACK]) 75 | self._left_distance = self._convert_log_to_distance(data[self.LEFT]) 76 | self._right_distance = self._convert_log_to_distance(data[self.RIGHT]) 77 | if self.DOWN in data: 78 | self._down_distance = self._convert_log_to_distance(data[self.DOWN] 79 | ) 80 | 81 | def stop(self): 82 | self._log_config.delete() 83 | 84 | @property 85 | def up(self): 86 | return self._up_distance 87 | 88 | @property 89 | def left(self): 90 | return self._left_distance 91 | 92 | @property 93 | def right(self): 94 | return self._right_distance 95 | 96 | @property 97 | def front(self): 98 | return self._front_distance 99 | 100 | @property 101 | def back(self): 102 | return self._back_distance 103 | 104 | @property 105 | def down(self): 106 | return self._down_distance 107 | 108 | def __enter__(self): 109 | self.start() 110 | return self 111 | 112 | def __exit__(self, exc_type, exc_val, exc_tb): 113 | self.stop() 114 | -------------------------------------------------------------------------------- /cflib/utils/param_file_helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2024 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | from threading import Event 23 | 24 | from cflib.crazyflie import Crazyflie 25 | from cflib.localization.param_io import ParamFileManager 26 | 27 | 28 | class ParamFileHelper: 29 | '''ParamFileHelper is a helper to synchonously write multiple paramteters 30 | from a file and store them in persistent memory''' 31 | 32 | def __init__(self, crazyflie): 33 | if isinstance(crazyflie, Crazyflie): 34 | self._cf = crazyflie 35 | self.persistent_sema = None 36 | self.success = False 37 | else: 38 | raise TypeError('ParamFileHelper only takes a Crazyflie Object') 39 | 40 | def _persistent_stored_callback(self, complete_name, success): 41 | self.success = success 42 | if not success: 43 | print(f'Persistent params: failed to store {complete_name}!') 44 | else: 45 | print(f'Persistent params: stored {complete_name}!') 46 | self.persistent_sema.set() 47 | 48 | def store_params_from_file(self, filename): 49 | params = ParamFileManager().read(filename) 50 | for param, state in params.items(): 51 | self.persistent_sema = Event() 52 | self._cf.param.set_value(param, state.stored_value) 53 | self._cf.param.persistent_store(param, self._persistent_stored_callback) 54 | self.persistent_sema.wait() 55 | if not self.success: 56 | break 57 | return self.success 58 | -------------------------------------------------------------------------------- /cflib/utils/reset_estimator.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from cflib.crazyflie.log import LogConfig 4 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 5 | from cflib.crazyflie.syncLogger import SyncLogger 6 | 7 | 8 | def reset_estimator(crazyflie): 9 | if isinstance(crazyflie, SyncCrazyflie): 10 | cf = crazyflie.cf 11 | else: 12 | cf = crazyflie 13 | cf.param.set_value('kalman.resetEstimation', '1') 14 | time.sleep(0.1) 15 | cf.param.set_value('kalman.resetEstimation', '0') 16 | 17 | _wait_for_position_estimator(cf) 18 | 19 | 20 | def _wait_for_position_estimator(cf): 21 | print('Waiting for estimator to find position...', end='\r') 22 | 23 | log_config = LogConfig(name='Kalman Variance', period_in_ms=500) 24 | log_config.add_variable('kalman.varPX', 'float') 25 | log_config.add_variable('kalman.varPY', 'float') 26 | log_config.add_variable('kalman.varPZ', 'float') 27 | 28 | var_y_history = [1000] * 10 29 | var_x_history = [1000] * 10 30 | var_z_history = [1000] * 10 31 | 32 | threshold = 0.001 33 | 34 | with SyncLogger(cf, log_config) as logger: 35 | for log_entry in logger: 36 | data = log_entry[1] 37 | 38 | var_x_history.append(data['kalman.varPX']) 39 | var_x_history.pop(0) 40 | var_y_history.append(data['kalman.varPY']) 41 | var_y_history.pop(0) 42 | var_z_history.append(data['kalman.varPZ']) 43 | var_z_history.pop(0) 44 | 45 | min_x = min(var_x_history) 46 | max_x = max(var_x_history) 47 | min_y = min(var_y_history) 48 | max_y = max(var_y_history) 49 | min_z = min(var_z_history) 50 | max_z = max(var_z_history) 51 | 52 | if (max_x - min_x) < threshold and ( 53 | max_y - min_y) < threshold and ( 54 | max_z - min_z) < threshold: 55 | print('Waiting for estimator to find position...success!') 56 | break 57 | -------------------------------------------------------------------------------- /cflib/utils/uri_helper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # || ____ _ __ 5 | # +------+ / __ )(_) /_______________ _____ ___ 6 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 7 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 8 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 9 | # 10 | # Copyright (C) 2021 Bitcraze AB 11 | # 12 | # Crazyflie Nano Quadcopter Client 13 | # 14 | # This program is free software; you can redistribute it and/or 15 | # modify it under the terms of the GNU General Public License 16 | # as published by the Free Software Foundation; either version 2 17 | # of the License, or (at your option) any later version. 18 | # 19 | # This program is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU General Public License for more details. 23 | # 24 | # You should have received a copy of the GNU General Public License 25 | # along with this program. If not, see 26 | import os 27 | import sys 28 | 29 | 30 | def uri_from_env(env='CFLIB_URI', default='radio://0/80/2M/E7E7E7E7E7') -> str: 31 | try: 32 | return os.environ[env] 33 | except KeyError: 34 | return default 35 | 36 | 37 | def address_from_env(env='CFLIB_URI', default=0xE7E7E7E7E7) -> int: 38 | try: 39 | uri = os.environ[env] 40 | except KeyError: 41 | return default 42 | 43 | # Get the address part of the uri 44 | address = uri.rsplit('/', 1)[-1] 45 | try: 46 | return int(address, 16) 47 | except ValueError: 48 | print('address is not hexadecimal! (%s)' % address, file=sys.stderr) 49 | return None 50 | -------------------------------------------------------------------------------- /docs/api/cflib/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: API reference for CFLib 3 | page_id: api_reference 4 | --- 5 | 6 | {% include_relative_generated index.md_raw info="Placeholder for generated file. Run `tb build-docs` to generate content." %} 7 | -------------------------------------------------------------------------------- /docs/api/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Auto-generated API Documentation 3 | page_id: api_index 4 | sort_order: 5 5 | --- 6 | 7 | This section contains auto-generated documentation of the classes and functions of the Crazyflie python library. 8 | 9 | {% sub_page_menu %} 10 | -------------------------------------------------------------------------------- /docs/development/eeprom.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Reset EEPROM 3 | page_id: eeprom 4 | --- 5 | 6 | 7 | 8 | The EEPROM stores configuration data, which persists even after a 9 | firmware update. You might want to reset this information. For example, 10 | if you forget the address of your Crazyflie, you won\'t be able to 11 | connect wirelessly anymore. In order to reset the EEPROM, follow the 12 | following steps: 13 | 14 | 1. Unplug your Crazyradio 15 | 2. Connect the Crazyflie to the PC using a USB-cable 16 | 3. Execute the following from the examples 17 | 18 | 19 | 20 | python3 write-eeprom.py 21 | 22 | This will find your first Crazyflie (which is the one you connected over 23 | USB) and write the default values to the EEPROM. 24 | -------------------------------------------------------------------------------- /docs/development/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Development 3 | page_id: development_index 4 | sort_order: 4 5 | --- 6 | 7 | In this section you will find information about development of the Crazyflie python library 8 | 9 | {% sub_page_menu %} 10 | -------------------------------------------------------------------------------- /docs/development/wireshark.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Debugging CRTP using Wireshark 3 | page_id: wireshark_debugging 4 | redirect: /wireshark/wireshark/ 5 | --- 6 | 7 | Wireshark is a free and open-source packet analyzer. It is used for network troubleshooting, analysis, software and communications protocol development, and education. It makes analyzing what is going on with packet based protocols easier. 8 | 9 | We have written a plugin to Wireshark that can display [CRTP](https://www.bitcraze.io/documentation/repository/crazyflie-firmware/master/functional-areas/crtp/) packets (what this library uses to communicate with Crazyflies). 10 | 11 | ![Image of CRTP dissector in Wireshark](../images/wireshark-dissector.png) 12 | 13 | In order for Wireshark (and our dissector plugin) to be able to decode CRTP you need to do *two* things: 14 | 15 | ## 1. Install the plugin crtp-dissector.lua 16 | 17 | Wireshark looks for plugins in both a personal plugin folder and a global plugin folder. Lua plugins are stored in the plugin folders; compiled plugins are stored in subfolders of the plugin folders, with the subfolder name being the Wireshark minor version number (X.Y). There is another hierarchical level for each Wireshark plugin type (libwireshark, libwiretap and codecs). So for example the location for a libwireshark plugin foo.so (foo.dll on Windows) would be PLUGINDIR/X.Y/epan (libwireshark used to be called libepan; the other folder names are codecs and wiretap). 18 | 19 | On Windows: 20 | 21 | - The personal plugin folder is %APPDATA%\Wireshark\plugins. 22 | - The global plugin folder is WIRESHARK\plugins. 23 | 24 | On Unix-like systems: 25 | 26 | - The personal plugin folder is ~/.local/lib/wireshark/plugins. 27 | 28 | Copy the `tools/crtp-dissector.lua` file into the personal plugin folder and restart Wireshark or hit `CTRL+SHIFT+L` 29 | 30 | ## 2. Generate a [PCAP](https://en.wikipedia.org/wiki/Pcap) file 31 | 32 | The de facto standard network packet capture format is libpcap (pcap), which is used in packet analyzers such as tcpdump/WinDump and Wireshark. The pcap file format is a binary format, with support for nanosecond-precision timestamps. 33 | 34 | To tell the CFLIB to generate a PCAP file of what it thinks the CRTP traffic looks like you can go: 35 | 36 | ```bash 37 | $ CRTP_PCAP_LOG=filename.pcap python3 examples/swarm/hl-commander-swarm.py 38 | $ wireshark filename.pcap 39 | ``` 40 | and on Windows based computers in a shell window: 41 | 42 | ```bash 43 | > set CRTP_PCAP_LOG=filename.pcap 44 | > python3 examples/swarm/hl-commander-swarm.py 45 | > wireshark filename.pcap 46 | ``` 47 | To generate a PCAP file and open it with Wireshark. You can also use the text based `tshark` tool, and you can add a filter, for instance, only shoow CRTP port 8 (Highlevel setpoint): 48 | 49 | ```bash 50 | $ tshark -r test.pcap "crtp.port == 8" 51 | 6 0.003333 00:DE:AD:BE:EF (10) → Radio #0 CRTP 13 Setpoint Highlevel 52 | 13158 5.176752 Radio #0 → 00:DE:AD:BE:EF (10) CRTP 24 Setpoint Highlevel 53 | 13163 5.178626 00:DE:AD:BE:EF (10) → Radio #0 CRTP 13 Setpoint Highlevel 54 | 17768 8.179085 Radio #0 → 00:DE:AD:BE:EF (10) CRTP 32 Setpoint Highlevel 55 | 17773 8.181294 00:DE:AD:BE:EF (10) → Radio #0 CRTP 13 Setpoint Highlevel 56 | 21223 10.181543 Radio #0 → 00:DE:AD:BE:EF (10) CRTP 32 Setpoint Highlevel 57 | 21228 10.183293 00:DE:AD:BE:EF (10) → Radio #0 CRTP 13 Setpoint Highlevel 58 | 24030 12.182588 Radio #0 → 00:DE:AD:BE:EF (10) CRTP 32 Setpoint Highlevel 59 | 24035 12.185723 00:DE:AD:BE:EF (10) → Radio #0 CRTP 13 Setpoint Highlevel 60 | 26449 14.184183 Radio #0 → 00:DE:AD:BE:EF (10) CRTP 32 Setpoint Highlevel 61 | 26454 14.185862 00:DE:AD:BE:EF (10) → Radio #0 CRTP 13 Setpoint Highlevel 62 | 28388 16.187258 Radio #0 → 00:DE:AD:BE:EF (10) CRTP 24 Setpoint Highlevel 63 | 28393 16.188963 00:DE:AD:BE:EF (10) → Radio #0 CRTP 13 Setpoint Highlevel 64 | 30533 18.190823 Radio #0 → 00:DE:AD:BE:EF (10) CRTP 11 Setpoint Highlevel 65 | ``` 66 | 67 | Happy hacking! 68 | -------------------------------------------------------------------------------- /docs/functional-areas/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Functional Areas 3 | page_id: functional_areas_index 4 | sort_order: 3 5 | --- 6 | 7 | In this section you will find information about functional areas 8 | 9 | {% sub_page_menu %} 10 | -------------------------------------------------------------------------------- /docs/images/wireshark-dissector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcraze/crazyflie-lib-python/6a427318a0a2a2c3f231861a087d49d04db211b2/docs/images/wireshark-dissector.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Home 3 | page_id: home 4 | --- 5 | 6 | cflib is an API written in Python that is used to communicate with the Crazyflie 7 | and Crazyflie 2.x quadcopters. It is intended to be used by client software to 8 | communicate with and control a Crazyflie quadcopter. For instance the [Crazyflie PC client](https://github.com/bitcraze/crazyflie-clients-python) uses the cflib. 9 | 10 | ## Contribute 11 | 12 | Everyone is encouraged to contribute to the CrazyFlie library by forking the Github repository and making a pull request or opening an issue. 13 | -------------------------------------------------------------------------------- /docs/installation/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: installation 3 | page_id: installation_index 4 | sort_order: 1 5 | --- 6 | 7 | In this section you will find installation instructions 8 | 9 | {% sub_page_menu %} 10 | -------------------------------------------------------------------------------- /docs/installation/install.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | page_id: install 4 | --- 5 | 6 | ## Requirements 7 | 8 | This project requires Python 3.10+. 9 | 10 | To install on Python 3.13, build tools and Python development headers are required. 11 | 12 | ***NOTE: Running with python 3.13 on macOS is not officially supported*** 13 | 14 | See below sections for more platform-specific requirements. 15 | ## Install from Source 16 | ### Clone the repository 17 | ``` 18 | git clone https://github.com/bitcraze/crazyflie-lib-python.git 19 | ``` 20 | ### Install cflib from source 21 | ``` 22 | cd crazyflie-lib-python 23 | pip install -e . 24 | ``` 25 | 26 | ### Uninstall cflib 27 | 28 | ``` 29 | pip uninstall cflib 30 | ``` 31 | 32 | Note: If you are developing for the cflib you must use python3. On Ubuntu (20.04+) use `pip3` instead of `pip`. 33 | 34 | ### Linux, macOS, Windows 35 | 36 | The following should be executed in the root of the crazyflie-lib-python file tree. 37 | 38 | #### Virtualenv 39 | This section contains a very short description of how to use [virtualenv (local python environment)](https://virtualenv.pypa.io/en/latest/) 40 | with package dependencies. If you don't want to use virualenv and don't mind installing cflib dependencies system-wide 41 | you can skip this section. 42 | 43 | * Install virtualenv: `pip install virtualenv` 44 | * create an environment: `virtualenv venv` 45 | * Activate the environment: `source venv/bin/activate` 46 | 47 | 48 | * To deactivate the virtualenv when you are done using it `deactivate` 49 | 50 | ### Pre-commit hooks (Ubuntu) 51 | If you want some extra help with keeping to the mandated Python coding style you can install hooks that verify your style at commit time. This is done by running: 52 | ``` 53 | $ pip3 install pre-commit 54 | ``` 55 | go to crazyflie-lib-python root folder and run 56 | ``` 57 | $ pre-commit install 58 | $ pre-commit run --all-files 59 | ``` 60 | This will run the lint checkers defined in `.pre-commit-config-yaml` on your proposed changes and alert you if you need to change anything. 61 | 62 | ## Testing 63 | ### With docker and the toolbelt 64 | 65 | For information and installation of the 66 | [toolbelt.](https://github.com/bitcraze/toolbelt) 67 | 68 | * Check to see if you pass tests: `tb test` 69 | * Check to see if you pass style guidelines: `tb verify` 70 | 71 | Note: Docker and the toolbelt is an optional way of running tests and reduces the 72 | work needed to maintain your python environment. 73 | 74 | ## Platform notes 75 | 76 | ### Linux 77 | 78 | With linux, the crazyradio is easily recognized, but you have to setup UDEVpermissions. Look at the [usb permission instructions](/docs/installation/usb_permissions.md) to setup udev on linux. 79 | 80 | ### Windows 81 | 82 | Look at the [Zadig crazyradio instructions](https://www.bitcraze.io/documentation/repository/crazyradio-firmware/master/building/usbwindows/) to install crazyradio on Windows 83 | 84 | If you're using Python 3.13, you need to install [Visual Studio](https://visualstudio.microsoft.com/downloads/). During the installation process, you only need to select the Desktop Development with C++ workload in the Visual Studio Installer. 85 | 86 | ### macOS 87 | If you are using python 3.12 on mac you need to install libusb using homebrew. 88 | ``` 89 | $ brew install libusb 90 | ``` 91 | 92 | If your Homebrew installation is in a non default location or if you want to install libusb in some other way you 93 | might need to link the libusb library like this; 94 | ``` 95 | $ export DYLD_LIBRARY_PATH="YOUR_HOMEBREW_PATH/lib:$DYLD_LIBRARY_PATH" 96 | ``` 97 | -------------------------------------------------------------------------------- /docs/installation/usb_permissions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: USB permissions 3 | page_id: usd_permissions 4 | --- 5 | 6 | ### Linux 7 | 8 | The following steps make it possible to use the USB Radio and Crazyflie 2 over USB without being root. 9 | 10 | ``` 11 | sudo groupadd plugdev 12 | sudo usermod -a -G plugdev $USER 13 | ``` 14 | 15 | You will need to log out and log in again in order to be a member of the plugdev group. 16 | 17 | Copy-paste the following in your console, this will create the file ```/etc/udev/rules.d/99-bitcraze.rules```: 18 | ``` 19 | cat < /dev/null 20 | # Crazyradio (normal operation) 21 | SUBSYSTEM=="usb", ATTRS{idVendor}=="1915", ATTRS{idProduct}=="7777", MODE="0664", GROUP="plugdev" 22 | # Bootloader 23 | SUBSYSTEM=="usb", ATTRS{idVendor}=="1915", ATTRS{idProduct}=="0101", MODE="0664", GROUP="plugdev" 24 | # Crazyflie (over USB) 25 | SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5740", MODE="0664", GROUP="plugdev" 26 | EOF 27 | ``` 28 | 29 | You can reload the udev-rules using the following: 30 | ``` 31 | sudo udevadm control --reload-rules 32 | sudo udevadm trigger 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/user-guides/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: User guides 3 | page_id: user_guides_index 4 | sort_order: 2 5 | --- 6 | 7 | In this section you will find user guides 8 | 9 | {% sub_page_menu %} 10 | -------------------------------------------------------------------------------- /examples/autonomy/motion_commander_demo.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2017 Bitcraze AB 10 | # 11 | # Crazyflie Nano Quadcopter Client 12 | # 13 | # This program is free software; you can redistribute it and/or 14 | # modify it under the terms of the GNU General Public License 15 | # as published by the Free Software Foundation; either version 2 16 | # of the License, or (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | """ 25 | This script shows the basic use of the MotionCommander class. 26 | 27 | Simple example that connects to the crazyflie at `URI` and runs a 28 | sequence. This script requires some kind of location system, it has been 29 | tested with (and designed for) the flow deck. 30 | 31 | The MotionCommander uses velocity setpoints. 32 | 33 | Change the URI variable to your Crazyflie configuration. 34 | """ 35 | import logging 36 | import time 37 | 38 | import cflib.crtp 39 | from cflib.crazyflie import Crazyflie 40 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 41 | from cflib.positioning.motion_commander import MotionCommander 42 | from cflib.utils import uri_helper 43 | 44 | URI = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 45 | 46 | # Only output errors from the logging framework 47 | logging.basicConfig(level=logging.ERROR) 48 | 49 | 50 | if __name__ == '__main__': 51 | # Initialize the low-level drivers 52 | cflib.crtp.init_drivers() 53 | 54 | with SyncCrazyflie(URI, cf=Crazyflie(rw_cache='./cache')) as scf: 55 | # Arm the Crazyflie 56 | scf.cf.platform.send_arming_request(True) 57 | time.sleep(1.0) 58 | 59 | # We take off when the commander is created 60 | with MotionCommander(scf) as mc: 61 | time.sleep(1) 62 | 63 | # There is a set of functions that move a specific distance 64 | # We can move in all directions 65 | mc.forward(0.8) 66 | mc.back(0.8) 67 | time.sleep(1) 68 | 69 | mc.up(0.5) 70 | mc.down(0.5) 71 | time.sleep(1) 72 | 73 | # We can also set the velocity 74 | mc.right(0.5, velocity=0.8) 75 | time.sleep(1) 76 | mc.left(0.5, velocity=0.4) 77 | time.sleep(1) 78 | 79 | # We can do circles or parts of circles 80 | mc.circle_right(0.5, velocity=0.5, angle_degrees=180) 81 | 82 | # Or turn 83 | mc.turn_left(90) 84 | time.sleep(1) 85 | 86 | # We can move along a line in 3D space 87 | mc.move_distance(-1, 0.0, 0.5, velocity=0.6) 88 | time.sleep(1) 89 | 90 | # There is also a set of functions that start a motion. The 91 | # Crazyflie will keep on going until it gets a new command. 92 | 93 | mc.start_left(velocity=0.5) 94 | # The motion is started and we can do other stuff, printing for 95 | # instance 96 | for _ in range(5): 97 | print('Doing other work') 98 | time.sleep(0.2) 99 | 100 | # And we can stop 101 | mc.stop() 102 | 103 | # We land when the MotionCommander goes out of scope 104 | -------------------------------------------------------------------------------- /examples/cache/.gitkeep: -------------------------------------------------------------------------------- 1 | I'm here to prevent git from removing the directory 2 | -------------------------------------------------------------------------------- /examples/console/console.py: -------------------------------------------------------------------------------- 1 | # 2 | # || ____ _ __ 3 | # +------+ / __ )(_) /_______________ _____ ___ 4 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 5 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 6 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 7 | # 8 | # Copyright (C) 2021 Bitcraze AB 9 | # 10 | # This program is free software: you can redistribute it and/or modify 11 | # it under the terms of the GNU General Public License as published by 12 | # the Free Software Foundation, either version 3 of the License, or 13 | # (at your option) any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import time 23 | 24 | import cflib.crtp # noqa 25 | from cflib.crazyflie import Crazyflie 26 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 27 | from cflib.utils import uri_helper 28 | 29 | # Reads the CFLIB_URI environment variable for URI or uses default 30 | uri = uri_helper.uri_from_env(default='radio://0/30/2M/E7E7E7E7E7') 31 | 32 | 33 | def console_callback(text: str): 34 | '''A callback to run when we get console text from Crazyflie''' 35 | # We do not add newlines to the text received, we get them from the 36 | # Crazyflie at appropriate places. 37 | print(text, end='') 38 | 39 | 40 | if __name__ == '__main__': 41 | # Initialize the low-level drivers 42 | cflib.crtp.init_drivers() 43 | 44 | # Create Crazyflie object, with cache to avoid re-reading param and log TOC 45 | cf = Crazyflie(rw_cache='./cache') 46 | 47 | # Add a callback to whenever we receive 'console' text from the Crazyflie 48 | # This is the output from DEBUG_PRINT calls in the firmware. 49 | # 50 | # This could also be a Python lambda, something like: 51 | # cf.console.receivedChar.add_callback(lambda text: logger.info(text)) 52 | cf.console.receivedChar.add_callback(console_callback) 53 | 54 | # This will connect the Crazyflie with the URI specified above. 55 | # You might have to restart your Crazyflie in order to get output 56 | # from console, since not much is written during regular uptime. 57 | with SyncCrazyflie(uri, cf=cf) as scf: 58 | print('[host] Connected, use ctrl-c to quit.') 59 | 60 | while True: 61 | time.sleep(1) 62 | -------------------------------------------------------------------------------- /examples/led-ring/led_mem_set.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 Bitcraze AB 10 | # 11 | # Crazyflie Nano Quadcopter Client 12 | # 13 | # This program is free software; you can redistribute it and/or 14 | # modify it under the terms of the GNU General Public License 15 | # as published by the Free Software Foundation; either version 2 16 | # of the License, or (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | """ 25 | Simple example that connects to the crazyflie at `URI` and writes to 26 | the LED memory so that individual leds in the LED-ring can be set, 27 | it has been tested with (and designed for) the LED-ring deck. 28 | 29 | Change the URI variable to your Crazyflie configuration. 30 | """ 31 | import logging 32 | import time 33 | 34 | import cflib.crtp 35 | from cflib.crazyflie import Crazyflie 36 | from cflib.crazyflie.mem import MemoryElement 37 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 38 | from cflib.utils import uri_helper 39 | 40 | URI = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 41 | 42 | # Only output errors from the logging framework 43 | logging.basicConfig(level=logging.ERROR) 44 | 45 | 46 | if __name__ == '__main__': 47 | # Initialize the low-level drivers 48 | cflib.crtp.init_drivers() 49 | 50 | with SyncCrazyflie(URI, cf=Crazyflie(rw_cache='./cache')) as scf: 51 | cf = scf.cf 52 | 53 | # Set virtual mem effect effect 54 | cf.param.set_value('ring.effect', '13') 55 | 56 | # Get LED memory and write to it 57 | mem = cf.mem.get_mems(MemoryElement.TYPE_DRIVER_LED) 58 | if len(mem) > 0: 59 | mem[0].leds[0].set(r=0, g=100, b=0) 60 | mem[0].leds[3].set(r=0, g=0, b=100) 61 | mem[0].leds[6].set(r=100, g=0, b=0) 62 | mem[0].leds[9].set(r=100, g=100, b=100) 63 | mem[0].write_data(None) 64 | 65 | time.sleep(2) 66 | -------------------------------------------------------------------------------- /examples/led-ring/led_param_set.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 Bitcraze AB 10 | # 11 | # Crazyflie Nano Quadcopter Client 12 | # 13 | # This program is free software; you can redistribute it and/or 14 | # modify it under the terms of the GNU General Public License 15 | # as published by the Free Software Foundation; either version 2 16 | # of the License, or (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | """ 25 | Simple example that connects to the crazyflie at `URI` and writes to 26 | parameters that control the LED-ring, 27 | it has been tested with (and designed for) the LED-ring deck. 28 | 29 | Change the URI variable to your Crazyflie configuration. 30 | """ 31 | import logging 32 | import time 33 | 34 | import cflib.crtp 35 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 36 | from cflib.utils import uri_helper 37 | 38 | # URI to the Crazyflie to connect to 39 | URI = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 40 | 41 | # Only output errors from the logging framework 42 | logging.basicConfig(level=logging.ERROR) 43 | 44 | 45 | if __name__ == '__main__': 46 | # Initialize the low-level drivers 47 | cflib.crtp.init_drivers() 48 | 49 | with SyncCrazyflie(URI) as scf: 50 | cf = scf.cf 51 | 52 | # Set solid color effect 53 | cf.param.set_value('ring.effect', '7') 54 | # Set the RGB values 55 | cf.param.set_value('ring.solidRed', '100') 56 | cf.param.set_value('ring.solidGreen', '0') 57 | cf.param.set_value('ring.solidBlue', '0') 58 | time.sleep(2) 59 | 60 | # Set black color effect 61 | cf.param.set_value('ring.effect', '0') 62 | time.sleep(1) 63 | 64 | # Set fade to color effect 65 | cf.param.set_value('ring.effect', '14') 66 | # Set fade time i seconds 67 | cf.param.set_value('ring.fadeTime', '1.0') 68 | # Set the RGB values in one uint32 0xRRGGBB 69 | cf.param.set_value('ring.fadeColor', int('0000A0', 16)) 70 | time.sleep(1) 71 | cf.param.set_value('ring.fadeColor', int('00A000', 16)) 72 | time.sleep(1) 73 | cf.param.set_value('ring.fadeColor', int('A00000', 16)) 74 | time.sleep(1) 75 | -------------------------------------------------------------------------------- /examples/led-ring/led_timing.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 Bitcraze AB 10 | # 11 | # Crazyflie Nano Quadcopter Client 12 | # 13 | # This program is free software; you can redistribute it and/or 14 | # modify it under the terms of the GNU General Public License 15 | # as published by the Free Software Foundation; either version 2 16 | # of the License, or (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | """ 25 | This example demonstrate the LEDTIMING led ring sequence memory. This memory and 26 | led-ring effect allows to pre-program a LED sequence to be played autonomously 27 | by the Crazyflie. 28 | 29 | Change the URI variable to your Crazyflie configuration. 30 | """ 31 | import logging 32 | import time 33 | 34 | import cflib.crtp 35 | from cflib.crazyflie import Crazyflie 36 | from cflib.crazyflie.mem import MemoryElement 37 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 38 | from cflib.utils import uri_helper 39 | 40 | # URI to the Crazyflie to connect to 41 | URI = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 42 | 43 | # Only output errors from the logging framework 44 | logging.basicConfig(level=logging.ERROR) 45 | 46 | 47 | if __name__ == '__main__': 48 | # Initialize the low-level drivers 49 | cflib.crtp.init_drivers() 50 | 51 | with SyncCrazyflie(URI, cf=Crazyflie(rw_cache='./cache')) as scf: 52 | cf = scf.cf 53 | 54 | # Get LED memory and write to it 55 | mem = cf.mem.get_mems(MemoryElement.TYPE_DRIVER_LEDTIMING) 56 | if len(mem) > 0: 57 | mem[0].add(25, {'r': 100, 'g': 0, 'b': 0}) 58 | mem[0].add(0, {'r': 0, 'g': 100, 'b': 0}, leds=1) 59 | mem[0].add(0, {'r': 0, 'g': 100, 'b': 0}, leds=2) 60 | mem[0].add(3000, {'r': 0, 'g': 100, 'b': 0}, leds=3, rotate=1) 61 | mem[0].add(50, {'r': 0, 'g': 0, 'b': 100}, leds=1) 62 | mem[0].add(25, {'r': 0, 'g': 0, 'b': 100}, leds=0, fade=True) 63 | mem[0].add(25, {'r': 100, 'g': 0, 'b': 100}, leds=1) 64 | mem[0].add(25, {'r': 100, 'g': 0, 'b': 0}) 65 | mem[0].add(50, {'r': 100, 'g': 0, 'b': 100}) 66 | mem[0].write_data(None) 67 | else: 68 | print('No LED ring present') 69 | 70 | # Set virtual mem effect effect 71 | cf.param.set_value('ring.effect', '0') 72 | time.sleep(2) 73 | cf.param.set_value('ring.effect', '17') 74 | time.sleep(2) 75 | -------------------------------------------------------------------------------- /examples/lighthouse/read_lighthouse_mem.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | """ 23 | Example of how to read the Lighthouse base station geometry and 24 | calibration memory from a Crazyflie 25 | """ 26 | import logging 27 | from threading import Event 28 | 29 | import cflib.crtp # noqa 30 | from cflib.crazyflie import Crazyflie 31 | from cflib.crazyflie.mem import LighthouseMemHelper 32 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 33 | from cflib.utils import uri_helper 34 | 35 | # Only output errors from the logging framework 36 | logging.basicConfig(level=logging.ERROR) 37 | 38 | 39 | class ReadMem: 40 | def __init__(self, uri): 41 | self._event = Event() 42 | 43 | with SyncCrazyflie(uri, cf=Crazyflie(rw_cache='./cache')) as scf: 44 | helper = LighthouseMemHelper(scf.cf) 45 | 46 | helper.read_all_geos(self._geo_read_ready) 47 | self._event.wait() 48 | 49 | self._event.clear() 50 | 51 | helper.read_all_calibs(self._calib_read_ready) 52 | self._event.wait() 53 | 54 | def _geo_read_ready(self, geo_data): 55 | for id, data in geo_data.items(): 56 | print('---- Geometry for base station', id + 1) 57 | data.dump() 58 | print() 59 | self._event.set() 60 | 61 | def _calib_read_ready(self, calib_data): 62 | for id, data in calib_data.items(): 63 | print('---- Calibration data for base station', id + 1) 64 | data.dump() 65 | print() 66 | self._event.set() 67 | 68 | 69 | if __name__ == '__main__': 70 | # URI to the Crazyflie to connect to 71 | uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 72 | 73 | # Initialize the low-level drivers 74 | cflib.crtp.init_drivers() 75 | 76 | ReadMem(uri) 77 | -------------------------------------------------------------------------------- /examples/link_quality/latency.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ,---------, ____ _ __ 4 | # | ,-^-, | / __ )(_) /_______________ _____ ___ 5 | # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2024 Bitcraze AB 10 | # 11 | # This program is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, in version 3. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import time 23 | 24 | import cflib.crtp # noqa 25 | from cflib.crazyflie import Crazyflie 26 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 27 | from cflib.utils import uri_helper 28 | 29 | # Reads the CFLIB_URI environment variable for URI or uses default 30 | uri = uri_helper.uri_from_env(default='radio://0/90/2M/E7E7E7E7E7') 31 | 32 | 33 | def latency_callback(latency: float): 34 | """A callback to run when we get an updated latency estimate""" 35 | print(f'Latency: {latency:.3f} ms') 36 | 37 | 38 | if __name__ == '__main__': 39 | # Initialize the low-level drivers 40 | cflib.crtp.init_drivers() 41 | 42 | # Create Crazyflie object, with cache to avoid re-reading param and log TOC 43 | cf = Crazyflie(rw_cache='./cache') 44 | 45 | # Add a callback to whenever we receive an updated latency estimate 46 | # 47 | # This could also be a Python lambda, something like: 48 | cf.link_statistics.latency.latency_updated.add_callback(latency_callback) 49 | 50 | # This will connect the Crazyflie with the URI specified above. 51 | with SyncCrazyflie(uri, cf=cf) as scf: 52 | print('[host] Connected, use ctrl-c to quit.') 53 | 54 | while True: 55 | time.sleep(1) 56 | -------------------------------------------------------------------------------- /examples/loco_nodes/lps_reboot_to_bootloader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2017 Bitcraze AB 10 | # 11 | # Crazyflie Nano Quadcopter Client 12 | # 13 | # This program is free software; you can redistribute it and/or 14 | # modify it under the terms of the GNU General Public License 15 | # as published by the Free Software Foundation; either version 2 16 | # of the License, or (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | """ 25 | Simple example that connects to the first Crazyflie found, and then sends the 26 | reboot signals to 6 anchors ID from 0 to 5. The reset signals is sent 27 | 10 times in a row to make sure all anchors are reset to bootloader. 28 | """ 29 | import logging 30 | import time 31 | from threading import Thread 32 | 33 | import cflib 34 | from cflib.crazyflie import Crazyflie 35 | from cflib.utils import uri_helper 36 | from lpslib.lopoanchor import LoPoAnchor 37 | 38 | uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 39 | 40 | logging.basicConfig(level=logging.ERROR) 41 | 42 | 43 | class LpsRebootToBootloader: 44 | """Example that connects to a Crazyflie and ramps the motors up/down and 45 | the disconnects""" 46 | 47 | def __init__(self, link_uri): 48 | """ Initialize and run the example with the specified link_uri """ 49 | 50 | self._cf = Crazyflie() 51 | 52 | self._cf.connected.add_callback(self._connected) 53 | self._cf.disconnected.add_callback(self._disconnected) 54 | self._cf.connection_failed.add_callback(self._connection_failed) 55 | self._cf.connection_lost.add_callback(self._connection_lost) 56 | 57 | self._cf.open_link(link_uri) 58 | 59 | print('Connecting to %s' % link_uri) 60 | 61 | def _connected(self, link_uri): 62 | """ This callback is called form the Crazyflie API when a Crazyflie 63 | has been connected and the TOCs have been downloaded.""" 64 | 65 | # Start a separate thread to do the motor test. 66 | # Do not hijack the calling thread! 67 | Thread(target=self._reboot_thread).start() 68 | 69 | def _connection_failed(self, link_uri, msg): 70 | """Callback when connection initial connection fails (i.e no Crazyflie 71 | at the specified address)""" 72 | print('Connection to %s failed: %s' % (link_uri, msg)) 73 | 74 | def _connection_lost(self, link_uri, msg): 75 | """Callback when disconnected after a connection has been made (i.e 76 | Crazyflie moves out of range)""" 77 | print('Connection to %s lost: %s' % (link_uri, msg)) 78 | 79 | def _disconnected(self, link_uri): 80 | """Callback when the Crazyflie is disconnected (called in all cases)""" 81 | print('Disconnected from %s' % link_uri) 82 | 83 | def _reboot_thread(self): 84 | 85 | anchors = LoPoAnchor(self._cf) 86 | 87 | print('Sending reboot signal to all anchors 10 times in a row ...') 88 | for retry in range(10): 89 | for anchor_id in range(6): 90 | anchors.reboot(anchor_id, anchors.REBOOT_TO_BOOTLOADER) 91 | time.sleep(0.1) 92 | 93 | self._cf.close_link() 94 | 95 | 96 | if __name__ == '__main__': 97 | # Initialize the low-level drivers 98 | cflib.crtp.init_drivers() 99 | 100 | le = LpsRebootToBootloader(uri) 101 | -------------------------------------------------------------------------------- /examples/logging/basiclogSync.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2016 Bitcraze AB 10 | # 11 | # Crazyflie Nano Quadcopter Client 12 | # 13 | # This program is free software; you can redistribute it and/or 14 | # modify it under the terms of the GNU General Public License 15 | # as published by the Free Software Foundation; either version 2 16 | # of the License, or (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | """ 25 | Simple example that connects to the first Crazyflie found, logs the Stabilizer 26 | and prints it to the console. After 10s the application disconnects and exits. 27 | 28 | This example utilizes the SyncCrazyflie and SyncLogger classes. 29 | """ 30 | import logging 31 | import time 32 | 33 | import cflib.crtp 34 | from cflib.crazyflie import Crazyflie 35 | from cflib.crazyflie.log import LogConfig 36 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 37 | from cflib.crazyflie.syncLogger import SyncLogger 38 | from cflib.utils import uri_helper 39 | 40 | 41 | uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 42 | 43 | # Only output errors from the logging framework 44 | logging.basicConfig(level=logging.ERROR) 45 | 46 | 47 | if __name__ == '__main__': 48 | # Initialize the low-level drivers 49 | cflib.crtp.init_drivers() 50 | 51 | lg_stab = LogConfig(name='Stabilizer', period_in_ms=10) 52 | lg_stab.add_variable('stabilizer.roll', 'float') 53 | lg_stab.add_variable('stabilizer.pitch', 'float') 54 | lg_stab.add_variable('stabilizer.yaw', 'float') 55 | 56 | cf = Crazyflie(rw_cache='./cache') 57 | with SyncCrazyflie(uri, cf=cf) as scf: 58 | # Note: it is possible to add more than one log config using an 59 | # array. 60 | # with SyncLogger(scf, [lg_stab, other_conf]) as logger: 61 | with SyncLogger(scf, lg_stab) as logger: 62 | endTime = time.time() + 10 63 | 64 | for log_entry in logger: 65 | timestamp = log_entry[0] 66 | data = log_entry[1] 67 | logconf_name = log_entry[2] 68 | 69 | print('[%d][%s]: %s' % (timestamp, logconf_name, data)) 70 | 71 | if time.time() > endTime: 72 | break 73 | -------------------------------------------------------------------------------- /examples/memory/read-l5.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | """ 23 | Example of how to read the memory from the multiranger 24 | """ 25 | import logging 26 | import time 27 | from threading import Event 28 | 29 | import matplotlib.pyplot as plt 30 | 31 | import cflib.crtp # noqa 32 | from cflib.crazyflie import Crazyflie 33 | from cflib.crazyflie.mem import MemoryElement 34 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 35 | from cflib.utils import uri_helper 36 | 37 | # Only output errors from the logging framework 38 | logging.basicConfig(level=logging.ERROR) 39 | 40 | 41 | class ReadMem: 42 | def __init__(self, uri): 43 | self._event = Event() 44 | self._cf = Crazyflie(rw_cache='./cache') 45 | 46 | with SyncCrazyflie(uri, cf=self._cf) as scf: 47 | mems = scf.cf.mem.get_mems(MemoryElement.TYPE_DECK_MULTIRANGER) 48 | 49 | count = len(mems) 50 | if count != 1: 51 | raise Exception('Unexpected nr of memories found:', count) 52 | 53 | mem = mems[0] 54 | 55 | data = [[0 for x in range(8)] for y in range(8)] 56 | im = plt.imshow(data, cmap='gray', vmin=0, vmax=400) 57 | 58 | start_time = time.time() 59 | for frames in range(100): 60 | data = mem.read_data_sync() 61 | im.set_data(data) 62 | plt.pause(0.01) 63 | 64 | end_time = time.time() 65 | print('FPS: {}'.format(100/(end_time - start_time))) 66 | 67 | 68 | if __name__ == '__main__': 69 | # URI to the Crazyflie to connect to 70 | uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 71 | 72 | # Initialize the low-level drivers 73 | cflib.crtp.init_drivers() 74 | 75 | rm = ReadMem(uri) 76 | -------------------------------------------------------------------------------- /examples/memory/read-ow.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2014 Bitcraze AB 10 | # 11 | # Crazyflie Nano Quadcopter Client 12 | # 13 | # This program is free software; you can redistribute it and/or 14 | # modify it under the terms of the GNU General Public License 15 | # as published by the Free Software Foundation; either version 2 16 | # of the License, or (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | """ 25 | Simple example that connects to a Crazyflie, looks for 26 | 1-wire memories and lists its contents. 27 | """ 28 | import logging 29 | import sys 30 | from threading import Event 31 | 32 | import cflib.crtp # noqa 33 | from cflib.crazyflie import Crazyflie 34 | from cflib.crazyflie.mem import MemoryElement 35 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 36 | from cflib.utils import uri_helper 37 | 38 | uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 39 | 40 | # Only output errors from the logging framework 41 | logging.basicConfig(level=logging.ERROR) 42 | 43 | update_event = Event() 44 | 45 | 46 | def read_ow_mems(cf): 47 | mems = cf.mem.get_mems(MemoryElement.TYPE_1W) 48 | print(f'Found {len(mems)} 1-wire memories') 49 | 50 | for m in mems: 51 | update_event.clear() 52 | 53 | print(f'Reading id={m.id}') 54 | m.update(data_updated_cb) 55 | success = update_event.wait(timeout=5.0) 56 | if not success: 57 | print(f'Mem read time out for memory {m.id}') 58 | sys.exit(1) 59 | 60 | 61 | def data_updated_cb(mem): 62 | print(f'Got id={mem.id}') 63 | print(f'\tAddr : {mem.addr}') 64 | print(f'\tType : {mem.type}') 65 | print(f'\tSize : {mem.size}') 66 | print(f'\tValid : {mem.valid}') 67 | print(f'\tName : {mem.name}') 68 | print(f'\tVID : 0x{mem.vid:02X}') 69 | print(f'\tPID : 0x{mem.pid:02X}') 70 | print(f'\tPins : 0x{mem.pins:02X}') 71 | print('\tElements : ') 72 | 73 | for key, element in mem.elements.items(): 74 | print(f'\t\t{key}={element}') 75 | 76 | update_event.set() 77 | 78 | 79 | if __name__ == '__main__': 80 | # Initialize the low-level drivers 81 | cflib.crtp.init_drivers() 82 | 83 | with SyncCrazyflie(uri, cf=Crazyflie(rw_cache='./cache')) as scf: 84 | read_ow_mems(scf.cf) 85 | -------------------------------------------------------------------------------- /examples/memory/read-paa3905.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | """ 23 | Example of how to read the memory from the multiranger 24 | """ 25 | import logging 26 | import time 27 | from threading import Event 28 | 29 | import matplotlib.pyplot as plt 30 | 31 | import cflib.crtp # noqa 32 | from cflib.crazyflie import Crazyflie 33 | from cflib.crazyflie.mem import MemoryElement 34 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 35 | from cflib.utils import uri_helper 36 | 37 | # Only output errors from the logging framework 38 | logging.basicConfig(level=logging.ERROR) 39 | 40 | 41 | class ReadMem: 42 | def __init__(self, uri): 43 | self._event = Event() 44 | self._cf = Crazyflie(rw_cache='./cache') 45 | 46 | with SyncCrazyflie(uri, cf=self._cf) as scf: 47 | mems = scf.cf.mem.get_mems(MemoryElement.TYPE_DECK_PAA3905) 48 | 49 | count = len(mems) 50 | if count != 1: 51 | raise Exception('Unexpected nr of memories found:', count) 52 | 53 | mem = mems[0] 54 | 55 | data = [[0 for x in range(35)] for y in range(35)] 56 | im = plt.imshow(data, cmap='gray', vmin=0, vmax=255, origin='upper') 57 | 58 | start_time = time.time() 59 | for frames in range(100): 60 | data = mem.read_data_sync() 61 | im.set_data(data) 62 | plt.pause(0.01) 63 | 64 | end_time = time.time() 65 | print('FPS: {}'.format(100/(end_time - start_time))) 66 | time.sleep(5) 67 | 68 | 69 | if __name__ == '__main__': 70 | # URI to the Crazyflie to connect to 71 | # uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 72 | uri = uri_helper.uri_from_env(default='usb://0') 73 | 74 | # Initialize the low-level drivers 75 | cflib.crtp.init_drivers() 76 | 77 | rm = ReadMem(uri) 78 | -------------------------------------------------------------------------------- /examples/memory/read_deck_mem.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | """ 23 | Example of how to read the memory from a deck 24 | """ 25 | import logging 26 | from threading import Event 27 | 28 | import cflib.crtp # noqa 29 | from cflib.crazyflie import Crazyflie 30 | from cflib.crazyflie.mem import MemoryElement 31 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 32 | from cflib.utils import uri_helper 33 | 34 | # Only output errors from the logging framework 35 | logging.basicConfig(level=logging.ERROR) 36 | 37 | 38 | class ReadMem: 39 | def __init__(self, uri): 40 | self._event = Event() 41 | self._cf = Crazyflie(rw_cache='./cache') 42 | 43 | with SyncCrazyflie(uri, cf=self._cf) as scf: 44 | mems = scf.cf.mem.get_mems(MemoryElement.TYPE_DECK_MEMORY) 45 | 46 | count = len(mems) 47 | if count != 1: 48 | raise Exception('Unexpected nr of memories found:', count) 49 | 50 | mem = mems[0] 51 | mem.query_decks(self.query_complete_cb) 52 | self._event.wait() 53 | 54 | if len(mem.deck_memories.items()) == 0: 55 | print('No memories to read') 56 | 57 | for id, deck_mem in mem.deck_memories.items(): 58 | print('-----') 59 | print('Deck id:', id) 60 | print('Name:', deck_mem.name) 61 | print('is_started:', deck_mem.is_started) 62 | print('supports_read:', deck_mem.supports_read) 63 | print('supports_write:', deck_mem.supports_write) 64 | 65 | if deck_mem.supports_fw_upgrade: 66 | print('This deck supports FW upgrades') 67 | print( 68 | f' The required FW is: hash={deck_mem.required_hash}, ' 69 | f'length={deck_mem.required_length}, name={deck_mem.name}') 70 | print(' is_fw_upgrade_required:', deck_mem.is_fw_upgrade_required) 71 | if (deck_mem.is_bootloader_active): 72 | print(' In bootloader mode, ready to be flashed') 73 | else: 74 | print(' In FW mode') 75 | print('') 76 | 77 | if deck_mem.supports_read: 78 | print('Reading first 10 bytes of memory') 79 | self._event.clear() 80 | deck_mem.read(0, 10, self.read_complete, self.read_failed) 81 | self._event.wait() 82 | 83 | def query_complete_cb(self, deck_memories): 84 | self._event.set() 85 | 86 | def read_complete(self, addr, data): 87 | print(data) 88 | self._event.set() 89 | 90 | def read_failed(self, addr): 91 | print('Read failed @ {}'.format(addr)) 92 | self._event.set() 93 | 94 | 95 | if __name__ == '__main__': 96 | # URI to the Crazyflie to connect to 97 | uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 98 | 99 | # Initialize the low-level drivers 100 | cflib.crtp.init_drivers() 101 | 102 | rm = ReadMem(uri) 103 | -------------------------------------------------------------------------------- /examples/multiranger/multiranger_push.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # || ____ _ __ 5 | # +------+ / __ )(_) /_______________ _____ ___ 6 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 7 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 8 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 9 | # 10 | # Copyright (C) 2017 Bitcraze AB 11 | # 12 | # Crazyflie Python Library 13 | # 14 | # This program is free software; you can redistribute it and/or 15 | # modify it under the terms of the GNU General Public License 16 | # as published by the Free Software Foundation; either version 2 17 | # of the License, or (at your option) any later version. 18 | # 19 | # This program is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU General Public License for more details. 23 | # You should have received a copy of the GNU General Public License 24 | # along with this program. If not, see . 25 | """ 26 | Example scripts that allows a user to "push" the Crazyflie 2.0 around 27 | using your hands while it's hovering. 28 | 29 | This examples uses the Flow and Multi-ranger decks to measure distances 30 | in all directions and tries to keep away from anything that comes closer 31 | than 0.2m by setting a velocity in the opposite direction. 32 | 33 | The demo is ended by either pressing Ctrl-C or by holding your hand above the 34 | Crazyflie. 35 | 36 | For the example to run the following hardware is needed: 37 | * Crazyflie 2.0 38 | * Crazyradio PA 39 | * Flow deck 40 | * Multiranger deck 41 | """ 42 | import logging 43 | import sys 44 | import time 45 | 46 | import cflib.crtp 47 | from cflib.crazyflie import Crazyflie 48 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 49 | from cflib.positioning.motion_commander import MotionCommander 50 | from cflib.utils import uri_helper 51 | from cflib.utils.multiranger import Multiranger 52 | 53 | URI = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 54 | 55 | if len(sys.argv) > 1: 56 | URI = sys.argv[1] 57 | 58 | # Only output errors from the logging framework 59 | logging.basicConfig(level=logging.ERROR) 60 | 61 | 62 | def is_close(range): 63 | MIN_DISTANCE = 0.2 # m 64 | 65 | if range is None: 66 | return False 67 | else: 68 | return range < MIN_DISTANCE 69 | 70 | 71 | if __name__ == '__main__': 72 | # Initialize the low-level drivers 73 | cflib.crtp.init_drivers() 74 | 75 | cf = Crazyflie(rw_cache='./cache') 76 | with SyncCrazyflie(URI, cf=cf) as scf: 77 | # Arm the Crazyflie 78 | scf.cf.platform.send_arming_request(True) 79 | time.sleep(1.0) 80 | 81 | with MotionCommander(scf) as motion_commander: 82 | with Multiranger(scf) as multiranger: 83 | keep_flying = True 84 | 85 | while keep_flying: 86 | VELOCITY = 0.5 87 | velocity_x = 0.0 88 | velocity_y = 0.0 89 | 90 | if is_close(multiranger.front): 91 | velocity_x -= VELOCITY 92 | if is_close(multiranger.back): 93 | velocity_x += VELOCITY 94 | 95 | if is_close(multiranger.left): 96 | velocity_y -= VELOCITY 97 | if is_close(multiranger.right): 98 | velocity_y += VELOCITY 99 | 100 | if is_close(multiranger.up): 101 | keep_flying = False 102 | 103 | motion_commander.start_linear_motion( 104 | velocity_x, velocity_y, 0) 105 | 106 | time.sleep(0.1) 107 | 108 | print('Demo terminated!') 109 | -------------------------------------------------------------------------------- /examples/parameters/persistent_params_from_file.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ,---------, ____ _ __ 4 | # | ,-^-, | / __ )(_) /_______________ _____ ___ 5 | # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2024 Bitcraze AB 10 | # 11 | # This program is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, in version 3. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | """ 23 | Example to show how to write several persistent parameters from a yaml file. 24 | The params in the file should be formatted like this; 25 | 26 | params: 27 | activeMarker.back: 28 | default_value: 3 29 | is_stored: true 30 | stored_value: 30 31 | type: persistent_param_state 32 | version: '1' 33 | """ 34 | import argparse 35 | import logging 36 | 37 | import cflib.crtp 38 | from cflib.crazyflie import Crazyflie 39 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 40 | from cflib.utils import uri_helper 41 | from cflib.utils.param_file_helper import ParamFileHelper 42 | 43 | 44 | uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 45 | 46 | # Only output errors from the logging framework 47 | logging.basicConfig(level=logging.ERROR) 48 | 49 | 50 | if __name__ == '__main__': 51 | parser = argparse.ArgumentParser() 52 | parser.add_argument('-f', '--file', type=str, help='The yaml file containing the arguments. ') 53 | args = parser.parse_args() 54 | 55 | cflib.crtp.init_drivers() 56 | 57 | cf = Crazyflie(rw_cache='./cache') 58 | with SyncCrazyflie(uri, cf=cf) as scf: 59 | writer = ParamFileHelper(scf.cf) 60 | writer.store_params_from_file(args.file) 61 | -------------------------------------------------------------------------------- /examples/positioning/flowsequenceSync.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2016 Bitcraze AB 10 | # 11 | # Crazyflie Nano Quadcopter Client 12 | # 13 | # This program is free software; you can redistribute it and/or 14 | # modify it under the terms of the GNU General Public License 15 | # as published by the Free Software Foundation; either version 2 16 | # of the License, or (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | """ 25 | Simple example that connects to the crazyflie at `URI` and runs a figure 8 26 | sequence. This script requires some kind of location system, it has been 27 | tested with the flow deck and the lighthouse positioning system. 28 | 29 | Change the URI variable to your Crazyflie configuration. 30 | """ 31 | import logging 32 | import time 33 | 34 | import cflib.crtp 35 | from cflib.crazyflie import Crazyflie 36 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 37 | from cflib.utils import uri_helper 38 | from cflib.utils.reset_estimator import reset_estimator 39 | 40 | URI = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 41 | 42 | # Only output errors from the logging framework 43 | logging.basicConfig(level=logging.ERROR) 44 | 45 | 46 | if __name__ == '__main__': 47 | # Initialize the low-level drivers 48 | cflib.crtp.init_drivers() 49 | 50 | with SyncCrazyflie(URI, cf=Crazyflie(rw_cache='./cache')) as scf: 51 | cf = scf.cf 52 | 53 | reset_estimator(scf) 54 | time.sleep(1) 55 | 56 | # Arm the Crazyflie 57 | cf.platform.send_arming_request(True) 58 | time.sleep(1.0) 59 | 60 | for y in range(10): 61 | cf.commander.send_hover_setpoint(0, 0, 0, y / 25) 62 | time.sleep(0.1) 63 | 64 | for _ in range(20): 65 | cf.commander.send_hover_setpoint(0, 0, 0, 0.4) 66 | time.sleep(0.1) 67 | 68 | for _ in range(50): 69 | cf.commander.send_hover_setpoint(0.5, 0, 36 * 2, 0.4) 70 | time.sleep(0.1) 71 | 72 | for _ in range(50): 73 | cf.commander.send_hover_setpoint(0.5, 0, -36 * 2, 0.4) 74 | time.sleep(0.1) 75 | 76 | for _ in range(20): 77 | cf.commander.send_hover_setpoint(0, 0, 0, 0.4) 78 | time.sleep(0.1) 79 | 80 | for y in range(10): 81 | cf.commander.send_hover_setpoint(0, 0, 0, (10 - y) / 25) 82 | time.sleep(0.1) 83 | 84 | cf.commander.send_stop_setpoint() 85 | # Hand control over to the high level commander to avoid timeout and locking of the Crazyflie 86 | cf.commander.send_notify_setpoint_stop() 87 | -------------------------------------------------------------------------------- /examples/positioning/matrix_light_printer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2018 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | """ 23 | This script implements a simple matrix light printer to be used with a 24 | camera with open shutter in a dark room. 25 | 26 | It requires a Crazyflie capable of controlling its position and with 27 | a Led ring attached to it. A piece of sticky paper can be attached on 28 | the led ring to orient the ring light toward the front. 29 | 30 | To control it position, Crazyflie requires an absolute positioning 31 | system such as the Lighthouse. 32 | """ 33 | import time 34 | 35 | import matplotlib.pyplot as plt 36 | 37 | import cflib.crtp 38 | from cflib.crazyflie import Crazyflie 39 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 40 | from cflib.positioning.position_hl_commander import PositionHlCommander 41 | from cflib.utils import uri_helper 42 | 43 | # URI to the Crazyflie to connect to 44 | uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 45 | 46 | 47 | class ImageDef: 48 | def __init__(self, file_name): 49 | self._image = plt.imread(file_name) 50 | 51 | self.x_pixels = self._image.shape[1] 52 | self.y_pixels = self._image.shape[0] 53 | 54 | width = 1.0 55 | height = self.y_pixels * width / self.x_pixels 56 | 57 | self.x_start = -width / 2.0 + 0.5 58 | self.y_start = 0.7 59 | 60 | self.x_step = width / self.x_pixels 61 | self.y_step = height / self.y_pixels 62 | 63 | def get_color(self, x_index, y_index): 64 | rgba = self._image[self.y_pixels - y_index - 1, x_index] 65 | rgb = [int(rgba[0] * 90), int(rgba[1] * 90), int(rgba[2] * 90)] 66 | return rgb 67 | 68 | 69 | BLACK = [0, 0, 0] 70 | 71 | 72 | def set_led_ring_solid(cf, rgb): 73 | cf.param.set_value('ring.effect', 7) 74 | print(rgb[0], rgb[1], rgb[2]) 75 | cf.param.set_value('ring.solidRed', rgb[0]) 76 | cf.param.set_value('ring.solidGreen', rgb[1]) 77 | cf.param.set_value('ring.solidBlue', rgb[2]) 78 | 79 | 80 | def matrix_print(cf, pc, image_def): 81 | set_led_ring_solid(cf, BLACK) 82 | time.sleep(3) 83 | 84 | for y_index in range(image_def.y_pixels): 85 | y = image_def.y_start + image_def.y_step * y_index 86 | 87 | pc.go_to(0, image_def.x_start - 0.1, y) 88 | time.sleep(1.0) 89 | 90 | scan_range = range(image_def.x_pixels) 91 | 92 | for x_index in scan_range: 93 | x = image_def.x_start + image_def.x_step * x_index 94 | 95 | color = image_def.get_color(x_index, y_index) 96 | 97 | print(x, y, color) 98 | 99 | pc.go_to(0, x, y) 100 | set_led_ring_solid(cf, color) 101 | 102 | set_led_ring_solid(cf, BLACK) 103 | 104 | print('---') 105 | 106 | 107 | if __name__ == '__main__': 108 | cflib.crtp.init_drivers() 109 | 110 | image_def = ImageDef('monalisa.png') 111 | 112 | with SyncCrazyflie(uri, cf=Crazyflie(rw_cache='./cache')) as scf: 113 | # Arm the Crazyflie 114 | scf.cf.platform.send_arming_request(True) 115 | time.sleep(1.0) 116 | 117 | with PositionHlCommander(scf, default_height=0.5, controller=PositionHlCommander.CONTROLLER_PID) as pc: 118 | matrix_print(scf.cf, pc, image_def) 119 | -------------------------------------------------------------------------------- /examples/positioning/monalisa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcraze/crazyflie-lib-python/6a427318a0a2a2c3f231861a087d49d04db211b2/examples/positioning/monalisa.png -------------------------------------------------------------------------------- /examples/radio/scan.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2014 Bitcraze AB 10 | # 11 | # Crazyflie Nano Quadcopter Client 12 | # 13 | # This program is free software; you can redistribute it and/or 14 | # modify it under the terms of the GNU General Public License 15 | # as published by the Free Software Foundation; either version 2 16 | # of the License, or (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | """ 25 | Simple example that scans for available Crazyflies with a certain address and lists them. 26 | """ 27 | import cflib.crtp 28 | 29 | # Initiate the low level drivers 30 | cflib.crtp.init_drivers() 31 | 32 | print('Scanning interfaces for Crazyflies...') 33 | available = cflib.crtp.scan_interfaces(address=int('E7E7E7E7E7', 16) 34 | ) 35 | print('Crazyflies found:') 36 | for i in available: 37 | print(i[0]) 38 | -------------------------------------------------------------------------------- /examples/step-by-step/sbs_connect_log_param.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2017 Bitcraze AB 10 | # 11 | # Crazyflie Nano Quadcopter Client 12 | # 13 | # This program is free software; you can redistribute it and/or 14 | # modify it under the terms of the GNU General Public License 15 | # as published by the Free Software Foundation; either version 2 16 | # of the License, or (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | import logging 25 | import time 26 | 27 | import cflib.crtp 28 | from cflib.crazyflie import Crazyflie 29 | from cflib.crazyflie.log import LogConfig 30 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 31 | from cflib.crazyflie.syncLogger import SyncLogger 32 | from cflib.utils import uri_helper 33 | 34 | # URI to the Crazyflie to connect to 35 | uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 36 | 37 | # Only output errors from the logging framework 38 | logging.basicConfig(level=logging.ERROR) 39 | 40 | 41 | def param_stab_est_callback(name, value): 42 | print('The crazyflie has parameter ' + name + ' set at number: ' + value) 43 | 44 | 45 | def simple_param_async(scf, groupstr, namestr): 46 | cf = scf.cf 47 | full_name = groupstr + '.' + namestr 48 | 49 | cf.param.add_update_callback(group=groupstr, name=namestr, 50 | cb=param_stab_est_callback) 51 | time.sleep(1) 52 | cf.param.set_value(full_name, 2) 53 | time.sleep(1) 54 | cf.param.set_value(full_name, 1) 55 | time.sleep(1) 56 | 57 | 58 | def log_stab_callback(timestamp, data, logconf): 59 | print('[%d][%s]: %s' % (timestamp, logconf.name, data)) 60 | 61 | 62 | def simple_log_async(scf, logconf): 63 | cf = scf.cf 64 | cf.log.add_config(logconf) 65 | logconf.data_received_cb.add_callback(log_stab_callback) 66 | logconf.start() 67 | time.sleep(5) 68 | logconf.stop() 69 | 70 | 71 | def simple_log(scf, logconf): 72 | 73 | with SyncLogger(scf, logconf) as logger: 74 | 75 | for log_entry in logger: 76 | 77 | timestamp = log_entry[0] 78 | data = log_entry[1] 79 | logconf_name = log_entry[2] 80 | 81 | print('[%d][%s]: %s' % (timestamp, logconf_name, data)) 82 | 83 | break 84 | 85 | 86 | def simple_connect(): 87 | 88 | print("Yeah, I'm connected! :D") 89 | time.sleep(3) 90 | print("Now I will disconnect :'(") 91 | 92 | 93 | if __name__ == '__main__': 94 | # Initialize the low-level drivers 95 | cflib.crtp.init_drivers() 96 | 97 | lg_stab = LogConfig(name='Stabilizer', period_in_ms=10) 98 | lg_stab.add_variable('stabilizer.roll', 'float') 99 | lg_stab.add_variable('stabilizer.pitch', 'float') 100 | lg_stab.add_variable('stabilizer.yaw', 'float') 101 | 102 | group = 'stabilizer' 103 | name = 'estimator' 104 | 105 | with SyncCrazyflie(uri, cf=Crazyflie(rw_cache='./cache')) as scf: 106 | 107 | # simple_connect() 108 | 109 | # simple_log(scf, lg_stab) 110 | 111 | # simple_log_async(scf, lg_stab) 112 | 113 | simple_param_async(scf, group, name) 114 | -------------------------------------------------------------------------------- /examples/swarm/hl-commander-swarm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | """ 23 | Simple example of a swarm using the High level commander. 24 | 25 | The swarm takes off and flies a synchronous square shape before landing. 26 | The trajectories are relative to the starting positions and the Crazyflies can 27 | be at any position on the floor when the script is started. 28 | 29 | This example is intended to work with any absolute positioning system. 30 | It aims at documenting how to use the High Level Commander together with 31 | the Swarm class. 32 | """ 33 | import time 34 | 35 | import cflib.crtp 36 | from cflib.crazyflie.swarm import CachedCfFactory 37 | from cflib.crazyflie.swarm import Swarm 38 | 39 | 40 | def activate_mellinger_controller(scf, use_mellinger): 41 | controller = 1 42 | if use_mellinger: 43 | controller = 2 44 | scf.cf.param.set_value('stabilizer.controller', controller) 45 | 46 | 47 | def arm(scf): 48 | scf.cf.platform.send_arming_request(True) 49 | time.sleep(1.0) 50 | 51 | 52 | def run_shared_sequence(scf): 53 | activate_mellinger_controller(scf, False) 54 | 55 | box_size = 1 56 | flight_time = 2 57 | 58 | commander = scf.cf.high_level_commander 59 | 60 | commander.takeoff(1.0, 2.0) 61 | time.sleep(3) 62 | 63 | commander.go_to(box_size, 0, 0, 0, flight_time, relative=True) 64 | time.sleep(flight_time) 65 | 66 | commander.go_to(0, box_size, 0, 0, flight_time, relative=True) 67 | time.sleep(flight_time) 68 | 69 | commander.go_to(-box_size, 0, 0, 0, flight_time, relative=True) 70 | time.sleep(flight_time) 71 | 72 | commander.go_to(0, -box_size, 0, 0, flight_time, relative=True) 73 | time.sleep(flight_time) 74 | 75 | commander.land(0.0, 2.0) 76 | time.sleep(2) 77 | 78 | commander.stop() 79 | 80 | 81 | uris = { 82 | 'radio://0/30/2M/E7E7E7E711', 83 | 'radio://0/30/2M/E7E7E7E712', 84 | # Add more URIs if you want more copters in the swarm 85 | # URIs in a swarm using the same radio must also be on the same channel 86 | } 87 | 88 | if __name__ == '__main__': 89 | cflib.crtp.init_drivers() 90 | factory = CachedCfFactory(rw_cache='./cache') 91 | with Swarm(uris, factory=factory) as swarm: 92 | swarm.reset_estimators() 93 | swarm.parallel_safe(arm) 94 | swarm.parallel_safe(run_shared_sequence) 95 | -------------------------------------------------------------------------------- /lpslib/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2017 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | """ The LPS lib is an API that is used to communicate with Loco Positioning 23 | anchors. 24 | 25 | The main purpose of the lib is to manage LPP (Loco Positioning Protocol). 26 | 27 | Initially it will use a Crazyflie with a Loco Positioning deck as a bridge to 28 | transfer information to LoPo anchors, but may in the future use other means 29 | of transportation. 30 | 31 | Example: 32 | cf = Crazyflie() 33 | cf.open_link("radio://0/125") 34 | 35 | anchor = LoPoAnchor(cf) 36 | anchor.set_position(1, (1.23, 4.56, 7.89)) 37 | 38 | """ 39 | -------------------------------------------------------------------------------- /lpslib/lopoanchor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2017 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | """ 23 | This class represents the connection to one or more Loco Positioning Anchors 24 | """ 25 | import struct 26 | 27 | 28 | class LoPoAnchor(): 29 | LPP_TYPE_POSITION = 1 30 | LPP_TYPE_REBOOT = 2 31 | LPP_TYPE_MODE = 3 32 | 33 | REBOOT_TO_BOOTLOADER = 0 34 | REBOOT_TO_FIRMWARE = 1 35 | 36 | MODE_TWR = 1 37 | MODE_TDOA = 2 # TDoA 2 38 | MODE_TDOA3 = 3 39 | 40 | def __init__(self, crazyflie): 41 | """ 42 | :param crazyflie: A crazyflie object to be used as a bridge to the LoPo 43 | system.""" 44 | self.crazyflie = crazyflie 45 | 46 | def set_position(self, anchor_id, position): 47 | """ 48 | Send a packet with a position to one anchor. 49 | :param anchor_id: The id of the targeted anchor. This is the first byte 50 | of the anchor address. 51 | :param position: The position of the anchor, as an array 52 | """ 53 | x = position[0] 54 | y = position[1] 55 | z = position[2] 56 | data = struct.pack('=61.0", "wheel", "setuptools_scm"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "cflib" 7 | dynamic = ["version"] 8 | description = "Crazyflie Python driver" 9 | authors = [ 10 | { name = "Bitcraze and contributors", email = "contact@bitcraze.io" }, 11 | ] 12 | 13 | readme = {file = "README.md", content-type = "text/markdown"} 14 | license = { text = "GPLv3" } 15 | keywords = ["driver", "crazyflie", "quadcopter"] 16 | 17 | classifiers = [ 18 | "Development Status :: 5 - Production/Stable", 19 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 20 | "Topic :: System :: Hardware :: Hardware Drivers", 21 | "Topic :: Scientific/Engineering", 22 | "Intended Audience :: Science/Research", 23 | "Intended Audience :: Education", 24 | "Intended Audience :: Developers", 25 | "Operating System :: POSIX :: Linux", 26 | "Operating System :: MacOS", 27 | "Operating System :: Microsoft :: Windows", 28 | 29 | # Supported Python versions 30 | "Programming Language :: Python :: 3.10", 31 | "Programming Language :: Python :: 3.11", 32 | "Programming Language :: Python :: 3.12", 33 | "Programming Language :: Python :: 3.13", 34 | ] 35 | requires-python = ">= 3.10" 36 | 37 | dependencies = [ 38 | "pyusb~=1.2", 39 | "libusb-package~=1.0", 40 | "scipy~=1.14", 41 | "numpy~=1.26", 42 | "packaging~=24.2", 43 | ] 44 | 45 | [project.urls] 46 | Homepage = "https://www.bitcraze.io" 47 | Documentation = "https://www.bitcraze.io/documentation/repository/crazyflie-lib-python/master/" 48 | Repository = "https://github.com/bitcraze/crazyflie-lib-python" 49 | Issues = "https://github.com/bitcraze/crazyflie-lib-python/issues" 50 | 51 | [project.optional-dependencies] 52 | dev = ["pre-commit"] 53 | 54 | [tool.setuptools] 55 | include-package-data = true 56 | 57 | [tool.setuptools.packages] 58 | find = { exclude = ["examples", "test"] } 59 | 60 | [tool.setuptools.package-data] 61 | "cflib.resources.binaries" = ["cflib/resources/binaries/*.bin"] 62 | 63 | [tool.setuptools_scm] 64 | version_scheme = "no-guess-dev" 65 | -------------------------------------------------------------------------------- /setup_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Install the Crazyflie PC client and set up permissions so that it can be used 3 | # by the current user immediately 4 | # Caution! This installs the Crazyflie PC client as root to your Python 5 | # site-packages directory. If you wish to install it as a normal user, use the 6 | # instructions in the documentation at 7 | # https://www.bitcraze.io/documentation/repository/crazyflie-clients-python/master/ 8 | # After installation, the Crazyflie PC client can be started with `cfclient`. 9 | # @author Daniel Lee 2013 10 | 11 | # Allow user to use USB radio without root permission 12 | groupadd plugdev 13 | usermod -a -G plugdev $USER 14 | echo SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"1915\", ATTRS{idProduct}==\"7777\", \ 15 | MODE=\"0664\", GROUP=\"plugdev\" > /etc/udev/rules.d/99-crazyradio.rules 16 | echo SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"0483\", ATTRS{idProduct}==\"5740\", \ 17 | MODE=\"0664\", GROUP=\"plugdev\" > /etc/udev/rules.d/99-crazyflie.rules 18 | 19 | # Install Crazyflie PC client 20 | python3 setup.py install 21 | -------------------------------------------------------------------------------- /sys_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcraze/crazyflie-lib-python/6a427318a0a2a2c3f231861a087d49d04db211b2/sys_test/__init__.py -------------------------------------------------------------------------------- /sys_test/single_cf_grounded/README.md: -------------------------------------------------------------------------------- 1 | # Tests for a single Crazyflie 2.x (USB & Crazyradio) without flight 2 | 3 | ## Preparation 4 | 5 | * attach a single Crazyflie over USB 6 | * attach a Crazyradio (PA) 7 | * (Optional) update URI in `single_cf_grounded.py` 8 | 9 | ## Execute Tests 10 | 11 | All tests: 12 | 13 | ``` 14 | python3 -m unittest discover . -v 15 | ``` 16 | 17 | A single test file, e.g.: 18 | 19 | ``` 20 | python3 test_power_switch.py 21 | ``` 22 | 23 | A concrete test case, e.g.: 24 | 25 | ``` 26 | python3 test_power_switch.py TestPowerSwitch.test_reboot 27 | ``` 28 | 29 | ## Environment Variables 30 | 31 | Prepend command with `USE_CFLINK=cpp` to run with cflinkcpp native link library. 32 | -------------------------------------------------------------------------------- /sys_test/single_cf_grounded/single_cf_grounded.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2021 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import unittest 23 | 24 | import cflib.crtp 25 | from cflib.crtp.crtpstack import CRTPPacket 26 | from cflib.crtp.crtpstack import CRTPPort 27 | from cflib.utils import uri_helper 28 | 29 | 30 | class TestSingleCfGrounded(unittest.TestCase): 31 | def setUp(self): 32 | cflib.crtp.init_drivers() 33 | self.radioUri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E7E7') 34 | self.usbUri = 'usb://0' 35 | 36 | def is_stm_connected(self): 37 | link = cflib.crtp.get_link_driver(self.radioUri) 38 | 39 | pk = CRTPPacket() 40 | pk.set_header(CRTPPort.LINKCTRL, 0) # Echo channel 41 | pk.data = b'test' 42 | link.send_packet(pk) 43 | for _ in range(10): 44 | pk_ack = link.receive_packet(0.1) 45 | print(pk_ack) 46 | if pk_ack is not None and pk.data == pk_ack.data: 47 | link.close() 48 | return True 49 | link.close() 50 | return False 51 | # print(pk_ack) 52 | # return pk_ack is not None 53 | -------------------------------------------------------------------------------- /sys_test/single_cf_grounded/test_bootloader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2021 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import time 23 | import unittest 24 | 25 | from single_cf_grounded import TestSingleCfGrounded 26 | 27 | from cflib.bootloader import Bootloader 28 | from cflib.bootloader.boottypes import BootVersion 29 | 30 | 31 | class TestBootloader(TestSingleCfGrounded): 32 | 33 | def test_boot_to_bootloader(self): 34 | self.assertTrue(self.is_stm_connected()) 35 | bl = Bootloader(self.radioUri) 36 | started = bl.start_bootloader(warm_boot=True) 37 | self.assertTrue(started) 38 | 39 | # t = bl.get_target(TargetTypes.NRF51) 40 | # print(t) 41 | 42 | bl.reset_to_firmware() 43 | bl.close() 44 | time.sleep(1) 45 | self.assertTrue(self.is_stm_connected()) 46 | self.assertTrue(BootVersion.is_cf2(bl.protocol_version)) 47 | 48 | 49 | if __name__ == '__main__': 50 | unittest.main() 51 | -------------------------------------------------------------------------------- /sys_test/single_cf_grounded/test_power_switch.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2021 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import time 23 | import unittest 24 | 25 | from single_cf_grounded import TestSingleCfGrounded 26 | 27 | from cflib.utils.power_switch import PowerSwitch 28 | 29 | 30 | class TestPowerSwitch(TestSingleCfGrounded): 31 | 32 | def test_reboot(self): 33 | self.assertTrue(self.is_stm_connected()) 34 | s = PowerSwitch(self.radioUri) 35 | s.stm_power_down() 36 | s.close() 37 | self.assertFalse(self.is_stm_connected()) 38 | s = PowerSwitch(self.radioUri) 39 | s.stm_power_up() 40 | s.close() 41 | time.sleep(2) 42 | self.assertTrue(self.is_stm_connected()) 43 | 44 | 45 | if __name__ == '__main__': 46 | unittest.main() 47 | -------------------------------------------------------------------------------- /sys_test/swarm_test_rig/README.md: -------------------------------------------------------------------------------- 1 | # Tests using the Roadrunner Testrig 2 | 3 | ## Preparation 4 | 5 | * Power Roadrunner Testrig 6 | * attach a Crazyradio (PA) to PC 7 | * Flash the firmware using `./cload_all.sh .zip>` 8 | 9 | ## Execute Tests 10 | 11 | All tests: 12 | 13 | ``` 14 | python3 -m unittest discover . -v 15 | ``` 16 | 17 | There is also a script in `tools/build/sys-test` that essentially does that. 18 | 19 | A single test file, e.g.: 20 | 21 | ``` 22 | python3 test_connection.py 23 | ``` 24 | 25 | A concrete test case, e.g.: 26 | 27 | ``` 28 | python3 test_connection.py TestConnection.test_that_connection_time_scales_with_more_devices_without_cache 29 | ``` 30 | -------------------------------------------------------------------------------- /sys_test/swarm_test_rig/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | -------------------------------------------------------------------------------- /sys_test/swarm_test_rig/cload_all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | file=$1 4 | 5 | python3 -m cfloader flash $file nrf51-fw stm32-fw -w radio://0/42/2M/E7E7E74201 6 | python3 -m cfloader flash $file nrf51-fw stm32-fw -w radio://0/42/2M/E7E7E74202 7 | python3 -m cfloader flash $file nrf51-fw stm32-fw -w radio://0/42/2M/E7E7E74203 8 | python3 -m cfloader flash $file nrf51-fw stm32-fw -w radio://0/42/2M/E7E7E74204 9 | python3 -m cfloader flash $file nrf51-fw stm32-fw -w radio://0/42/2M/E7E7E74205 10 | python3 -m cfloader flash $file nrf51-fw stm32-fw -w radio://0/42/2M/E7E7E74206 11 | python3 -m cfloader flash $file nrf51-fw stm32-fw -w radio://0/42/2M/E7E7E74207 12 | python3 -m cfloader flash $file nrf51-fw stm32-fw -w radio://0/42/2M/E7E7E74208 13 | python3 -m cfloader flash $file nrf51-fw stm32-fw -w radio://0/42/2M/E7E7E74209 14 | python3 -m cfloader flash $file nrf51-fw stm32-fw -w radio://0/42/2M/E7E7E7420A 15 | -------------------------------------------------------------------------------- /sys_test/swarm_test_rig/rig_support.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import time 23 | 24 | from cflib.utils.power_switch import PowerSwitch 25 | 26 | 27 | class RigSupport: 28 | def __init__(self): 29 | self.all_uris = [ 30 | 'radio://0/42/2M/E7E7E74201', 31 | 'radio://0/42/2M/E7E7E74202', 32 | 'radio://0/42/2M/E7E7E74203', 33 | 'radio://0/42/2M/E7E7E74204', 34 | 'radio://0/42/2M/E7E7E74205', 35 | 'radio://0/42/2M/E7E7E74206', 36 | 'radio://0/42/2M/E7E7E74207', 37 | 'radio://0/42/2M/E7E7E74208', 38 | 'radio://0/42/2M/E7E7E74209', 39 | 'radio://0/42/2M/E7E7E7420A', 40 | ] 41 | 42 | def restart_devices(self, uris): 43 | print('Restarting devices') 44 | 45 | for uri in uris: 46 | PowerSwitch(uri).stm_power_down() 47 | 48 | time.sleep(1) 49 | 50 | for uri in uris: 51 | PowerSwitch(uri).stm_power_up() 52 | 53 | # Wait for devices to boot 54 | time.sleep(8) 55 | -------------------------------------------------------------------------------- /sys_test/swarm_test_rig/test_logging.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2019 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import unittest 23 | 24 | import cflib.crtp 25 | from cflib.crazyflie import Crazyflie 26 | from cflib.crazyflie.log import LogConfig 27 | from cflib.crazyflie.swarm import CachedCfFactory 28 | from cflib.crazyflie.swarm import Swarm 29 | from cflib.crazyflie.syncCrazyflie import SyncCrazyflie 30 | from cflib.crazyflie.syncLogger import SyncLogger 31 | from sys_test.swarm_test_rig.rig_support import RigSupport 32 | 33 | 34 | class TestLogging(unittest.TestCase): 35 | def setUp(self): 36 | cflib.crtp.init_drivers() 37 | self.test_rig_support = RigSupport() 38 | 39 | def test_that_requested_logging_is_received_properly_from_one_cf(self): 40 | # Fixture 41 | uri = self.test_rig_support.all_uris[0] 42 | self.test_rig_support.restart_devices([uri]) 43 | cf = Crazyflie(rw_cache='./cache') 44 | 45 | # Test and Assert 46 | with SyncCrazyflie(uri, cf=cf) as scf: 47 | self.assert_add_logging_and_get_non_zero_value(scf) 48 | 49 | def test_that_requested_logging_is_received_properly_from_all_cfs(self): 50 | # Fixture 51 | uris = self.test_rig_support.all_uris 52 | self.test_rig_support.restart_devices(uris) 53 | factory = CachedCfFactory(rw_cache='./cache') 54 | 55 | # Test and Assert 56 | with Swarm(uris, factory=factory) as swarm: 57 | swarm.parallel_safe(self.assert_add_logging_and_get_non_zero_value) 58 | 59 | def assert_add_logging_and_get_non_zero_value(self, scf): 60 | log_name = 'stabilizer.roll' 61 | expected = 0.0 62 | 63 | lg_conf = LogConfig(name='SysTest', period_in_ms=10) 64 | lg_conf.add_variable(log_name, 'float') 65 | 66 | with SyncLogger(scf, lg_conf) as logger: 67 | for log_entry in logger: 68 | actual = log_entry[1][log_name] 69 | break 70 | 71 | self.assertNotAlmostEqual(expected, actual, places=4) 72 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcraze/crazyflie-lib-python/6a427318a0a2a2c3f231861a087d49d04db211b2/test/__init__.py -------------------------------------------------------------------------------- /test/crazyflie/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcraze/crazyflie-lib-python/6a427318a0a2a2c3f231861a087d49d04db211b2/test/crazyflie/__init__.py -------------------------------------------------------------------------------- /test/crazyflie/mem/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcraze/crazyflie-lib-python/6a427318a0a2a2c3f231861a087d49d04db211b2/test/crazyflie/mem/__init__.py -------------------------------------------------------------------------------- /test/crtp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcraze/crazyflie-lib-python/6a427318a0a2a2c3f231861a087d49d04db211b2/test/crtp/__init__.py -------------------------------------------------------------------------------- /test/crtp/test_crtpstack.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) Bitcraze AB 10 | # 11 | # Crazyflie Nano Quadcopter Client 12 | # 13 | # This program is free software; you can redistribute it and/or 14 | # modify it under the terms of the GNU General Public License 15 | # as published by the Free Software Foundation; either version 2 16 | # of the License, or (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | import unittest 25 | 26 | from cflib.crtp.crtpstack import CRTPPacket 27 | 28 | 29 | class CRTPPacketTest(unittest.TestCase): 30 | 31 | def setUp(self): 32 | self.callback_count = 0 33 | self.sut = CRTPPacket() 34 | 35 | def test_that_port_and_channle_is_encoded_in_header(self): 36 | # Fixture 37 | self.sut.set_header(2, 1) 38 | 39 | # Test 40 | actual = self.sut.get_header() 41 | 42 | # Assert 43 | expected = 0x2d 44 | self.assertEqual(expected, actual) 45 | 46 | def test_that_port_is_truncated_in_header(self): 47 | # Fixture 48 | port = 0xff 49 | self.sut.set_header(port, 0) 50 | 51 | # Test 52 | actual = self.sut.get_header() 53 | 54 | # Assert 55 | expected = 0xfc 56 | self.assertEqual(expected, actual) 57 | 58 | def test_that_channel_is_truncated_in_header(self): 59 | # Fixture 60 | channel = 0xff 61 | self.sut.set_header(0, channel) 62 | 63 | # Test 64 | actual = self.sut.get_header() 65 | 66 | # Assert 67 | expected = 0x0f 68 | self.assertEqual(expected, actual) 69 | 70 | def test_that_port_and_channel_is_encoded_in_header_when_set_separat(self): 71 | # Fixture 72 | self.sut.port = 2 73 | self.sut.channel = 1 74 | 75 | # Test 76 | actual = self.sut.get_header() 77 | 78 | # Assert 79 | expected = 0x2d 80 | self.assertEqual(expected, actual) 81 | 82 | def test_that_default_header_is_set_when_constructed(self): 83 | # Fixture 84 | 85 | # Test 86 | actual = self.sut.get_header() 87 | 88 | # Assert 89 | expected = 0x0c 90 | self.assertEqual(expected, actual) 91 | 92 | def test_that_header_is_set_when_constructed(self): 93 | # Fixture 94 | sut = CRTPPacket(header=0x21) 95 | 96 | # Test 97 | actual = sut.get_header() 98 | 99 | # Assert 100 | self.assertEqual(0x2d, actual) 101 | self.assertEqual(2, sut.port) 102 | self.assertEqual(1, sut.channel) 103 | -------------------------------------------------------------------------------- /test/localization/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcraze/crazyflie-lib-python/6a427318a0a2a2c3f231861a087d49d04db211b2/test/localization/__init__.py -------------------------------------------------------------------------------- /test/localization/fixtures/parameters.yaml: -------------------------------------------------------------------------------- 1 | params: 2 | activeMarker.back: 3 | default_value: 3 4 | is_stored: true 5 | stored_value: 10 6 | cppm.angPitch: 7 | default_value: 50.0 8 | is_stored: true 9 | stored_value: 55.0 10 | ctrlMel.i_range_z: 11 | default_value: 0.4000000059604645 12 | is_stored: true 13 | stored_value: 0.44999998807907104 14 | type: persistent_param_state 15 | version: '1' 16 | -------------------------------------------------------------------------------- /test/localization/fixtures/system_config.yaml: -------------------------------------------------------------------------------- 1 | calibs: 2 | 0: 3 | sweeps: 4 | - curve: 3.0 5 | gibmag: 4.0 6 | gibphase: 5.0 7 | ogeemag: 6.0 8 | ogeephase: 7.0 9 | phase: 1.0 10 | tilt: 2.0 11 | - curve: 3.1 12 | gibmag: 4.1 13 | gibphase: 5.1 14 | ogeemag: 6.1 15 | ogeephase: 7.1 16 | phase: 1.1 17 | tilt: 2.1 18 | uid: 1234 19 | 1: 20 | sweeps: 21 | - curve: 3.5 22 | gibmag: 4.5 23 | gibphase: 5.5 24 | ogeemag: 6.5 25 | ogeephase: 7.5 26 | phase: 1.5 27 | tilt: 2.5 28 | - curve: 3.51 29 | gibmag: 4.51 30 | gibphase: 5.51 31 | ogeemag: 6.51 32 | ogeephase: 7.51 33 | phase: 1.51 34 | tilt: 2.51 35 | uid: 9876 36 | geos: 37 | 0: 38 | origin: 39 | - 1.0 40 | - 2.0 41 | - 3.0 42 | rotation: 43 | - - 4.0 44 | - 5.0 45 | - 6.0 46 | - - 7.0 47 | - 8.0 48 | - 9.0 49 | - - 10.0 50 | - 11.0 51 | - 12.0 52 | 1: 53 | origin: 54 | - 21.0 55 | - 22.0 56 | - 23.0 57 | rotation: 58 | - - 24.0 59 | - 25.0 60 | - 26.0 61 | - - 27.0 62 | - 28.0 63 | - 29.0 64 | - - 30.0 65 | - 31.0 66 | - 32.0 67 | systemType: 1 68 | type: lighthouse_system_configuration 69 | version: '1' 70 | -------------------------------------------------------------------------------- /test/localization/lighthouse_fixtures.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ,---------, ____ _ __ 4 | # | ,-^-, | / __ )(_) /_______________ _____ ___ 5 | # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2022 Bitcraze AB 10 | # 11 | # This program is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, in version 3. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import numpy as np 23 | 24 | from cflib.localization.lighthouse_bs_vector import LighthouseBsVector 25 | from cflib.localization.lighthouse_bs_vector import LighthouseBsVectors 26 | from cflib.localization.lighthouse_types import LhDeck4SensorPositions 27 | from cflib.localization.lighthouse_types import Pose 28 | 29 | 30 | class LighthouseFixtures: 31 | """ 32 | Fixtures to be used in lighthouse unit tests 33 | """ 34 | 35 | # Stock objects 36 | # BS0 is pointing along the X-axis 37 | BS0_POSE = Pose(t_vec=(-2.0, 1.0, 3.0)) 38 | # BS1 is pointing along the Y-axis 39 | BS1_POSE = Pose.from_rot_vec(R_vec=(0.0, 0.0, np.pi / 2), t_vec=(0.0, -2.0, 3.0)) 40 | # BS2 is pointing along the negative Y-axis 41 | BS2_POSE = Pose.from_rot_vec(R_vec=(0.0, 0.0, -np.pi / 2), t_vec=(0.0, 2.0, 3.0)) 42 | # BS3 is pointing along the negative X-axis 43 | BS3_POSE = Pose.from_rot_vec(R_vec=(0.0, 0.0, np.pi), t_vec=(2.0, 0.0, 2.0)) 44 | 45 | # CF_ORIGIN is in the origin, pointing along the X-axis 46 | CF_ORIGIN_POSE = Pose() 47 | 48 | # CF1 is pointing along the X-axis 49 | CF1_POSE = Pose(t_vec=(0.3, 0.2, 0.1)) 50 | 51 | # CF2 is pointing along the Y-axis 52 | CF2_POSE = Pose.from_rot_vec(R_vec=(0.0, 0.0, np.pi / 2), t_vec=(1.0, 0.0, 0.0)) 53 | 54 | def __init__(self) -> None: 55 | self.angles_cf_origin_bs0 = self.synthesize_angles(self.CF_ORIGIN_POSE, self.BS0_POSE) 56 | self.angles_cf_origin_bs1 = self.synthesize_angles(self.CF_ORIGIN_POSE, self.BS1_POSE) 57 | 58 | self.angles_cf1_bs1 = self.synthesize_angles(self.CF1_POSE, self.BS1_POSE) 59 | self.angles_cf1_bs2 = self.synthesize_angles(self.CF1_POSE, self.BS2_POSE) 60 | 61 | self.angles_cf2_bs0 = self.synthesize_angles(self.CF2_POSE, self.BS0_POSE) 62 | self.angles_cf2_bs1 = self.synthesize_angles(self.CF2_POSE, self.BS1_POSE) 63 | self.angles_cf2_bs2 = self.synthesize_angles(self.CF2_POSE, self.BS2_POSE) 64 | self.angles_cf2_bs3 = self.synthesize_angles(self.CF2_POSE, self.BS3_POSE) 65 | 66 | def synthesize_angles(self, pose_cf: Pose, pose_bs: Pose) -> LighthouseBsVectors: 67 | """ 68 | Genereate a LighthouseBsVectors object based 69 | """ 70 | 71 | result = LighthouseBsVectors() 72 | for sens_pos_ref_cf in LhDeck4SensorPositions.positions: 73 | sens_pos_ref_global = pose_cf.rotate_translate(sens_pos_ref_cf) 74 | sens_pos_ref_bs = pose_bs.inv_rotate_translate(sens_pos_ref_global) 75 | result.append(LighthouseBsVector.from_cart(sens_pos_ref_bs)) 76 | return result 77 | -------------------------------------------------------------------------------- /test/localization/lighthouse_test_base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ,---------, ____ _ __ 4 | # | ,-^-, | / __ )(_) /_______________ _____ ___ 5 | # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2022 Bitcraze AB 10 | # 11 | # This program is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, in version 3. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import unittest 23 | 24 | import numpy as np 25 | import numpy.typing as npt 26 | from scipy.spatial.transform import Rotation 27 | 28 | from cflib.localization.lighthouse_types import Pose 29 | 30 | 31 | class LighthouseTestBase(unittest.TestCase): 32 | """ 33 | Utilitis to simplify testing of lighthouse code 34 | """ 35 | 36 | def assertVectorsAlmostEqual(self, expected: npt.ArrayLike, actual: npt.ArrayLike, places: int = 5) -> None: 37 | _expected = np.array(expected) 38 | _actual = np.array(actual) 39 | 40 | self.assertEqual(_expected.shape[0], _actual.shape[0], 'Shape differs') 41 | 42 | for i in range(_expected.shape[0]): 43 | self.assertAlmostEqual(_expected[i], _actual[i], places, f'Lists differs in element {i}') 44 | 45 | def assertPosesAlmostEqual(self, expected: Pose, actual: Pose, places: int = 5): 46 | translation_diff = expected.translation - actual.translation 47 | self.assertAlmostEqual(0.0, np.linalg.norm(translation_diff), places, 48 | f'Translation different, expected: {expected.translation}, actual: {actual.translation}') 49 | 50 | _expected_rot = Rotation.from_rotvec(expected.rot_vec) 51 | _actual_rot = Rotation.from_rotvec(actual.rot_vec) 52 | 53 | # Compute the rotation needed to go from expected to actual. 54 | # This avoids sign ambiguity in rotvecs (e.g., π vs. -π) by comparing actual orientation difference. 55 | _relative_rot = _expected_rot.inv() * _actual_rot 56 | 57 | angle_diff = _relative_rot.magnitude() 58 | self.assertAlmostEqual(0.0, angle_diff, places, 59 | f'Rotation different, expected: {expected.rot_vec}, actual: {actual.rot_vec}') 60 | -------------------------------------------------------------------------------- /test/localization/test_ippe_cf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ,---------, ____ _ __ 4 | # | ,-^-, | / __ )(_) /_______________ _____ ___ 5 | # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2022 Bitcraze AB 10 | # 11 | # This program is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, in version 3. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | from test.localization.lighthouse_fixtures import LighthouseFixtures 23 | from test.localization.lighthouse_test_base import LighthouseTestBase 24 | 25 | import numpy as np 26 | 27 | from cflib.localization.ippe_cf import IppeCf 28 | from cflib.localization.lighthouse_types import LhDeck4SensorPositions 29 | from cflib.localization.lighthouse_types import Pose 30 | 31 | 32 | class TestIppeCf(LighthouseTestBase): 33 | def setUp(self): 34 | self.fixtures = LighthouseFixtures() 35 | 36 | def test_that_pose_is_found(self): 37 | # Fixture 38 | pose_bs = Pose(t_vec=(0.0, 0.0, 0.0)) 39 | pose_cf = Pose(t_vec=(1.0, 0.0, -1.0)) 40 | 41 | U = LhDeck4SensorPositions.positions 42 | Q = self.fixtures.synthesize_angles(pose_cf, pose_bs).projection_pair_list() 43 | 44 | # The CF pose seen from the base station 45 | expected_0 = pose_cf 46 | 47 | # Not sure if (why) this is the expected mirror solution 48 | expected_1 = Pose.from_rot_vec(R_vec=(0.0, -np.pi / 2, 0.0), t_vec=pose_cf.translation) 49 | 50 | # Test 51 | actual = IppeCf.solve(U, Q) 52 | 53 | # Assert 54 | actual_pose_0 = Pose(actual[0].R, actual[0].t) 55 | self.assertPosesAlmostEqual(expected_0, actual_pose_0, places=3) 56 | 57 | actual_pose_1 = Pose(actual[1].R, actual[1].t) 58 | self.assertPosesAlmostEqual(expected_1, actual_pose_1, places=3) 59 | -------------------------------------------------------------------------------- /test/localization/test_lighthouse_bs_geo.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ,---------, ____ _ __ 4 | # | ,-^-, | / __ )(_) /_______________ _____ ___ 5 | # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2021 Bitcraze AB 10 | # 11 | # This program is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, in version 3. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import unittest 23 | 24 | from cflib.localization import LighthouseBsGeoEstimator 25 | 26 | 27 | class TestLighthouseBsGeoEstimator(unittest.TestCase): 28 | def setUp(self): 29 | 30 | self.sut = LighthouseBsGeoEstimator() 31 | 32 | def test_that_sanity_check_finds_coordinate_out_of_bounds(self): 33 | # Fixture 34 | pos_bs_in_cf_coord = [0, -20, 0] 35 | 36 | # Test 37 | actual = self.sut.sanity_check_result(pos_bs_in_cf_coord) 38 | 39 | # Assert 40 | self.assertFalse(actual) 41 | 42 | def test_that_sanity_check_passes_ok_position(self): 43 | # Fixture 44 | pos_bs_in_cf_coord = [0, 1, 2] 45 | 46 | # Test 47 | actual = self.sut.sanity_check_result(pos_bs_in_cf_coord) 48 | 49 | # Assert 50 | self.assertTrue(actual) 51 | -------------------------------------------------------------------------------- /test/localization/test_lighthouse_sample_matcher.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ,---------, ____ _ __ 4 | # | ,-^-, | / __ )(_) /_______________ _____ ___ 5 | # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2021 Bitcraze AB 10 | # 11 | # This program is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, in version 3. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import unittest 23 | 24 | from cflib.localization.lighthouse_bs_vector import LighthouseBsVector 25 | from cflib.localization.lighthouse_sample_matcher import LighthouseSampleMatcher 26 | from cflib.localization.lighthouse_types import LhMeasurement 27 | 28 | 29 | class TestLighthouseSampleMatcher(unittest.TestCase): 30 | def setUp(self): 31 | self.vec0 = LighthouseBsVector(0.0, 0.0) 32 | self.vec1 = LighthouseBsVector(0.1, 0.1) 33 | self.vec2 = LighthouseBsVector(0.2, 0.2) 34 | self.vec3 = LighthouseBsVector(0.3, 0.3) 35 | 36 | self.samples = [ 37 | LhMeasurement(timestamp=1.000, base_station_id=0, angles=self.vec0), 38 | LhMeasurement(timestamp=1.015, base_station_id=1, angles=self.vec1), 39 | LhMeasurement(timestamp=1.020, base_station_id=0, angles=self.vec2), 40 | LhMeasurement(timestamp=1.035, base_station_id=1, angles=self.vec3), 41 | ] 42 | 43 | def test_that_samples_are_aggregated(self): 44 | # Fixture 45 | 46 | # Test 47 | actual = LighthouseSampleMatcher.match(self.samples, max_time_diff=0.010) 48 | 49 | # Assert 50 | self.assertEqual(1.000, actual[0].timestamp) 51 | self.assertEqual(1, len(actual[0].angles_calibrated)) 52 | self.assertEqual(self.vec0, actual[0].angles_calibrated[0]) 53 | 54 | self.assertEqual(1.015, actual[1].timestamp) 55 | self.assertEqual(2, len(actual[1].angles_calibrated)) 56 | self.assertEqual(self.vec1, actual[1].angles_calibrated[1]) 57 | self.assertEqual(self.vec2, actual[1].angles_calibrated[0]) 58 | 59 | self.assertEqual(1.035, actual[2].timestamp) 60 | self.assertEqual(1, len(actual[2].angles_calibrated)) 61 | self.assertEqual(self.vec3, actual[2].angles_calibrated[1]) 62 | 63 | def test_that_single_bs_samples_are_fitered_out(self): 64 | # Fixture 65 | 66 | # Test 67 | actual = LighthouseSampleMatcher.match(self.samples, max_time_diff=0.010, min_nr_of_bs_in_match=2) 68 | 69 | # Assert 70 | self.assertEqual(1.015, actual[0].timestamp) 71 | self.assertEqual(2, len(actual[0].angles_calibrated)) 72 | self.assertEqual(self.vec1, actual[0].angles_calibrated[1]) 73 | self.assertEqual(self.vec2, actual[0].angles_calibrated[0]) 74 | 75 | def test_that_empty_sample_list_works(self): 76 | # Fixture 77 | 78 | # Test 79 | actual = LighthouseSampleMatcher.match([]) 80 | 81 | # Assert 82 | self.assertEqual(0, len(actual)) 83 | -------------------------------------------------------------------------------- /test/localization/test_lighthouse_types.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ,---------, ____ _ __ 4 | # | ,-^-, | / __ )(_) /_______________ _____ ___ 5 | # | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2022 Bitcraze AB 10 | # 11 | # This program is free software: you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation, in version 3. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | from test.localization.lighthouse_test_base import LighthouseTestBase 23 | 24 | import numpy as np 25 | 26 | from cflib.localization.lighthouse_types import Pose 27 | 28 | 29 | class TestLighthouseTypes(LighthouseTestBase): 30 | def setUp(self): 31 | pass 32 | 33 | def test_default_matrix_constructor(self): 34 | # Fixture 35 | # Test 36 | actual = Pose() 37 | 38 | # Assert 39 | self.assertEqual(0.0, np.linalg.norm(actual.translation)) 40 | self.assertEqual(0.0, np.linalg.norm(actual.rot_matrix - np.identity(3))) 41 | 42 | def test_default_rot_vec_constructor(self): 43 | # Fixture 44 | # Test 45 | actual = Pose.from_rot_vec() 46 | 47 | # Assert 48 | self.assertEqual(0.0, np.linalg.norm(actual.translation)) 49 | self.assertEqual(0.0, np.linalg.norm(actual.rot_matrix - np.identity(3))) 50 | 51 | def test_rotate_translate(self): 52 | # Fixture 53 | transform = Pose.from_rot_vec(R_vec=(0.0, 0.0, np.pi / 2), t_vec=(1.0, 0.0, 0.0)) 54 | point = (2.0, 0.0, 0.0) 55 | 56 | # Test 57 | actual = transform.rotate_translate(point) 58 | 59 | # Assert 60 | self.assertAlmostEqual(1.0, actual[0]) 61 | self.assertAlmostEqual(2.0, actual[1]) 62 | self.assertAlmostEqual(0.0, actual[2]) 63 | 64 | def test_rotate_translate_and_back(self): 65 | # Fixture 66 | transform = Pose.from_rot_vec(R_vec=(1.0, 2.0, 3.0), t_vec=(0.1, 0.2, 0.3)) 67 | expected = (2.0, 3.0, 4.0) 68 | 69 | # Test 70 | actual = transform.inv_rotate_translate(transform.rotate_translate(expected)) 71 | 72 | # Assert 73 | self.assertAlmostEqual(expected[0], actual[0]) 74 | self.assertAlmostEqual(expected[1], actual[1]) 75 | self.assertAlmostEqual(expected[2], actual[2]) 76 | 77 | def test_rotate_translate_pose(self): 78 | # Fixture 79 | transform = Pose.from_rot_vec(R_vec=(0.0, 0.0, np.pi / 2), t_vec=(1.0, 0.0, 0.0)) 80 | pose = Pose(t_vec=(2.0, 0.0, 0.0)) 81 | expected = Pose.from_rot_vec(R_vec=(0.0, 0.0, np.pi / 2), t_vec=(1.0, 2.0, 0.0)) 82 | 83 | # Test 84 | actual = transform.rotate_translate_pose(pose) 85 | 86 | # Assert 87 | self.assertPosesAlmostEqual(expected, actual) 88 | 89 | def test_rotate_translate_pose_and_back(self): 90 | # Fixture 91 | transform = Pose.from_rot_vec(R_vec=(1.0, 2.0, 3.0), t_vec=(0.1, 0.2, 0.3)) 92 | expected = Pose(t_vec=(2.0, 3.0, 4.0)) 93 | 94 | # Test 95 | actual = transform.inv_rotate_translate_pose(transform.rotate_translate_pose(expected)) 96 | 97 | # Assert 98 | self.assertPosesAlmostEqual(expected, actual) 99 | -------------------------------------------------------------------------------- /test/positioning/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcraze/crazyflie-lib-python/6a427318a0a2a2c3f231861a087d49d04db211b2/test/positioning/__init__.py -------------------------------------------------------------------------------- /test/support/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcraze/crazyflie-lib-python/6a427318a0a2a2c3f231861a087d49d04db211b2/test/support/__init__.py -------------------------------------------------------------------------------- /test/support/asyncCallbackCaller.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2016 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import time 23 | from threading import Thread 24 | 25 | 26 | class AsyncCallbackCaller: 27 | 28 | def __init__(self, cb=None, delay=0, args=(), kwargs={}): 29 | self.caller = cb 30 | self.delay = delay 31 | self.args = args 32 | self.kwargs = kwargs 33 | self.call_count = 0 34 | 35 | def trigger(self, *args, **kwargs): 36 | self.call_count += 1 37 | Thread(target=self._make_call).start() 38 | 39 | def call_and_wait(self): 40 | thread = Thread(target=self._make_call) 41 | thread.start() 42 | thread.join() 43 | 44 | def _make_call(self): 45 | time.sleep(self.delay) 46 | self.caller.call(*self.args, **self.kwargs) 47 | -------------------------------------------------------------------------------- /test/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcraze/crazyflie-lib-python/6a427318a0a2a2c3f231861a087d49d04db211b2/test/utils/__init__.py -------------------------------------------------------------------------------- /test/utils/fixtures/five_params.yaml: -------------------------------------------------------------------------------- 1 | params: 2 | activeMarker.back: 3 | default_value: 3 4 | is_stored: true 5 | stored_value: 10 6 | activeMarker.front: 7 | default_value: 3 8 | is_stored: true 9 | stored_value: 10 10 | activeMarker.left: 11 | default_value: 3 12 | is_stored: true 13 | stored_value: 10 14 | cppm.angPitch: 15 | default_value: 50.0 16 | is_stored: true 17 | stored_value: 55.0 18 | ctrlMel.i_range_z: 19 | default_value: 0.4000000059604645 20 | is_stored: true 21 | stored_value: 0.44999998807907104 22 | type: persistent_param_state 23 | version: '1' 24 | -------------------------------------------------------------------------------- /test/utils/fixtures/single_param.yaml: -------------------------------------------------------------------------------- 1 | params: 2 | activeMarker.back: 3 | default_value: 3 4 | is_stored: true 5 | stored_value: 10 6 | type: persistent_param_state 7 | version: '1' 8 | -------------------------------------------------------------------------------- /test/utils/test_callbacks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) Bitcraze AB 10 | # 11 | # Crazyflie Nano Quadcopter Client 12 | # 13 | # This program is free software; you can redistribute it and/or 14 | # modify it under the terms of the GNU General Public License 15 | # as published by the Free Software Foundation; either version 2 16 | # of the License, or (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | # You should have received a copy of the GNU General Public License 23 | # along with this program. If not, see . 24 | import unittest 25 | 26 | from cflib.utils.callbacks import Caller 27 | 28 | 29 | class CallerTest(unittest.TestCase): 30 | 31 | def setUp(self): 32 | self.callback_count = 0 33 | self.sut = Caller() 34 | 35 | def test_that_callback_is_added(self): 36 | # Fixture 37 | 38 | # Test 39 | self.sut.add_callback(self._callback) 40 | 41 | # Assert 42 | self.sut.call() 43 | self.assertEqual(1, self.callback_count) 44 | 45 | def test_that_callback_is_added_only_one_time(self): 46 | # Fixture 47 | 48 | # Test 49 | self.sut.add_callback(self._callback) 50 | self.sut.add_callback(self._callback) 51 | 52 | # Assert 53 | self.sut.call() 54 | self.assertEqual(1, self.callback_count) 55 | 56 | def test_that_multiple_callbacks_are_added(self): 57 | # Fixture 58 | 59 | # Test 60 | self.sut.add_callback(self._callback) 61 | self.sut.add_callback(self._callback2) 62 | 63 | # Assert 64 | self.sut.call() 65 | self.assertEqual(2, self.callback_count) 66 | 67 | def test_that_callback_is_removed(self): 68 | # Fixture 69 | self.sut.add_callback(self._callback) 70 | 71 | # Test 72 | self.sut.remove_callback(self._callback) 73 | 74 | # Assert 75 | self.sut.call() 76 | self.assertEqual(0, self.callback_count) 77 | 78 | def test_that_callback_is_called_with_arguments(self): 79 | # Fixture 80 | self.sut.add_callback(self._callback_with_args) 81 | 82 | # Test 83 | self.sut.call('The token') 84 | 85 | # Assert 86 | self.assertEqual('The token', self.callback_token) 87 | 88 | def _callback(self): 89 | self.callback_count += 1 90 | 91 | def _callback2(self): 92 | self.callback_count += 1 93 | 94 | def _callback_with_args(self, token): 95 | self.callback_token = token 96 | -------------------------------------------------------------------------------- /test/utils/test_encoding.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # || ____ _ __ 4 | # +------+ / __ )(_) /_______________ _____ ___ 5 | # | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ 6 | # +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ 7 | # || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ 8 | # 9 | # Copyright (C) 2023 Bitcraze AB 10 | # 11 | # This program is free software; you can redistribute it and/or 12 | # modify it under the terms of the GNU General Public License 13 | # as published by the Free Software Foundation; either version 2 14 | # of the License, or (at your option) any later version. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | import unittest 23 | 24 | import numpy as np 25 | 26 | from cflib.utils.encoding import compress_quaternion 27 | from cflib.utils.encoding import decompress_quaternion 28 | 29 | 30 | class EncodingTest(unittest.TestCase): 31 | 32 | def test_compress_decompress(self): 33 | # Fixture 34 | expected = self._normalize_quat([1, 2, 3, 4]) 35 | 36 | # Test 37 | compressed = compress_quaternion(expected) 38 | actual = decompress_quaternion(compressed) 39 | 40 | # Assert 41 | np.testing.assert_allclose(actual, expected, 0.001) 42 | 43 | def test_compress_decompress_not_normalized(self): 44 | # Fixture 45 | quat = [1, 2, 3, 4] 46 | expected = self._normalize_quat(quat) 47 | 48 | # Test 49 | compressed = compress_quaternion(quat) 50 | actual = decompress_quaternion(compressed) 51 | 52 | # Assert 53 | np.testing.assert_allclose(actual, expected, 0.001) 54 | 55 | def test_other_largest_component(self): 56 | # Fixture 57 | quat = [5, 10, 3, 4] 58 | expected = self._normalize_quat(quat) 59 | 60 | # Test 61 | compressed = compress_quaternion(quat) 62 | actual = decompress_quaternion(compressed) 63 | 64 | # Assert 65 | np.testing.assert_allclose(actual, expected, 0.001) 66 | 67 | def _normalize_quat(self, quat): 68 | quat = np.array(quat) 69 | return quat / np.linalg.norm(quat) 70 | -------------------------------------------------------------------------------- /tools/build-docs/build-docs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | scriptDir=$(dirname $0) 6 | cflibDir=$scriptDir/../../cflib 7 | apiRefDir=$scriptDir/../../docs/api 8 | templateDir=$apiRefDir/template 9 | tempDir=$scriptDir/../../.tmp 10 | 11 | mkdir -p $apiRefDir 12 | mkdir -p $tempDir 13 | 14 | [ -n "$API_REF_BASE" ] || { 15 | export API_REF_BASE=$(readlink -fn $tempDir) 16 | } 17 | 18 | # 19 | # If cflib is not installed (mostly its dependencies) then pdoc3 cannot 20 | # traverse the library and get docstrings. 21 | # 22 | # The rewriting of HOME is a hack to make this work in a bare-bone Docker 23 | # environment. 24 | # 25 | 26 | # Make a temporary install in the temp dir 27 | HOME=$tempDir pip3 install --upgrade pip 28 | HOME=$tempDir pip3 install -e $scriptDir/../../ 29 | 30 | # Generate documentation 31 | HOME=$tempDir pdoc3 --force $cflibDir --output-dir $tempDir --template-dir $templateDir 32 | 33 | # Modify the generated content to fit our file tree 34 | ## Rename root file that will be included in another md file 35 | rootFile=$tempDir/cflib/index.md_raw 36 | mv $tempDir/cflib/index.md $rootFile 37 | ## Remove the front matter at the top 38 | sed -i '1,4d' $rootFile 39 | ## Links are not processed properly in the included content. Hack it by removing index.md 40 | sed -i s/index\.md// $rootFile 41 | 42 | # Copy to the md file tree 43 | cp -r $tempDir/cflib/* $apiRefDir/cflib/. 44 | 45 | # Clean up 46 | rm -r $tempDir 47 | -------------------------------------------------------------------------------- /tools/build/bdist: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import os.path as _path 4 | import subprocess 5 | 6 | __author__ = 'arnaud' 7 | 8 | try: 9 | script_dir = os.path.dirname(os.path.realpath(__file__)) 10 | root = _path.normpath(_path.join(script_dir, '../..')) 11 | 12 | subprocess.check_call(['python3', '-m', 'build', '--wheel'], cwd=root) 13 | 14 | print('Wheel built') 15 | except subprocess.CalledProcessError as e: 16 | print('Error: Cannot build the wheel') 17 | raise e 18 | -------------------------------------------------------------------------------- /tools/build/build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import subprocess 4 | 5 | script_dir = os.path.dirname(os.path.realpath(__file__)) 6 | 7 | subprocess.check_call([script_dir + '/verify']) 8 | subprocess.check_call([script_dir + '/test']) 9 | subprocess.check_call([script_dir + '/bdist']) 10 | -------------------------------------------------------------------------------- /tools/build/sys-test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import os.path as _path 4 | import subprocess 5 | 6 | try: 7 | script_dir = os.path.dirname(os.path.realpath(__file__)) 8 | root = _path.normpath(_path.join(script_dir, '../../sys_test')) 9 | 10 | print('**** Running sys tests ****') 11 | subprocess.check_call(['python3', '-m', 'unittest', 'discover', root, '-v']) 12 | 13 | print('Unit tests pass') 14 | except subprocess.CalledProcessError as e: 15 | print('Error: Unit tests fail') 16 | raise e 17 | -------------------------------------------------------------------------------- /tools/build/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import os.path as _path 4 | import subprocess 5 | 6 | try: 7 | script_dir = os.path.dirname(os.path.realpath(__file__)) 8 | root = _path.normpath(_path.join(script_dir, '../../test')) 9 | 10 | print('') 11 | print('**** Running tests in python3 ****') 12 | subprocess.check_call(['python3', '-m', 'unittest', 'discover', root]) 13 | 14 | print('Unit tests pass') 15 | except subprocess.CalledProcessError as e: 16 | print('Error: Unit tests fail') 17 | raise e 18 | -------------------------------------------------------------------------------- /tools/build/verify: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import subprocess 3 | 4 | try: 5 | subprocess.check_call(['pre-commit', 'run', '--all-files']) 6 | print('verify pass') 7 | except subprocess.CalledProcessError as e: 8 | print('Error: verify fail') 9 | raise e 10 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | project = cflib 3 | envlist = py27,py34 4 | 5 | [testenv] 6 | deps = -rrequirements-dev.txt 7 | commands= 8 | coverage erase 9 | coverage run -m unittest discover ./test 10 | coverage report --show-missing 11 | pre-commit run --all-files 12 | 13 | 14 | [testenv:venv] 15 | envdir = venv-{[tox]project} 16 | commands = 17 | 18 | [flake8] 19 | max-line-length:120 20 | --------------------------------------------------------------------------------