diff --git a/decode.c b/decode.c new file mode 100644 index 0000000..9ca57bd --- /dev/null +++ b/decode.c @@ -0,0 +1,197 @@ +#include +#include +#include +#include +#include +// 定义缓冲区大小(可以根据实际需求调整) +#define BUFFER_SIZE 1024 // 每次读取1024条记录 + +// MPU传感器数据结构 +typedef struct { + int16_t ax; // 加速度计 X轴 + int16_t ay; // 加速度计 Y轴 + int16_t az; // 加速度计 Z轴 + int16_t gx; // 陀螺仪 X轴 + int16_t gy; // 陀螺仪 Y轴 + int16_t gz; // 陀螺仪 Z轴 +} sensor_data_t; + +// 完整的数据记录结构 +#pragma pack(1) +typedef struct { + uint8_t y; // 年(相对值,需要加上2000) + uint8_t month; // 月 + uint8_t d; // 日 + uint8_t h; // 时 + uint8_t m; // 分 + uint8_t s; // 秒 + uint16_t ms; // 毫秒 + int16_t deepth; // 深度 + sensor_data_t data; // MPU传感器数据 +} info_t; +#pragma pack() + +void print_usage() { + printf("用法:\n"); + printf("decode.exe -f <文件名> [-o <输出文件名>]\n"); + printf("选项:\n"); + printf(" -f <文件名> 指定输入文件\n"); + printf(" -o <文件名> 指定输出文件(可选,默认输出到与输入同名的.csv文件)\n"); + printf(" -h 显示帮助信息\n"); + printf("\n示例:\n"); + printf("decode.exe -f 20241112.BIN\n"); + printf("decode.exe -f 20241112.BIN -o output.csv\n"); +} + +// 原有的decode函数保持不变 +void decode(char *filename) { + FILE *file = fopen(filename, "rb"); + if (!file) { + fprintf(stderr, "无法打开文件\n"); + return; + } + + // 获取文件大小 + fseek(file, 0, SEEK_END); + long fileSize = ftell(file); + fseek(file, 0, SEEK_SET); + + // 计算记录数量 + int recordCount = fileSize / sizeof(info_t); + + // 打印CSV格式的表头 + printf("年,月,日,时,分,秒,毫秒,深度,ax,ay,az,gx,gy,gz\n"); + + // 使用缓冲区读取以提高效率 + info_t buffer[BUFFER_SIZE]; + size_t records_read; + + while ((records_read = fread(buffer, sizeof(info_t), BUFFER_SIZE, file)) > 0) { + for (size_t i = 0; i < records_read; i++) { + // 直接使用小端序数据,无需转换 + // 单字节数据:y, month, d, h, m, s + // 双字节数据:ms, deepth, ax, ay, az, gx, gy, gz 已经是正确的小端序 + printf("%04d,%02d,%02d,%02d,%02d,%02d,%03d,%04d,%d,%d,%d,%d,%d,%d\n", + buffer[i].y + 2000, // 年份偏移2000 + buffer[i].month, // 月 + buffer[i].d, // 日 + buffer[i].h, // 时 + buffer[i].m, // 分 + buffer[i].s, // 秒 + buffer[i].ms, // 毫秒,uint16_t + buffer[i].deepth, // 深度,uint16_t + buffer[i].data.ax, // 加速度计x,int16_t + buffer[i].data.ay, // 加速度计y,int16_t + buffer[i].data.az, // 加速度计z,int16_t + buffer[i].data.gx, // 陀螺仪x,int16_t + buffer[i].data.gy, // 陀螺仪y,int16_t + buffer[i].data.gz // 陀螺仪z,int16_t + ); + } + } + + fclose(file); + + fprintf(stderr, "共解析 %d 条记录\n", recordCount); +} + +// 生成输出文件名 +char* generate_output_filename(const char* input_file) { + size_t len = strlen(input_file); + char* output = (char*)malloc(len + 5); // +5 为了容纳 .csv 和结束符 + + // 复制输入文件名 + strcpy(output, input_file); + + // 找到最后一个点的位置 + char* dot = strrchr(output, '.'); + if (dot != NULL) { + // 如果找到点,在点的位置替换后缀 + strcpy(dot, ".csv"); + } else { + // 如果没有找到点,直接在末尾添加.csv + strcat(output, ".csv"); + } + + return output; +} + +int main(int argc, char *argv[]) { + // 设置控制台输出代码页为 UTF-8 + SetConsoleOutputCP(65001); + + char *input_file = NULL; + char *output_file = NULL; + + // 解析命令行参数 + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0) { + print_usage(); + return 0; + } + else if (strcmp(argv[i], "-f") == 0) { + if (i + 1 < argc) { + input_file = argv[i + 1]; + i++; + } else { + printf("错误:-f 参数后需要指定文件名\n"); + print_usage(); + return 1; + } + } + else if (strcmp(argv[i], "-o") == 0) { + if (i + 1 < argc) { + output_file = argv[i + 1]; + i++; + } else { + printf("错误:-o 参数后需要指定文件名\n"); + print_usage(); + return 1; + } + } + else { + fprintf(stderr, "错误:未知参数 %s\n", argv[i]); + print_usage(); + return 1; + } + } + + // 检查必需参数 + if (input_file == NULL) { + fprintf(stderr, "错误:未指定输入文件\n"); + print_usage(); + return 1; + } + + // 检查输入文件是否存在 + FILE *file = fopen(input_file, "rb"); + if (!file) { + printf("错误:无法打开输入文件 %s\n", input_file); + return 1; + } + fclose(file); + + // 如果没有指定输出文件,生成默认的输出文件名 + char *auto_output_file = NULL; + if (output_file == NULL) { + auto_output_file = generate_output_filename(input_file); + output_file = auto_output_file; + } + + // 重定向标准输出到文件 + if (freopen(output_file, "w", stdout) == NULL) { + printf("错误:无法创建输出文件 %s\n", output_file); + if (auto_output_file) free(auto_output_file); + return 1; + } + + // 解析文件 + decode(input_file); + + // 清理 + if (auto_output_file) { + free(auto_output_file); + } + + return 0; +} diff --git a/deepth.c b/deepth.c new file mode 100644 index 0000000..e75c92f --- /dev/null +++ b/deepth.c @@ -0,0 +1,93 @@ +#include +#include // 包含 strtof() +#include +#include + +#define START_CMD 'start()' +#define STOP_CMD 'stop()' + +/** +压力 +接口协议: $P=XXX.XXX空格;<回车><换行> +压力整数位部分固定三位,位数不够前面补零,小数部分固定三位;单位 dbar。 +负(-)号占一位,正(+)号不显示。 +例如:$P=005.000 ; +$P=-01.000 ; +注:压力小于 1000dbar,整数部分是 3 位,小数部分是 3 位;超过或等于 1000dabr,整数部分是 4 位,小数 +部分是 3 位。 +例如:$P=999.999; +$P=1000.000 +**/ + + +/** + * @brief 通过字符串获取压力值,单位dbar + * 1dbar=1.02m + * @param str 传入的字符串,例如"$P=6999.454 ;" + * @return dbar数值对应的深度值,单位m + * 成功返回深度值,失败返回INT16_MIN + */ +int16_t dbar2Deepth(char *str) +{ + //char str[] = "$P=6999.454 ;"; + char *start, *end; + float num; + int8_t err; + + // 查找等号后的字符位置 + start = strchr(str, '='); // 找到 '=' 的位置 + if (start != NULL) + { + start++; // 跳过 '=' + + // 使用 strtof() 函数从字符串中提取浮点数 + num = strtof(start, &end); + + // 检查转换是否成功,并且确保后面是分号 ' ' + if (*end == ' ') + { + printf("提取的dbar数值: %f\n", num); + err = 0; + } + else + { + err = 1; + printf("转换失败,剩余未转换的字符: %s\n", end); + } + } + else + { + err = -1; + printf("没有找到等号。\n"); + } + + if (err == 0 || err == 1) + { + //1dbar=1.02m + return (int16_t)(num * 1.02); + } + if (err == -1) + { + return INT16_MIN; + } +} + + + +/** + * @brief 获取当前深度值 + * 通过 dbar2Deepth() 函数将字符串 "$P=6999.454 ;" 转换为 + * 压力值对应的深度值,单位m + * @return 深度值,单位m + */ +int16_t getDeepth() { + char str[] = "$P= 6999.454 ;"; + return dbar2Deepth(str); +} + +int main() { + // char str[] = "$P=-1999.454 ;"; + + + printf("当前深度:%d\n", getDeepth()); +} diff --git a/demo/hamming.c b/demo/hamming.c new file mode 100644 index 0000000..de55c20 --- /dev/null +++ b/demo/hamming.c @@ -0,0 +1,109 @@ +#include "hamming.h" + +/** + * @brief 计算一个字节中1的个数 + * @param byte 输入字节 + * @return 返回字节中1的个数 + */ +static inline int countOnes(unsigned char byte) { + static const unsigned char ones[16] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4}; + return ones[byte & 0x0F] + ones[byte >> 4]; +} + + +/** + * @brief 计算汉明码的校验位 + * @param data 输入数据数组 + * @param size 数据大小 + * @param parity 输出的校验位数组(3字节) + * @note 校验位计算包括: + * - 对每个数据位进行异或运算生成校验位 + * - 最后一个校验字节的最高位用于总体奇偶校验 + */ +void calculateParity(const unsigned char *data, int size, unsigned char *parity) { + // 初始化校验位数组 + memset(parity, 0, PARITY_BYTES); + int totalOnes = 0; + + // 对每个数据字节 + for(int i = 0; i < size; i++) { + unsigned char byte = data[i]; + totalOnes += countOnes(byte); + + // 对字节中的每一位 + for(int j = 0; j < 8; j++) { + if(byte & (1 << j)) { + // 计算该位在整个数据中的位置 + int bitPos = i * 8 + j; + + // 更新相应的校验位 + for(int k = 0; k < 20; k++) { + if(bitPos & (1 << k)) { + if(k < 8) + parity[0] ^= (1 << k); + else if(k < 16) + parity[1] ^= (1 << (k - 8)); + else + parity[2] ^= (1 << (k - 16)); + } + } + } + } + } + + // 添加总体奇偶校验 + if(totalOnes % 2) { + parity[2] |= 0x08; // 设置最高位 + } +} + +/** + * @brief 检测并纠正数据中的错误 + * @param data 需要检查的数据 + * @param size 数据大小 + * @param parity 原始校验位 + * @return 返回检测结果: + * 0 - 无错误 + * 1 - 检测到并纠正了单比特错误 + * -1 - 校验位错误 + * -2 - 检测到多位错误 + */ +int detectAndCorrect(unsigned char *data, int size, const unsigned char *parity) { + unsigned char currentParity[PARITY_BYTES]; + calculateParity(data, size, currentParity); + // printf("parity: %02X %02X %02X\n", parity[2], parity[1], parity[0]); + // printf("currentParity: %02X %02X %02X\n", currentParity[2], currentParity[1], currentParity[0]); + + // 计算syndrome + unsigned int syndrome = 0; + syndrome |= (currentParity[0] ^ parity[0]); + syndrome |= (currentParity[1] ^ parity[1]) << 8; + syndrome |= ((currentParity[2] ^ parity[2]) & 0x07) << 16; + + // 检查总体奇偶校验 + int parityError = ((currentParity[2] ^ parity[2]) & 0x08) ? 1 : 0; + + if(syndrome == 0) { + if(parityError) { + return -2; // 检测到多位错误 + } + return 0; // 无错误 + } + + if(!parityError) { + return -1; // 校验位错误(syndrome不为0但奇偶校验正确) + } + + // 单比特错误 + int bitPos = syndrome; + int bytePos = bitPos / 8; + int bitInByte = bitPos % 8; + + if(bytePos < size) { + data[bytePos] ^= (1 << bitInByte); + // printf("纠正位置:字节 %d,位 %d\n", bytePos, bitInByte); + return 1; + } + + return -2; // 检测到多位错误 +} \ No newline at end of file diff --git a/demo/hamming.h b/demo/hamming.h new file mode 100644 index 0000000..5b42639 --- /dev/null +++ b/demo/hamming.h @@ -0,0 +1,14 @@ +#ifndef HAMMING_H +#define HAMMING_H + +#include +#include + + +#define PARITY_BYTES 3 // 3字节校验位 + +// 函数声明 +void calculateParity(const unsigned char *data, int size, unsigned char *parity); +int detectAndCorrect(unsigned char *data, int size, const unsigned char *parity); + +#endif // HAMMING_H \ No newline at end of file diff --git a/demo/main.c b/demo/main.c new file mode 100644 index 0000000..e8fda83 --- /dev/null +++ b/demo/main.c @@ -0,0 +1,64 @@ +#include "hamming.h" +#define DATA_SIZE 10231 + +int mainn() { + unsigned char data[DATA_SIZE], newData[DATA_SIZE]; + unsigned char parity[3] = {0}; + + // 初始化测试数据 + for(int i = 0; i < DATA_SIZE; i++) { + data[i] = i & 0xff; + newData[i] = data[i]; + } + + // 计算校验位 + calculateParity(data, DATA_SIZE, parity); + size_t k = 0; + for (size_t i = 0; i < DATA_SIZE; i++) + { + + // 注入错误 + newData[i] ^= (1 << 7); + + // 检测并纠正错误 + int result = detectAndCorrect(newData, DATA_SIZE, parity); + + // 输出结果 + // printf("错误检测结果: "); + switch(result) { + case 0: + printf("无错误\n"); + break; + case 1: + // printf("错误已纠正\n"); + k++; + break; + case -1: + // printf("校验位错误\n"); + k=-1; + break; + case -2: + printf("检测到多位错误\n"); + k=-1; + break; + } + if (k == -1) + { + printf("数据恢复失败\n"); + break; + } + + + if (memcmp(newData, data, DATA_SIZE) == 0) { + // printf("恢复后数据与原始数据一致\n"); + } else { + printf("恢复后数据与原始数据不一致\n"); + } + if ( i == DATA_SIZE - 1) + { + printf("数据恢复完毕,k=%d\n",k); + } + + } + return 0; +} \ No newline at end of file