burst-link-protocol 0.1.0__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- burst_link_protocol-0.1.0/.github/dependabot.yml +11 -0
- burst_link_protocol-0.1.0/.github/workflows/pip.yml +38 -0
- burst_link_protocol-0.1.0/.github/workflows/wheel.yml +82 -0
- burst_link_protocol-0.1.0/.gitignore +7 -0
- burst_link_protocol-0.1.0/.vscode/c_cpp_properties.json +23 -0
- burst_link_protocol-0.1.0/.vscode/launch.json +25 -0
- burst_link_protocol-0.1.0/.vscode/settings.json +86 -0
- burst_link_protocol-0.1.0/.vscode/tasks.json +20 -0
- burst_link_protocol-0.1.0/CMakeLists.txt +90 -0
- burst_link_protocol-0.1.0/PKG-INFO +18 -0
- burst_link_protocol-0.1.0/README.md +69 -0
- burst_link_protocol-0.1.0/pyproject.toml +56 -0
- burst_link_protocol-0.1.0/src/burst_interface.h +67 -0
- burst_link_protocol-0.1.0/src/burst_link_protocol/__init__.py +4 -0
- burst_link_protocol-0.1.0/src/burst_link_protocol/main.py +41 -0
- burst_link_protocol-0.1.0/src/crc.c +20 -0
- burst_link_protocol-0.1.0/src/crc.h +4 -0
- burst_link_protocol-0.1.0/src/decoder.c +150 -0
- burst_link_protocol-0.1.0/src/encoder.c +91 -0
- burst_link_protocol-0.1.0/src/python_bindings.cpp +80 -0
- burst_link_protocol-0.1.0/test/test_c_validation.py +52 -0
- burst_link_protocol-0.1.0/test/test_python_validation.py +27 -0
- burst_link_protocol-0.1.0/test/test_speed.py +20 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
name: Pip
|
2
|
+
|
3
|
+
on:
|
4
|
+
workflow_dispatch:
|
5
|
+
pull_request:
|
6
|
+
push:
|
7
|
+
branches:
|
8
|
+
- master
|
9
|
+
|
10
|
+
jobs:
|
11
|
+
build:
|
12
|
+
name: Build with Pip
|
13
|
+
runs-on: ${{ matrix.platform }}
|
14
|
+
strategy:
|
15
|
+
fail-fast: false
|
16
|
+
matrix:
|
17
|
+
# platform: [windows-latest, macos-latest, ubuntu-latest]
|
18
|
+
platform: [ ubuntu-latest]
|
19
|
+
python-version: ["3.12"]
|
20
|
+
|
21
|
+
steps:
|
22
|
+
- uses: actions/checkout@v4
|
23
|
+
|
24
|
+
- uses: actions/setup-python@v5
|
25
|
+
with:
|
26
|
+
python-version: ${{ matrix.python-version }}
|
27
|
+
|
28
|
+
- name: Set min macOS version
|
29
|
+
if: runner.os == 'macOS'
|
30
|
+
run: |
|
31
|
+
echo "MACOSX_DEPLOYMENT_TARGET=10.14" >> $GITHUB_ENV
|
32
|
+
|
33
|
+
- name: Build and install
|
34
|
+
run: |
|
35
|
+
pip install --verbose .
|
36
|
+
|
37
|
+
- name: Test
|
38
|
+
run: python -m pytest
|
@@ -0,0 +1,82 @@
|
|
1
|
+
name: Wheels
|
2
|
+
|
3
|
+
on:
|
4
|
+
workflow_dispatch:
|
5
|
+
pull_request:
|
6
|
+
push:
|
7
|
+
branches:
|
8
|
+
- master
|
9
|
+
release:
|
10
|
+
types:
|
11
|
+
- published
|
12
|
+
|
13
|
+
jobs:
|
14
|
+
build_sdist:
|
15
|
+
name: Build SDist
|
16
|
+
runs-on: ubuntu-latest
|
17
|
+
steps:
|
18
|
+
- uses: actions/checkout@v4
|
19
|
+
with:
|
20
|
+
submodules: true
|
21
|
+
|
22
|
+
- name: Build SDist
|
23
|
+
run: pipx run build --sdist
|
24
|
+
|
25
|
+
- name: Check metadata
|
26
|
+
run: pipx run twine check dist/*
|
27
|
+
|
28
|
+
- uses: actions/upload-artifact@v4
|
29
|
+
with:
|
30
|
+
name: dist-sdist
|
31
|
+
path: dist/*.tar.gz
|
32
|
+
|
33
|
+
|
34
|
+
build_wheels:
|
35
|
+
name: Wheels on ${{ matrix.os }}
|
36
|
+
runs-on: ${{ matrix.os }}
|
37
|
+
strategy:
|
38
|
+
fail-fast: false
|
39
|
+
matrix:
|
40
|
+
os: [ubuntu-latest
|
41
|
+
# , macos-13, macos-14, windows-latest
|
42
|
+
]
|
43
|
+
|
44
|
+
steps:
|
45
|
+
- uses: actions/checkout@v4
|
46
|
+
with:
|
47
|
+
submodules: true
|
48
|
+
|
49
|
+
- uses: pypa/cibuildwheel@v2.22
|
50
|
+
# only build 3.12
|
51
|
+
env:
|
52
|
+
# CIBW_BUILD: "cp312-*"
|
53
|
+
CIBW_BUILD: "cp312-manylinux_x86_64"
|
54
|
+
|
55
|
+
- name: Verify clean directory
|
56
|
+
run: git diff --exit-code
|
57
|
+
shell: bash
|
58
|
+
|
59
|
+
- name: Upload wheels
|
60
|
+
uses: actions/upload-artifact@v4
|
61
|
+
with:
|
62
|
+
path: wheelhouse/*.whl
|
63
|
+
name: dist-${{ matrix.os }}
|
64
|
+
|
65
|
+
upload_all:
|
66
|
+
name: Upload if release
|
67
|
+
needs: [build_wheels, build_sdist]
|
68
|
+
runs-on: ubuntu-latest
|
69
|
+
# if: github.event_name == 'release' && github.event.action == 'published'
|
70
|
+
permissions:
|
71
|
+
id-token: write
|
72
|
+
environment: pypi_release
|
73
|
+
|
74
|
+
steps:
|
75
|
+
- uses: actions/setup-python@v5
|
76
|
+
- uses: actions/download-artifact@v4
|
77
|
+
with:
|
78
|
+
path: dist
|
79
|
+
pattern: dist-*
|
80
|
+
merge-multiple: true
|
81
|
+
|
82
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
@@ -0,0 +1,23 @@
|
|
1
|
+
{
|
2
|
+
"configurations": [
|
3
|
+
{
|
4
|
+
"name": "Win32",
|
5
|
+
"includePath": [
|
6
|
+
"${workspaceFolder}/**",
|
7
|
+
"${workspaceFolder}/.venv/Lib/site-packages/nanobind/include/nanobind", //To be fixed
|
8
|
+
"${env:PYTHON_INCLUDE}"
|
9
|
+
],
|
10
|
+
"defines": [
|
11
|
+
"_DEBUG",
|
12
|
+
"UNICODE",
|
13
|
+
"_UNICODE"
|
14
|
+
],
|
15
|
+
"windowsSdkVersion": "10.0.26100.0",
|
16
|
+
"compilerPath": "cl.exe",
|
17
|
+
"cStandard": "c17",
|
18
|
+
"cppStandard": "c++17",
|
19
|
+
"intelliSenseMode": "windows-msvc-x64"
|
20
|
+
}
|
21
|
+
],
|
22
|
+
"version": 4
|
23
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
{
|
2
|
+
"version": "0.2.0",
|
3
|
+
"configurations": [
|
4
|
+
{
|
5
|
+
"name": "Python C++ Debug",
|
6
|
+
"type": "pythoncpp",
|
7
|
+
"request": "launch",
|
8
|
+
"pythonLaunchName": "Python: Current File",
|
9
|
+
"cppAttachName": "(Windows) Attach",
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"name": "(Windows) Attach",
|
13
|
+
"type": "cppvsdbg",
|
14
|
+
"request": "attach",
|
15
|
+
"processId": ""
|
16
|
+
},
|
17
|
+
{
|
18
|
+
"name": "Python: Current File",
|
19
|
+
"type": "debugpy",
|
20
|
+
"request": "launch",
|
21
|
+
"program": "test/test.py",
|
22
|
+
"console": "integratedTerminal"
|
23
|
+
}
|
24
|
+
]
|
25
|
+
}
|
@@ -0,0 +1,86 @@
|
|
1
|
+
{
|
2
|
+
"python-envs.pythonProjects": [],
|
3
|
+
"files.associations": {
|
4
|
+
"algorithm": "cpp",
|
5
|
+
"array": "cpp",
|
6
|
+
"atomic": "cpp",
|
7
|
+
"bit": "cpp",
|
8
|
+
"cctype": "cpp",
|
9
|
+
"charconv": "cpp",
|
10
|
+
"chrono": "cpp",
|
11
|
+
"clocale": "cpp",
|
12
|
+
"cmath": "cpp",
|
13
|
+
"compare": "cpp",
|
14
|
+
"complex": "cpp",
|
15
|
+
"concepts": "cpp",
|
16
|
+
"cstdarg": "cpp",
|
17
|
+
"cstddef": "cpp",
|
18
|
+
"cstdint": "cpp",
|
19
|
+
"cstdio": "cpp",
|
20
|
+
"cstdlib": "cpp",
|
21
|
+
"cstring": "cpp",
|
22
|
+
"ctime": "cpp",
|
23
|
+
"cwchar": "cpp",
|
24
|
+
"exception": "cpp",
|
25
|
+
"filesystem": "cpp",
|
26
|
+
"format": "cpp",
|
27
|
+
"forward_list": "cpp",
|
28
|
+
"functional": "cpp",
|
29
|
+
"initializer_list": "cpp",
|
30
|
+
"iomanip": "cpp",
|
31
|
+
"ios": "cpp",
|
32
|
+
"iosfwd": "cpp",
|
33
|
+
"iostream": "cpp",
|
34
|
+
"istream": "cpp",
|
35
|
+
"iterator": "cpp",
|
36
|
+
"limits": "cpp",
|
37
|
+
"list": "cpp",
|
38
|
+
"locale": "cpp",
|
39
|
+
"map": "cpp",
|
40
|
+
"memory": "cpp",
|
41
|
+
"new": "cpp",
|
42
|
+
"optional": "cpp",
|
43
|
+
"ostream": "cpp",
|
44
|
+
"ratio": "cpp",
|
45
|
+
"set": "cpp",
|
46
|
+
"sstream": "cpp",
|
47
|
+
"stdexcept": "cpp",
|
48
|
+
"stop_token": "cpp",
|
49
|
+
"streambuf": "cpp",
|
50
|
+
"string": "cpp",
|
51
|
+
"system_error": "cpp",
|
52
|
+
"thread": "cpp",
|
53
|
+
"tuple": "cpp",
|
54
|
+
"type_traits": "cpp",
|
55
|
+
"typeinfo": "cpp",
|
56
|
+
"unordered_map": "cpp",
|
57
|
+
"unordered_set": "cpp",
|
58
|
+
"utility": "cpp",
|
59
|
+
"variant": "cpp",
|
60
|
+
"vector": "cpp",
|
61
|
+
"xfacet": "cpp",
|
62
|
+
"xhash": "cpp",
|
63
|
+
"xiosbase": "cpp",
|
64
|
+
"xlocale": "cpp",
|
65
|
+
"xlocbuf": "cpp",
|
66
|
+
"xlocinfo": "cpp",
|
67
|
+
"xlocmes": "cpp",
|
68
|
+
"xlocmon": "cpp",
|
69
|
+
"xlocnum": "cpp",
|
70
|
+
"xloctime": "cpp",
|
71
|
+
"xmemory": "cpp",
|
72
|
+
"xstring": "cpp",
|
73
|
+
"xtr1common": "cpp",
|
74
|
+
"xtree": "cpp",
|
75
|
+
"xutility": "cpp",
|
76
|
+
"crc.h": "c",
|
77
|
+
"decoder.h": "c",
|
78
|
+
"stddef.h": "c",
|
79
|
+
"stdint.h": "c"
|
80
|
+
},
|
81
|
+
"python.testing.pytestArgs": [
|
82
|
+
"test"
|
83
|
+
],
|
84
|
+
"python.testing.unittestEnabled": false,
|
85
|
+
"python.testing.pytestEnabled": true
|
86
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
{
|
2
|
+
"version": "2.0.0",
|
3
|
+
"tasks": [
|
4
|
+
{
|
5
|
+
"type": "shell",
|
6
|
+
"label": "pip install",
|
7
|
+
"command": "pip",
|
8
|
+
"args": [
|
9
|
+
"install",
|
10
|
+
"--no-build-isolation",
|
11
|
+
"-ve",
|
12
|
+
"."
|
13
|
+
],
|
14
|
+
"group": {
|
15
|
+
"kind": "build",
|
16
|
+
"isDefault": true
|
17
|
+
},
|
18
|
+
}
|
19
|
+
]
|
20
|
+
}
|
@@ -0,0 +1,90 @@
|
|
1
|
+
cmake_minimum_required(VERSION 3.15...3.26)
|
2
|
+
|
3
|
+
project(i_think_this_name_does_not_matter LANGUAGES C CXX)
|
4
|
+
|
5
|
+
if (NOT SKBUILD)
|
6
|
+
message(WARNING "\
|
7
|
+
This CMake file is meant to be executed using 'scikit-build'. Running
|
8
|
+
it directly will almost certainly not produce the desired result. If
|
9
|
+
you are a user trying to install this package, please use the command
|
10
|
+
below, which will install all necessary build dependencies, compile
|
11
|
+
the package in an isolated environment, and then install it.
|
12
|
+
=====================================================================
|
13
|
+
$ pip install .
|
14
|
+
=====================================================================
|
15
|
+
If you are a software developer, and this is your own package, then
|
16
|
+
it is usually much more efficient to install the build dependencies
|
17
|
+
in your environment once and use the following command that avoids
|
18
|
+
a costly creation of a new virtual environment at every compilation:
|
19
|
+
=====================================================================
|
20
|
+
$ pip install nanobind scikit-build-core[pyproject]
|
21
|
+
$ pip install --no-build-isolation -ve .
|
22
|
+
=====================================================================
|
23
|
+
You may optionally add -Ceditable.rebuild=true to auto-rebuild when
|
24
|
+
the package is imported. Otherwise, you need to re-run the above
|
25
|
+
after editing C++ files.")
|
26
|
+
endif()
|
27
|
+
|
28
|
+
# Try to import all Python components potentially needed by nanobind
|
29
|
+
find_package(Python 3.8
|
30
|
+
REQUIRED COMPONENTS Interpreter Development.Module
|
31
|
+
OPTIONAL_COMPONENTS Development.SABIModule)
|
32
|
+
|
33
|
+
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
34
|
+
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
|
35
|
+
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
|
36
|
+
endif()
|
37
|
+
|
38
|
+
option(COVERAGE "Enable coverage reporting" ON)
|
39
|
+
if(COVERAGE)
|
40
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -fprofile-arcs -ftest-coverage")
|
41
|
+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g -fprofile-arcs -ftest-coverage")
|
42
|
+
endif()
|
43
|
+
|
44
|
+
execute_process(
|
45
|
+
COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
|
46
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE NB_DIR)
|
47
|
+
list(APPEND CMAKE_PREFIX_PATH "${NB_DIR}")
|
48
|
+
|
49
|
+
# Import nanobind through CMake's find_package mechanism
|
50
|
+
find_package(nanobind CONFIG REQUIRED)
|
51
|
+
|
52
|
+
# We are now ready to compile the actual extension module
|
53
|
+
nanobind_add_module(
|
54
|
+
# Name of the extension
|
55
|
+
burst_interface_c
|
56
|
+
|
57
|
+
# Target the stable ABI for Python 3.12+, which reduces
|
58
|
+
# the number of binary wheels that must be built. This
|
59
|
+
# does nothing on older Python versions
|
60
|
+
STABLE_ABI
|
61
|
+
|
62
|
+
# Build libnanobind statically and merge it into the
|
63
|
+
# extension (which itself remains a shared library)
|
64
|
+
#
|
65
|
+
# If your project builds multiple extensions, you can
|
66
|
+
# replace this flag by NB_SHARED to conserve space by
|
67
|
+
# reusing a shared libnanobind across libraries
|
68
|
+
NB_STATIC
|
69
|
+
|
70
|
+
# Source code goes here
|
71
|
+
src/decoder.c
|
72
|
+
src/encoder.c
|
73
|
+
src/python_bindings.cpp
|
74
|
+
src/crc.c
|
75
|
+
)
|
76
|
+
|
77
|
+
nanobind_add_stub(
|
78
|
+
|
79
|
+
burst_interface_c_stub
|
80
|
+
INSTALL_TIME
|
81
|
+
MODULE burst_interface_c
|
82
|
+
OUTPUT burst_interface_c.pyi
|
83
|
+
PYTHON_PATH $<TARGET_FILE_DIR:burst_interface_c>
|
84
|
+
DEPENDS burst_interface_c
|
85
|
+
)
|
86
|
+
|
87
|
+
target_include_directories(burst_interface_c PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
88
|
+
|
89
|
+
# Install directive for scikit-build-core
|
90
|
+
install(TARGETS burst_interface_c LIBRARY DESTINATION ".")
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: burst-link-protocol
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: Binary Utility for Reliable Stream Transfer (BURST) is a library for encoding and decoding binary data streams into and from a byte stream.
|
5
|
+
Author-Email: Floris vernieuwe <floris@vernieuwe.eu>
|
6
|
+
Requires-Python: <4.0,>=3.10
|
7
|
+
Requires-Dist: cobs<2.0.0,>=1.2.1
|
8
|
+
Requires-Dist: numpy<3.0.0,>=2.2.3
|
9
|
+
Requires-Dist: crc<8.0.0,>=7.1.0
|
10
|
+
Requires-Dist: pytest<9.0.0,>=8.3.4
|
11
|
+
Requires-Dist: pytest-cov<7.0.0,>=6.0.0
|
12
|
+
Requires-Dist: pytest-benchmark<6.0.0,>=5.1.0
|
13
|
+
Requires-Dist: scikit-build-core[pyproject]<0.11.0,>=0.10.7; extra == "dev"
|
14
|
+
Requires-Dist: nanobind<3.0.0,>=2.5.0; extra == "dev"
|
15
|
+
Requires-Dist: pytest<9.0.0,>=8.3.4; extra == "dev"
|
16
|
+
Requires-Dist: pytest-cov<7.0.0,>=6.0.0; extra == "dev"
|
17
|
+
Requires-Dist: pytest-benchmark<6.0.0,>=5.1.0; extra == "dev"
|
18
|
+
Provides-Extra: dev
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# BURST interface
|
2
|
+
Binary Utility for Reliable Stream Transfer (BURST) is a library for encoding and decoding binary data streams, a packet format.
|
3
|
+
It combines a 16 bit checksum and cobs encoding to convert packets into a format that can be sent over a stream.
|
4
|
+
|
5
|
+
This projects is written so it can be used both in python, c and c++ based project
|
6
|
+
|
7
|
+
# Installation instuctions
|
8
|
+
|
9
|
+
## As an user
|
10
|
+
|
11
|
+
Simple installation
|
12
|
+
```sh
|
13
|
+
pip install -e .
|
14
|
+
```
|
15
|
+
|
16
|
+
## As a developer
|
17
|
+
|
18
|
+
Fast build
|
19
|
+
```sh
|
20
|
+
pip install --no-build-isolation -ve .
|
21
|
+
```
|
22
|
+
|
23
|
+
Auto rebuild on run
|
24
|
+
```sh
|
25
|
+
pip install --no-build-isolation -Ceditable.rebuild=true -ve .
|
26
|
+
```
|
27
|
+
|
28
|
+
|
29
|
+
### Python Stub files generation
|
30
|
+
|
31
|
+
They are generated automatically buy can also be generated
|
32
|
+
|
33
|
+
```
|
34
|
+
python -m nanobind.stubgen -m nanobind_example_ext
|
35
|
+
```
|
36
|
+
|
37
|
+
# Publishing instructions
|
38
|
+
|
39
|
+
```
|
40
|
+
|
41
|
+
```
|
42
|
+
|
43
|
+
# Test
|
44
|
+
|
45
|
+
```sh
|
46
|
+
pytest
|
47
|
+
```
|
48
|
+
|
49
|
+
# BURST protocol
|
50
|
+
TODO
|
51
|
+
* STAGE 1
|
52
|
+
* Convert cpp to c files [OK]
|
53
|
+
* Formalise naming [OK]
|
54
|
+
* Add c encode functions [OK]
|
55
|
+
* Test c encode functions [OK]
|
56
|
+
* Update README
|
57
|
+
* Improve poetry.toml [OK]
|
58
|
+
|
59
|
+
|
60
|
+
* STAGE 2
|
61
|
+
* Add CI/CD on github to compile x86
|
62
|
+
* Fix dependencies once compilation succeeds
|
63
|
+
* Publish on pypi
|
64
|
+
* STAGE 3
|
65
|
+
* Add a way to get C test coverage
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
[project]
|
2
|
+
name = "burst-link-protocol"
|
3
|
+
version = "0.1.0" # Choose one version reference here
|
4
|
+
description = "Binary Utility for Reliable Stream Transfer (BURST) is a library for encoding and decoding binary data streams into and from a byte stream."
|
5
|
+
requires-python = ">=3.10,<4.0"
|
6
|
+
authors = [
|
7
|
+
{ name = "Floris vernieuwe", email = "floris@vernieuwe.eu" }
|
8
|
+
]
|
9
|
+
|
10
|
+
dependencies = [
|
11
|
+
"cobs>=1.2.1,<2.0.0",
|
12
|
+
"numpy>=2.2.3,<3.0.0",
|
13
|
+
"crc>=7.1.0,<8.0.0",
|
14
|
+
"pytest>=8.3.4,<9.0.0",
|
15
|
+
"pytest-cov>=6.0.0,<7.0.0",
|
16
|
+
"pytest-benchmark>=5.1.0,<6.0.0"
|
17
|
+
|
18
|
+
]
|
19
|
+
|
20
|
+
|
21
|
+
[project.optional-dependencies]
|
22
|
+
dev = [
|
23
|
+
"scikit-build-core[pyproject]>=0.10.7,<0.11.0",
|
24
|
+
"nanobind>=2.5.0,<3.0.0",
|
25
|
+
"pytest>=8.3.4,<9.0.0",
|
26
|
+
"pytest-cov>=6.0.0,<7.0.0",
|
27
|
+
"pytest-benchmark>=5.1.0,<6.0.0"
|
28
|
+
]
|
29
|
+
|
30
|
+
[build-system]
|
31
|
+
requires = ["scikit-build-core>=0.10", "nanobind>=1.3.2"]
|
32
|
+
build-backend = "scikit_build_core.build"
|
33
|
+
|
34
|
+
[tool.scikit-build]
|
35
|
+
minimum-version = "build-system.requires"
|
36
|
+
cmake.build-type = "Debug"
|
37
|
+
cmake.args = ["-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCOVERAGE=ON"]
|
38
|
+
build-dir = "build/{wheel_tag}"
|
39
|
+
wheel.py-api = "cp312"
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
[tool.cibuildwheel]
|
44
|
+
# Necessary to see build output from the actual compilation
|
45
|
+
build-verbosity = 1
|
46
|
+
|
47
|
+
# Run pytest to ensure that the package was correctly built
|
48
|
+
# test-command = "pytest test"
|
49
|
+
# test-requires = "pytest"
|
50
|
+
|
51
|
+
# Don't test Python 3.8 wheels on macOS/arm64
|
52
|
+
test-skip="cp38-macosx_*:arm64"
|
53
|
+
|
54
|
+
# Needed for full C++17 support
|
55
|
+
[tool.cibuildwheel.macos.environment]
|
56
|
+
MACOSX_DEPLOYMENT_TARGET = "10.14"
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#ifndef BURST_INTERFACE_H
|
2
|
+
#define BURST_INTERFACE_H
|
3
|
+
|
4
|
+
#include <stddef.h>
|
5
|
+
#include <stdint.h>
|
6
|
+
#include <stdbool.h>
|
7
|
+
|
8
|
+
#define COBS_DELIMITER 0x00
|
9
|
+
#define COBS_MAX_CODE 0xFF
|
10
|
+
|
11
|
+
// Status codes returned by the encoder.
|
12
|
+
typedef enum
|
13
|
+
{
|
14
|
+
BURST_DATA_CONSUMED,
|
15
|
+
BURST_PACKET_READY,
|
16
|
+
BURST_OVERFLOW_ERROR,
|
17
|
+
BURST_ENCODE_ERROR,
|
18
|
+
BURST_CRC_ERROR,
|
19
|
+
BURST_DECODE_ERROR
|
20
|
+
} burst_status_t;
|
21
|
+
|
22
|
+
typedef struct
|
23
|
+
{
|
24
|
+
uint8_t *data;
|
25
|
+
size_t size;
|
26
|
+
} burst_packet_t;
|
27
|
+
|
28
|
+
typedef struct
|
29
|
+
{
|
30
|
+
uint8_t *buffer; // Output buffer for encoded packets.
|
31
|
+
size_t buffer_size; // Total size of the output buffer.
|
32
|
+
size_t out_head; // Current offset (number of bytes written).
|
33
|
+
} burst_encoder_t;
|
34
|
+
|
35
|
+
typedef struct
|
36
|
+
{
|
37
|
+
uint8_t *buffer; // Output buffer for decoded data.
|
38
|
+
size_t buffer_size; // Size of the output buffer.
|
39
|
+
size_t out_head; // Current count of decoded bytes stored.
|
40
|
+
|
41
|
+
uint8_t current_code; // Current block’s code byte; 0 indicates a new block is
|
42
|
+
// expected.
|
43
|
+
uint8_t bytes_remaining; // Number of data bytes left to copy for the current
|
44
|
+
// block.
|
45
|
+
bool pending_zero; // true if a zero should be inserted before starting the
|
46
|
+
// next block.
|
47
|
+
|
48
|
+
bool finished; // true if the packet is complete and available in the buffer
|
49
|
+
} burst_decoder_t;
|
50
|
+
|
51
|
+
|
52
|
+
// Encoder
|
53
|
+
void burst_encoder_init(burst_encoder_t *ctx, uint8_t *buffer, size_t size);
|
54
|
+
burst_status_t burst_encoder_add_packet(burst_encoder_t *ctx, const uint8_t *data, size_t size);
|
55
|
+
burst_packet_t burst_encoder_flush(burst_encoder_t *ctx);
|
56
|
+
|
57
|
+
|
58
|
+
// Decoder
|
59
|
+
void burst_decoder_init(burst_decoder_t *ctx, uint8_t *buffer, size_t size);
|
60
|
+
burst_status_t bust_decoder_add_data(burst_decoder_t *ctx, const uint8_t *data, size_t size,
|
61
|
+
size_t *consumed_bytes);
|
62
|
+
void burst_decoder_reset(burst_decoder_t *ctx);
|
63
|
+
burst_status_t burst_decoder_add_byte(burst_decoder_t *ctx, uint8_t byte);
|
64
|
+
|
65
|
+
burst_packet_t burst_decoder_get_packet(burst_decoder_t *ctx);
|
66
|
+
|
67
|
+
#endif // ENCODER_H
|
@@ -0,0 +1,41 @@
|
|
1
|
+
from cobs import cobs
|
2
|
+
from crc import Calculator,Crc16
|
3
|
+
|
4
|
+
crc = Calculator(Crc16.IBM_3740)
|
5
|
+
|
6
|
+
class BurstInterfacePy:
|
7
|
+
buffer = b""
|
8
|
+
|
9
|
+
def __init__(self):
|
10
|
+
pass
|
11
|
+
|
12
|
+
@staticmethod
|
13
|
+
def crc16( data: bytes) -> bytes:
|
14
|
+
return crc.checksum(data).to_bytes(2, "big")
|
15
|
+
|
16
|
+
|
17
|
+
@staticmethod
|
18
|
+
def encode_packet(packet: bytes) -> bytes:
|
19
|
+
packet_with_crc = packet + BurstInterfacePy.crc16(packet)
|
20
|
+
return cobs.encode(packet_with_crc) + b"\x00"
|
21
|
+
|
22
|
+
@staticmethod
|
23
|
+
def decode_packet(packet: bytes) -> bytes:
|
24
|
+
# decode and check crc
|
25
|
+
decoded = cobs.decode(packet)
|
26
|
+
|
27
|
+
if BurstInterfacePy.crc16(decoded[:-2]) != decoded[-2:]:
|
28
|
+
raise ValueError("CRC mismatch")
|
29
|
+
|
30
|
+
return decoded[:-2]
|
31
|
+
|
32
|
+
def encode(self, packets: list[bytes]) -> bytes:
|
33
|
+
return b"".join([self.encode_packet(packet) for packet in packets])
|
34
|
+
|
35
|
+
def decode(self, steam: bytes) -> list[bytes]:
|
36
|
+
self.buffer += steam
|
37
|
+
separated_packets = self.buffer.split(b"\x00")
|
38
|
+
# Add last packet to buffer
|
39
|
+
self.buffer = separated_packets.pop()
|
40
|
+
return [self.decode_packet(packet) for packet in separated_packets]
|
41
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#include <stddef.h>
|
2
|
+
#include <stdint.h>
|
3
|
+
|
4
|
+
/*
|
5
|
+
* Calculate CRC16-CCITT (polynomial 0x1021, initial value 0xFFFF) over
|
6
|
+
* the provided data.
|
7
|
+
*/
|
8
|
+
uint16_t crc16_ccitt(const uint8_t *data, size_t length) {
|
9
|
+
uint16_t crc = 0xFFFF;
|
10
|
+
for (size_t i = 0; i < length; i++) {
|
11
|
+
crc ^= ((uint16_t)data[i]) << 8;
|
12
|
+
for (int j = 0; j < 8; j++) {
|
13
|
+
if (crc & 0x8000)
|
14
|
+
crc = (crc << 1) ^ 0x1021;
|
15
|
+
else
|
16
|
+
crc <<= 1;
|
17
|
+
}
|
18
|
+
}
|
19
|
+
return crc;
|
20
|
+
}
|
@@ -0,0 +1,150 @@
|
|
1
|
+
#include "burst_interface.h"
|
2
|
+
#include "crc.h"
|
3
|
+
#include <stdbool.h>
|
4
|
+
#include <stddef.h>
|
5
|
+
#include <stdint.h>
|
6
|
+
#include <stdio.h>
|
7
|
+
|
8
|
+
void burst_decoder_init(burst_decoder_t *ctx, uint8_t *buffer, size_t size)
|
9
|
+
{
|
10
|
+
ctx->buffer = buffer;
|
11
|
+
ctx->buffer_size = size;
|
12
|
+
burst_decoder_reset(ctx);
|
13
|
+
}
|
14
|
+
|
15
|
+
burst_status_t bust_decoder_add_data(burst_decoder_t *ctx, const uint8_t *data, size_t size,
|
16
|
+
size_t *consumed_bytes)
|
17
|
+
{
|
18
|
+
// If the decoder was finished, reset it.
|
19
|
+
if (ctx->finished)
|
20
|
+
{
|
21
|
+
burst_decoder_reset(ctx);
|
22
|
+
}
|
23
|
+
|
24
|
+
for (size_t i = 0; i < size; i++)
|
25
|
+
{
|
26
|
+
uint8_t byte = data[i];
|
27
|
+
(*consumed_bytes)++;
|
28
|
+
|
29
|
+
burst_status_t result = burst_decoder_add_byte(ctx, byte);
|
30
|
+
|
31
|
+
if (result != BURST_DATA_CONSUMED)
|
32
|
+
{
|
33
|
+
ctx->finished = true;
|
34
|
+
return result;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
return BURST_DATA_CONSUMED;
|
38
|
+
}
|
39
|
+
void burst_decoder_reset(burst_decoder_t *ctx)
|
40
|
+
{
|
41
|
+
ctx->out_head = 0;
|
42
|
+
ctx->current_code = 0;
|
43
|
+
ctx->bytes_remaining = 0;
|
44
|
+
ctx->pending_zero = false;
|
45
|
+
ctx->finished = false;
|
46
|
+
}
|
47
|
+
|
48
|
+
burst_status_t burst_decoder_complete_packet(burst_decoder_t *ctx)
|
49
|
+
{
|
50
|
+
// Ensure we have at least two bytes for the CRC.
|
51
|
+
if (ctx->out_head < CRC_SIZE)
|
52
|
+
{
|
53
|
+
return BURST_CRC_ERROR;
|
54
|
+
}
|
55
|
+
|
56
|
+
// Calculate the CRC over the packet data excluding the last two CRC bytes.
|
57
|
+
uint16_t computed_crc = crc16_ccitt(ctx->buffer, ctx->out_head - CRC_SIZE);
|
58
|
+
|
59
|
+
// Extract the received CRC from the last two bytes (big-endian).
|
60
|
+
uint16_t received_crc =
|
61
|
+
((uint16_t)ctx->buffer[ctx->out_head - CRC_SIZE] << 8) |
|
62
|
+
ctx->buffer[ctx->out_head - 1];
|
63
|
+
|
64
|
+
// Check if the CRCs match.
|
65
|
+
if (computed_crc != received_crc)
|
66
|
+
{
|
67
|
+
return BURST_CRC_ERROR;
|
68
|
+
}
|
69
|
+
|
70
|
+
// CRC check passed, we can remove it from the packet.
|
71
|
+
ctx->out_head -= CRC_SIZE;
|
72
|
+
return BURST_PACKET_READY;
|
73
|
+
}
|
74
|
+
|
75
|
+
// If byte is a delimiter but a block is not complete, return COBS_DECODE_ERROR
|
76
|
+
// If the buffer is full, return COBS_OVERFLOW_ERROR
|
77
|
+
// If the byte is consumed, but the packet is not complete, return COBS_DATA_CONSUMED
|
78
|
+
// If the packet is complete, return COBS_PACKET_READY
|
79
|
+
burst_status_t burst_decoder_add_byte(burst_decoder_t *ctx, uint8_t byte)
|
80
|
+
{
|
81
|
+
// Check if there is space for more data.
|
82
|
+
if (ctx->out_head >= ctx->buffer_size) {
|
83
|
+
return BURST_OVERFLOW_ERROR;
|
84
|
+
}
|
85
|
+
|
86
|
+
// If the byte is a delimiter, decide if it terminates the packet.
|
87
|
+
if (byte == COBS_DELIMITER) {
|
88
|
+
// If in the middle of a block, a delimiter is not allowed.
|
89
|
+
if (ctx->current_code != 0) {
|
90
|
+
return BURST_DECODE_ERROR;
|
91
|
+
}
|
92
|
+
// Otherwise, the packet is complete.
|
93
|
+
return burst_decoder_complete_packet(ctx);
|
94
|
+
}
|
95
|
+
|
96
|
+
// If a zero is pending from a previous block, insert it now.
|
97
|
+
if (ctx->pending_zero) {
|
98
|
+
if (ctx->out_head >= ctx->buffer_size) {
|
99
|
+
return BURST_OVERFLOW_ERROR;
|
100
|
+
}
|
101
|
+
ctx->buffer[ctx->out_head++] = COBS_DELIMITER;
|
102
|
+
ctx->pending_zero = false;
|
103
|
+
// Now, treat the current byte as a new block code.
|
104
|
+
ctx->current_code = byte;
|
105
|
+
ctx->bytes_remaining = (byte > 0 ? byte - 1 : 0);
|
106
|
+
return BURST_DATA_CONSUMED;
|
107
|
+
}
|
108
|
+
|
109
|
+
// If not currently in a block, this byte is the new block code.
|
110
|
+
if (ctx->current_code == 0) {
|
111
|
+
ctx->current_code = byte;
|
112
|
+
ctx->bytes_remaining = (byte > 0 ? byte - 1 : 0);
|
113
|
+
return BURST_DATA_CONSUMED;
|
114
|
+
}
|
115
|
+
|
116
|
+
// Otherwise, we are in the middle of a block so treat the byte as data.
|
117
|
+
ctx->buffer[ctx->out_head++] = byte;
|
118
|
+
if (ctx->bytes_remaining > 0) {
|
119
|
+
ctx->bytes_remaining--;
|
120
|
+
}
|
121
|
+
|
122
|
+
// When the block is complete...
|
123
|
+
if (ctx->bytes_remaining == 0) {
|
124
|
+
// If the block's code is less than COBS_MAX_CODE, a zero is pending.
|
125
|
+
if (ctx->current_code < COBS_MAX_CODE) {
|
126
|
+
ctx->pending_zero = true;
|
127
|
+
}
|
128
|
+
ctx->current_code = 0;
|
129
|
+
}
|
130
|
+
|
131
|
+
return BURST_DATA_CONSUMED;
|
132
|
+
}
|
133
|
+
|
134
|
+
|
135
|
+
burst_packet_t burst_decoder_get_packet(burst_decoder_t *ctx)
|
136
|
+
{
|
137
|
+
|
138
|
+
if (!ctx->finished)
|
139
|
+
{
|
140
|
+
burst_packet_t packet;
|
141
|
+
packet.data = NULL;
|
142
|
+
packet.size = 0;
|
143
|
+
return packet;
|
144
|
+
}
|
145
|
+
|
146
|
+
burst_packet_t packet;
|
147
|
+
packet.data = ctx->buffer;
|
148
|
+
packet.size = ctx->out_head;
|
149
|
+
return packet;
|
150
|
+
}
|
@@ -0,0 +1,91 @@
|
|
1
|
+
#include "burst_interface.h"
|
2
|
+
#include "crc.h" // Assumes crc16_ccitt() is available.
|
3
|
+
#include <stddef.h>
|
4
|
+
#include <stdint.h>
|
5
|
+
|
6
|
+
// COBS encoding with CRC appending.
|
7
|
+
// The input to be encoded is the raw packet data followed by the two CRC bytes.
|
8
|
+
// The algorithm works by maintaining a "code" (the count of nonzero bytes)
|
9
|
+
// and inserting that count at the start of each block. When a zero is encountered
|
10
|
+
// (or when the block length reaches COBS_MAX_CODE) the block is terminated.
|
11
|
+
burst_status_t burst_encoder_add_packet(burst_encoder_t *ctx, const uint8_t *data, size_t size)
|
12
|
+
{
|
13
|
+
// Compute the CRC over the raw packet data.
|
14
|
+
uint16_t crc = crc16_ccitt(data, size);
|
15
|
+
uint8_t crc_high = (crc >> 8) & 0xFF;
|
16
|
+
uint8_t crc_low = crc & 0xFF;
|
17
|
+
// The total number of bytes to encode: raw data + 2 bytes of CRC.
|
18
|
+
size_t total_bytes = size + CRC_SIZE;
|
19
|
+
|
20
|
+
// Initialize COBS block state.
|
21
|
+
uint8_t code = 1; // Code value starts at 1.
|
22
|
+
// Reserve space for the code byte.
|
23
|
+
if (ctx->out_head >= ctx->buffer_size)
|
24
|
+
return BURST_OVERFLOW_ERROR;
|
25
|
+
size_t code_index = ctx->out_head;
|
26
|
+
ctx->buffer[ctx->out_head++] = 0; // Placeholder for the code.
|
27
|
+
|
28
|
+
// Process each byte from the raw data and then the CRC.
|
29
|
+
for (size_t i = 0; i < total_bytes; i++) {
|
30
|
+
uint8_t byte;
|
31
|
+
if (i < size)
|
32
|
+
byte = data[i];
|
33
|
+
else if (i == size)
|
34
|
+
byte = crc_high;
|
35
|
+
else // i == size + 1
|
36
|
+
byte = crc_low;
|
37
|
+
|
38
|
+
if (byte == 0) {
|
39
|
+
// Write the current code to the reserved position.
|
40
|
+
ctx->buffer[code_index] = code;
|
41
|
+
// Start a new block.
|
42
|
+
code = 1;
|
43
|
+
if (ctx->out_head >= ctx->buffer_size)
|
44
|
+
return BURST_OVERFLOW_ERROR;
|
45
|
+
code_index = ctx->out_head;
|
46
|
+
ctx->buffer[ctx->out_head++] = 0; // Reserve placeholder for new code.
|
47
|
+
} else {
|
48
|
+
// Append the nonzero byte.
|
49
|
+
if (ctx->out_head >= ctx->buffer_size)
|
50
|
+
return BURST_OVERFLOW_ERROR;
|
51
|
+
ctx->buffer[ctx->out_head++] = byte;
|
52
|
+
code++;
|
53
|
+
// If the maximum code value is reached, finish the block.
|
54
|
+
if (code == COBS_MAX_CODE) {
|
55
|
+
ctx->buffer[code_index] = code;
|
56
|
+
code = 1;
|
57
|
+
if (ctx->out_head >= ctx->buffer_size)
|
58
|
+
return BURST_OVERFLOW_ERROR;
|
59
|
+
code_index = ctx->out_head;
|
60
|
+
ctx->buffer[ctx->out_head++] = 0; // Reserve new placeholder.
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
// Finalize the last block.
|
66
|
+
ctx->buffer[code_index] = code;
|
67
|
+
|
68
|
+
// Append the packet delimiter.
|
69
|
+
if (ctx->out_head >= ctx->buffer_size)
|
70
|
+
return BURST_OVERFLOW_ERROR;
|
71
|
+
ctx->buffer[ctx->out_head++] = COBS_DELIMITER;
|
72
|
+
|
73
|
+
return BURST_PACKET_READY;
|
74
|
+
}
|
75
|
+
|
76
|
+
void burst_encoder_init(burst_encoder_t *ctx, uint8_t *buffer, size_t size)
|
77
|
+
{
|
78
|
+
ctx->buffer = buffer;
|
79
|
+
ctx->buffer_size = size;
|
80
|
+
ctx->out_head = 0;
|
81
|
+
}
|
82
|
+
|
83
|
+
burst_packet_t burst_encoder_flush(burst_encoder_t *ctx)
|
84
|
+
{
|
85
|
+
burst_packet_t packet;
|
86
|
+
packet.data = ctx->buffer;
|
87
|
+
packet.size = ctx->out_head;
|
88
|
+
// Reset the encoder context for the next use.
|
89
|
+
ctx->out_head = 0;
|
90
|
+
return packet;
|
91
|
+
}
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#include <nanobind/nanobind.h>
|
2
|
+
extern "C"
|
3
|
+
{
|
4
|
+
#include <burst_interface.h>
|
5
|
+
}
|
6
|
+
|
7
|
+
namespace nb = nanobind;
|
8
|
+
using namespace nb::literals;
|
9
|
+
|
10
|
+
struct BurstInterface
|
11
|
+
{
|
12
|
+
burst_decoder_t decoder;
|
13
|
+
uint8_t decoder_buffer[1024] = {0};
|
14
|
+
burst_encoder_t encoder;
|
15
|
+
uint8_t encoder_buffer[1024] = {0};
|
16
|
+
|
17
|
+
BurstInterface()
|
18
|
+
{
|
19
|
+
burst_decoder_init(&decoder, decoder_buffer, sizeof(decoder_buffer));
|
20
|
+
burst_encoder_init(&encoder, encoder_buffer, sizeof(encoder_buffer));
|
21
|
+
}
|
22
|
+
|
23
|
+
nb::list decode(nb::bytes data, bool fail_on_crc_error = false)
|
24
|
+
{
|
25
|
+
nb::list result;
|
26
|
+
uint8_t *data_ptr = (uint8_t *)data.data();
|
27
|
+
size_t data_size = data.size();
|
28
|
+
|
29
|
+
size_t bytes_consumed = 0;
|
30
|
+
while (bytes_consumed < data_size)
|
31
|
+
{
|
32
|
+
burst_status_t status = bust_decoder_add_data(&decoder, data_ptr + bytes_consumed, data_size - bytes_consumed, &bytes_consumed);
|
33
|
+
|
34
|
+
if (status == BURST_PACKET_READY)
|
35
|
+
{
|
36
|
+
burst_packet_t packet = burst_decoder_get_packet(&decoder);
|
37
|
+
nb::bytes packet_bytes(reinterpret_cast<const char *>(packet.data), packet.size);
|
38
|
+
result.append(packet_bytes);
|
39
|
+
}
|
40
|
+
|
41
|
+
if (fail_on_crc_error)
|
42
|
+
{
|
43
|
+
if (status == BURST_CRC_ERROR)
|
44
|
+
{
|
45
|
+
throw std::runtime_error("CRC error");
|
46
|
+
}
|
47
|
+
if (status == BURST_DECODE_ERROR)
|
48
|
+
{
|
49
|
+
throw std::runtime_error("Decode error");
|
50
|
+
}
|
51
|
+
if (status == BURST_OVERFLOW_ERROR)
|
52
|
+
{
|
53
|
+
throw std::runtime_error("Overflow error");
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
return result;
|
58
|
+
}
|
59
|
+
|
60
|
+
nb::bytes encode(nb::list data)
|
61
|
+
{
|
62
|
+
for (size_t i = 0; i < data.size(); i++)
|
63
|
+
{
|
64
|
+
nb::bytes data_bytes = data[i];
|
65
|
+
burst_encoder_add_packet(&encoder, (uint8_t *)data_bytes.data(), data_bytes.size());
|
66
|
+
}
|
67
|
+
// flush the encoder
|
68
|
+
burst_packet_t packet = burst_encoder_flush(&encoder);
|
69
|
+
return nb::bytes(reinterpret_cast<const char *>(packet.data), packet.size);
|
70
|
+
}
|
71
|
+
};
|
72
|
+
|
73
|
+
NB_MODULE(burst_interface_c, m)
|
74
|
+
{
|
75
|
+
|
76
|
+
nb::class_<BurstInterface>(m, "BurstInterfaceC")
|
77
|
+
.def(nb::init<>())
|
78
|
+
.def("decode", &BurstInterface::decode, "data"_a, "fail_on_crc_error"_a = false)
|
79
|
+
.def("encode", &BurstInterface::encode, "packets"_a);
|
80
|
+
}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
from burst_link_protocol import BurstInterfacePy,BurstInterfaceC
|
3
|
+
import pytest
|
4
|
+
import numpy as np
|
5
|
+
|
6
|
+
def test_c_decoder_python():
|
7
|
+
python_interface = BurstInterfacePy()
|
8
|
+
c_interface = BurstInterfaceC()
|
9
|
+
|
10
|
+
packets = [b"Hello, world!", b"Goodbye, world!"]
|
11
|
+
|
12
|
+
data = python_interface.encode(packets)
|
13
|
+
decoded = c_interface.decode(data)
|
14
|
+
print(decoded)
|
15
|
+
assert packets == decoded
|
16
|
+
|
17
|
+
def test_python_crc_validation():
|
18
|
+
interface = BurstInterfacePy()
|
19
|
+
packets = [b"Hello, world!", b"Goodbye, world!"]
|
20
|
+
|
21
|
+
data = bytearray(interface.encode(packets))
|
22
|
+
|
23
|
+
for i in range( len(data)):
|
24
|
+
c_interface = BurstInterfaceC()
|
25
|
+
|
26
|
+
data_copy = data.copy()
|
27
|
+
|
28
|
+
# modify byte x
|
29
|
+
data_copy[i] = (data_copy[i] + 1) % 256
|
30
|
+
with pytest.raises(Exception):
|
31
|
+
decoded = c_interface.decode(bytes(data_copy),fail_on_crc_error=True)
|
32
|
+
print(decoded)
|
33
|
+
assert len(decoded) == len(packets), f"Expected {len(packets)} packets, got {len(decoded)}"
|
34
|
+
|
35
|
+
def test_max_size_error():
|
36
|
+
interface = BurstInterfacePy()
|
37
|
+
c_interface = BurstInterfaceC()
|
38
|
+
packets = np.random.bytes(1500)
|
39
|
+
with pytest.raises(Exception):
|
40
|
+
data = c_interface.decode(interface.encode([packets]),fail_on_crc_error=True)
|
41
|
+
|
42
|
+
def test_encoding():
|
43
|
+
interface = BurstInterfacePy()
|
44
|
+
c_interface = BurstInterfaceC()
|
45
|
+
packets = [b"Hello, world!", b"Goodbye, world!"]
|
46
|
+
|
47
|
+
assert interface.encode(packets) == c_interface.encode(packets)
|
48
|
+
|
49
|
+
# if __name__ == "__main__":
|
50
|
+
# # test_c_decoder_python()
|
51
|
+
# # test_python_crc_validation()
|
52
|
+
# # max_size_error()
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
from burst_link_protocol import BurstInterfacePy
|
3
|
+
import pytest
|
4
|
+
|
5
|
+
def test_python():
|
6
|
+
interface = BurstInterfacePy()
|
7
|
+
packets = [b"Hello, world!", b"Goodbye, world!"]
|
8
|
+
|
9
|
+
data = interface.encode(packets)
|
10
|
+
decoded = interface.decode(data)
|
11
|
+
assert packets == decoded
|
12
|
+
|
13
|
+
def test_python_crc_validation():
|
14
|
+
interface = BurstInterfacePy()
|
15
|
+
packets = [b"Hello, world!", b"Goodbye, world!"]
|
16
|
+
|
17
|
+
data = bytearray(interface.encode(packets))
|
18
|
+
|
19
|
+
for i in range( len(data)):
|
20
|
+
data_copy = data.copy()
|
21
|
+
|
22
|
+
# modify byte x
|
23
|
+
data_copy[i] = (data_copy[i] + 1) % 256
|
24
|
+
with pytest.raises(Exception):
|
25
|
+
decoded = interface.decode(bytes(data_copy))
|
26
|
+
print(decoded)
|
27
|
+
assert len(decoded) == len(packets), f"Expected {len(packets)} packets, got {len(decoded)}"
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
from burst_link_protocol import BurstInterfacePy,BurstInterfaceC
|
3
|
+
import pytest
|
4
|
+
import numpy as np
|
5
|
+
|
6
|
+
def prepare_packets(packet_size:int) -> bytes:
|
7
|
+
python_interface = BurstInterfacePy()
|
8
|
+
return python_interface.encode([np.zeros(packet_size).tobytes()])
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
@pytest.mark.parametrize("n", [1, 10, 100, 1000])
|
13
|
+
def test_performance_c(benchmark, n):
|
14
|
+
c_interface = BurstInterfaceC()
|
15
|
+
benchmark(c_interface.decode, prepare_packets(n))
|
16
|
+
|
17
|
+
@pytest.mark.parametrize("n", [1, 10, 100, 1000])
|
18
|
+
def test_performance_python(benchmark, n):
|
19
|
+
c_interface = BurstInterfacePy()
|
20
|
+
benchmark(c_interface.decode, prepare_packets(n))
|