实验八音频频谱分析仪设计与实现.docx
《实验八音频频谱分析仪设计与实现.docx》由会员分享,可在线阅读,更多相关《实验八音频频谱分析仪设计与实现.docx(19页珍藏版)》请在冰点文库上搜索。
实验八音频频谱分析仪设计与实现
实验八音频频谱分析仪设计与实现
1、实验目的:
1、学习怎样用matlab构建gui界面以及掌握其控件的基本属性。
2、掌握回调函数的写法。
3、掌握set,get等设置控件属性的基本函数。
4、学会用matlab产生三角波,锯齿波,方波以及白噪声等波形。
2、实验原理:
MATLAB是一个数据分析和处理功能十分强大的工程实用软件,他的数据采集工具箱为实现数据的输入和输出提供了十分方便的函数和命令[3]。
本文将给出基于声卡与MATLAB的声音信号频谱分析仪的设计原理与实现方法,功能包括:
(1)音频信号信号输入,从声卡输入、从WAV文件输入、从标准信号发生器输入;
(2)信号波形分析,包括幅值、频率、周期、相位的估计,以及统计量峰值、均值、均方值和方差的计算;
(3)信号频谱分析,频率、周期的估计,图形显示幅值谱、相位谱、实频谱、虚频谱和功率谱的曲线。
1、频率(周期)检测
对周期信号来说,可以用时域波形分析来确定信号的周期,也就是计算相邻的两个信号波峰的时间差、或过零点的时间差。
这里采用过零点(ti)的时间差T(周期)。
频率即为f=1/T,由于能够求得多个T值(ti有多个),故采用它们的平均值作为周期的估计值。
2、幅值检测
在一个周期内,求出信号最大值ymax与最小值ymin的差的一半,即A=(ymax-ymin)/2,同样,也会求出多个A值,但第1个A值对应的ymax和ymin不是在一个周期内搜索得到的,故以除第1个以外的A值的平均作为幅值的估计值。
3、相位检测
采用过零法,即通过判断与同频零相位信号过零点时刻,计算其时间差,然后换成相应的相位差。
φ=2π(1-ti/T),{x}表示x的小数部分,同样,以φ的平均值作为相位的估计值。
频率、幅值和相位估计的流程如图1所示。
其中tin表示第n个过零点,yi为第i个采样点的值,Fs为采样频率。
图1
4、数字信号统计量估计
(1)峰值P的估计
在样本数据x中找出最大值与最小值,其差值为双峰值,双峰值的一半即为峰值。
P=0.5[max(yi)-min(yi)]
(2)均值估计
式中,N为样本容量,下同。
(3)均方值估计
(4)方差估计
5、频谱分析原理
时域分析只能反映信号的幅值随时间的变化情况,除单频率分量的简单波形外,很难明确提示信号的频率组成和各频率分量大小,而频谱分析能很好的解决此问题。
由于从频域能获得的主要是频率信息,所以本节主要介绍频率(周期)的估计与频谱图的生成。
(1)DFT与FFT
对于给定的时域信号y,可以通过Fourier变换得到频域信息Y。
Y可按下式计算
式中,N为样本容量,Δt=1/Fs为采样间隔。
采样信号的频谱是一个连续的频谱,不可能计算出所有的点的值,故采用离散Fourier变换(DFT),即
式中,Δf=Fs/N。
但上式的计算效率很低,因为有大量的指数(等价于三角函数)运算,故实际中多采用快速Fourier变换(FFT)。
其原理即是将重复的三角函数算计的中间结果保存起来,以减少重复三角函数计算带来的时间浪费。
由于三角函数计算的重复量相当大,故FFT能极大地提高运算效率。
(2)频率、周期的估计
对于Y(kΔf),如果当kΔf=
时,Y(kΔf)取最大值,则
为频率的估计值,由于采样间隔的误差,
也存在误差,其误差最大为Δf/2。
周期T=1/f。
从原理上可以看出,如果在标准信号中混有噪声,用上述方法仍能够精确地估计出原标准信号的频率和周期,这个将在下一章做出验证
(3)频谱图
为了直观地表示信号的频率特性,工程上常常将Fourier变换的结果用图形的方式表示,即频谱图。
以频率f为横坐标,|Y(f)|为纵坐标,可以得到幅值谱;
以频率f为横坐标,argY(f)为纵坐标,可以得到相位谱;
以频率f为横坐标,ReY(f)为纵坐标,可以得到实频谱;
以频率f为横坐标,ImY(f)为纵坐标,可以得到虚频谱。
根据采样定理,只有频率不超过Fs/2的信号才能被正确采集,即Fourier变换的结果中频率大于Fs/2的部分是不正确的部分,故不在频谱图中显示。
即横坐标f∈[0,Fs/2]
(4)模块划分
模块化就是把程序划分成独立命名且可独立访问的模块,每个模块完成一个子功能,把这些模块集成起来构成一个整体,可以完成指定的功能满足用户需求。
根据人类解决一般问题的经验,如果一个问题由两个问题组合而成,那么它的复杂程度大于分别考虑每个问题时的复杂程度之和,也就是说把复杂的问题分解成许多容易解决的小问题,原来的问题也就容易解决了。
这就是模块化的根据。
在模块划分时应遵循如下规则[4]:
改进软件结构提高模块独立性;模块规模应该适中;深度、宽度、扇出和扇入都应适当;模块的作用域应该在控制域之内;力争降低模块接口的复杂程度;设计单入口单出口的模块;模块功能应该可以预测。
本着上述的启发式规则,对软件进行如图2所示的模块划分。
图2频谱分析仪的模块划分
3、实验内容:
查阅相关资料,利用所提供的代码,构建一个GUI界面,设计一个音频频谱分析仪,实现相关功能。
4、实验程序:
生成一个wav文件命名为handel.wav:
loadhandel.mat
hfile='handel.wav';
Fs=11025;
wavwrite(y,Fs,hfile)
[y,Fs,'int16',readinfo]=wavread(hfile);
1、设置坐标轴标注:
handles.inputtype=0;
xlabel(handles.plot1,'freqency(Hz)');
xlabel(handles.plot2,'freqency(Hz)');
xlabel(handles.plot3,'freqency(Hz)');
xlabel(handles.plot4,'freqency(Hz)');
xlabel(handles.plot5,'freqency(Hz)');
ylabel(handles.plot1,'amplitude');
ylabel(handles.plot2,'phase(rad)');
ylabel(handles.plot3,'real');
ylabel(handles.plot4,'Imaginary');
ylabel(handles.plot5,'power');
handles.output=hObject;
guidata(hObject,handles);%更新句柄值
2、输入形式设置:
(1)、声卡输入方式:
set(findobj('Tag','recordtime'),'enable','on');
%关闭与其他方式输入有关的对象的使能
set(findobj('Tag','filename'),'enable','off');
set(findobj('Tag','freq'),'enable','off');
set(findobj('Tag','amp'),'enable','off');
set(findobj('Tag','phase'),'enable','off');
set(handles.channel,'enable','off');
set(handles.fileopen,'enable','off');
set(handles.gensig,'enable','off');
set(handles.wavetype,'enable','off');
set(handles.add,'enable','off');
set(handles.startrecord,'enable','on');
(2)、WAV文件输入方式:
set(findobj('Tag','freq'),'enable','off');
set(findobj('Tag','amp'),'enable','off');
set(findobj('Tag','phase'),'enable','off');
set(findobj('Tag','recordtime'),'enable','off');
set(handles.channel,'enable','on');
set(handles.fileopen,'enable','on');
set(handles.gensig,'enable','off');
set(handles.wavetype,'enable','off');
set(handles.add,'enable','off');
set(handles.startrecord,'enable','off');
(3)、信号发生器方式输入:
set(findobj('Tag','freq'),'enable','on');
set(findobj('Tag','amp'),'enable','on');
set(findobj('Tag','phase'),'enable','on');
set(findobj('Tag','recordtime'),'enable','off');
set(handles.channel,'enable','off');
set(handles.fileopen,'enable','off');
set(handles.gensig,'enable','on');
set(handles.wavetype,'enable','on');
set(handles.add,'enable','on');
set(handles.startrecord,'enable','off');
3、控件设置:
(1)、startrecord按钮的回调函数程序:
Fs=str2double(get(findobj('Tag','samplerate'),'String'));%得到才样频率
handles.y=wavrecord(str2double(get(handles.recordtime,'String'))*Fs,Fs,'int16');%获得采样数据
handles.y=double(handles.y);%数据类型转换,用于FFT变换
handles.inputtype=1;
guidata(hObject,handles);
plot(handles.time,handles.y);
title('WAVE');
ysize=size(handles.y)
set(handles.samplenum,'String',num2str(ysize
(1)));
(2)、open按钮的回调函数的程序:
temp=wavread(get(findobj('Tag','filename'),'String'));
channel=str2double(get(handles.channel,'String'));
handles.y=temp(:
channel);
handles.y=double(handles.y);%数据类型转换,用于FFT变换
handles.inputtype=2;
guidata(hObject,handles);
plot(handles.time,handles.y);
title('WAVE');
ysize=size(handles.y)
set(handles.samplenum,'String',num2str(ysize
(1)));
(3)、producewave按钮的回调函数的程序:
Fs=str2double(get(findobj('Tag','samplerate'),'String'));%得到才样频率
N=str2double(get(findobj('Tag','samplenum'),'String'));%得到才样点数
x=linspace(0,N/Fs,N)';%加转置是为了与handles.y的维数保持一致
soundtype=get(handles.wavetype,'Value');
frequency=str2double(get(handles.freq,'String'));
amp=str2double(get(handles.amp,'String'));
phase=str2double(get(handles.phase,'String'));
switchsoundtype
case1
y=amp*sin(2*pi*x*frequency+phase);%正弦波
case2
y=amp*sign(sin(2*pi*x*frequency+phase));%方波
case3
y=amp*sawtooth(2*pi*x*frequency+phase,0.5);%三角波
case4
y=amp*sawtooth(2*pi*x*frequency+phase);%锯齿波
case5
y=amp*(2*rand(size(x))-1);%白噪声
otherwise
errordlg('Illegalwavetype','Chooseerrer');
end
ifget(handles.add,'Value')==0.0
handles.y=y;
else
handles.y=handles.y+y;
end
handles.inputtype=3;
guidata(hObject,handles);
plot(handles.time,handles.y);
title('WAVE');
axis([0N-str2double(get(handles.amp,'String'))str2double(get(handles.amp,'String'))]);
(4)、timeanalyse回调函数的程序:
Fs=str2double(get(findobj('Tag','samplerate'),'String'));
N=str2double(get(findobj('Tag','samplenum'),'String'));
ifhandles.inputtype==0
msgbox('Nowaveexist!
Pleasechooseainputtype!
');
return;
end
n=1;
ymax=max([handles.y
(1)handles.y
(2)]);
ymin=min([handles.y
(1)handles.y
(2)]);
from=str2double(get(handles.pointfrom,'String'));
to=str2double(get(handles.pointto,'String'));
iffrom<1|to-from<5;
msgbox('Errorrange!
');
return;
end
fori=from+2:
to-1;
ifhandles.y(i-1)<0&handles.y(i-2)<0&handles.y(i)>=0&handles.y(i+1)>0
ifhandles.y(i)==0
ti(n)=i;
else
ti(n)=i-handles.y(i)/(handles.y(i)-handles.y(i-1));
end
amp(n)=(ymax-ymin)/2;
ymax=0;
ymin=0;
n=n+1;
else
ifymaxymax=handles.y(i);
end
ifymin>handles.y(i)
ymin=handles.y(i);
end
end
end
n=n-1;
%freqenceandperiodicity
fori=1:
n-1
T(i)=ti(i+1)-ti(i);
end
freq=Fs/mean(T);
set(handles.outt,'String',1/freq);
set(handles.outfreq,'String',num2str(freq));
set(handles.outamp,'String',num2str(mean(amp(2:
n-1))));%amplitude
phase=2*pi*(1-(ti(1:
n-1)-1)./T+floor((ti(1:
n-1)-1)./T));%phase
set(handles.outphase,'String',num2str(mean(phase)));%peak
set(handles.outpeak,'String',(max(handles.y(from:
to))-min(handles.y(from:
to)))/2);%mean
set(handles.outmean,'String',mean(handles.y(from:
to)));%meansquare
set(handles.outmeansquare,'String',mean(handles.y(from:
to).^2));
set(handles.outs,'String',std(handles.y(from:
to))^2);
(5)、frequecyanalyse回调函数的程序:
Fs=str2double(get(findobj('Tag','samplerate'),'String'));
N=str2double(get(findobj('Tag','samplenum'),'String'));
ifhandles.inputtype==0
msgbox('Nowaveexist!
Pleasechooseainputtype!
');
return;
end
from=str2double(get(handles.pointfrom,'String'));
to=str2double(get(handles.pointto,'String'));
sample=handles.y(from:
to);
f=linspace(0,Fs/2,(to-from+1)/2);
Y=fft(sample,to-from+1);
[C,I]=max(abs(Y));
set(handles.foutt,'String',1/f(I));
set(handles.foutfreq,'String',f(I));
Y=Y(1:
(to-from+1)/2);
plot(handles.plot1,f,2*sqrt(Y.*conj(Y)));
plot(handles.plot2,f,angle(Y));
plot(handles.plot3,f,real(Y));
plot(handles.plot4,f,imag(Y));
plot(handles.plot5,f,abs(Y).^2);
xlabel(handles.plot1,'freqency(Hz)');
xlabel(handles.plot2,'freqency(Hz)');
xlabel(handles.plot3,'freqency(Hz)');
xlabel(handles.plot4,'freqency(Hz)');
xlabel(handles.plot5,'freqency(Hz)');
ylabel(handles.plot1,'amplitude');
ylabel(handles.plot2,'phase(rad)');
ylabel(handles.plot3,'real');
ylabel(handles.plot4,'Imaginary');
ylabel(handles.plot5,'power');
(6)、analyseall回调函数的程序:
ifget(hObject,'Value')==0.0
set(handles.pointfrom,'Enable','on');
set(handles.pointto,'Enable','on');
else
set(handles.pointfrom,'String','1','Enable','off');
set(handles.pointto,'String',get(handles.samplenum,'String'),'Enable','off');
End
(7)、play按钮的回调函数:
Fs=str2double(get(findobj('Tag','samplerate'),'String'));
wavplay(handles.y,Fs);
(8)、时域slider回调函数的程序:
val=get(hObject,'value');
val=10^(val*5+1);
x=get(handles.xmove,'Value')*str2double(get(handles.samplenum,'String'));
axis(handles.time,[xval+xmin(handles.y)max(handles.y)]);
(9)、频域slider2回调函数的程序:
val=get(hObject,'value');
val=10^(val*5+1);
xlim(handles.plot1,[0val]);
xlim(handles.plot2,[0val]);
xlim(handles.plot3,[0val]);
xlim(handles.plot4,[0val]);
5、实验结果分析:
1、录音及对录音信号的时域和频域分析:
从上图可以看出:
音频频谱都集中在2000hz以下,高于该频率的分量很少,且幅度低。
2、输入wav文件及对其进行时域和频域分析:
从这个图和上一图可以看出音频信号的频率都比较低,大部分能量都集中在2000hz以下。
而且此图中的音频信号能量集中在2000hz以内的某两段频率内。
3、信号发生器产生波形:
(1)、sin波形
从上图可以看出正弦信号的幅度谱,功率谱都是一个单一脉冲。
(2)、方波:
(3)、三角波:
从图上可以看出:
三角波的幅度谱有两条谱线组成,功率谱由一条谱线组成。
(4)、白噪声:
从图中可以看出:
白噪声的功率谱比较均匀,其他谱线也比较均匀。
(5)、从信号中取一部分进行时域和频域分析,如下所示是从handel.wav文件中取前1000个点进行了分析:
(6)、测试混叠:
上面两图中的前一个是用handel.wav文件与sin波形进行的混叠测试,后一个图是用白噪声与正弦波形进行的混叠测试,从中可以看出带噪声的正弦信号的频率估计,时域为267.7701,频域的估计结果为300.015,与所设定的300相比可以看出,带噪声的正弦信号的频率估计用频域法误差小。
在作频率估计时,如果信号的噪声很小,采用时域分析的方法较好;如果信号的噪声较大,采用频域分析的方法较好。
6、收获及体会:
1、通过本次实验初步掌