├── .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 [](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 | 
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 |
--------------------------------------------------------------------------------