initial commit
This commit is contained in:
290
.gitignore
vendored
Normal file
290
.gitignore
vendored
Normal file
@@ -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
|
||||||
|
|
||||||
10
.vscode/settings.json
vendored
Normal file
10
.vscode/settings.json
vendored
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
29
CMakeLists.txt
Normal file
29
CMakeLists.txt
Normal file
@@ -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"
|
||||||
|
)
|
||||||
123
hx_ringbuffer.c
Normal file
123
hx_ringbuffer.c
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
hx_ringbuffer.h
Normal file
46
hx_ringbuffer.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#ifndef _BY_RINGBUF_H__
|
||||||
|
#define _BY_RINGBUF_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
// 定义宏来选择内存分配方式
|
||||||
|
// 如果定义了 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__
|
||||||
81
jpeg_corrupt_test.py
Normal file
81
jpeg_corrupt_test.py
Normal file
@@ -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()
|
||||||
141
recv_test.c
Normal file
141
recv_test.c
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
1
serial_data.txt
Normal file
1
serial_data.txt
Normal file
File diff suppressed because one or more lines are too long
262
serial_module.c
Normal file
262
serial_module.c
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
#include <Python.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <zlib.h> // 引入 zlib 库
|
||||||
|
#include <fec.h> // 引入 libfec 库
|
||||||
|
#include "hx_serial.h"
|
||||||
|
#include "hx_ringbuffer.h"
|
||||||
|
#include <pthread.h> // 引入 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);
|
||||||
|
}
|
||||||
BIN
serial_module.so
Executable file
BIN
serial_module.so
Executable file
Binary file not shown.
269
serial_module_bk.c
Normal file
269
serial_module_bk.c
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
#include <Python.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <zlib.h> // 引入 zlib 库
|
||||||
|
#include <fec.h> // 引入 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);
|
||||||
|
}
|
||||||
BIN
test_recv.jpg
Normal file
BIN
test_recv.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
21
test_recv.py
Normal file
21
test_recv.py
Normal file
@@ -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)
|
||||||
BIN
test_send.jpg
Normal file
BIN
test_send.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
43
test_send.py
Normal file
43
test_send.py
Normal file
@@ -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}")
|
||||||
BIN
tool_capserial
Executable file
BIN
tool_capserial
Executable file
Binary file not shown.
77
tool_capserial.c
Normal file
77
tool_capserial.c
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#include "hx_serial.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
33
tool_ckframe.py
Normal file
33
tool_ckframe.py
Normal file
@@ -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")
|
||||||
Reference in New Issue
Block a user