matlab不调用conv函数实现卷积运算并用C语言进行卷积计算
1.实验要求,matlab实现卷积运算(不调用函数和方法)
1.1 卷积原理讲解
上述两式分别为离散卷积运算和连续卷积运算。实际上我们的电脑要进行卷积运算就是将连续的函数离散化,再来进行离散的卷积计算。
1.2 卷积计算步骤(图解法)
1.翻折 先在坐标轴上画出 和 ,将 以纵坐标为坐标轴折叠成 。
2.移位 将 移位 ,得到 [或将 移位 ,得到 ],当n或t为正数时右移n或t,当n或t为负数时左移n或t。
3.相乘 将 [或 ]与 [或 ]的对应序列值[(数值)]对应相乘
4.相加 将所有的乘机累加[(积分)]起来,就得到了 [或 ]
1.3 matlab画出两个窗函数
以下列出的是产生两个门函数的matlab代码,式中主要运用了matlab的f=@(x) 函数句柄 ,它主要是包含了函数的路径、函数名、类型以及可能存在的重载方法。函数句柄必须通过专门的定义创建的,而一般的图像的句柄是自动建立的。使用该形式的函数的方法相比于.m函数文件的优点就是,因为在计算机开始执行运算的时候是需要在整个的matlab代码中去寻找是否存在该.m文件,我们也知道,matlab整个的文件数量是非常多的,所以直接调用.m文件是会耗费一些时间的,而如果我们直接采用 @ 的方式则会节省一部分时间。
clc
clear
%定义函数,该函数主要实现任意形式的门函数
high_1=1;
left_1=0;
right_1=1;
%定义要产生的窗函数的左右区间范围以及高度
f_1=@(x) (high_1)*(x>=left_1 & x<right_1);%0-1的阶跃 高1
f_2=@(x) (high_1+0.5)*(x>=left_1+2 & x<right_1+2);%0-1的阶跃 高1.5
x=-4:0.0006:4;
y_1=f_1(x);%得到对应的窗函数的y轴数据
y_2=f_2(x);
%开始画第一个图
figure(1)
hold on
plot(x,y_1,'b','LineWidth',2.5);
%加载x(t)图像
grid on;
axis([-1 5 0 2])%确定画图范围
plot(x,y_2,'r','linewidth',2.5);
%加载y(t)图像
%以下是坐标轴设置
xlabel('s/t')
ylabel('f(x)')
title('需要卷积的两个窗函数的时域图像')
legend('x(t)','h(t)');
hold off;
1.4 对h(t)进行翻折得到
figure(1)
hold on
grid on;
axis([-6 0 0 2])%确定画图范围
plot(-x,y_2,'r','linewidth',2.5);
%加载y(t)图像
%以下是坐标轴设置
xlabel('\tau')
ylabel('h(-\tau)')
title('对函数h(\tau)翻折')
legend('h(-\tau)');
hold off;
1.5 对 进行移位
figure(1);
hold on
plot(x,y_1,'b','LineWidth',2.5);
%画出x(t)函数
grid on;
title('h(-\tau)移位演示')
xlabel('\tau')
ylabel('h(-\tau)')
%完成坐标轴设置
for n=-1:0.08:5
%开始画卷积conv(t)=x(t)*h(t)
%step1 得到h(-x)
if n~=-1
pause(0.0003);%暂停
delete(text1);
end
h=f_2(n-x);
plot_n=plot(x,h,'--r','linewidth',2.5);
axis([-4 5 0 2]);
%设置坐标轴范围
grid on;
pause(0.001);%暂停
delete(plot_n);%删除原曲线
text(0.5,1.8,'当前的n数值为:');
text1=text(3.5,1.8,num2str(n*1.0),'color','r');
end
hold off
%停止接收数据
1.6 对两个函数进行累乘加,并绘制卷积图像
在这一部分,我将附上整个卷积运算的全部代码和其运行结果演示。
clc
clear
%定义函数,该函数主要实现任意形式的门函数
high_1=1;
left_1=0;
right_1=1;
%定义要产生的窗函数的左右区间范围以及高度
f_1=@(x) (high_1)*(x>=left_1 & x<right_1);%0-1的阶跃 高1
f_2=@(x) (high_1+0.5)*(x>=left_1+2 & x<right_1+2);%0-1的阶跃 高1.5
x=-4:0.0006:4;
y_1=f_1(x);%得到对应的窗函数的y轴数据
y_2=f_2(x);
%开始画第一个图
subplot(2,1,1);
figure(1)
hold on
plot(x,y_1,'b','LineWidth',2.5);
%加载x(t)图像
grid on;
axis([-1 5 0 2])%确定画图范围
plot(x,y_2,'r','linewidth',2.5);
%加载y(t)图像
%以下是坐标轴设置
xlabel('s/t')
ylabel('f(x)')
title('需要卷积的两个窗函数的时域图像')
legend('x(t)','h(t)');
hold off;
%画出第二个演示图像
subplot(2,1,2);
figure(1)
hold on
plot(x,y_1,'b','LineWidth',2.5);
%画出x(t)函数
grid on;
title('卷积演示')
xlabel('时间\tau')
ylabel('s(\tau)')
%完成坐标轴设置
for n=-1:0.04:5
%开始画卷积conv(t)=x(t)*h(t)
%step1 得到h(-x)
h=f_2(n-x);
plot_n=plot(x,h,'--r','linewidth',2.5);
axis([-4 5 0 2]);
%设置坐标轴范围
grid on;
pause(0.001);%暂停
delete(plot_n);%删除原曲线
%开始计算卷积
sum=0;
for tao=-2:0.01:5
sum=sum+0.01*f_1(tao)*f_2(n-tao);
end
%此处完成加和操作。
disp(sum);
plot(n,sum,'.k')
%此次执行结束相当于完成了n=-1时的图像的卷积
end
hold off
%停止接收数据
2. 实验要求,使用c语言画出该图像
首先讲解一下使用c语言产生该函数的数据,然后使用gnuplot画图工具调用这些数据并重绘数据图形。以下我将 分别列出产生这两个窗函数的c程序。
2.1 产生在0-1区间上的窗函数数据
windows.c
#include<stdio.h>
int change(double x){
int y=0;
y=(int)((x)>0 && (x)<=1);
return y;
}
int main()
{
double x;
//产生第一个窗函数的负半轴部分数据
for (int i=4000;i>=0;i--){
//此for循环产生
x=-i/1000.0;
int y=8;
float result=0;
y=change(x);
//调用函数实现窗函数数字转换
result=(float)y*1.5;
//设置窗函数的高度
printf("%.3f\t%.2f\n",x,result);
}
//产生第一个窗函数的正半轴部分
for (int i=1;i<=4000;i++){
//此for循环产生
x=i/1000.0;
int y=8;
float result=0;
y=change(x);
//调用函数实现窗函数数字转换
result=(float)y*1.0;
//设置窗函数的高度
printf("%.3f\t%.2f\n",x,result);
}
return 0;
}
在编辑完该程序之后,我们再采用gcc编译器对其编译,并将其输出结果存入到res.dat文件中。
E:\研一c语言工程训练\gnuplot>gcc windows.c -o windows
E:\研一c语言工程训练\gnuplot>windows.exe > res.dat
2.2产生2-3区间上的窗函数
此窗函数其非零部分高度设置一样设置为1.5,和matlab里边的设置保持一致。
window2.c
#include<stdio.h>
int change(double x){
int y=0;
y=(int)((x)>2 && (x)<=3);
return y;
}
int main()
{
double x;
//产生第一个窗函数的负半轴部分数据
for (int i=4000;i>=0;i--){
//此for循环产生
x=-i/1000.0;
int y=8;
float result=0;
y=change(x);
//调用函数实现窗函数数字转换
result=(float)y*1.5;
//设置窗函数的高度
printf("%.3f\t%.2f\n",x,result);
}
//产生第一个窗函数的正半轴部分
for (int i=1;i<=4000;i++){
//此for循环产生
x=i/1000.0;
int y=8;
float result=0;
y=change(x);
//调用函数实现窗函数数字转换
result=(float)y*1.5;
//设置窗函数的高度
printf("%.3f\t%.2f\n",x,result);
}
return 0;
}
同样,在编辑完该程序之后,我们再采用gcc编译器对其编译,并将其输出结果存入到res2.dat文件中。
E:\研一c语言工程训练\gnuplot>gcc window2.c -o window2
E:\研一c语言工程训练\gnuplot>window2.exe > res2.dat
2.3 使用gnuplot重绘两个窗函数
在命令行输入如下命令,对图像进行一些基础设置和绘图。
gnuplot> set title('pictures of different windows function')
gnuplot> set xlabel 'time/s'
gnuplot> set ylabel 'f(t)'
gnuplot> plot [-4:4] [0:2] "res.dat" u 1:2 w l lt 6 lw 2,"res2.dat" w l lt 7 lw 2
执行完之后则会产生如下图。
可以看到c语言产生的绘制图片与matlab绘画出来的并没有什么差别。
2.4 c语言实现时域卷积
对于c语言实现卷积,此处我对于上边的代码又进行了修改以及综合,在该文件内,我分别将自变量t的取值范围、窗函数1的取值、窗函数2的取值以及两个窗函数的卷积存储在对应的数组内。然后再对这几个数组统一访问,输出到本地文件。
#include<stdio.h>
double change(double x){
//定义函数1,该函数主要实现对窗函数1数值的转换,使得只在0-1范围内有取值且为1
double y=0;
y=(double)((x)>0 && (x)<=1);
return y;
}
double change2(double x){
//定义函数2,该函数主要实现对窗函数2数值的转换,使得只在2-3范围内有取值且为1
double y=0;
y=(double)((x)>2 && (x)<=3);
return y;
}
int main()
{
double x;
double x_nums[8001] = {0.0};
double y_1[8001]={0.0};
double y_2[8001]={0.0};
double y_y[8001]={0.0};
//其中x_nums中存储的是-4:0.001:4
//y_1中存储的是在-4:0.001:4条件下窗函数1的取值
//y_2中存储的是-4:0.001:4条件下窗函数2的取值
//y_y中存储的是-4:0.001:4条件下窗函数1和窗函数卷积的取值
int j=0;
//产生第一个窗函数的负半轴部分数据
for (int i=4000;i>=0;i--){
//此for循环产生
x=-(i/1000.0);
j=4000-i;
double y,y2;
float result,result2;
y=change(x);
y2=change2(x);
//调用函数实现窗函数数字转换
result=y;
result2=y2;
x_nums[j]=x;
y_1[j]=result;
y_2[j]=result2*1.5;
//设置窗函数的高度
//printf("%.3f\t%.2f\n",x,result);
}
//产生第一个窗函数的正半轴部分
for (int i=1;i<=4000;i++){
//此for循环产生
x=i/1000.0;
j=i+4000;
double y,y2;
double result,result2;
y=change(x);
y2=change2(x);
//调用函数实现窗函数数字转换
result=y;
result2=y2;
//设置窗函数的高度
//printf("%.3f\t%.2f\n",x,result);
x_nums[j]=x;
y_1[j]=result;
y_2[j]=result2*1.5;
}
//开始计算卷积
//step 1.计算负半轴部分卷积
for(int i=4000;i>=0;i--){
x=-(i/1000.0);
j=4000-i;
//计算tao为负的情况
double sum;
for (int t=200;t>=0;t--){
double tao=-(t/100.0);
sum=sum+0.01*change(tao)*change2(x-tao)*1.5;
}
//计算tao为正的情况
for (int t=1;t<=500;t++){
double tao=(t/100.0);
sum=sum+0.01*change(tao)*change2(x-tao)*1.5;
}
y_y[j]=sum;
}
//step 2.计算正半轴部分卷积
for(int i=1;i<=4000;i++){
x=(i/1000.0);
j=4000+i;
//计算tao为负的情况
double sum=0;
for (int t=200;t>=0;t--){
double tao=-(t/100.0);
sum=sum+0.01*change(tao)*change2(x-tao)*1.5;
}
//计算tao为正的情况
for (int t=1;t<=500;t++){
double tao=(t/100.0);
sum=sum+0.01*change(tao)*change2(x-tao)*1.5;
}
y_y[j]=sum;
}
//for循环输出画图需要的数据
for(int i=0;i<=8000;i++) {
printf("%.3f\t%.2f\t%.2f\t%.2f\n",x_nums[i],y_1[i],y_2[i],y_y[i]);
//其中x_nums中存储的是-4:0.001:4
//y_1中存储的是在-4:0.001:4条件下窗函数1的取值
//y_2中存储的是-4:0.001:4条件下窗函数2的取值
//y_y中存储的是-4:0.001:4条件下窗函数1和窗函数卷积的取值
}
return 0;
}
其具体的操作流程如下所示:
在上一步的操作中,我们已经实现了将我们绘图需要的数据存到了本地,接下来我们就要使用gnuplot来对这些数据绘制。
plot [-3:5] [0:2] "convolution.dat" u 1:2 w l lt 6 lw 2.5,"convolution.dat" u 1:3 w l lt 7 lw 2.5,"convolution.dat" u 1:4 w p pt 1 ps 0.1
具体的操作如下所示:
3. c语言图像与matlab图像的比对
另外,此时我们还需要将C语言画出来的卷积图像和matlab绘制出来的图像进行比对。下图中图1为c语言绘制的,图2为matlab绘制的。
3.1 总结 & 体会
本次实践分别使用matlab和c语言,在不调用其它函数包的情况下,自主绘画了两个窗函数并且实现了两个函数的时域卷积练习。通过本次实践,更加深刻的理解了卷积的概念,对于信号与系统以及数字语音处理的学习提供了帮助。另外希望此次博客可以为自己以后的练习提供帮助吧。