commit 9eb3879aab6d8b9776fcdc02c0c3b9414177cf74 Author: CaoWangrenbo Date: Wed Feb 19 16:09:44 2025 +0800 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a3585c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,290 @@ +# Created by https://www.toptal.com/developers/gitignore/api/c,c++,visualstudiocode,python,cmake +# Edit at https://www.toptal.com/developers/gitignore?templates=c,c++,visualstudiocode,python,cmake + +### C ### +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +### C++ ### +# Prerequisites + +# Compiled Object files +*.slo + +# Precompiled Headers + +# Compiled Dynamic libraries + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai + +# Executables + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +CMakeUserPresets.json + +# External projects +*-prefix/ + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/c,c++,visualstudiocode,python,cmake + +# 排除生成产物 +!serial_module.so + diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..325b0a1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "files.associations": { + "hx_serial.h": "c", + "stdlib.h": "c", + "fec.h": "c", + "unistd.h": "c", + "hx_ringbuffer.h": "c", + "zlib.h": "c" + } +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b78a879 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.10) +project(serial_module) + +# 查找 Python 开发包 +set(Python3_ROOT_DIR ~/miniconda3/envs/videomea_deploy) +find_package(Python3 REQUIRED COMPONENTS Development) + +# 添加 Python 头文件路径 +include_directories(${Python3_INCLUDE_DIRS}) + +# 添加系统头文件路径(如果 hx_serial.h 在默认位置,可以省略) +# include_directories(/usr/include /usr/local/include) + +# 添加源文件 +set(SOURCES serial_module.c hx_ringbuffer.c) + +# 创建 Python 模块 +Python3_add_library(serial_module MODULE ${SOURCES}) + +find_package(ZLIB REQUIRED) + +target_link_libraries(serial_module PRIVATE hx-serial fec ${ZLIB_LIBRARIES}) + +# 设置输出目录和文件名 +set_target_properties(serial_module PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR} + PREFIX "" + SUFFIX ".so" +) \ No newline at end of file diff --git a/hx_ringbuffer.c b/hx_ringbuffer.c new file mode 100644 index 0000000..572df63 --- /dev/null +++ b/hx_ringbuffer.c @@ -0,0 +1,123 @@ +#include +#include +#include + +#include "hx_ringbuffer.h" + +int by_ringbuf_init(by_ringbuf_t *rb, int size) { +#ifdef BY_RINGBUF_STATIC_ALLOC + // 使用预先分配的内存 + static uint8_t static_buffer[BY_RINGBUF_STATIC_BUFFER_SIZE]; + + if (size > sizeof(static_buffer)) { + return -1; // 预先分配的内存不足 + } + rb->buffer = static_buffer; +#else + // 使用 malloc 动态分配内存 + rb->buffer = (uint8_t *)malloc(size); + if (rb->buffer == NULL) { + return -1; // 分配内存失败 + } +#endif + + rb->size = size; + rb->head = 0; + rb->tail = 0; + rb->is_full = false; + return 0; +} + +void by_ringbuf_free(by_ringbuf_t *rb) { +#ifndef BY_RINGBUF_STATIC_ALLOC + // 只有使用 malloc 分配的内存才需要释放 + if (rb->buffer) { + free(rb->buffer); + rb->buffer = NULL; + } +#endif + + rb->size = 0; + rb->head = 0; + rb->tail = 0; + rb->is_full = false; +} + +int by_ringbuf_append(by_ringbuf_t *rb, const uint8_t *data, int len) { + if (len > by_ringbuf_available_space(rb)) { + return -1; // 缓冲区空间不足 + } + + for (int i = 0; i < len; i++) { + rb->buffer[rb->head] = data[i]; + rb->head = (rb->head + 1) % rb->size; + if (rb->head == rb->tail) { + rb->is_full = true; + } + } + + return len; +} + +int by_ringbuf_pop(by_ringbuf_t *rb, uint8_t *data, int len) { + if (len > by_ringbuf_available_data(rb)) { + return -1; // 数据不足 + } + + for (int i = 0; i < len; i++) { + data[i] = rb->buffer[rb->tail]; + rb->tail = (rb->tail + 1) % rb->size; + rb->is_full = false; + } + + return len; +} + +int by_ringbuf_find(by_ringbuf_t *rb, const uint8_t *pattern, int pattern_len) { + int available_data = by_ringbuf_available_data(rb); + if (pattern_len > available_data) { + return -1; // 数据不足,无法查找 + } + + for (int i = 0; i <= available_data - pattern_len; i++) { + int match = 1; + for (int j = 0; j < pattern_len; j++) { + int index = (rb->tail + i + j) % rb->size; + if (rb->buffer[index] != pattern[j]) { + match = 0; + break; + } + } + if (match) { + return i; // 返回匹配的起始位置 + } + } + + return -1; // 未找到匹配 +} + +int by_ringbuf_available_data(by_ringbuf_t *rb) { + if (rb->is_full) { + return rb->size; + } + return (rb->head - rb->tail + rb->size) % rb->size; +} + +int by_ringbuf_available_space(by_ringbuf_t *rb) { + return rb->size - by_ringbuf_available_data(rb); +} + +// 调试函数:打印环形缓冲区中的数据及其序号 +void by_ringbuf_debug_print(by_ringbuf_t *rb) { + int available_data = by_ringbuf_available_data(rb); + if (available_data == 0) { + printf("Ring buffer is empty.\n"); + return; + } + + printf("Ring buffer content (size: %d, available data: %d):\n", rb->size, available_data); + for (int i = 0; i < available_data; i++) { + int index = (rb->tail + i) % rb->size; + printf("[%04d] 0x%02X\n", i, rb->buffer[index]); + } +} \ No newline at end of file diff --git a/hx_ringbuffer.h b/hx_ringbuffer.h new file mode 100644 index 0000000..8577a35 --- /dev/null +++ b/hx_ringbuffer.h @@ -0,0 +1,46 @@ +#ifndef _BY_RINGBUF_H__ +#define _BY_RINGBUF_H__ + +#include +#include + +// 定义宏来选择内存分配方式 +// 如果定义了 BY_RINGBUF_STATIC_ALLOC,则使用预先分配的内存 +// 否则使用 malloc 动态分配内存 +// #define BY_RINGBUF_STATIC_ALLOC + +#define BY_RINGBUF_STATIC_BUFFER_SIZE (8192) + +typedef struct { + uint8_t *buffer; // 缓冲区指针 + int size; // 缓冲区大小 + int head; // 写指针 + int tail; // 读指针 + bool is_full; // 缓冲区是否已满 +} by_ringbuf_t; + +// 初始化环形缓冲区 +extern int by_ringbuf_init(by_ringbuf_t *rb, int size); + +// 释放环形缓冲区 +extern void by_ringbuf_free(by_ringbuf_t *rb); + +// 向缓冲区追加数据 +extern int by_ringbuf_append(by_ringbuf_t *rb, const uint8_t *data, int len); + +// 从缓冲区弹出数据 +extern int by_ringbuf_pop(by_ringbuf_t *rb, uint8_t *data, int len); + +// 查找缓冲区中是否包含特定数据 +extern int by_ringbuf_find(by_ringbuf_t *rb, const uint8_t *pattern, int pattern_len); + +// 获取缓冲区中可用数据的大小 +extern int by_ringbuf_available_data(by_ringbuf_t *rb); + +// 获取缓冲区中空闲空间的大小 +extern int by_ringbuf_available_space(by_ringbuf_t *rb); + +// 调试函数:打印环形缓冲区中的数据及其序号 +extern void by_ringbuf_debug_print(by_ringbuf_t *rb); + +#endif // _BY_RINGBUF_H__ \ No newline at end of file diff --git a/jpeg_corrupt_test.py b/jpeg_corrupt_test.py new file mode 100644 index 0000000..602eb94 --- /dev/null +++ b/jpeg_corrupt_test.py @@ -0,0 +1,81 @@ +import cv2 +import numpy as np + +def corrupt_data(data, corruption_type='modify', severity=1): + """ + 对二进制数据进行损坏 + :param data: 原始二进制数据 + :param corruption_type: 损坏类型 ('modify', 'shift', 'loss') + :param severity: 损坏严重程度 (1-10) + :return: 损坏后的二进制数据 + """ + data = bytearray(data) # 转换为可变的 bytearray + length = len(data) + + if corruption_type == 'modify': + # 修改部分数据 + for i in range(severity * 5): + idx = np.random.randint(0, length) + data[idx] = np.random.randint(0, 256) + + elif corruption_type == 'shift': + # 数据移位 + shift = severity * 10 + data = data[shift:] + data[:shift] + + elif corruption_type == 'loss': + # 数据丢失 + loss_start = np.random.randint(0, length - severity * 10) + data[loss_start:loss_start + severity * 10] = b'\x00' * (severity * 10) + + return bytes(data) + +def main(): + # 打开摄像头 + cap = cv2.VideoCapture(0) + + if not cap.isOpened(): + print("无法打开摄像头") + return + + while True: + # 读取一帧图像 + ret, frame = cap.read() + if not ret: + print("无法读取图像") + break + + frame = cv2.resize(frame, (224, 224)) + + # 显示原始图像 + cv2.imshow('Original Image', frame) + + # 将图像编码为 JPEG 格式的二进制数据 + ret, jpeg_data = cv2.imencode('.jpg', frame) + if not ret: + print("图像编码失败") + break + + # 对二进制数据进行损坏 + corrupted_data = corrupt_data(jpeg_data, corruption_type='modify', severity=2) + + # 解码损坏后的二进制数据 + corrupted_image = cv2.imdecode(np.frombuffer(corrupted_data, dtype=np.uint8), cv2.IMREAD_COLOR) + + if corrupted_image is None: + print("损坏后的数据无法解码") + else: + # 显示损坏后的图像 + cv2.imshow('Corrupted Image', corrupted_image) + + # 等待用户按键 + key = cv2.waitKey(0) + if key == ord('q'): # 按下 'q' 键退出 + break + + # 释放摄像头并关闭所有窗口 + cap.release() + cv2.destroyAllWindows() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/recv_test b/recv_test new file mode 100755 index 0000000..cc3bb3d Binary files /dev/null and b/recv_test differ diff --git a/recv_test.c b/recv_test.c new file mode 100644 index 0000000..0900bdc --- /dev/null +++ b/recv_test.c @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#include "hx_serial.h" +#include "hx_ringbuffer.h" + +#define FRAME_HEADER_1 0x55 +#define FRAME_HEADER_2 0xAA +#define MAX_DATA_LEN 232 +#define CRC32_LEN 4 +#define FRAME_HEADER_LEN 2 +#define FRAME_SEQ_LEN 1 +#define DATA_LEN_LEN 1 +#define FRAME_OVERHEAD (FRAME_HEADER_LEN + FRAME_SEQ_LEN + DATA_LEN_LEN + CRC32_LEN) +#define MAX_FRAME_LEN (FRAME_OVERHEAD + MAX_DATA_LEN) + +// CRC32 校验函数(假设已经实现) +uint32_t crc32(const uint8_t *data, int len); + +// 解析一帧数据 +int parse_frame(by_ringbuf_t *ringbuf, uint8_t *output_data, int *output_len) { + uint8_t frame[MAX_FRAME_LEN]; + int available_data = by_ringbuf_available_data(ringbuf); + // printf("Available data: %d\n", available_data); + // 检查是否有足够的数据解析一帧 + if (available_data < FRAME_OVERHEAD) { + return -1; // 数据不足,无法解析 + }else{ + // by_ringbuf_debug_print(ringbuf); + } + + // 查找帧头 + uint8_t header[2] = {FRAME_HEADER_1, FRAME_HEADER_2}; + int header_pos = by_ringbuf_find(ringbuf, header, 2); + printf("Header pos: %d\n", header_pos); + if (header_pos < 0) { + return -1; // 没有找到帧头 + } + + // 弹出帧头之前的数据 + by_ringbuf_pop(ringbuf, frame, header_pos); + + // 检查是否有足够的数据解析一帧 + if (by_ringbuf_available_data(ringbuf) < MAX_FRAME_LEN) { + return -1; // 数据不足,无法解析 + } + + // 读取帧数据 + by_ringbuf_pop(ringbuf, frame, MAX_FRAME_LEN); + + // 解析帧序号、有效数据长度、数据段和 CRC32 + uint8_t seq = frame[FRAME_HEADER_LEN]; + uint8_t valid_data_len = frame[FRAME_HEADER_LEN + FRAME_SEQ_LEN]; + uint8_t *data_segment = &frame[FRAME_HEADER_LEN + FRAME_SEQ_LEN + DATA_LEN_LEN]; + uint32_t received_crc = *(uint32_t *)&frame[FRAME_HEADER_LEN + FRAME_SEQ_LEN + DATA_LEN_LEN + MAX_DATA_LEN]; + + printf("Received frame: %d, valid_data_len: %d, received_crc: %08X\n", seq, valid_data_len, received_crc); + + // // 计算 CRC32 校验 + // uint32_t calculated_crc = crc32(data_segment, valid_data_len); + // if (received_crc != calculated_crc) { + // return -1; // CRC 校验失败,丢弃该帧 + // } + + // 将有效数据拼接到输出缓冲区 + memcpy(&output_data[*output_len], data_segment, valid_data_len); + *output_len += valid_data_len; + + // for(int i = 0; i < valid_data_len; i++){ + // printf("0x%02X ", data_segment[i]); + // } + + // 判断是否为最后一帧 + if (valid_data_len < MAX_DATA_LEN) { + return 1; // 最后一帧,解析完成 + } + + return 0; // 成功解析一帧,但可能还有更多帧 +} + +// 边读取边解析串口数据 +int parse_serial_data(by_serial_t *serial_port, by_ringbuf_t *ringbuf, uint8_t *output_data, int *output_len) { + uint8_t buffer[MAX_FRAME_LEN]; + int ret; + + // 从串口读取数据到环形缓冲区 + ret = by_serial_read(serial_port, buffer, MAX_FRAME_LEN); + if (ret > 0) { + by_ringbuf_append(ringbuf, buffer, ret); + } + + // 尝试解析环形缓冲区中的数据 + while (1) { + int parse_result = parse_frame(ringbuf, output_data, output_len); + if (parse_result == 1) { + // printf("Parsed data length: %d\n", *output_len); + return 0; // 解析完成 + } else if (parse_result == -1) { + // printf("Failed to parse frame\n"); + break; // 数据不足或解析失败,退出循环 + } + } + + return -1; // 未找到完整帧 +} + +// 示例 CRC32 校验函数(需要根据实际情况实现) +uint32_t crc32(const uint8_t *data, int len) { + // 这里实现 CRC32 校验算法 + // 例如使用查表法或其他方法计算 CRC32 + return 0; // 返回计算出的 CRC32 值 +} + +int main() { + by_serial_t serial_port; + by_ringbuf_t ringbuf; + uint8_t output_data[4096]; + int output_len = 0; + + // 初始化串口和环形缓冲区 + by_serial_init(&serial_port, "/dev/ttyUSB1"); + by_ringbuf_init(&ringbuf, 4096); + + // 边读取边解析串口数据 + while (1) { + if (parse_serial_data(&serial_port, &ringbuf, output_data, &output_len) == 0) { + printf("Parsed data length: %d\n", output_len); + // 处理解析后的数据 + break; // 解析完成,退出循环 + } else { + // printf("Waiting for more data...\n"); + } + + usleep(1000); + } + + // 释放资源 + by_ringbuf_free(&ringbuf); + return 0; +} \ No newline at end of file diff --git a/serial_data.txt b/serial_data.txt new file mode 100644 index 0000000..a9513cf --- /dev/null +++ b/serial_data.txt @@ -0,0 +1 @@ +55 AA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4B 13 47 E7 FC 55 AA 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F6 DB 09 E1 FC 55 AA 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 31 82 DA EB FC 55 AA 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FC 00 00 00 00 00 00 00 00 00 00 00 00 8C 4A 94 ED FC 55 AA 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 BF 31 7C FE FD 55 AA 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 F9 32 F8 FC 55 AA 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C5 A0 E1 F2 FC 55 AA 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FC 00 00 00 00 00 00 00 00 00 00 00 78 68 AF F4 FC 55 AA 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A3 56 31 D5 FC 55 AA 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1E 9E 7F D3 FC 55 AA 0A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 D9 C7 AC D9 FC 55 AA 0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ No newline at end of file diff --git a/serial_module.c b/serial_module.c new file mode 100644 index 0000000..eb92914 --- /dev/null +++ b/serial_module.c @@ -0,0 +1,262 @@ +#define PY_SSIZE_T_CLEAN +#include +#include +#include +#include +#include +#include // 引入 zlib 库 +#include // 引入 libfec 库 +#include "hx_serial.h" +#include "hx_ringbuffer.h" +#include // 引入 pthread 库 + +// 宏定义 +#define USE_FEC +#ifdef USE_FEC +#define FEC_SIZE 32 // 前向纠错冗余数据大小 +#else +#define FEC_SIZE 0 // 前向纠错冗余数据大小 +#endif +#define FRAME_HEADER 0xAA55 // 帧头 +#define FRAME_SIZE (240) // 每帧大小 +#define HEADER_SIZE (4) // 帧头 + 帧序号 + 数据长度 +#define CHECKSUM_SIZE (4) // CRC32 校验和大小(4 字节) +#define DATA_SIZE (FRAME_SIZE - HEADER_SIZE - CHECKSUM_SIZE - FEC_SIZE) // 数据段大小 +#define RING_BUFFER_SIZE (1024 * 10) // 环形缓冲区大小 - default 10KB +#define QUEUE_MAX_SIZE 1024 // 队列最大容量 + +// 全局变量 +static by_serial_t serial_port; +static by_ringbuf_t ring_buffer; +static uint8_t send_buffer[FRAME_SIZE]; +static uint8_t frame_counter = 0; +static uint8_t data_len = 0; + +// 定义队列结构体 +typedef struct { + uint8_t *data; + int length; +} FrameData; + +// 队列相关定义 +static FrameData frame_queue[QUEUE_MAX_SIZE]; // 存储接收到的数据帧 +static int queue_head = 0; // 队列头指针 +static int queue_tail = 0; // 队列尾指针 +static pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER; // 保护队列的互斥锁 +static bool stop_receiving = false; // 控制接收线程停止的标志 + +// 前向声明 +void *receive_thread_func(void *arg); + +// 计算 CRC32 校验和 +uint32_t calculate_crc32(const uint8_t *data, size_t length) { + return crc32(0, data, length); +} + +// 解析一帧数据 +int parse_frame(by_ringbuf_t *ringbuf, uint8_t *output_data, int *output_len) { + uint8_t frame[FRAME_SIZE]; + int available_data = by_ringbuf_available_data(ringbuf); + + // 检查是否有足够的数据解析一帧 + if (available_data < HEADER_SIZE + CHECKSUM_SIZE + FEC_SIZE) { + return -1; // 数据不足,无法解析 + } + + // 查找帧头 + uint16_t header = FRAME_HEADER; + int header_pos = by_ringbuf_find(ringbuf, (uint8_t *)&header, 2); + if (header_pos < 0) { + return -1; // 没有找到帧头 + } + + // 弹出帧头之前的数据 + by_ringbuf_pop(ringbuf, frame, header_pos); + + // 检查是否有足够的数据解析一帧 + if (by_ringbuf_available_data(ringbuf) < FRAME_SIZE) { + return -1; // 数据不足,无法解析 + } + + // 读取帧数据 + by_ringbuf_pop(ringbuf, frame, FRAME_SIZE); + + // 解析帧序号、有效数据长度、数据段和 CRC32 + uint8_t seq = frame[2]; + uint8_t valid_data_len = frame[2 + 1]; + uint8_t *data_segment = &frame[2 + 1 + 1]; + uint32_t received_crc = *(uint32_t *)&frame[2 + 1 + 1 + DATA_SIZE]; + + // 计算 CRC32 校验 + uint32_t calculated_crc = calculate_crc32(frame, HEADER_SIZE + DATA_SIZE + FEC_SIZE); + if (received_crc != calculated_crc) { + printf("CRC mismatch! Expected: %08X, Received: %08X\n", calculated_crc, received_crc); + return -1; // CRC 校验失败,丢弃该帧 + } + + // 将有效数据拼接到输出缓冲区 + memcpy(&output_data[*output_len], data_segment, valid_data_len); + *output_len += valid_data_len; + + // 判断是否为最后一帧 + if (valid_data_len < DATA_SIZE) { + printf("Received last frame!\n"); + return 1; // 最后一帧,解析完成 + } + return 0; // 成功解析一帧,但可能还有更多帧 +} + +// 接收线程函数 +void *receive_thread_func(void *arg) { + while (!stop_receiving) { + uint8_t output_data[8192]; + int output_len = 0; + + // 从串口读取数据到环形缓冲区 + uint8_t buffer[FRAME_SIZE]; + int ret = by_serial_read(&serial_port, buffer, FRAME_SIZE); + if (ret > 0) { + by_ringbuf_append(&ring_buffer, buffer, ret); + } + + // 尝试解析环形缓冲区中的数据 + while (1) { + int parse_result = parse_frame(&ring_buffer, output_data, &output_len); + if (parse_result == 1) { + // 将解析后的数据放入队列 + pthread_mutex_lock(&queue_mutex); + if ((queue_tail + 1) % QUEUE_MAX_SIZE != queue_head) { // 队列未满 + frame_queue[queue_tail].data = malloc(output_len); + memcpy(frame_queue[queue_tail].data, output_data, output_len); + frame_queue[queue_tail].length = output_len; + queue_tail = (queue_tail + 1) % QUEUE_MAX_SIZE; + } else { + printf("Queue is full, dropping frame!\n"); + } + pthread_mutex_unlock(&queue_mutex); + break; + } else if (parse_result == -1) { + break; // 数据不足或解析失败,退出循环 + } + } + + usleep(1000); // 避免占用过多 CPU + } + return NULL; +} + +// 初始化串口 +static PyObject *serial_init(PyObject *self, PyObject *args) { + const char *dev_name; + if (!PyArg_ParseTuple(args, "s", &dev_name)) { + return NULL; + } + + if (by_serial_init(&serial_port, dev_name) != 0) { + PyErr_SetString(PyExc_IOError, "Failed to initialize serial port"); + return NULL; + } + + // 初始化环形缓冲区 + if (by_ringbuf_init(&ring_buffer, RING_BUFFER_SIZE) != 0) { + PyErr_SetString(PyExc_IOError, "Failed to initialize ring buffer"); + return NULL; + } + + // 启动接收线程 + pthread_t receive_thread; + stop_receiving = false; + if (pthread_create(&receive_thread, NULL, receive_thread_func, NULL) != 0) { + PyErr_SetString(PyExc_RuntimeError, "Failed to start receive thread"); + return NULL; + } + + Py_RETURN_NONE; +} + +// 发送数据 +static PyObject *serial_send(PyObject *self, PyObject *args) { + const char *data; + Py_ssize_t length; + if (!PyArg_ParseTuple(args, "s#", &data, &length)) { + return NULL; + } + + size_t offset = 0; + while (offset < length) { + memset(send_buffer, 0, FRAME_SIZE); + + // 构造帧头 + uint16_t header = FRAME_HEADER; + memcpy(send_buffer, &header, 2); + + // 构造帧序号和数据长度 + memcpy(send_buffer + 2, &frame_counter, 1); + if (length - offset > DATA_SIZE) { + data_len = DATA_SIZE; + } else { + data_len = length - offset; + } + memcpy(send_buffer + 3, &data_len, 1); + + // 拷贝该帧对应数据段 + memcpy(send_buffer + HEADER_SIZE, data + offset, data_len); + + // 计算 CRC32 校验和 + uint32_t crc = calculate_crc32(send_buffer, HEADER_SIZE + DATA_SIZE + FEC_SIZE); + memcpy(send_buffer + HEADER_SIZE + DATA_SIZE + FEC_SIZE, &crc, CHECKSUM_SIZE); + + // 发送帧 + if (by_serial_write(&serial_port, (const char *)send_buffer, FRAME_SIZE) != 0) { + PyErr_SetString(PyExc_IOError, "Failed to send data over serial port"); + return NULL; + } + + offset += DATA_SIZE; + frame_counter++; + usleep(80000); + } + + frame_counter = 0; + Py_RETURN_NONE; +} + +// 接收数据 +static PyObject *serial_receive(PyObject *self, PyObject *args) { + pthread_mutex_lock(&queue_mutex); + if (queue_head == queue_tail) { + pthread_mutex_unlock(&queue_mutex); + PyErr_SetString(PyExc_IOError, "No data available in the queue"); + return NULL; + } + + // 获取队列中最早的数据包 + FrameData frame = frame_queue[queue_head]; + queue_head = (queue_head + 1) % QUEUE_MAX_SIZE; + pthread_mutex_unlock(&queue_mutex); + + // 返回解析后的数据 + PyObject *result = Py_BuildValue("y#", frame.data, frame.length); + free(frame.data); // 释放内存 + return result; +} + +// 模块方法表 +static PyMethodDef SerialMethods[] = { + {"init", serial_init, METH_VARARGS, "Initialize serial port"}, + {"send", serial_send, METH_VARARGS, "Send data over serial port"}, + {"receive", serial_receive, METH_VARARGS, "Receive data from serial port"}, + {NULL, NULL, 0, NULL}}; + +// 模块定义 +static struct PyModuleDef serialmodule = { + PyModuleDef_HEAD_INIT, + "serial_module", + NULL, + -1, + SerialMethods}; + +// 模块初始化函数 +PyMODINIT_FUNC PyInit_serial_module(void) { + return PyModule_Create(&serialmodule); +} \ No newline at end of file diff --git a/serial_module.so b/serial_module.so new file mode 100755 index 0000000..6a5dec9 Binary files /dev/null and b/serial_module.so differ diff --git a/serial_module_bk.c b/serial_module_bk.c new file mode 100644 index 0000000..39e749e --- /dev/null +++ b/serial_module_bk.c @@ -0,0 +1,269 @@ +#define PY_SSIZE_T_CLEAN +#include +#include +#include +#include +#include +#include // 引入 zlib 库 +#include // 引入 libfec 库 +#include "hx_serial.h" +#include "hx_ringbuffer.h" + +// #define USE_FEC + +#ifdef USE_FEC +#define FEC_SIZE 32 // 前向纠错冗余数据大小 +#else +#define FEC_SIZE 0 // 前向纠错冗余数据大小 +#endif +#define FRAME_HEADER 0xAA55 // 帧头 +#define FRAME_SIZE (240) // 每帧大小 +#define HEADER_SIZE (4) // 帧头 + 帧序号 + 数据长度 +#define CHECKSUM_SIZE (4) // CRC32 校验和大小(4 字节) +#define DATA_SIZE (FRAME_SIZE - HEADER_SIZE - CHECKSUM_SIZE - FEC_SIZE) // 数据段大小 +#define RING_BUFFER_SIZE (1024 * 10) // 环形缓冲区大小 - default 10KB + +// 全局变量:串口设备和发送缓冲区 +static by_serial_t serial_port; +static by_ringbuf_t ring_buffer; +static uint8_t send_buffer[FRAME_SIZE]; +static uint8_t send_buffer_test[FRAME_SIZE]; +static uint8_t frame_counter = 0; +static uint8_t data_len = 0; + +// 计算 CRC32 校验和 +uint32_t calculate_crc32(const uint8_t *data, size_t length) { + return crc32(0, data, length); +} + +// 前向纠错编码 +void fec_encode(uint8_t *data, size_t data_length, uint8_t *fec_data) { + encode_rs_8(data, fec_data, 0); +} + +// 前向纠错解码 +int fec_decode(uint8_t *data, size_t data_length, uint8_t *fec_data) { + return decode_rs_8(data, (int *)fec_data, 0, 0); +} + +// 解析一帧数据 +int parse_frame(by_ringbuf_t *ringbuf, uint8_t *output_data, int *output_len) { + uint8_t frame[FRAME_SIZE]; + int available_data = by_ringbuf_available_data(ringbuf); + + // 检查是否有足够的数据解析一帧 + if (available_data < HEADER_SIZE + CHECKSUM_SIZE + FEC_SIZE) { + return -1; // 数据不足,无法解析 + } + + // 查找帧头 + uint16_t header = FRAME_HEADER; + int header_pos = by_ringbuf_find(ringbuf, (uint8_t *)&header, 2); + if (header_pos < 0) { + return -1; // 没有找到帧头 + } + + // 弹出帧头之前的数据 + by_ringbuf_pop(ringbuf, frame, header_pos); + + // 检查是否有足够的数据解析一帧 + if (by_ringbuf_available_data(ringbuf) < FRAME_SIZE) { + return -1; // 数据不足,无法解析 + } + + // 读取帧数据 + by_ringbuf_pop(ringbuf, frame, FRAME_SIZE); + + // 解析帧序号、有效数据长度、数据段和 CRC32 + uint8_t seq = frame[2]; + uint8_t valid_data_len = frame[2 + 1]; + uint8_t *data_segment = &frame[2 + 1 + 1]; + uint32_t received_crc = *(uint32_t *)&frame[2 + 1 + 1 + DATA_SIZE]; + + printf("Received frame: %d, valid_data_len: %d, received_crc: %08X\n", seq, valid_data_len, received_crc); + + for(uint8_t i = 0; i < FRAME_SIZE; i++) { + printf("%02X ", frame[i]); + } + + printf("\n"); + + // 计算 CRC32 校验 + // uint32_t calculated_crc = calculate_crc32(data_segment, valid_data_len); + uint32_t calculated_crc = calculate_crc32(frame, HEADER_SIZE + DATA_SIZE + FEC_SIZE); + if (received_crc != calculated_crc) { + printf("CRC mismatch! Expected: %08X, Received: %08X\n", calculated_crc, received_crc); + return -1; // CRC 校验失败,丢弃该帧 + } + + // 将有效数据拼接到输出缓冲区 + memcpy(&output_data[*output_len], data_segment, valid_data_len); + *output_len += valid_data_len; + + // 判断是否为最后一帧 + if (valid_data_len < DATA_SIZE) { + printf("Received last frame!\n"); + return 1; // 最后一帧,解析完成 + } + + return 0; // 成功解析一帧,但可能还有更多帧 +} + +// 边读取边解析串口数据 +int parse_serial_data(by_serial_t *serial_port, by_ringbuf_t *ringbuf, uint8_t *output_data, int *output_len) { + uint8_t buffer[FRAME_SIZE]; + int ret; + + // 从串口读取数据到环形缓冲区 + ret = by_serial_read(serial_port, buffer, FRAME_SIZE); + if (ret > 0) { + by_ringbuf_append(ringbuf, buffer, ret); + } + + // 尝试解析环形缓冲区中的数据 + while (1) { + int parse_result = parse_frame(ringbuf, output_data, output_len); + if (parse_result == 1) { + return 0; // 解析完成 + } else if (parse_result == -1) { + break; // 数据不足或解析失败,退出循环 + } + } + + return -1; // 未找到完整帧 +} + +// 初始化串口 +static PyObject *serial_init(PyObject *self, PyObject *args) { + const char *dev_name; + + if (!PyArg_ParseTuple(args, "s", &dev_name)) { + return NULL; + } + + if (by_serial_init(&serial_port, dev_name) != 0) { + PyErr_SetString(PyExc_IOError, "Failed to initialize serial port"); + return NULL; + } + + // 初始化环形缓冲区 + if (by_ringbuf_init(&ring_buffer, RING_BUFFER_SIZE) != 0) { + PyErr_SetString(PyExc_IOError, "Failed to initialize ring buffer"); + return NULL; + } + + frame_counter = 0; // 重置帧计数器 + Py_RETURN_NONE; +} + +// 发送数据 +static PyObject *serial_send(PyObject *self, PyObject *args) { + const char *data; + Py_ssize_t length; + + if (!PyArg_ParseTuple(args, "s#", &data, &length)) { + return NULL; + } + + printf("Send data len = %ld\n", length); + + size_t offset = 0; + uint8_t idx = 0; + while (offset < length) { + memset(send_buffer, 0, FRAME_SIZE); + + // 构造帧头 + uint16_t header = FRAME_HEADER; + memcpy(send_buffer, &header, 2); + + // 构造帧序号 + memcpy(send_buffer + 2, &frame_counter, 1); + + // 构造数据长度 + if (length - offset > DATA_SIZE) { + data_len = DATA_SIZE; + } else { + data_len = length - offset; + } + memcpy(send_buffer + 3, &data_len, 1); + + // 拷贝该帧对应数据段 + memcpy(send_buffer + HEADER_SIZE, data + offset, data_len); + +#ifdef USE_FEC + // 将 FEC 数据附加到帧中 + memcpy(send_buffer + HEADER_SIZE + DATA_SIZE, fec_data, 32); +#endif + + // 计算 CRC32 校验和 + uint32_t crc = calculate_crc32(send_buffer, HEADER_SIZE + DATA_SIZE + FEC_SIZE); + memcpy(send_buffer + HEADER_SIZE + DATA_SIZE + FEC_SIZE, &crc, CHECKSUM_SIZE); + + for(uint8_t i = 0; i < FRAME_SIZE; i++) { + printf("%02x ", send_buffer[i]); + } + + // 发送帧 + if (by_serial_write(&serial_port, (const char *)send_buffer, FRAME_SIZE) != 0) { + PyErr_SetString(PyExc_IOError, "Failed to send data over serial port"); + return NULL; + } + + offset += DATA_SIZE; + frame_counter++; + + printf("* frame [%d]\n", frame_counter); + + // usleep(50000); + usleep(80000); + } + + frame_counter = 0; + + printf("****************\n"); + + Py_RETURN_NONE; +} + +// 接收数据 +static PyObject *serial_receive(PyObject *self, PyObject *args) { + uint8_t output_data[8192]; + int output_len = 0; + int timeout = 5000; // 超时时间,单位为毫秒 + int elapsed_time = 0; + + // TODO 增加当接收新帧时的处理 + while (elapsed_time < timeout) { + if (parse_serial_data(&serial_port, &ring_buffer, output_data, &output_len) == 0) { + // 返回解析后的数据 + printf("Parsed data length: %d\n", output_len); + return Py_BuildValue("y#", output_data, output_len); + } + usleep(1000); // 等待 1 毫秒 + elapsed_time += 1; + } + + // 超时未接收到完整帧 + PyErr_SetString(PyExc_TimeoutError, "Timeout while waiting for data"); + return NULL; +} + +// 模块方法表 +static PyMethodDef SerialMethods[] = { + {"init", serial_init, METH_VARARGS, "Initialize serial port"}, + {"send", serial_send, METH_VARARGS, "Send data over serial port"}, + {"receive", serial_receive, METH_VARARGS, "Receive data from serial port"}, + {NULL, NULL, 0, NULL}}; + +// 模块定义 +static struct PyModuleDef serialmodule = { + PyModuleDef_HEAD_INIT, + "serial_module", + NULL, + -1, + SerialMethods}; + +// 模块初始化函数 +PyMODINIT_FUNC PyInit_serial_module(void) { + return PyModule_Create(&serialmodule); +} \ No newline at end of file diff --git a/test_recv.jpg b/test_recv.jpg new file mode 100644 index 0000000..5dcabf5 Binary files /dev/null and b/test_recv.jpg differ diff --git a/test_recv.py b/test_recv.py new file mode 100644 index 0000000..6ef147c --- /dev/null +++ b/test_recv.py @@ -0,0 +1,21 @@ +import serial_module +import cv2 +import numpy as np + +serial_module.init("/dev/ttyUSB1") +while True: + try: + data = serial_module.receive() + # print("Received data:", data) + if data: + frame = np.frombuffer(data, np.uint8) + # 将数据转换为图像 + image = cv2.imdecode(frame, cv2.IMREAD_COLOR) + # 显示图像 + cv2.imshow('Received Image', image) + cv2.imwrite('test_recv.jpg', image) + # 等待按键输入 + if cv2.waitKey(1) & 0xFF == ord('q'): + break + except Exception as e: + print("Error:", e) \ No newline at end of file diff --git a/test_send.jpg b/test_send.jpg new file mode 100644 index 0000000..469e208 Binary files /dev/null and b/test_send.jpg differ diff --git a/test_send.py b/test_send.py new file mode 100644 index 0000000..3eddd33 --- /dev/null +++ b/test_send.py @@ -0,0 +1,43 @@ +import serial_module +import cv2 +import time + +# 初始化串口 +serial_module.init("/dev/ttyUSB0") + +cap = cv2.VideoCapture(0) + +# while True: +for _ in range(100): + time_via = time.time() + ret, frame = cap.read() + frame = cv2.resize(frame, (224,224)) + cv2.imshow('frame',frame) + frame_jpg = cv2.imencode('.jpg', frame, [int(cv2.IMWRITE_JPEG_QUALITY), 50])[1].tobytes() + print(len(frame_jpg)) + serial_module.send(frame_jpg) + cv2.imwrite('test_send.jpg', frame) + # # 创建一个大小为 3.5K 的二进制数据 + # file_size = int(3.5 * 1024) + # empty_binary_data = bytearray(file_size) + # empty_binary_data = bytes(empty_binary_data) + + # # 发送数据 + # serial_module.send(empty_binary_data) + + print(time.time() - time_via) + time.sleep(4) + if cv2.waitKey(1) & 0xFF == ord('q'): + break + +# # 发送数据 +# data = b"Your binary data here" # 替换为你的二进制数据 +# serial_module.send(data) + +# # 接收数据 +# result = serial_module.receive() +# if result: +# frame_number, pdu, rssi = result +# print(f"Frame Number: {frame_number}") +# print(f"PDU: {pdu}") +# print(f"RSSI: {rssi}") \ No newline at end of file diff --git a/tool_capserial b/tool_capserial new file mode 100755 index 0000000..40c3f46 Binary files /dev/null and b/tool_capserial differ diff --git a/tool_capserial.c b/tool_capserial.c new file mode 100644 index 0000000..e42bfd0 --- /dev/null +++ b/tool_capserial.c @@ -0,0 +1,77 @@ +#include "hx_serial.h" +#include +#include +#include +#include + +#define BUFFER_SIZE 1024 +#define OUTPUT_FILE "serial_data.txt" + +int main() { + by_serial_t serial_port; + char buffer[BUFFER_SIZE]; + int bytes_read; + int total_bytes_read = 0; + FILE *output_file; + + // 初始化串口 + if (by_serial_init(&serial_port, "/dev/ttyUSB1") != 0) { + fprintf(stderr, "Failed to initialize serial port\n"); + return -1; + } + + by_serial_set_baudrate(&serial_port, 115200); + by_serial_set_parity(&serial_port, 8, 1, 'N'); + + // 打开输出文件 + output_file = fopen(OUTPUT_FILE, "wb"); + if (output_file == NULL) { + fprintf(stderr, "Failed to open output file\n"); + close(serial_port.fd); + return -1; + } + + printf("Reading from serial port. Press ENTER to stop...\n"); + + // 循环读取串口数据 + while (1) { + // // 检查用户输入 + // if (fgetc(stdin) == '\n') { + // break; + // } + + // 检查缓冲区中是否有数据 + int used_len = by_serial_get_used_buffer_len(&serial_port); + if (used_len <= 0) { + usleep(100000); // 等待 100ms + // printf("no data\n"); + continue; + }else{ + printf("used_len = %d; ", used_len); + } + + // 读取数据 + bytes_read = by_serial_read(&serial_port, buffer, BUFFER_SIZE); + if (bytes_read < 0) { + fprintf(stderr, "Error reading from serial port\n"); + break; + } + + total_bytes_read += bytes_read; + printf("Total bytes read: %d\n", total_bytes_read); + + // 将数据以 ASCII 格式写入文件 + // fwrite(buffer, 1, bytes_read, output_file); + for (int i = 0; i < bytes_read; i++) { + fprintf(output_file, "%02X ", (unsigned char)buffer[i]); + } + fprintf(stderr, "Wrote %d bytes to file\n", bytes_read); + } + + // 关闭文件和串口 + fclose(output_file); + close(serial_port.fd); + + printf("Data capture stopped. Data saved to %s\n", OUTPUT_FILE); + return 0; +} \ No newline at end of file diff --git a/tool_ckframe.py b/tool_ckframe.py new file mode 100644 index 0000000..566d446 --- /dev/null +++ b/tool_ckframe.py @@ -0,0 +1,33 @@ +def calculate_frame_lengths(file_content): + # 将文件内容转换为十六进制字节列表 + hex_data = file_content.split() + + frame_lengths = [] + frame_start = 0 + + # 遍历字节列表,查找帧头 + for i in range(len(hex_data) - 1): + if hex_data[i] == '55' and hex_data[i + 1] == 'AA': + # 找到帧头,计算当前帧的长度 + if frame_start != 0: + frame_length = i - frame_start + frame_lengths.append(frame_length) + frame_start = i + + # 处理最后一帧 + if frame_start != 0: + frame_length = len(hex_data) - frame_start + frame_lengths.append(frame_length) + + return frame_lengths + +# 读取文件内容 +with open('serial_data.txt', 'r') as file: + file_content = file.read() + +# 计算每一帧的长度 +frame_lengths = calculate_frame_lengths(file_content) + +# 输出每一帧的长度 +for i, length in enumerate(frame_lengths): + print(f"Frame {i + 1}: Length = {length} bytes") \ No newline at end of file