短时傅里叶变换函数编写

文章目录

      • 傅里叶变换与短时傅里叶变换
      • 什么是窗?
      • 短时傅里叶变换窗口、步长等参数解析
      • 自己动手实现短时傅里叶变换

傅里叶变换与短时傅里叶变换

在了解短时傅里叶变换之前,首先要知道是什么是傅里叶变换( fourier transformation,FT),傅里叶变换就是将一个信号分解为不同频率的复指数信号的和,例如我们要对一段钢琴曲进行傅里叶变换,它的结果能够告诉我们这一段时间内按下了那些琴键(简单假设为不同琴键对应不同频率的声音),注意我们处理的时长是整个时间段。傅里叶变换的公式可由下面给出:
F ( ω ) = F [ s ( t ) ] = ∫ − ∞ + ∞ s ( t ) e − j ω t d t F(\omega)=\mathcal{F}[s(t)]=\int_{-\infty}^{+\infty} s(t)e^{-j\omega t}dt F(ω)=F[s(t)]=+s(t)etdt
可以看出来,傅里叶变换得到的是一个谱,它是不同频率下对应的幅度谱和相位谱,幅度谱我们用的比较多。利用傅里叶变换能够对信号
进行分析。根据信号是否为周期信号,以及时域上是否连续,又可以分为傅里叶级数展开、离散时间傅里叶变换、离散傅里叶变换等等,在此不再赘述,《信号与系统》和《数字信号处理》的相关书籍介绍很多。
下面是短时傅里叶变换,(short-time fourier transformation,STFT),有时候也叫加窗傅里叶变换,时间窗口使得信号只在某一小区间内有效,这就避免了传统的傅里叶变换在时频局部表达能力上的不足,使得傅里叶变化有了局部定位能力。公式如下:
Ω ( t , f ) = Γ [ s ( t ) ] = ∫ − ∞ + ∞ s ( τ ) ω ( τ − t ) e − j 2 π f τ d τ \Omega(t,f)=\Gamma[s(t)]=\int_{-\infty}^{+\infty} s(\tau)\omega (\tau-t)e^{-j2\pi f \tau }d\tau Ω(t,f)=Γ[s(t)]=+s(τ)ω(τt)ej2πfτdτ
还是以钢琴音乐的信号分析,虽然傅里叶变换能够告诉我们这段音乐中包含那些琴键的声音,但是我们无法知道是在哪些时间点按下这些琴键的。傅里叶变换是一种频域分析共工具,而短时傅里叶变换就是为了解决以上问题而提出的一种时频域方法,后面还有人提出了小波变换。
mailab中可以采用stft函数和spectrogram函数来实现,当然也可以自己动手实现。

什么是窗?

在信号处理中,我们经常听到海明窗(hamming window)、汉宁窗(hanning window)之类的名字,这是什么意思呢?简单来说,窗就是一个函数,它的形状像窗,所以类似的函数都叫窗。
例如我们处理的语音信号一般在10ms到30ms之间,我们可以把它看成是平稳的。为了处理语音信号,我们要对语音信号进行加窗,也就是一次仅处理窗中的数据。因为实际的语音信号是很长的,我们不能也不必对非常长的数据进行一次性处理。明智的解决办法就是每次取一段数据,进行分析,然后再取下一段数据,再进行分析。
怎么仅取一段数据呢,一种方式就是构造一个函数。这个函数在某一区间有非零值,而在其余区间皆为0。汉明窗就是这样的一种函数。它主要部分的形状像 s i n ( x ) sin(x) sin(x)在0到 π \pi π区间的形状,而其余部分都是0,这样的函数乘上其他任何一个函数 f ( x ) f(x) f(x) f ( x ) f(x) f(x)只有一部分有非零值。
为什么海明窗这样取呢,因为之后我们会对海明窗中的数据进行FFT,它假设一个窗内的信号是代表一个周期的信号。(也就是说窗的左端和右端应该大致能连在一起)而通常一小段音频数据没有明显的周期性,加上汉明窗后,数据形状就有点周期的感觉了。
因为加上海明窗,只有中间的数据体现出来了,两边的数据信息丢失了,所以等会移窗的时候,只会移1/3或1/2窗,这样被前一帧或二帧丢失的数据又重新得到了体现。
在matlab中可以借助函数很轻松的生成窗,例如我们要创建一个长度为64点的汉宁窗

L = 64;
wvtool(hann(L))

短时傅里叶变换函数编写_第1张图片

图1 长度为64点的汉宁窗

短时傅里叶变换窗口、步长等参数解析

短时傅里叶变换最重要的参数就是窗口长度,它表示了取多长的信号进行一次fft,取得点数多了,信号的频率分辨率会高,步长是指每次滑动的时候,滑几个点过去,步长越小时间分辨率越高,窗口数目是指滑动的次数,也就是进行了多少次fft。下面用一个图来展示这些参数之间的关系:
短时傅里叶变换函数编写_第2张图片

图2 窗口长度和起始坐标值

就如同上图展示的,加入信号长度为N,窗口长度为M,那么窗口滑动的次数就是(N-M)/2,假如我们将短时傅里叶变换的结果按列保存,每一列代表此前窗口下的fft的结果,那么这个矩阵的列数就是窗口数,行数就是fft的点数,一般可以取2的几次方,点数不够的情况下会自动补零。窗口长度M和信号长度N确定,步长为H的话,滑动的次数就是(N-M+1)/2,窗口的数量也就确定了。短时傅里叶变换之后,时频图中的时间轴也会相应的变短,如上图所示,步长为1的时候,变短的长度就是M-1。还有一点,步长决定了两个相邻窗口的重叠元素的数量,即M-H。在我下面的仿真中我取的步长是1,信号长度是240,窗口长度是32,fft的点数是32,滑动次数是209,短时傅里叶变换的结果存储为1个32*209的矩阵,时间轴由240个点变为了209个。

自己动手实现短时傅里叶变换

这里参考了知乎笔记matlab短时傅里叶变换
自定义函数如下:

function [STFT, f, t] = mystft(x, win, hop, fs)
% 该函数用于短时傅里叶变换
%  Author:huasir 2023.11.22 @Beijing
% Input : 
%   * x: 输入信号
%   * win:窗,可以采用汉宁窗
%   * hop:窗口滑动的步长,可以为1
%   * fs:采样频率,由输入信号的采样率决定
% Output : 
%    * STFT:短时傅里叶变换的结果,每一列代表某一个窗口下的fft结果,只保留了幅度谱
%    * f: 频率坐标
%    * t: 时间坐标
x = reshape(x,length(x),1); %将向量转为列向量,与后续的点乘相对应
xlen = length(x);%信号长度
wlen = length(win);%窗长度
L = 1+fix((xlen-wlen)/hop);%窗口数目
STFT = zeros(wlen,L);%返回空矩阵
%循环进行傅里叶变换
for k = 0:L-1
    xw = x(1+k*hop : wlen+k*hop).*win; %滑动窗口
    X = fft(xw,wlen); %离散傅里叶变换
    X = fftshift(X);  %将频率按顺序排列:负频率→直流→正频率
    X=abs(X)/wlen*2;  %将fft之后的幅度乘以N/2
    STFT(:,1+k) = X;  %存储
end
%频率坐标
f=linspace(-fs/2,fs/2,wlen);
%时间坐标
t = (wlen/2:hop:wlen/2+(L-1)*hop)/fs;%秒
end

以线性调频信号为例,下面是采用这个自定义函数进行短时傅里叶变换并绘制时频图的代码:

%% 对LFM信号进行短时傅里叶变换
% 主要内容:线性调频信号的生成、雷达回波的模拟、对雷达回波进行短时傅里叶变换
% 短时傅里叶变换采用的是自定义函数
% Author: zhenhualiu 2023.11.22
%=========================================================================%
%                        雷达参数设置                                     %
%=========================================================================%
clear all;close all;clc;
BandWidth = 2.0e6;  %雷达发射信号带宽,带宽=B=1/tau,tau是脉冲宽度
TimeWidth = 40.0e-6; %雷达发射信号的脉冲时宽
PRT = 100e-6;       %雷达发射脉冲重复周期(s),100us对应1/2*100*300=15000米最大无模糊距离
Fs = 4.0e6;         %采样频率
NoisePower = -12;   %噪声功率
SigPower = 1;           %目标功率,无量纲
TargetDistance = 2000;  %目标距离,单位:m,相对于的距离门DelayNumber = fix(Fs*2*TargetDistance/C); %把目标距离换算成采样点(距离门)
plotEnableHigh = 1; %绘图控制符
%=========================================================================%
%             调用函数产生线性调频信号、雷达回波和脉压系数                %
%=========================================================================%
[LFMPulse,targetEchoPRT,matchedFilterCoeff,pulseNumber,PRTNumber] = GenerateLFMSignal(BandWidth,TimeWidth,PRT,Fs,SigPower,TargetDistance,plotEnableHigh);%调用函数
%=========================================================================%
%=========================================================================%
%                  设置窗、步长并进行短时傅里叶变换                       %
%=========================================================================%
wlen=32;%设置窗口长度。窗口越长时间分辨率越差,频率分辨率越好。
hop = 1; %每次平移的步长,最小为1。越小图像时间精度越好,但计算量大。
win = hanning(wlen, 'periodic');%窗函数
figure; %绘制汉宁窗
plot(win);title('汉宁窗');
[S2, f1, t1] = mystft(targetEchoPRT, win, hop, Fs); %采用自定义函数进行短时傅里叶变换
%=========================================================================%
%                     绘制短时傅里叶变换的伪彩色图                        %
%=========================================================================%
figure;
Figure=pcolor(t1,f1,S2); %绘制伪彩色图
colormap('jet'); %指定色系:parula、turbo、jet等等
shading flat;%将每个网格片用同一个颜色进行着色,且网格线也用相应的颜色,从而使得图形表面显得更加光滑。
shading interp;%在网格片内采用颜色插值处理,得出的表面图显得最光滑。
colorbar; %显示图像的颜色条,提供对图像彩色的可视化表示,使得用户能够更直观的理解图像数据的分布和范围

其中生成线性调频信号的函数如下:

function [LFMPulse,targetEchoPRT,matchedFilterCoeff,pulseNumber,PRTNumber] = GenerateLFMSignal(bandWidth,pulseDuration,PRTDuration,samplingFrequency,signalPower,targetDistece,plotEnableHigh)
% 该函数用于产生线性调频信号,以及雷达的目标反射回波,仅产生单个回波
%  Author:huasir 2023.9.21 @Beijing
% Input : 
%   * bandWidth: 信号带宽 ,参考值:2.0e6 表示2MHz
%   * pulseDuration:脉冲持续时间,参考值:40.0e-6 表示40ms
%   * PRTDuration:脉冲重复周期,参考值:240ms
%   * samplingFrequency:采样频率,参考值:2倍的信号带宽
%   * signalPower:信号能量,参考值:1
%   * targetDistece:目标距离,最大无模糊距离由脉冲重复周期决定。计算公式:1/2*PRTDuration*光速
%   * plotEnableHigh: 绘图控制符,1:打开绘图,0:关闭绘图
% Output : 
%    * LFMPulse:线性调频信号
%    * targetEchoPRT: 目标反射回波
%    * matchedFilterCoeff: 匹配滤波器系数
%    * pulseNumber:当前采样率下线性调频信号的采样点数
%    * PRTNumber:1个PRT对应的采样点数
C = 3.0e8;      %光速(m/s)
BandWidth = bandWidth;  %雷达发射信号带宽,带宽=B=1/tau,tau是脉冲宽度
TimeWidth = pulseDuration; %雷达发射信号的脉冲时宽

PRT = PRTDuration;       %雷达发射脉冲重复周期(s),240us对应1/2*240*300=360000米最大无模糊距离
Fs = samplingFrequency;         %采样频率
SampleNumber = fix(Fs*PRT);
%=========================================================================%
%                        目标参数设置                                     %
%=========================================================================%
SigPower = signalPower;           %目标功率,无量纲
TargetDistance = targetDistece; %目标距离,单位:m
DelayNumber = fix(Fs*2*TargetDistance/C); %把目标距离换算成采样点(距离门)
%=========================================================================%
%                        产生线性调频信号、匹配滤波器                     %
%=========================================================================%
number = fix(Fs*TimeWidth); %回波采样点数=脉压系数长度=暂态点数目+1
if rem(number,2)~=0
    nember = nember + 1;
end
Chirp = zeros(1,number);
for i = -fix(number/2):fix(number/2)-1
    Chirp(i+fix(number/2)+1)=exp(1j*(pi*(BandWidth/TimeWidth)*(i/Fs)^2));%产生复ChIrp信号
end
coeff = conj(fliplr(Chirp)); %把Chirp信号翻转并把复数共轭,产生脉压系数
%=========================================================================%
%                      绘制线性调频信号                                   %
%=========================================================================%
if plotEnableHigh == 1
    figure;
    plot(real(Chirp)); %绘制线性调频信号
    xlabel('Sampling points'); ylabel('Amplitude');title('线性调频信号实部');
end
SignalTemp = zeros(1,SampleNumber); %1个PRT
SignalTemp(DelayNumber+1:DelayNumber+number) = sqrt(SigPower)*Chirp;%将线性调频信号按照距离进行延时
if plotEnableHigh == 1
    figure;
    plot(real(SignalTemp)); %绘制1个完整的PRT的雷达回波信号
    xlabel('Range bin'); ylabel('Amplitude');title('雷达回波的实部');
end
%=========================================================================%
%                          进行脉冲压缩                                   %
%=========================================================================%
Echo = SignalTemp; % 目标回波
pc_time0 = conv(Echo,coeff); % 回波和滤波器卷积的结果
pc_time1 = pc_time0(number:number+SampleNumber-1); %去掉暂态点
realTargetRange = find(abs(pc_time1)==max(abs(pc_time1)))-1; %由脉压结果目标距离
fprintf('The target range bin is  %d',realTargetRange);
if plotEnableHigh == 1
    figure; %时域脉压结果
    subplot(2,1,1);plot(abs(pc_time0),'r-');
    xlabel('Range bin'); ylabel('Amplitude');title('时域脉压结果');
    subplot(2,1,2);plot(abs(pc_time1),'r-');
    xlabel('Range bin'); ylabel('Amplitude');title('去掉暂态点的时域脉压结果');
end
%=========================================================================%
%                              返回参数                                   %
%=========================================================================%
LFMPulse = Chirp; %线性调频信号
targetEchoPRT = SignalTemp; %目标反射回波
matchedFilterCoeff = coeff; %匹配滤波器系数
pulseNumber = number; %线性调频信号(仅脉内)的采样点数
PRTNumber = SampleNumber; %目标反射回波(整个PRT)的采样点数
end

绘制的线性调频信号的时域图和时频图如下:
短时傅里叶变换函数编写_第3张图片

图3. 线性调频信号时域图

短时傅里叶变换函数编写_第4张图片

图4. 线性调频信号频谱图

短时傅里叶变换函数编写_第5张图片

图5. 线性调频信号时频域图

你可能感兴趣的:(语音识别,人工智能)