matlab和c语言在不调用包(库)的情况下实现时域卷积

1.实验要求,matlab实现卷积运算(不调用函数和方法)

1.1 卷积原理讲解

y [ n ] = k = x [ k ] h [ n k ] = x [ n ] h [ n ] s ( t ) = f 1 ( t ) f 2 ( t ) = t f 1 ( τ ) f 1 ( t τ ) d τ y[n]=\sum_{k=-\infty}^{\infty}{x[k]h[n-k]}=x[n]*h[n]\\s(t)=f_1(t)*f_2(t)=\int_{-\infty}^tf_1(\tau)f_1(t-\tau)d\tau
\qquad 上述两式分别为离散卷积运算和连续卷积运算。实际上我们的电脑要进行卷积运算就是将连续的函数离散化,再来进行离散的卷积计算。

1.2 卷积计算步骤(图解法)

1.翻折 先在坐标轴上画出 x ( m ) x(m) h ( m ) h(m) ,将 h ( m ) h(m) 以纵坐标为坐标轴折叠成 h ( m ) h(-m)
2.移位 h ( m ) h(-m) 移位 n n ,得到 h ( n m ) h(n-m) [或将 f 1 ( τ ) f_1(-\tau) 移位 t t ,得到 f 1 ( t τ ) f_1(t-\tau) ],当n或t为正数时右移n或t,当n或t为负数时左移n或t。
3.相乘 h ( n m ) h(n-m) [或 f 1 ( t τ ) f_1(t-\tau) ]与 x ( m ) x(m) [或 f 1 ( τ ) f_1(\tau) ]的对应序列值[(数值)]对应相乘
4.相加 将所有的乘机累加[(积分)]起来,就得到了 y ( n ) y(n) [或 s ( t ) s(t) ]

1.3 matlab画出两个窗函数

\qquad 以下列出的是产生两个门函数的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 对 h ( τ ) h(-\tau) 进行移位

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语言画出该图像

\qquad 首先讲解一下使用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;
}

\quad 在编辑完该程序之后,我们再采用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;
}

\qquad 同样,在编辑完该程序之后,我们再采用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语言实现时域卷积

\qquad 对于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;
}

其具体的操作流程如下所示:
在这里插入图片描述
\qquad 在上一步的操作中,我们已经实现了将我们绘图需要的数据存到了本地,接下来我们就要使用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图像的比对

\qquad 另外,此时我们还需要将C语言画出来的卷积图像和matlab绘制出来的图像进行比对。下图中图1为c语言绘制的,图2为matlab绘制的。
在这里插入图片描述
在这里插入图片描述

3.1 总结 & 体会

\qquad 本次实践分别使用matlab和c语言,在不调用其它函数包的情况下,自主绘画了两个窗函数并且实现了两个函数的时域卷积练习。通过本次实践,更加深刻的理解了卷积的概念,对于信号与系统以及数字语音处理的学习提供了帮助。另外希望此次博客可以为自己以后的练习提供帮助吧。

发布了44 篇原创文章 · 获赞 37 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_38468077/article/details/101640571