C语言信号处理,离线版(全域、后处理)中值滤波和低通滤波

一些说明&简要介绍

对于一些数据,需要在pc或者云端进行滤波处理,比如这里的中值(去基线)和低通(去毛刺)。因为平台强大,当数据量大时,如果滤波做的不是太复杂,或者说做的比较简单,那么处理起来还是比较快的,那么就能实用。

这里有个例子,因为是后处理,则信号处理前是多长(多少点),处理后就是多少点。对于中值滤波,需要将边沿点进行扩展(补点),或者在边沿进行窗口缩小处理。对于低通滤波,则确定需要对边沿进行补点,并合理处理延时。

简单来说,中值滤波边沿补点处理有很多方式,matlab的有中值滤波系统函数median,但是不带这个参数(印象中好像是带的,今天再次去查了很久,发现没有,不知道是不是版本问题还是其他),直接给答案吧:常见的可以是补零、边沿值复制填充和对称填充。这个对称填充,又可以是一次对称填充和逐元素对称填充,等等。肯定还有很多其他的填充方式,暂且不深究。我们这里采用补零填充。

低通滤波,我们这里用FIR,所以是固定延时的(延时点数:阶数/2),延时可以解释下:即滤到当前点时,结果是当前点前阶数/2点的结果。所以,要滤出当前点的值,则需要滤到当前点后阶数/2点,其实,就是将滤波器系数窗口(FIR滤波器系数窗口是一个对称的类似汉宁窗的窗口)的中间值(我们选择偶数阶的滤波器,那么系数中间值是一个值,其两边的系数刚好对称)对齐需要滤波的信号点,滤出的就是这个点无延时的结果。如此说来,也就知道了我们的滤波是怎么处理的,即对称扩展。需要注意的是,不是一次扩展,而是对边沿的每个点都对称扩展,即:因为其前面点不够,将后面的阶数/2个点对称扩展到该点的前面,然后用滤波窗口滤波,实现了无延时且滤波正确的结果。

比较有意思的是,我们既可以单独测试中值和低通滤波,还可以将两个滤波器串联起来——其实就是两次滤波,因为是后处理,信号可以处理成无延时,也就是说,只要滤波正确,多少次滤波都可以。

下面是C代码:

main.c

//#include<stdio.h>
//#include<stdlib.h>
//#include<math.h>
//
//
//#include"Median.h"
//
//
//
//#define M_PI 3.1415
//#define N 200
//#define F1 3
//#define F2 40
//
//int  main()
//{
//    // 生成正弦信号
//    int a[N] = { 0 }, b[N] = { 0 };
//    int magnify = 100;
//    for (int i = 0; i < N; i++)
//    {
//        a[i] = (int)(magnify *sin(2 * M_PI * i * F1 / N) + 0.25 * magnify *sin(2 * M_PI * i * F2 / N));
//    }
//
//    FILE* fp;
//
//    fp = fopen("text.txt", "w");
//    if (feof(fp))
//    {
//        printf("NULL");
//        exit(0);  // 表示如果读取为空文件就正常退出
//    }
//
//    for (int i = 0; i < N; i++)
//        fprintf(fp, "%d\n", a[i]);
//    fclose(fp);
//
//   int fir_len = 10;
//   int m;
//
//    // 中值滤波
//    m = Median(a, b, N, fir_len);
//
//
//    fp = fopen("text_fir.txt", "w");
//    if (feof(fp))
//    {
//        printf("NULL");
//        exit(0);//表示如果读取为空文件就正常退出
//    }
//
//    for (int i = 0; i < N; i++)
//        fprintf(fp, "%d\n", b[i]);
//    fclose(fp);
//
//
//    return 0;
//}
//#include<stdio.h>
#include<stdlib.h>
#include <string.h>
#include "Lowpass.h"
#include "Median.h"#define SampleRate (256)int  main()
{//errno_t err;FILE* file;//#pragma warning(disable:4996)file = fopen("mydata.txt", "rb"); // 以二进制模式打开文件供读取//err = fopen_s(&file, "D:\WorkDoc\algoPro\AlgoSDKPro\AlgoSDK\mydata.txt", "rb");if (file == NULL){printf("找不到文件");return 0;}fseek(file, 0, SEEK_END); // 将文件指针移动到文件末尾long fileSize = ftell(file); // 获取文件大小printf("文件大小%ld", fileSize);rewind(file); // 将文件指针重新定位到文件开头char* buffer = (char*)malloc(fileSize + 1); // 分配足够大的缓冲区来存储文件内容if (buffer == NULL) {perror("Memory allocation failed");return 1;}fread(buffer, 1, fileSize, file); // 一次性读取文件全部内容buffer[fileSize] = '\0'; // 添加字符串结束符//char buffer[100]; // 假设每次读取最多包含 100 个字符int comma_count = 0; // 逗号计数器//while (fgets(buffer, sizeof(buffer), file)) {for (int i = 0; buffer[i] != '\0'; i++) {if (buffer[i] == ',') {comma_count++;}}//}printf("Number of commas in the file: %d\n", comma_count);fclose(file);if (comma_count > 0){int data_size = comma_count + 1;// 使用 malloc 动态分配数组内存空间//int* data_in = (int*)malloc(data_size * sizeof(int));//int* data_out = (int*)malloc(data_size * sizeof(int));// 使用 calloc 动态分配数组内存空间并初始化为0int* data_in = (int*)calloc(data_size, sizeof(int));int* data_out = (int*)calloc(data_size, sizeof(int));int* data_out_finish = (int*)calloc(data_size, sizeof(int));// //int data_in[data_size] = { 0 };//int data_out[data_size] = { 0 };char* token = strtok(buffer, ","); //strtok引用#include <string.h>int index = 0;while (token != NULL) {//double number = atof(token);  // 将分割出的字符串转换为浮点数int number = atoi(token);//printf("%s\n", token);data_in[index++] = number;token = strtok(NULL, ",");}printf("输出原始的ADC数值:");//输出滤波前的数值for (int j = 0; j < data_size; j++){printf("%d ", data_in[j]);}调用算法 startLowpassInit();int lRet = Lowpass(data_in, data_size, data_out, SampleRate);if (lRet) {free(buffer);return 1;}int fir_len = 10;int mRet = Median(data_out, data_out_finish, data_size, fir_len);调用算法 endprintf("\n------------------------------------------------------------------\n");printf(" \n输出滤波后的数值");//输出滤波后的数值for (int j = 0; j < data_size; j++){printf("%d ", data_out[j]);}}free(buffer);return 0;
}

开头的注释部分,是C自己生成正弦型信号测试中值滤波的。后面部分是读入数据文件测试低通和中值串联的。

Lowpass.h

#pragma onceextern void LowpassInit();
extern int Lowpass(int* data_in,int data_len, int* data_out,int SampleRate);

Lowpass.c

#include"stdio.h"
#include"stdlib.h"
#include"string.h"#include "assert.h" // FIR 45Hz低通
static float fir_coef_b[129] ={0.001364, -0.001009, -0.001358, -0.000845, 0.000328, 0.000870, 0.000046, -0.001164, -0.001094, 0.000425, 0.001572, 0.000723, -0.001273, -0.001857, -0.000006, 0.002128, 0.001681, -0.001116, -0.002802, -0.000929, 0.002459, 0.002985, -0.000451, -0.003734, -0.002413, 0.002331, 0.004545, 0.000925, -0.004396, -0.004473, 0.001453, 0.006163, 0.003177, -0.004447, -0.007040, -0.000497, 0.007527, 0.006437, -0.003443, -0.009946, -0.003896, 0.008190, 0.010827, -0.000795, -0.012942, -0.009279, 0.007504, 0.016601, 0.004484, -0.015728, -0.017795, 0.004292, 0.024643, 0.014802, -0.017997, -0.033204, -0.004871, 0.039100, 0.040318, -0.019479, -0.078933, -0.045026, 0.108777, 0.295845, 0.380006, 0.295845, 0.108777, -0.045026, -0.078933, -0.019479, 0.040318, 0.039100, -0.004871, -0.033204, -0.017997, 0.014802, 0.024643, 0.004292, -0.017795, -0.015728, 0.004484, 0.016601, 0.007504, -0.009279, -0.012942, -0.000795, 0.010827, 0.008190, -0.003896, -0.009946, -0.003443, 0.006437, 0.007527, -0.000497, -0.007040, -0.004447, 0.003177, 0.006163, 0.001453, -0.004473, -0.004396, 0.000925, 0.004545, 0.002331, -0.002413, -0.003734, -0.000451, 0.002985, 0.002459, -0.000929, -0.002802, -0.001116, 0.001681, 0.002128, -0.000006, -0.001857, -0.001273, 0.000723, 0.001572, 0.000425, -0.001094, -0.001164, 0.000046, 0.000870, 0.000328, -0.000845, -0.001358, -0.001009, 0.001364,
};
static float gain = 1.01;typedef struct {int DataBuf[129];float fDataBuf[129];float DataAfFir;
}LowpassPra_t;LowpassPra_t  LowpassPra = { 0 };void fir_filter_zhh(float* sig_in, float* sig_out,int sig_len)
{assert(sig_len > 0);int i, j;for (i = 0; i < sig_len; i++) {sig_out[i] = 0;for (j = 0; j < 129; j++) {sig_out[i] += sig_in[i - j] * fir_coef_b[j];}sig_out[i] *= gain;}
}// 数组逆序 float版
void reverse_arry_float(float* arry,int arry_len) // arry_len可以为1,不报错,此时arry只有一个元素,调用后不变
{int i = 0;  //循环变量1, i的值为数组第一个元素的下标int j = arry_len - 1;  //循环变量2, j的值为数组最后一个元素的下标float fir_idx_buf;  //互换时的中间存储变量for (; i < j; ++i, --j)  /*因为i和j已经初始化过了, 所以表达式1可以省略, 但表达式1后面的分号不能省。*/{fir_idx_buf = arry[i];arry[i] = arry[j];arry[j] = fir_idx_buf;}
}void LowpassInit() 
{memset(LowpassPra.DataBuf, 0, sizeof(LowpassPra.DataBuf));}// 数据按帧传入,同时要传入帧号
int Lowpass(int* data_in,int data_len, int* data_out,int SampleRate)
{// 限制采样率必须是256if (SampleRate != 256) {//printf("SampleRate error!\n");return 1;}int i,j; for (i = 0;i < data_len;i++) {if( i < 64 ) {  // 向左边扩展memcpy(&LowpassPra.DataBuf[0], &data_in[i + 1], 64 * sizeof(data_in[0]));memcpy( &LowpassPra.DataBuf[64], &data_in[i], 65 * sizeof(data_in[0]) );reverse_arry_float(&LowpassPra.DataBuf[0], 64);for (j = 0; j <= 128;j++) {LowpassPra.fDataBuf[j] = (float)LowpassPra.DataBuf[j];}}else if( i < data_len - 64 ) {for (j = 0; j <= 128; j++) {LowpassPra.fDataBuf[j] = (float)data_in[i - 64 + j];}}else {  // 向右边扩展memcpy(&LowpassPra.DataBuf[0], &data_in[i - 64], 65 * sizeof(data_in[0]));memcpy(&LowpassPra.DataBuf[64 + 1], &data_in[i - 64], 64 * sizeof(data_in[0]));reverse_arry_float(&LowpassPra.DataBuf[64 + 1], 64);for (j = 0; j <= 128; j++) {LowpassPra.fDataBuf[j] = (float)LowpassPra.DataBuf[j];}}fir_filter_zhh(&LowpassPra.fDataBuf[128], &LowpassPra.DataAfFir, 1);data_out[i] = (int)LowpassPra.DataAfFir;}return  0;
}

Median.h

#pragma onceextern int Median(int* sig, int* sig_out,int sig_len,int fir_len);

Median.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>#include <assert.h>void quick_sort_int(int* a, int low, int high);中值滤波函数
// C代码方式模拟MATLAB中值滤波函数
// sig:  输入信号,必须是行向量,且元素个数至少是2
// sig_len:信号总长度
// fir_len : 窗口长度, 2到信号长度之间,或者后续再另外设置最小最大值。
int Median(int* sig, int* sig_out,int sig_len,int fir_len)
{// 输入信号长度检查,必须大于1assert(sig_len > 1);// 检查fir_len,整数型,最少2,最大为信号sig的长度assert(fir_len > 1 && fir_len <= sig_len);int i;int median_tmp;// 需要缓存的数组,无论哪种扩展,无论fir_len奇偶,都是补fir_len - 1个点int* sig_buf = (int*)malloc((sig_len + fir_len - 1) * sizeof(int));if (sig_buf == NULL) {//判空printf("sig_buf malloc error!\n");//打印错误信息return 1;}memset(sig_buf, 0, (sig_len + fir_len - 1) * sizeof(int));// 补零扩展int* buf = (int*)malloc(fir_len * sizeof(int));if (buf == NULL) {//判空free(sig_buf);printf("buf malloc 1 error!\n");//打印错误信息return 1;}if (fir_len % 2 == 0) { // 偶数memset(sig_buf, 0, (fir_len / 2) * sizeof(int));memcpy(sig_buf + fir_len / 2, sig, sig_len * sizeof(int));memset(sig_buf + fir_len / 2 + sig_len, 0, (fir_len / 2 - 1) * sizeof(int));// 滤波过程for (i = fir_len / 2; i < sig_len + fir_len / 2; i++) {memcpy(buf, &sig_buf[i - fir_len / 2], fir_len * sizeof(int));quick_sort_int(buf, 0, fir_len - 1);// 升序排序median_tmp = (int)(buf[fir_len / 2 - 1] + buf[fir_len / 2]) / 2;sig_out[i - fir_len / 2] = median_tmp;}}else {// 奇数memset(sig_buf, 0, ((fir_len - 1) / 2) * sizeof(int));memcpy(sig_buf + (fir_len - 1) / 2, sig, sig_len * sizeof(int));memset(sig_buf + (fir_len - 1) / 2 + sig_len, 0, ((fir_len - 1) / 2) * sizeof(int));// 滤波过程for (i = (fir_len - 1) / 2; i < sig_len + (fir_len - 1) / 2; i++) {memcpy(buf, &sig_buf[i - (fir_len - 1) / 2], fir_len * sizeof(int));quick_sort_int(buf, 0, fir_len - 1);// 升序排序median_tmp = buf[(fir_len - 1) / 2];sig_out[i - (fir_len - 1) / 2] = median_tmp;}}		free(sig_buf);free(buf);return 0;// 正常返回0
}void quick_sort_int(int* a,  int low,  int high)
{int i = low;	//第一位int j = high;	//最后一位int key = a[i]; //将第一个数作为基准值-- 先找到一个基准值while (i < j){while (i < j && a[j] >= key){j--;}a[i] = a[j];while (i < j && a[i] <= key){i++;}a[j] = a[i];}a[i] = key;if (i - 1 > low){quick_sort_int(a, low, i - 1);}if (i + 1 < high){quick_sort_int(a, i + 1, high);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/430360.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【QT基础】创建项目项目代码解释

目录 前言一&#xff0c;使⽤Qt Creator 新建项目1. 新建项目2. 选择项⽬模板3. 选择项⽬路径4. 选择构建系统5. 填写类信息设置界⾯6. 选择语⾔和翻译⽂件7. 选择Qt套件8. 选择版本控制系统9. 最终效果 二&#xff0c;项目代码说明1. main.cpp文件2. Widget.h文件3. Widget.cp…

吉时利keiithley2440高精度测试仪KEITHLEY2410/2450数字源表

Keithley 2440数字源表&#xff0c;40V&#xff0c;5A&#xff0c;50W 其他功能&#xff1a; 四象限运行基本精度为 0.012%&#xff0c;分辨率为 5 1⁄2 位具有可编程电流源和电压钳的 6 线 Ω 测量通过 GPIB 以 4 1⁄2 位数字读取 1700 个读数/秒内置比较器&#xff0c;用于…

【java面经】Redis速记

目录 基本概念 string hash list set zset 常见问题及解决 缓存穿透 缓存击穿 缓存雪崩 Redis内存管理策略 noeviction allkeys-lru allkeys-random volatile-random volatile-ttl Redis持久化机制 RDB快照 AOF追加文件 Redis多线程特性 Redis应用场景 缓…

力扣反转链表系列【25. K 个一组翻转链表】——由易到难,一次刷通!!!

力扣《反转链表》系列文章目录 刷题次序&#xff0c;由易到难&#xff0c;一次刷通&#xff01;&#xff01;&#xff01; 题目题解206. 反转链表反转链表的全部 题解192. 反转链表 II反转链表的指定段 题解224. 两两交换链表中的节点两个一组反转链表 题解325. K 个一组翻转…

深入剖析Docker容器安全:挑战与应对策略

随着容器技术的广泛应用&#xff0c;Docker已成为现代应用开发和部署的核心工具。它通过轻量级虚拟化技术实现应用的隔离与封装&#xff0c;提高了资源利用率。然而&#xff0c;随着Docker的流行&#xff0c;其安全问题也成为关注焦点。容器化技术虽然提供了良好的资源隔离&…

塑料瓶回收流水线分拣系统源码分享

塑料瓶回收流水线分拣检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comp…

信息安全数学基础(15)欧拉定理

前言 欧拉定理是数论中的一个重要定理&#xff0c;它建立了模运算下指数与模的互质关系。这个定理在密码学、信息安全等领域有着广泛的应用&#xff0c;特别是在公钥密码体制&#xff08;如RSA加密算法&#xff09;中。 一、表述 设 n 是一个正整数&#xff0c;a 是一个与 n 互…

C++速通LeetCode中等第3题-盛最多水的容器

双指针法&#xff1a;两个指针分别指向左右边界&#xff0c;记录最大面积&#xff0c;由于面积由短板决定&#xff0c;两个指针中较短的短指针向内移动一格&#xff0c;再次记录最大面积&#xff0c; 直到两指针相遇&#xff0c;得出答案。 class Solution { public:int maxAr…

【计算机网络篇】数据链路层 功能|组帧|流量控制与可靠传输机制

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 系列文章目录 【计算机网络篇】计算机网络概述 【计算机网络篇…

智慧交通,智能消防系统助力高铁站安全

智慧交通是一项基于现代技术的创新领域&#xff0c;正不断为我们生活带来便利。在智慧交通领域中&#xff0c;高铁站是一个非常重要的环节。高铁站作为人流密集的区域&#xff0c;安全问题一直备受关注。为了提升高铁站的安全性和效率&#xff0c;智慧消防设备监测与集中监控系…

5、论文阅读:深水下的图像增强

深水下的图像增强 前言介绍贡献UWCNN介绍网络架构残差Residuals块 Blocks网络层密集串联网络深度减少边界伪影网络损失Loss后处理前言 水下场景中,与波长相关的光吸收和散射会降低图像的可见度,导致对比度低和色偏失真。为了解决这个问题,我们提出了一种基于卷积神经网络的…

基于python深度学习遥感影像地物分类与目标识别、分割实践技术

我国高分辨率对地观测系统重大专项已全面启动&#xff0c;高空间、高光谱、高时间分辨率和宽地面覆盖于一体的全球天空地一体化立体对地观测网逐步形成&#xff0c;将成为保障国家安全的基础性和战略性资源。未来10年全球每天获取的观测数据将超过10PB&#xff0c;遥感大数据时…

【自学笔记】支持向量机(3)——软间隔

引入 上一回解决了SVM在曲线边界的上的使用&#xff0c;使得非线性数据集也能得到正确的分类。然而&#xff0c;对于一个大数据集来说&#xff0c;极有可能大体呈线性分类趋势&#xff0c;但是边界处混杂&#xff0c;若仍采用原来的方式&#xff0c;会得到极其复杂的超平面边界…

【C++篇】走进C++标准模板库:STL的奥秘与编程效率提升之道

文章目录 C STL 初探&#xff1a;打开标准模板库的大门前言第一章: 什么是STL&#xff1f;1.1 标准模板库简介1.2 STL的历史背景1.3 STL的组成 第二章: STL的版本与演进2.1 不同的STL版本2.2 STL的影响与重要性 第三章: 为什么学习 STL&#xff1f;3.1 从手动编写到标准化解决方…

字母与符号检测系统源码分享

字母与符号检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer V…

十二、JDK17的GC调优策略

文章目录 一、JVM有哪些参数可以调&#xff1f;二、从RocketMQ学习常用GC调优三部曲三、基于JDK17优化JVM内存布局1、定制堆内存大小2、定制非堆内存大小设置元空间设置线程栈空间设置热点代码缓存空间应用程序类数据共享 四、基于JDK17定制JVM的GC参数G1重要参数ZGC重要参数 五…

C++设计模式(更新中)

文章目录 1、创建型模式1.1 简单工厂&#xff08;Simple Factory&#xff09;&#xff08;1&#xff09;示例&#xff08;2&#xff09;总结 1.2 工厂方法&#xff08;Factory Method&#xff09;&#xff08;1&#xff09;示例&#xff08;2&#xff09;总结 1.3 抽象工厂&…

1--SpringBoot外卖项目介绍及环境搭建 详解

目录 软件开发整体流程 软件开发流程 角色分工 软件环境 苍穹外卖项目介绍 项目介绍 产品原型 技术选型 开发环境搭建 前端环境搭建 后端环境搭建 完善登录功能 导入接口文档 Swagger 介绍 使用方式 常用注解 软件开发整体流程 软件开发流程 需求分析&#x…

Microsoft 365 Copilot: Wave 2 发布,开启AI时代下的全新工作流

本周一&#xff08;9月16日&#xff09;&#xff0c;微软对 Microsoft 365 Copilot 办公辅助工具进行了重大升级&#xff0c;推出 Wave 2 版本。新版 Copilot 将为 Microsoft 365 用户带来一系列新功能和改进&#xff0c;进一步提升工作效率与用户体验&#xff0c;正式开启AI时…

一个能同时to B和to C、批发零售一体化的需求分析和系统设计

一些企业纠结自己的模式是to B还是to C&#xff0c;一些企业在to B和to C中转型&#xff0c;还有一些企业在做着to B的业务&#xff0c;也在做to C的代发&#xff0c;这些企业在不停地变更着业务&#xff0c;更换着系统&#xff0c;给企业带来巨大的资金和时间成本&#xff0c;…