burst-link-protocol 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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))
|