burst-link-protocol 1.0.4__tar.gz → 1.1.4__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.
Files changed (40) hide show
  1. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/.vscode/c_cpp_properties.json +1 -1
  2. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/.vscode/tasks.json +2 -1
  3. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/CMakeLists.txt +48 -19
  4. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/PKG-INFO +4 -1
  5. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/README.md +1 -0
  6. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/pyproject.toml +3 -1
  7. burst_link_protocol-1.0.4/src/decoder.c → burst_link_protocol-1.1.4/src/burst/burst_decoder.c +61 -4
  8. burst_link_protocol-1.1.4/src/burst/burst_decoder.h +56 -0
  9. burst_link_protocol-1.1.4/src/burst/burst_encoder.c +120 -0
  10. burst_link_protocol-1.1.4/src/burst/burst_encoder.h +38 -0
  11. burst_link_protocol-1.0.4/src/crc.c → burst_link_protocol-1.1.4/src/burst/burst_generic.c +1 -1
  12. burst_link_protocol-1.1.4/src/burst/burst_generic.h +30 -0
  13. burst_link_protocol-1.1.4/src/burst_link_protocol/__init__.py +5 -0
  14. burst_link_protocol-1.1.4/src/burst_link_protocol/serial_burst_interface.py +233 -0
  15. burst_link_protocol-1.1.4/src/burst_link_protocol.h +7 -0
  16. burst_link_protocol-1.1.4/src/python_bindings.cpp +75 -0
  17. burst_link_protocol-1.1.4/test/test_burst_decoding.py +25 -0
  18. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/uv.lock +23 -1
  19. burst_link_protocol-1.0.4/.github/dependabot.yml +0 -11
  20. burst_link_protocol-1.0.4/platform/zephyr/CMakeLists.txt +0 -9
  21. burst_link_protocol-1.0.4/src/burst_interface.h +0 -56
  22. burst_link_protocol-1.0.4/src/burst_link_protocol/__init__.py +0 -4
  23. burst_link_protocol-1.0.4/src/crc.h +0 -4
  24. burst_link_protocol-1.0.4/src/encoder.c +0 -92
  25. burst_link_protocol-1.0.4/src/python_bindings.cpp +0 -80
  26. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/.github/workflows/pip.yml +0 -0
  27. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/.github/workflows/wheel.yml +0 -0
  28. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/.gitignore +0 -0
  29. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/.vscode/launch.json +0 -0
  30. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/.vscode/settings.json +0 -0
  31. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/LICENSE +0 -0
  32. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/ROADMAP.md +0 -0
  33. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/docs/design.drawio +0 -0
  34. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/docs/design.svg +0 -0
  35. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/src/burst_link_protocol/main.py +0 -0
  36. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/test/test_burst.py +0 -0
  37. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/test/test_c.py +0 -0
  38. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/test/test_c_validation.py +0 -0
  39. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/test/test_python_validation.py +0 -0
  40. {burst_link_protocol-1.0.4 → burst_link_protocol-1.1.4}/test/test_speed.py +0 -0
@@ -4,7 +4,7 @@
4
4
  "name": "Win32",
5
5
  "includePath": [
6
6
  "${workspaceFolder}/**",
7
- "${workspaceFolder}/.venv/Lib/site-packages/nanobind/include/nanobind", //To be fixed
7
+ "${workspaceFolder/.venv/lib/python3.13/site-packages/nanobind/include/nanobind", //To be fixed
8
8
  "${env:PYTHON_INCLUDE}"
9
9
  ],
10
10
  "defines": [
@@ -4,8 +4,9 @@
4
4
  {
5
5
  "type": "shell",
6
6
  "label": "pip install",
7
- "command": "pip",
7
+ "command": "uv",
8
8
  "args": [
9
+ "pip",
9
10
  "install",
10
11
  "--no-build-isolation",
11
12
  "-ve",
@@ -1,8 +1,33 @@
1
1
  cmake_minimum_required(VERSION 3.15...3.26)
2
+ # If espidf is reading this CMakeLists.txt, skip so its ignored
2
3
 
3
- project(i_think_this_name_does_not_matter LANGUAGES C CXX)
4
4
 
5
- if (NOT SKBUILD)
5
+ # If ESP-IDF is reading this CMakeLists.txt, register it as an ESP-IDF component
6
+ if(DEFINED ENV{IDF_PATH})
7
+ idf_component_register(SRCS
8
+ "src/burst/burst_decoder.c"
9
+ "src/burst/burst_encoder.c"
10
+ "src/burst/burst_generic.c"
11
+ INCLUDE_DIRS "src")
12
+
13
+ return()
14
+ endif()
15
+
16
+ # If Zephyr is reading this CMakeLists.txt, register it as a Zephyr module
17
+ if(Zephyr_FOUND)
18
+ zephyr_library_named(burst-link-protocol)
19
+ zephyr_library_sources(
20
+ "src/burst/burst_decoder.c"
21
+ "src/burst/burst_encoder.c"
22
+ "src/burst/burst_generic.c"
23
+ )
24
+
25
+ target_include_directories(burst-link-protocol PUBLIC src)
26
+ return()
27
+ endif()
28
+
29
+ project(i_think_this_name_does_not_matter LANGUAGES C CXX)
30
+ if(NOT SKBUILD)
6
31
  message(WARNING "\
7
32
  This CMake file is meant to be executed using 'scikit-build'. Running
8
33
  it directly will almost certainly not produce the desired result. If
@@ -30,30 +55,34 @@ find_package(Python 3.8
30
55
  REQUIRED COMPONENTS Interpreter Development.Module
31
56
  OPTIONAL_COMPONENTS Development.SABIModule)
32
57
 
33
- if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
58
+ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
34
59
  set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
35
60
  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
36
61
  endif()
37
62
 
38
63
  option(COVERAGE "Enable coverage reporting" ON)
39
64
  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")
65
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -fprofile-arcs -ftest-coverage")
66
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g -fprofile-arcs -ftest-coverage")
42
67
  endif()
43
68
 
44
69
  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}")
70
+ COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
71
+ OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE NB_DIR)
72
+ list(APPEND CMAKE_PREFIX_PATH "${NB_DIR}")
48
73
 
49
74
  # Import nanobind through CMake's find_package mechanism
50
75
  find_package(nanobind CONFIG REQUIRED)
51
76
 
52
77
  # We are now ready to compile the actual extension module
53
78
  nanobind_add_module(
79
+
54
80
  # Name of the extension
55
81
  burst_interface_c
56
82
 
83
+ # domain name
84
+ NB_DOMAIN burst_interface_domain
85
+
57
86
  # Target the stable ABI for Python 3.12+, which reduces
58
87
  # the number of binary wheels that must be built. This
59
88
  # does nothing on older Python versions
@@ -68,23 +97,23 @@ nanobind_add_module(
68
97
  NB_STATIC
69
98
 
70
99
  # Source code goes here
71
- src/decoder.c
72
- src/encoder.c
100
+ src/burst/burst_decoder.c
101
+ src/burst/burst_encoder.c
102
+ src/burst/burst_generic.c
73
103
  src/python_bindings.cpp
74
- src/crc.c
75
104
  )
76
105
 
77
106
  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
107
+
108
+ burst_interface_c_stub
109
+ INSTALL_TIME
110
+ MODULE burst_interface_c
111
+ OUTPUT burst_interface_c.pyi
112
+ PYTHON_PATH $<TARGET_FILE_DIR:burst_interface_c>
113
+ DEPENDS burst_interface_c
85
114
  )
86
115
 
87
116
  target_include_directories(burst_interface_c PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
88
117
 
89
118
  # Install directive for scikit-build-core
90
- install(TARGETS burst_interface_c LIBRARY DESTINATION ".")
119
+ install(TARGETS burst_interface_c LIBRARY DESTINATION ".")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: burst-link-protocol
3
- Version: 1.0.4
3
+ Version: 1.1.4
4
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
5
  Author-Email: Floris vernieuwe <floris@vernieuwe.eu>
6
6
  License-File: LICENSE
@@ -14,6 +14,8 @@ Requires-Dist: pytest-cov<7.0.0,>=6.0.0
14
14
  Requires-Dist: pytest-benchmark<6.0.0,>=5.1.0
15
15
  Requires-Dist: scikit-build-core>=0.10.7
16
16
  Requires-Dist: nanobind>=2.5.0
17
+ Requires-Dist: pyserial>=3.5
18
+ Requires-Dist: janus>=2.0.0
17
19
  Provides-Extra: dev
18
20
  Requires-Dist: scikit-build-core[pyproject]<0.11.0,>=0.10.7; extra == "dev"
19
21
  Requires-Dist: nanobind<3.0.0,>=2.5.0; extra == "dev"
@@ -61,6 +63,7 @@ uv pip install --reinstall --no-build-isolation -ve .
61
63
  Auto rebuild on run
62
64
  ```sh
63
65
  uv pip install --reinstall --no-build-isolation -Ceditable.rebuild=true -ve .
66
+ # or
64
67
  pip install --no-build-isolation -Ceditable.rebuild=true -ve .
65
68
  ```
66
69
 
@@ -37,6 +37,7 @@ uv pip install --reinstall --no-build-isolation -ve .
37
37
  Auto rebuild on run
38
38
  ```sh
39
39
  uv pip install --reinstall --no-build-isolation -Ceditable.rebuild=true -ve .
40
+ # or
40
41
  pip install --no-build-isolation -Ceditable.rebuild=true -ve .
41
42
  ```
42
43
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "burst-link-protocol"
3
- version = "1.0.4" # Choose one version reference here
3
+ version = "1.1.4" # Choose one version reference here
4
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
5
  requires-python = ">=3.10,<4.0"
6
6
  authors = [
@@ -16,6 +16,8 @@ dependencies = [
16
16
  "pytest-benchmark>=5.1.0,<6.0.0",
17
17
  "scikit-build-core>=0.10.7",
18
18
  "nanobind>=2.5.0",
19
+ "pyserial>=3.5",
20
+ "janus>=2.0.0",
19
21
  ]
20
22
  license-files = ["LICENSE"]
21
23
 
@@ -1,11 +1,10 @@
1
+ #include "burst_decoder.h"
2
+
1
3
  #include <stdbool.h>
2
4
  #include <stddef.h>
3
5
  #include <stdint.h>
4
6
  #include <stdio.h>
5
7
 
6
- #include "burst_interface.h"
7
- #include "crc.h"
8
-
9
8
  void burst_decoder_init(burst_decoder_t *ctx, uint8_t *buffer, size_t size) {
10
9
  ctx->buffer = buffer;
11
10
  ctx->buffer_size = size;
@@ -54,7 +53,7 @@ burst_status_t burst_decoder_complete_packet(burst_decoder_t *ctx) {
54
53
  }
55
54
 
56
55
  // 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);
56
+ uint16_t computed_crc = burst_crc16(ctx->buffer, ctx->out_head - CRC_SIZE);
58
57
 
59
58
  // Extract the received CRC from the last two bytes (big-endian).
60
59
  uint16_t received_crc = ((uint16_t)ctx->buffer[ctx->out_head - CRC_SIZE] << 8) | ctx->buffer[ctx->out_head - 1];
@@ -82,6 +81,7 @@ burst_status_t burst_decoder_add_byte(burst_decoder_t *ctx, uint8_t byte) {
82
81
  return burst_decoder_complete_packet(ctx);
83
82
  }
84
83
 
84
+ /* fallthrough */
85
85
  case COBS_DECODE_READ_CODE:
86
86
 
87
87
  // If last code was 0xF, its a overhead byte
@@ -119,6 +119,9 @@ burst_status_t burst_decoder_add_byte(burst_decoder_t *ctx, uint8_t byte) {
119
119
 
120
120
  return BURST_DATA_CONSUMED;
121
121
  }
122
+
123
+ // This should never happen, but some compilers are dumb
124
+ return BURST_DECODE_ERROR;
122
125
  }
123
126
 
124
127
  burst_packet_t burst_decoder_get_packet(burst_decoder_t *ctx) {
@@ -134,3 +137,57 @@ burst_packet_t burst_decoder_get_packet(burst_decoder_t *ctx) {
134
137
  packet.size = ctx->out_head;
135
138
  return packet;
136
139
  }
140
+
141
+ void burst_managed_decoder_init(burst_managed_decoder_t *burst_managed_decoder, uint8_t *buffer, size_t size, burst_managed_decoder_callback_t callback,
142
+ void *user_data) {
143
+ burst_managed_decoder->callback_function = callback;
144
+ burst_managed_decoder->user_data = user_data;
145
+ burst_decoder_init(&burst_managed_decoder->decoder, buffer, size);
146
+ }
147
+
148
+ int burst_managed_decoder_handle_data(burst_managed_decoder_t *burst_managed_decoder, const uint8_t *data, size_t len) {
149
+ if (len == 0) {
150
+ return 0; // No data to process
151
+ }
152
+
153
+ burst_managed_decoder->statistics.bytes_ingested += len;
154
+ size_t bytes_consumed = 0;
155
+ while (bytes_consumed < len) {
156
+ uint8_t *data_ptr = (uint8_t *)data + bytes_consumed;
157
+ size_t data_len = len - bytes_consumed;
158
+
159
+ burst_status_t status = bust_decoder_add_data(&burst_managed_decoder->decoder, data_ptr, data_len, &bytes_consumed);
160
+
161
+ switch (status) {
162
+ case BURST_PACKET_READY: {
163
+ burst_packet_t packet = burst_decoder_get_packet(&burst_managed_decoder->decoder);
164
+
165
+ if (packet.size > 0 && burst_managed_decoder->callback_function != NULL) {
166
+ // Call the callback function with the received data
167
+ burst_managed_decoder->callback_function(packet.data, packet.size, burst_managed_decoder->user_data);
168
+ }
169
+
170
+ burst_managed_decoder->statistics.bytes_processed += packet.size;
171
+ burst_managed_decoder->statistics.packets_processed++;
172
+
173
+ continue;
174
+ }
175
+
176
+ case BURST_CRC_ERROR:
177
+ burst_managed_decoder->statistics.crc_errors++;
178
+ continue;
179
+
180
+ case BURST_DECODE_ERROR:
181
+ burst_managed_decoder->statistics.decode_errors++;
182
+ continue;
183
+
184
+ case BURST_OVERFLOW_ERROR:
185
+ burst_managed_decoder->statistics.overflow_errors++;
186
+ continue;
187
+
188
+ default:
189
+ continue;
190
+ }
191
+ }
192
+ return bytes_consumed;
193
+ }
@@ -0,0 +1,56 @@
1
+ #ifndef BURST_DECODER_H
2
+ #define BURST_DECODER_H
3
+
4
+ #include <burst/burst_generic.h>
5
+ #include <stdbool.h>
6
+ typedef int (*burst_managed_decoder_callback_t)(const uint8_t *data, size_t length, void *user_data);
7
+
8
+ typedef struct
9
+ {
10
+ uint32_t bytes_ingested; // Total number of bytes ingested
11
+ uint32_t bytes_processed; // Total number of bytes after processing
12
+ uint32_t packets_processed; // Successfully decoded packets
13
+
14
+ // Error statistics
15
+ uint32_t crc_errors;
16
+ uint32_t overflow_errors;
17
+ uint32_t decode_errors;
18
+ } burst_decoder_statistics_t;
19
+
20
+ typedef struct
21
+ {
22
+ uint8_t *buffer; // Output buffer for decoded data.
23
+ size_t buffer_size; // Size of the output buffer.
24
+ size_t out_head; // Current count of decoded bytes stored.
25
+ enum cobs_decode_inc_state
26
+ {
27
+ COBS_DECODE_READ_CODE,
28
+ COBS_DECODE_RUN,
29
+ COBS_DECODE_FINISH_RUN
30
+ } state;
31
+ uint8_t block, code;
32
+
33
+ bool finished; // true if the packet is complete and available in the buffer
34
+
35
+ burst_decoder_statistics_t statistics; // Statistics for the decoder
36
+
37
+ } burst_decoder_t;
38
+
39
+ typedef struct
40
+ {
41
+ burst_decoder_t decoder; // Decoder instance
42
+ burst_decoder_statistics_t statistics; // Statistics for the decoder
43
+ burst_managed_decoder_callback_t callback_function; // Callback function for decoded packets
44
+ void *user_data; // User data for the callback function
45
+ } burst_managed_decoder_t;
46
+
47
+ void burst_decoder_init(burst_decoder_t *ctx, uint8_t *buffer, size_t size);
48
+ void burst_decoder_reset(burst_decoder_t *ctx);
49
+ burst_status_t burst_decoder_add_byte(burst_decoder_t *ctx, uint8_t byte);
50
+ burst_status_t bust_decoder_add_data(burst_decoder_t *ctx, const uint8_t *data, size_t size, size_t *consumed_bytes);
51
+ burst_packet_t burst_decoder_get_packet(burst_decoder_t *ctx);
52
+
53
+ void burst_managed_decoder_init(burst_managed_decoder_t *burst_managed_decoder, uint8_t *buffer, size_t size, burst_managed_decoder_callback_t callback, void *user_data);
54
+ int burst_managed_decoder_handle_data(burst_managed_decoder_t *burst_managed_decoder, const uint8_t *data, size_t len);
55
+
56
+ #endif // BURST_DECODER_H
@@ -0,0 +1,120 @@
1
+ #include "burst_encoder.h"
2
+
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
+ // Compute the CRC over the raw packet data.
13
+ uint16_t crc = burst_crc16(data, size);
14
+
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) return BURST_OVERFLOW_ERROR;
24
+ size_t code_index = ctx->out_head;
25
+ ctx->buffer[ctx->out_head++] = 0; // Placeholder for the code.
26
+
27
+ // Process each byte from the raw data and then the CRC.
28
+ for (size_t i = 0; i < total_bytes; i++) {
29
+ uint8_t byte;
30
+ if (i < size)
31
+ byte = data[i];
32
+ else if (i == size)
33
+ byte = crc_high;
34
+ else // i == size + 1
35
+ byte = crc_low;
36
+
37
+ if (byte == 0) {
38
+ // Write the current code to the reserved position.
39
+ ctx->buffer[code_index] = code;
40
+ // Start a new block.
41
+ code = 1;
42
+ if (ctx->out_head >= ctx->buffer_size) return BURST_OVERFLOW_ERROR;
43
+ code_index = ctx->out_head;
44
+ ctx->buffer[ctx->out_head++] = 0; // Reserve placeholder for new code.
45
+ } else {
46
+ // Append the nonzero byte.
47
+ if (ctx->out_head >= ctx->buffer_size) return BURST_OVERFLOW_ERROR;
48
+ ctx->buffer[ctx->out_head++] = byte;
49
+ code++;
50
+ // If the maximum code value is reached, finish the block.
51
+ if (code == COBS_MAX_CODE) {
52
+ ctx->buffer[code_index] = code;
53
+ code = 1;
54
+ if (ctx->out_head >= ctx->buffer_size) return BURST_OVERFLOW_ERROR;
55
+ code_index = ctx->out_head;
56
+ ctx->buffer[ctx->out_head++] = 0; // Reserve new placeholder.
57
+ }
58
+ }
59
+ }
60
+
61
+ // Finalize the last block.
62
+ ctx->buffer[code_index] = code;
63
+
64
+ // Append the packet delimiter.
65
+ if (ctx->out_head >= ctx->buffer_size) return BURST_OVERFLOW_ERROR;
66
+ ctx->buffer[ctx->out_head++] = COBS_DELIMITER;
67
+
68
+ return BURST_PACKET_READY;
69
+ }
70
+
71
+ void burst_encoder_init(burst_encoder_t *ctx, uint8_t *buffer, size_t size) {
72
+ ctx->buffer = buffer;
73
+ ctx->buffer_size = size;
74
+ ctx->out_head = 0;
75
+ }
76
+
77
+ burst_packet_t burst_encoder_flush(burst_encoder_t *ctx) {
78
+ burst_packet_t packet;
79
+ packet.data = ctx->buffer;
80
+ packet.size = ctx->out_head;
81
+ // Reset the encoder context for the next use.
82
+ ctx->out_head = 0;
83
+ return packet;
84
+ }
85
+
86
+ void burst_managed_encoder_init(burst_managed_encoder_t *burst_managed_encoder, uint8_t *buffer, size_t size) {
87
+ burst_encoder_init(&burst_managed_encoder->encoder, buffer, size);
88
+ }
89
+
90
+ int burst_managed_encoder_add_packet(burst_managed_encoder_t *burst_managed_encoder, const uint8_t *data, size_t len) {
91
+
92
+ if (len == 0) {
93
+ return 0; // No data to process
94
+ }
95
+
96
+ burst_managed_encoder->statistics.bytes_ingested += len;
97
+
98
+ burst_status_t status = burst_encoder_add_packet(&burst_managed_encoder->encoder, data, len);
99
+
100
+ if (status == BURST_OVERFLOW_ERROR) {
101
+ // Overflow error, reset the encoder and return an error
102
+ burst_managed_encoder->statistics.overflow_errors++;
103
+ burst_managed_encoder->statistics.bytes_discarted += burst_encoder_flush(&burst_managed_encoder->encoder).size;
104
+
105
+ return -1;
106
+ }
107
+
108
+ burst_managed_encoder->statistics.packets_processed++;
109
+ return 0;
110
+ }
111
+
112
+ int burst_managed_encoder_free_space(burst_managed_encoder_t *burst_managed_encoder) {
113
+ return burst_managed_encoder->encoder.buffer_size - burst_managed_encoder->encoder.out_head;
114
+ }
115
+
116
+ burst_packet_t burst_managed_encoder_flush(burst_managed_encoder_t *burst_managed_encoder) {
117
+ burst_packet_t packet = burst_encoder_flush(&burst_managed_encoder->encoder);
118
+ burst_managed_encoder->statistics.bytes_processed += packet.size;
119
+ return packet;
120
+ }
@@ -0,0 +1,38 @@
1
+ #ifndef BURST_ENCODER_H
2
+ #define BURST_ENCODER_H
3
+
4
+ #include <burst/burst_generic.h>
5
+ #include <stdint.h>
6
+
7
+ typedef struct {
8
+ uint32_t bytes_ingested; // Total number of bytes ingested
9
+ uint32_t bytes_processed; // Total number of bytes after processing
10
+ uint32_t packets_processed; // Successfully decoded packets
11
+
12
+ // Error statistics
13
+ uint32_t overflow_errors;
14
+ uint32_t bytes_discarted; // Total number of bytes discarded
15
+
16
+ } burst_encoder_statistics_t;
17
+
18
+ typedef struct {
19
+ uint8_t *buffer; // Output buffer for encoded packets.
20
+ size_t buffer_size; // Total size of the output buffer.
21
+ size_t out_head; // Current offset (number of bytes written).
22
+ } burst_encoder_t;
23
+
24
+ typedef struct {
25
+ burst_encoder_t encoder; // Decoder instance
26
+ burst_encoder_statistics_t statistics; // Statistics for the decoder
27
+ } burst_managed_encoder_t;
28
+
29
+ // Encoder
30
+ void burst_encoder_init(burst_encoder_t *ctx, uint8_t *buffer, size_t size);
31
+ burst_status_t burst_encoder_add_packet(burst_encoder_t *ctx, const uint8_t *data, size_t size);
32
+ burst_packet_t burst_encoder_flush(burst_encoder_t *ctx);
33
+ void burst_managed_encoder_init(burst_managed_encoder_t *burst_managed_encoder, uint8_t *buffer, size_t size);
34
+ int burst_managed_encoder_add_packet(burst_managed_encoder_t *burst_managed_encoder, const uint8_t *data, size_t len);
35
+ burst_packet_t burst_managed_encoder_flush(burst_managed_encoder_t *burst_managed_encoder);
36
+ int burst_managed_encoder_free_space(burst_managed_encoder_t *burst_managed_encoder);
37
+
38
+ #endif // BURST_ENCODER_H
@@ -5,7 +5,7 @@
5
5
  * Calculate CRC16-CCITT (polynomial 0x1021, initial value 0xFFFF) over
6
6
  * the provided data.
7
7
  */
8
- uint16_t crc16_ccitt(const uint8_t *data, size_t length) {
8
+ uint16_t burst_crc16(const uint8_t *data, size_t length) {
9
9
  uint16_t crc = 0xFFFF;
10
10
  for (size_t i = 0; i < length; i++) {
11
11
  crc ^= ((uint16_t)data[i]) << 8;
@@ -0,0 +1,30 @@
1
+
2
+ #ifndef BURST_GENERIC_H
3
+ #define BURST_GENERIC_H
4
+ #include <stdint.h>
5
+ #include <stddef.h>
6
+
7
+ // Status codes returned by the encoder.
8
+ typedef enum
9
+ {
10
+ BURST_DATA_CONSUMED,
11
+ BURST_PACKET_READY,
12
+ BURST_OVERFLOW_ERROR,
13
+ BURST_ENCODE_ERROR,
14
+ BURST_CRC_ERROR,
15
+ BURST_DECODE_ERROR
16
+ } burst_status_t;
17
+
18
+ typedef struct
19
+ {
20
+ uint8_t *data;
21
+ size_t size;
22
+ } burst_packet_t;
23
+
24
+ #define COBS_DELIMITER 0x00
25
+ #define COBS_MAX_CODE 0xFF
26
+ #define CRC_SIZE sizeof(uint16_t)
27
+
28
+ uint16_t burst_crc16(const uint8_t *data, size_t length);
29
+
30
+ #endif // BURST_GENERIC_H
@@ -0,0 +1,5 @@
1
+ from burst_interface_c import BurstInterfaceC
2
+ from .main import BurstInterfacePy
3
+ from .serial_burst_interface import SerialBurstInterface
4
+
5
+ __all__ = ["BurstInterfaceC", "BurstInterfacePy", "SerialBurstInterface"]
@@ -0,0 +1,233 @@
1
+ from burst_interface_c import BurstInterfaceC
2
+ import serial
3
+ import time
4
+ import threading
5
+ import asyncio
6
+ import janus
7
+ from pydantic import BaseModel, Field
8
+
9
+ def to_si(value: float, suffix: str) -> str:
10
+ """
11
+ Convert a value to a string with SI suffix.
12
+ """
13
+ if value == 0:
14
+ return "0"
15
+ elif value < 1e-3:
16
+ return f"{value:.2f} {suffix}"
17
+ elif value < 1e3:
18
+ return f"{value:.2f} {suffix}"
19
+ elif value < 1e6:
20
+ return f"{value / 1e3:.2f} k{suffix}"
21
+ elif value < 1e9:
22
+ return f"{value / 1e6:.2f} M{suffix}"
23
+ else:
24
+ return f"{value / 1e9:.2f} G{suffix}"
25
+
26
+
27
+ class BurstSerialStatistics(BaseModel):
28
+ last_update_timestamp: float = Field(default_factory=time.time)
29
+
30
+ bytes_handled: int = 0
31
+ bytes_processed: int = 0
32
+ packets_processed: int = 0
33
+ crc_errors: int = 0
34
+ overflow_errors: int = 0
35
+ decode_errors: int = 0
36
+
37
+ handled_bytes_per_second: float = 0
38
+ processed_bytes_per_second: float = 0
39
+ processed_packets_per_second: float = 0
40
+
41
+ def update(
42
+ self,
43
+ bytes_handled,
44
+ bytes_processed,
45
+ packets_processed,
46
+ crc_errors,
47
+ overflow_errors,
48
+ decode_errors,
49
+ ):
50
+
51
+ now = time.time()
52
+ if now - self.last_update_timestamp > 1:
53
+ delta_time = now - self.last_update_timestamp
54
+ self.last_update_timestamp = now
55
+
56
+ self.handled_bytes_per_second = (
57
+ (bytes_handled - self.bytes_handled) / delta_time
58
+ )
59
+ self.processed_bytes_per_second = (
60
+ (bytes_processed - self.bytes_processed) / delta_time
61
+ )
62
+ self.processed_packets_per_second = (
63
+ (packets_processed - self.packets_processed) / delta_time
64
+ )
65
+
66
+ self.bytes_handled = bytes_handled
67
+ self.bytes_processed = bytes_processed
68
+ self.packets_processed = packets_processed
69
+ self.crc_errors = crc_errors
70
+ self.overflow_errors = overflow_errors
71
+ self.decode_errors = decode_errors
72
+
73
+ return self
74
+
75
+ def __str__(self):
76
+ return (
77
+ f"Byte Raw: {to_si(self.bytes_handled, 'B')} ({to_si(self.handled_bytes_per_second*8, 'bps')}), "
78
+ f"Bytes processed: {to_si(self.bytes_processed, 'B')} ({to_si(self.processed_bytes_per_second*8, 'bps')}), "
79
+ f"Packets processed: {self.packets_processed} ({to_si(self.processed_packets_per_second, 'packets/s')}), "
80
+ f"Errors (CRC: {self.crc_errors}, Overflow: {self.overflow_errors}, Decode: {self.decode_errors})"
81
+ )
82
+
83
+ class SerialBurstInterface:
84
+ debug_timings = False
85
+ debug_io = False
86
+
87
+ kill = False
88
+ block_size = 1000
89
+ RATE_CHECK_INTERVAL = 1
90
+
91
+ interface: BurstInterfaceC
92
+ last_rate_timestamp: float = 0
93
+
94
+ statitsics: BurstSerialStatistics
95
+
96
+ @classmethod
97
+ def from_serial(cls, port: str, bitrate: int):
98
+ serial_handle: serial.Serial = serial.Serial(port, bitrate, timeout=0.5)
99
+ serial_handle.set_buffer_size(rx_size=100 * 1024, tx_size=100 * 1024) # type: ignore
100
+ return cls(serial_handle)
101
+
102
+ def __init__(self, serial_handle: serial.Serial):
103
+ self.handle = serial_handle
104
+ self.handle.reset_input_buffer()
105
+ self.handle.reset_output_buffer()
106
+
107
+ self.current_stats = BurstSerialStatistics()
108
+ self.statitsics = BurstSerialStatistics()
109
+
110
+ self.receive_task_handle = threading.Thread(target=self.receive_task, daemon=True)
111
+ self.transmit_task_handle = threading.Thread(target=self.transmit_task, daemon=True)
112
+
113
+ self.interface = BurstInterfaceC()
114
+ self.transmit_packet_queue = janus.Queue()
115
+ self.receive_packet_queue = janus.Queue()
116
+
117
+ self.receive_task_handle.start()
118
+ self.transmit_task_handle.start()
119
+
120
+ @property
121
+ def statistics(self):
122
+ return self.current_stats.update(
123
+ self.interface.bytes_handled,
124
+ self.interface.bytes_processed,
125
+ self.interface.packets_processed,
126
+ self.interface.crc_errors,
127
+ self.interface.overflow_errors,
128
+ self.interface.decode_errors,
129
+ )
130
+
131
+ def close(self):
132
+ self.kill = True
133
+ self.handle.close()
134
+ self.transmit_packet_queue.close()
135
+ self.receive_packet_queue.close()
136
+
137
+ def receive_task(self):
138
+ try:
139
+ while True:
140
+ # Read incoming data
141
+ data = self.handle.read(self.block_size)
142
+
143
+ if self.kill:
144
+ break
145
+
146
+ if data:
147
+ if self.debug_io:
148
+ print(f"Received burst frame: {' '.join([f'{x:02X}' for x in data])}, length: {len(data)}")
149
+ try:
150
+ decoded_packets = self.interface.decode(data, fail_on_crc_error=True)
151
+ except Exception as e:
152
+ print(f"Error decoding: {e}")
153
+ continue
154
+
155
+ for packet in decoded_packets:
156
+ # put all packets in the receive queue
157
+ if self.debug_io:
158
+ print(f"Received: {packet}")
159
+
160
+ self.receive_packet_queue.sync_q.put(packet)
161
+
162
+ time.sleep(0.001)
163
+
164
+ except Exception as e:
165
+ print(f"Error in read task: {e}")
166
+ self.close()
167
+
168
+ def transmit_task(self):
169
+ try:
170
+ while True:
171
+ packet = self.transmit_packet_queue.sync_q.get()
172
+ if self.debug_io:
173
+ print(f"Transmitting packet: {' '.join([f'{x:02X}' for x in packet])}")
174
+
175
+ data = self.interface.encode([packet])
176
+
177
+ if self.debug_io:
178
+ from cobs import cobs
179
+
180
+ daat = cobs.decode(data[:-1])
181
+ # print in space separated hex
182
+ print(f"Transmitting burst frame: {' '.join([f'{x:02X}' for x in daat])}")
183
+
184
+ # print raw frame
185
+ print(f"Transmitting 'raw' burst frame: {' '.join([f'{x:02X}' for x in data])}")
186
+ self.handle.write(data)
187
+
188
+ except Exception as e:
189
+ print(f"Error in transmit task: {e}")
190
+ self.close()
191
+
192
+ async def send(self, data: bytes):
193
+ await self.transmit_packet_queue.async_q.put(data)
194
+
195
+ async def flush_receive_queue(self):
196
+ while not self.receive_packet_queue.async_q.empty():
197
+ self.receive_packet_queue.async_q.get_nowait()
198
+
199
+ async def send_with_response(self, data: bytes):
200
+ # Flush all other packets
201
+ await self.flush_receive_queue()
202
+
203
+ start_time = time.time()
204
+ await self.transmit_packet_queue.async_q.put(data)
205
+ response = await self.receive_packet_queue.async_q.get()
206
+ end_time = time.time()
207
+
208
+ if self.debug_timings:
209
+ print(f"Time taken: {(end_time - start_time) * 1000} ms for {len(data)} bytes")
210
+
211
+ return response
212
+
213
+ async def receive(self):
214
+ return await self.receive_packet_queue.async_q.get()
215
+
216
+ def receive_all(self):
217
+ packets = []
218
+ while not self.receive_packet_queue.sync_q.empty():
219
+ packets.append(self.receive_packet_queue.sync_q.get())
220
+ return packets
221
+
222
+
223
+ async def main():
224
+ interface = SerialBurstInterface.from_serial("COM4", 115200)
225
+
226
+ for i in range(1000):
227
+ response = await interface.send_with_response(10 * f"Hello World {i}!".encode())
228
+ print(f"Received: {response}")
229
+ await asyncio.sleep(0.01)
230
+
231
+
232
+ if __name__ == "__main__":
233
+ asyncio.run(main())
@@ -0,0 +1,7 @@
1
+ #ifndef BURST_INTERFACE_H
2
+ #define BURST_INTERFACE_H
3
+
4
+ #include "burst/burst_encoder.h"
5
+ #include "burst/burst_decoder.h"
6
+
7
+ #endif
@@ -0,0 +1,75 @@
1
+ #include <nanobind/nanobind.h>
2
+ extern "C" {
3
+ #include <burst_link_protocol.h>
4
+ }
5
+
6
+ namespace nb = nanobind;
7
+ using namespace nb::literals;
8
+
9
+ struct BurstInterface {
10
+ burst_managed_decoder_t decoder = {0};
11
+ uint8_t decoder_buffer[1024] = {0};
12
+ burst_encoder_t encoder = {0};
13
+ uint8_t encoder_buffer[1024] = {0};
14
+
15
+ // Create buffer to hold the the vector of bytes likt a list
16
+
17
+ nb::list result;
18
+
19
+ BurstInterface() {
20
+ burst_managed_decoder_init(&decoder, decoder_buffer, sizeof(decoder_buffer), add_packet, this);
21
+ burst_encoder_init(&encoder, encoder_buffer, sizeof(encoder_buffer));
22
+ }
23
+
24
+ static int add_packet(const uint8_t *data, size_t size, void *user_data) {
25
+ // Create a bytes object from the data and append it to the result list
26
+ // nb::bytes packet_bytes(reinterpret_cast<const char *>(data), size);
27
+ // result.append(packet_bytes);
28
+ BurstInterface *self = static_cast<BurstInterface *>(user_data);
29
+ nb::bytes packet_bytes(reinterpret_cast<const char *>(data), size);
30
+ self->result.append(packet_bytes); // Append the packet to the result list
31
+ return 0; // Return 0 to indicate success
32
+ }
33
+ nb::list decode(nb::bytes data, bool fail_on_crc_error = false) {
34
+ result.clear(); // Clear the result list before returning
35
+ burst_managed_decoder_handle_data(&decoder, (uint8_t *)data.data(), data.size());
36
+
37
+ return result; // Return the result list containing the decoded packets
38
+ }
39
+
40
+ nb::bytes encode(nb::list data) {
41
+ for (size_t i = 0; i < data.size(); i++) {
42
+ nb::bytes data_bytes = data[i];
43
+ burst_encoder_add_packet(&encoder, (uint8_t *)data_bytes.data(), data_bytes.size());
44
+ }
45
+ // flush the encoder
46
+ burst_packet_t packet = burst_encoder_flush(&encoder);
47
+ return nb::bytes(reinterpret_cast<const char *>(packet.data), packet.size);
48
+ }
49
+
50
+ uint32_t get_bytes_ingested() { return decoder.statistics.bytes_ingested; }
51
+ uint32_t get_bytes_processed() { return decoder.statistics.bytes_processed; }
52
+ uint32_t get_packets_processed() { return decoder.statistics.packets_processed; }
53
+
54
+ uint32_t get_crc_error_count() { return decoder.statistics.crc_errors; }
55
+ uint32_t get_overrun_count() { return decoder.statistics.overflow_errors; }
56
+ uint32_t get_packet_error_count() { return decoder.statistics.decode_errors; }
57
+
58
+ // Statistics
59
+ };
60
+
61
+ NB_MODULE(burst_interface_c, m) {
62
+ nb::set_leak_warnings(false);
63
+
64
+ nb::class_<BurstInterface>(m, "BurstInterfaceC")
65
+ .def(nb::init<>())
66
+ .def("decode", &BurstInterface::decode, "data"_a, "fail_on_crc_error"_a = false)
67
+ .def("encode", &BurstInterface::encode, "packets"_a)
68
+ // Use a lambda
69
+ .def_prop_ro("bytes_handled", &BurstInterface::get_bytes_ingested)
70
+ .def_prop_ro("bytes_processed", &BurstInterface::get_bytes_processed)
71
+ .def_prop_ro("packets_processed", &BurstInterface::get_packets_processed)
72
+ .def_prop_ro("crc_errors", &BurstInterface::get_crc_error_count)
73
+ .def_prop_ro("overflow_errors", &BurstInterface::get_overrun_count)
74
+ .def_prop_ro("decode_errors", &BurstInterface::get_packet_error_count);
75
+ }
@@ -0,0 +1,25 @@
1
+ from burst_link_protocol import BurstInterfaceC, BurstInterfacePy
2
+
3
+ payloads = [
4
+ # Malformed packet
5
+ ("04 01 ff 0e 06 d3 23 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 03 85 1f 00 0b 02 04 02 07 02 0b 02 04 02 08 02 0b 02 04 02 0c 02 0b 02 02 02 0d 02 0b 02 02 03 aa 94 00 04 02 ff 0e 03 d3 23 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 03 85 1f 00")
6
+ # Good packet
7
+ ("04 02 ff 0e 03 e1 23 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 03 99 fe 00")
8
+ ("04 01 ff 0e 06 ef be ad de c1 02 0b 02 01 02 01 02 0b 02 04 02 02 02 0b 02 04 02 03 02 0b 02 04 02 04 02 0b 02 04 02 05 02 0b 02 04 02 06 02 0b 02 04 02 a6 02 0b 02 04 02 07 02 0b 02 04 02 08 02 0b 02 04 02 0c 02 0b 02 02 02 0d 02 0b 02 02 03 aa 94 00")
9
+ ]
10
+
11
+
12
+ def test_multi_encoded_payloads():
13
+ c_interface = BurstInterfaceC()
14
+ py_interface = BurstInterfacePy()
15
+ for payload in payloads:
16
+ payload = bytes.fromhex(payload.replace(" ", ""))
17
+ print(f"Testing payload: {payload.hex()}")
18
+ py_decoded_packet = py_interface.decode(payload)
19
+ print(f"Decoded payload: {py_decoded_packet}")
20
+ c_decoded_packet = c_interface.decode(payload, fail_on_crc_error=True)
21
+ print(f"Decoded payload: {c_decoded_packet}")
22
+
23
+
24
+ if __name__ == "__main__":
25
+ test_multi_encoded_payloads()
@@ -4,13 +4,15 @@ requires-python = ">=3.10, <4.0"
4
4
 
5
5
  [[package]]
6
6
  name = "burst-link-protocol"
7
- version = "1.0.3"
7
+ version = "1.1.4"
8
8
  source = { editable = "." }
9
9
  dependencies = [
10
10
  { name = "cobs" },
11
11
  { name = "crc" },
12
+ { name = "janus" },
12
13
  { name = "nanobind" },
13
14
  { name = "numpy" },
15
+ { name = "pyserial" },
14
16
  { name = "pytest" },
15
17
  { name = "pytest-benchmark" },
16
18
  { name = "pytest-cov" },
@@ -30,9 +32,11 @@ dev = [
30
32
  requires-dist = [
31
33
  { name = "cobs", specifier = ">=1.2.1,<2.0.0" },
32
34
  { name = "crc", specifier = ">=7.1.0,<8.0.0" },
35
+ { name = "janus", specifier = ">=2.0.0" },
33
36
  { name = "nanobind", specifier = ">=2.5.0" },
34
37
  { name = "nanobind", marker = "extra == 'dev'", specifier = ">=2.5.0,<3.0.0" },
35
38
  { name = "numpy", specifier = ">=2.2.3,<3.0.0" },
39
+ { name = "pyserial", specifier = ">=3.5" },
36
40
  { name = "pytest", specifier = ">=8.3.4,<9.0.0" },
37
41
  { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.4,<9.0.0" },
38
42
  { name = "pytest-benchmark", specifier = ">=5.1.0,<6.0.0" },
@@ -161,6 +165,15 @@ wheels = [
161
165
  { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 },
162
166
  ]
163
167
 
168
+ [[package]]
169
+ name = "janus"
170
+ version = "2.0.0"
171
+ source = { registry = "https://pypi.org/simple" }
172
+ sdist = { url = "https://files.pythonhosted.org/packages/d8/7f/69884b6618be4baf6ebcacc716ee8680a842428a19f403db6d1c0bb990aa/janus-2.0.0.tar.gz", hash = "sha256:0970f38e0e725400496c834a368a67ee551dc3b5ad0a257e132f5b46f2e77770", size = 22910 }
173
+ wheels = [
174
+ { url = "https://files.pythonhosted.org/packages/68/34/65604740edcb20e1bda6a890348ed7d282e7dd23aa00401cbe36fd0edbd9/janus-2.0.0-py3-none-any.whl", hash = "sha256:7e6449d34eab04cd016befbd7d8c0d8acaaaab67cb59e076a69149f9031745f9", size = 12161 },
175
+ ]
176
+
164
177
  [[package]]
165
178
  name = "nanobind"
166
179
  version = "2.5.0"
@@ -268,6 +281,15 @@ wheels = [
268
281
  { url = "https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5", size = 22335 },
269
282
  ]
270
283
 
284
+ [[package]]
285
+ name = "pyserial"
286
+ version = "3.5"
287
+ source = { registry = "https://pypi.org/simple" }
288
+ sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125 }
289
+ wheels = [
290
+ { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585 },
291
+ ]
292
+
271
293
  [[package]]
272
294
  name = "pytest"
273
295
  version = "8.3.5"
@@ -1,11 +0,0 @@
1
- version: 2
2
- updates:
3
- # Maintain dependencies for GitHub Actions
4
- - package-ecosystem: "github-actions"
5
- directory: "/"
6
- schedule:
7
- interval: "weekly"
8
- groups:
9
- actions:
10
- patterns:
11
- - "*"
@@ -1,9 +0,0 @@
1
- zephyr_library_named(burst-link-protocol)
2
- zephyr_library_sources(
3
- "../../src/decoder.c"
4
- "../../src/encoder.c"
5
- "../../src/crc.c"
6
- )
7
-
8
- target_include_directories(burst-link-protocol PUBLIC ../../src)
9
-
@@ -1,56 +0,0 @@
1
- #ifndef BURST_INTERFACE_H
2
- #define BURST_INTERFACE_H
3
-
4
- #include <stdbool.h>
5
- #include <stddef.h>
6
- #include <stdint.h>
7
-
8
- #define COBS_DELIMITER 0x00
9
- #define COBS_MAX_CODE 0xFF
10
-
11
- // Status codes returned by the encoder.
12
- typedef enum { BURST_DATA_CONSUMED, BURST_PACKET_READY, BURST_OVERFLOW_ERROR, BURST_ENCODE_ERROR, BURST_CRC_ERROR, BURST_DECODE_ERROR } burst_status_t;
13
-
14
- typedef struct {
15
- uint8_t *data;
16
- size_t size;
17
- } burst_packet_t;
18
-
19
- typedef struct {
20
- uint8_t *buffer; // Output buffer for encoded packets.
21
- size_t buffer_size; // Total size of the output buffer.
22
- size_t out_head; // Current offset (number of bytes written).
23
- } burst_encoder_t;
24
-
25
- typedef struct {
26
- uint8_t *buffer; // Output buffer for decoded data.
27
- size_t buffer_size; // Size of the output buffer.
28
- size_t out_head; // Current count of decoded bytes stored.
29
-
30
- // uint8_t remaining_bytes; // Current block’s code byte; 0 indicates a new block is
31
- // expected.
32
- // uint8_t bytes_remaining; // Number of data bytes left to copy for the current
33
- // block.
34
- // bool pending_zero; // true if a zero should be inserted before starting the
35
- // next block.
36
-
37
- enum cobs_decode_inc_state { COBS_DECODE_READ_CODE, COBS_DECODE_RUN, COBS_DECODE_FINISH_RUN } state;
38
- uint8_t block, code;
39
-
40
- bool finished; // true if the packet is complete and available in the buffer
41
- } burst_decoder_t;
42
-
43
- // Encoder
44
- void burst_encoder_init(burst_encoder_t *ctx, uint8_t *buffer, size_t size);
45
- burst_status_t burst_encoder_add_packet(burst_encoder_t *ctx, const uint8_t *data, size_t size);
46
- burst_packet_t burst_encoder_flush(burst_encoder_t *ctx);
47
-
48
- // Decoder
49
- void burst_decoder_init(burst_decoder_t *ctx, uint8_t *buffer, size_t size);
50
- burst_status_t bust_decoder_add_data(burst_decoder_t *ctx, const uint8_t *data, size_t size, size_t *consumed_bytes);
51
- void burst_decoder_reset(burst_decoder_t *ctx);
52
- burst_status_t burst_decoder_add_byte(burst_decoder_t *ctx, uint8_t byte);
53
-
54
- burst_packet_t burst_decoder_get_packet(burst_decoder_t *ctx);
55
-
56
- #endif // ENCODER_H
@@ -1,4 +0,0 @@
1
- from burst_interface_c import BurstInterfaceC
2
- from .main import BurstInterfacePy
3
-
4
- __all__ = ["BurstInterfaceC", "BurstInterfacePy"]
@@ -1,4 +0,0 @@
1
- #include <stdint.h>
2
-
3
- uint16_t crc16_ccitt(const uint8_t *data, size_t length);
4
- #define CRC_SIZE sizeof(uint16_t)
@@ -1,92 +0,0 @@
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
-
16
- uint8_t crc_high = (crc >> 8) & 0xFF;
17
- uint8_t crc_low = crc & 0xFF;
18
- // The total number of bytes to encode: raw data + 2 bytes of CRC.
19
- size_t total_bytes = size + CRC_SIZE;
20
-
21
- // Initialize COBS block state.
22
- uint8_t code = 1; // Code value starts at 1.
23
- // Reserve space for the code byte.
24
- if (ctx->out_head >= ctx->buffer_size)
25
- return BURST_OVERFLOW_ERROR;
26
- size_t code_index = ctx->out_head;
27
- ctx->buffer[ctx->out_head++] = 0; // Placeholder for the code.
28
-
29
- // Process each byte from the raw data and then the CRC.
30
- for (size_t i = 0; i < total_bytes; i++) {
31
- uint8_t byte;
32
- if (i < size)
33
- byte = data[i];
34
- else if (i == size)
35
- byte = crc_high;
36
- else // i == size + 1
37
- byte = crc_low;
38
-
39
- if (byte == 0) {
40
- // Write the current code to the reserved position.
41
- ctx->buffer[code_index] = code;
42
- // Start a new block.
43
- code = 1;
44
- if (ctx->out_head >= ctx->buffer_size)
45
- return BURST_OVERFLOW_ERROR;
46
- code_index = ctx->out_head;
47
- ctx->buffer[ctx->out_head++] = 0; // Reserve placeholder for new code.
48
- } else {
49
- // Append the nonzero byte.
50
- if (ctx->out_head >= ctx->buffer_size)
51
- return BURST_OVERFLOW_ERROR;
52
- ctx->buffer[ctx->out_head++] = byte;
53
- code++;
54
- // If the maximum code value is reached, finish the block.
55
- if (code == COBS_MAX_CODE) {
56
- ctx->buffer[code_index] = code;
57
- code = 1;
58
- if (ctx->out_head >= ctx->buffer_size)
59
- return BURST_OVERFLOW_ERROR;
60
- code_index = ctx->out_head;
61
- ctx->buffer[ctx->out_head++] = 0; // Reserve new placeholder.
62
- }
63
- }
64
- }
65
-
66
- // Finalize the last block.
67
- ctx->buffer[code_index] = code;
68
-
69
- // Append the packet delimiter.
70
- if (ctx->out_head >= ctx->buffer_size)
71
- return BURST_OVERFLOW_ERROR;
72
- ctx->buffer[ctx->out_head++] = COBS_DELIMITER;
73
-
74
- return BURST_PACKET_READY;
75
- }
76
-
77
- void burst_encoder_init(burst_encoder_t *ctx, uint8_t *buffer, size_t size)
78
- {
79
- ctx->buffer = buffer;
80
- ctx->buffer_size = size;
81
- ctx->out_head = 0;
82
- }
83
-
84
- burst_packet_t burst_encoder_flush(burst_encoder_t *ctx)
85
- {
86
- burst_packet_t packet;
87
- packet.data = ctx->buffer;
88
- packet.size = ctx->out_head;
89
- // Reset the encoder context for the next use.
90
- ctx->out_head = 0;
91
- return packet;
92
- }
@@ -1,80 +0,0 @@
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
- }