Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python bindings draft #863

Merged
merged 9 commits into from
Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ tools/make/config.mk
cflie.*
version.c
tags
*cffirmware*.so
cffirmware.py

/cf2.*
/tag.*
Expand All @@ -21,6 +23,7 @@ current_platform.mk

/generated/**
**/__pycache__/**
**/*.pyc

/docs/.jekyll-metadata
docs/.jekyll-cache
Expand Down
16 changes: 15 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -487,4 +487,18 @@ unit:
# The flag "-DUNITY_INCLUDE_DOUBLE" allows comparison of double values in Unity. See: https://stackoverflow.com/a/37790196
rake unit "DEFINES=$(CFLAGS) -DUNITY_INCLUDE_DOUBLE" "FILES=$(FILES)" "UNIT_TEST_STYLE=$(UNIT_TEST_STYLE)"

.PHONY: all clean build compile unit prep erase flash check_submodules trace openocd gdb halt reset flash_dfu flash_verify cload size print_version clean_version
# Python bindings
MOD_INC = $(CRAZYFLIE_BASE)/src/modules/interface
MOD_SRC = $(CRAZYFLIE_BASE)/src/modules/src

bindings_python: bindings/setup.py bin/cffirmware_wrap.c $(MOD_SRC)/*.c
$(PYTHON) bindings/setup.py build_ext --inplace

bin/cffirmware_wrap.c cffirmware.py: bindings/cffirmware.i $(MOD_INC)/*.h
swig -python -I$(MOD_INC) -o bin/cffirmware_wrap.c bindings/cffirmware.i
mv bin/cffirmware.py cffirmware.py

test_python: bindings_python
$(PYTHON) -m pytest test_python

.PHONY: all clean build compile unit prep erase flash check_submodules trace openocd gdb halt reset flash_dfu flash_verify cload size print_version clean_version bindings_python
70 changes: 70 additions & 0 deletions bindings/cffirmware.i
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
%module cffirmware

// ignore GNU specific compiler attributes
#define __attribute__(x)

%{
#define SWIG_FILE_WITH_INIT
#include "math3d.h"
%}

%include "math3d.h"

%inline %{
%}

%pythoncode %{
import numpy as np
%}

#define COPY_CTOR(structname) \
structname(struct structname const *x) { \
struct structname *y = malloc(sizeof(struct structname)); \
*y = *x; \
return y; \
} \
~structname() { \
free($self); \
} \

%extend vec {
COPY_CTOR(vec)

%pythoncode %{
def __repr__(self):
return "({}, {}, {})".format(self.x, self.y, self.z)

def __array__(self):
return np.array([self.x, self.y, self.z])

def __len__(self):
return 3

def __getitem__(self, i):
if 0 <= i and i < 3:
return _cffirmware.vindex(self, i)
else:
raise IndexError("vec index must be in {0, 1, 2}.")

# Unary operator overloads.
def __neg__(self):
return _cffirmware.vneg(self)

# Vector-scalar binary operator overloads.
def __rmul__(self, s):
return _cffirmware.vscl(s, self)

def __div__(self, s):
return self.__truediv__(s)

def __truediv__(self, s):
return _cffirmware.vdiv(self, s)

# Vector-vector binary operator overloads.
def __add__(self, other):
return _cffirmware.vadd(self, other)

def __sub__(self, other):
return _cffirmware.vsub(self, other)
%}
};
37 changes: 37 additions & 0 deletions bindings/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Compiles the cffirmware C extension."""

import distutils.command.build
from distutils.core import setup, Extension
import os

fw_dir = "."
include = [
os.path.join(fw_dir, "src/modules/interface"),
]

modules = [
# list firmware c-files here
]
fw_sources = [os.path.join(fw_dir, "src/modules/src", mod) for mod in modules]

cffirmware = Extension(
"_cffirmware",
include_dirs=include,
sources=fw_sources + ["bin/cffirmware_wrap.c"],
extra_compile_args=[
"-O3",
],
)

# Override build command to specify custom "build" directory
class BuildCommand(distutils.command.build.build):
def initialize_options(self):
distutils.command.build.build.initialize_options(self)
self.build_base = "bin"

setup(
name="cffirmware",
version="1.0",
cmdclass={"build": BuildCommand},
ext_modules=[cffirmware]
)
9 changes: 9 additions & 0 deletions test_python/test_math3d.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env python

import numpy as np
import cffirmware

def test_conversion_to_numpy():
v_cf = cffirmware.mkvec(1, 2, 3)
v_np = np.array(v_cf)
assert np.allclose(v_np, np.array([1,2,3]))
1 change: 1 addition & 0 deletions tools/build/build
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ set -e
scriptDir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )

${scriptDir}/test "${@}"
${scriptDir}/test_python "${@}"
${scriptDir}/make "${@}"
${scriptDir}/check_elf
6 changes: 6 additions & 0 deletions tools/build/test_python
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -e

scriptDir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )

make test_python "${@}"
2 changes: 1 addition & 1 deletion tools/make/targets.mk
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ clean_o: clean_version
@$(if $(QUIET), ,echo $(CLEAN_O_COMMAND$(VERBOSE)) )
@$(CLEAN_O_COMMAND)

CLEAN_COMMAND=rm -f cf*.elf cf*.hex cf*.bin cf*.dfu cf*.map $(BIN)/dep/*.d $(BIN)/*.o
CLEAN_COMMAND=rm -f cf*.elf cf*.hex cf*.bin cf*.dfu cf*.map cf*.py _cf*.so $(BIN)/dep/*.d $(BIN)/*.o $(BIN)/*.c
CLEAN_COMMAND_SILENT=" CLEAN"
clean:
@$(if $(QUIET), ,echo $(CLEAN_COMMAND$(VERBOSE)) )
Expand Down