welch法功率谱密度估计核心总结为:信号分段、段间互相交叠、每段加窗后用估算其功率谱密度,最后对所有段的估计结果进行平均得到该信号的功率谱密度。
版本1:
clc;clear;close all;
fs = 44100;
t = 0:1/fs:1-1/fs;
in = randn(size(t));
winlen = 4096;
overlap = winlen/2;
load("myfir64.mat"); % 自己设计的64阶fir低通滤波器
filtercoe = myfir64;
out = filter(filtercoe, 1, in);
%% 自己实现cpsd、pwelch
% 奇怪的是:MATLAB的buffer函数自带分段帧移特性,但为什么用它就出现曲线毛刺较多的现象呢?
% segments_i = buffer(in, winlen, overlap, "nodelay"); % 输入信号的分段交叠
% segments_o = buffer(out, winlen, overlap, "nodelay"); % 输出信号的分段交叠
% numSegments = size(segments_i, 2); % 分段数
wd = hanning(winlen);
spectrum_Pxx = zeros(winlen/2+1,1); % 存放每段估计的自功率谱密度
spectrum_Pxy = zeros(winlen/2+1,1); % 存放每段估计的互功率谱密度
numSegments = fix((length(in) - overlap) / (winlen - overlap));
for i = 1:numSegments % 分段估计
% 自己分段帧移
inSeg = in((i-1)*overlap+1:(i-1)*overlap+winlen);
outSeg = out((i-1)*overlap+1:(i-1)*overlap+winlen);
% 估计输入信号自功率谱密度
in_fft = fft(inSeg.*wd');
N = length(in_fft);
in_fft = in_fft(1:floor(N/2)+1);
in_fft(2:end-1) = 2*in_fft(2:end-1);
Pxx_ = (abs(in_fft).^2)';
% 估计输入、输出信号互功率谱密度
out_fft = fft(outSeg.*wd');
out_fft = out_fft(1:floor(N/2)+1);
out_fft(2:end-1) = 2*out_fft(2:end-1);
Pxy_ = abs(out_fft .* conj(in_fft))';
% 每段的功率谱密度存起来
spectrum_Pxx = Pxx_ + spectrum_Pxx;
spectrum_Pxy = Pxy_ + spectrum_Pxy;
end
f = fs*(0:N/2)/N;
% 计算均值
spectrumAvg_Pxx = spectrum_Pxx ./ numSegments;
spectrumAvg_Pxy = spectrum_Pxy ./ numSegments;
H2 = spectrumAvg_Pxy ./ spectrumAvg_Pxx;
subplot(211);
plot(f, 20*log10(H2));
title("幅频曲线(自己实现的自功率谱、互功率谱估计)");xlabel("频率");ylabel("db");
%% 自带pwelch、cpsd
[Pxx,f1] = pwelch(in, hanning(winlen), overlap, winlen, fs);
[Pxy,f2] = cpsd(in, out, hanning(winlen), overlap, winlen, fs);
subplot(212);
plot(f1, 20*log10(abs(Pxy ./ Pxx)));
title("幅频曲线(自带的pwelch、cpsd函数)");xlabel("频率");ylabel("db");
版本2:
clc;clear;close all;
fs = 44100;
t = 0:1/fs:1-1/fs;
x = randn(size(t));
load("myfir64.mat");
filtercoe = myfir64;
y = filter(filtercoe, 1, x);
[Hx, w] = freqz(filtercoe, 1, fs);
fx = w*fs/2/pi;
subplot(211);
plot(fx, 20*log10(abs(Hx)));
title('理想幅频特性曲线');
xlabel('频率(Hz)');
ylabel('幅值(dB)');
inputLen = length(x);
winLen = 4096;
frmInc = winLen/2;
fftLen = winLen;
frmLen = winLen;
win = hanning(winLen)';
totalFrm = fix((inputLen-frmInc)/(winLen-frmInc));
Pxx = zeros(fftLen/2 + 1,1);
Pyx = zeros(fftLen/2 + 1,1);
% Pxx = zeros(fftLen,1);
% Pyx = zeros(fftLen,1);
for frmIdx = 1:totalFrm
xFrm = x((frmIdx - 1) * frmInc + 1 : (frmIdx - 1)*frmInc+frmLen);
yFrm = y((frmIdx - 1) * frmInc + 1 : (frmIdx - 1)*frmInc+frmLen);
PyxTmp = CPSD_feed(xFrm,yFrm,win,fftLen)';
PxxTmp = CPSD_feed(xFrm,[],win,fftLen)';
Pyx = Pyx + PyxTmp;
Pxx = Pxx + PxxTmp;
end
% KMU = totalFrm*norm(win)^2;
Pyx = Pyx ./ totalFrm;
Pxx = Pxx ./ totalFrm;
freqRes = 20*log10(abs(Pyx) ./ abs(Pxx));
f = fs * (0:fftLen/2)/fftLen;
subplot(212);
plot(f,freqRes);
title("估计");
% plot(freqRes);
function Pyx = CPSD_feed(x,y,window,fftLen)
xw = x .* window;
X = fft(xw, fftLen);
if ~isempty(y)
yw = y .* window;
Y = fft(yw, fftLen);
Pyx = Y .* conj(X);
else
Pyx = X .* conj(X);
end
Pyx = Pyx(1:floor(fftLen/2) + 1);
end
版本3:
该版本用到如下方法:N点复序列求2个N点实序列的快速傅里叶变换
clc;clear;close all;
fs = 44100;
t = 0:1/fs:1-1/fs;
x = randn(size(t));
load("myfir64.mat");
filtercoe = myfir64;
y = filter(filtercoe, 1, x);
%% 理想的频响曲线绘制
[Hx, w] = freqz(filtercoe, 1, fs);
figure(1);
subplot(313);
fx = w*fs/2/pi;
plot(fx, 20*log10(abs(Hx)));
title('FIR滤波器幅频特性曲线');
xlabel('频率(Hz)');
ylabel('幅值(dB)');
figure(2);
subplot(211);
phaseResponse = angle(Hx);
plot(fx, phaseResponse);
title("理想相频特性曲线");
%% 自己实现cpsd、pwelch
winlen = 4096;
overlap = winlen/2;
nfft = winlen;
inputLen = length(x);
segNum = fix((inputLen - overlap) / (winlen - overlap));
spectrum_Pxx = zeros(winlen/2+1,1); % 存放每段估计的自功率谱密度
spectrum_Pxy = zeros(winlen/2+1,1); % 存放每段估计的互功率谱密度
for segIdx = 1:segNum % 分段估计
xSeg = x((segIdx-1)*overlap+1:(segIdx-1)*overlap+winlen); % 输入信号分段交叠
ySeg = y((segIdx-1)*overlap+1:(segIdx-1)*overlap+winlen); % 输出信号分段交叠
Pxx_ = myCpsd(xSeg, [], hanning(winlen), nfft); % 估计输入信号自功率谱密度
Pxy_ = myCpsd(xSeg, ySeg, hanning(winlen), nfft); % 估计输入、输出信号互功率谱密度
% 累加所有段的功率谱密度
spectrum_Pxx = spectrum_Pxx + Pxx_;
spectrum_Pxy = spectrum_Pxy + Pxy_;
end
f = fs*(0:nfft/2)/nfft;
% 计算均值
spectrumAvg_Pxx = spectrum_Pxx / segNum;
spectrumAvg_Pxy = spectrum_Pxy / segNum;
% 幅频曲线
H2 = spectrumAvg_Pxy ./ spectrumAvg_Pxx;
figure(1);
subplot(311);
plot(f, 20*log10(abs(H2)));
title("幅频曲线(自己实现的自功率谱、互功率谱估计)");xlabel("频率");ylabel("db");
% 相频曲线
phase_response = atan2(imag(H2),real(H2));
figure(2);
subplot(212);
plot(f, phase_response);
title("估计的相频特性曲线");
%% matlab自带pwelch、cpsd
[Pxx,f1] = pwelch(x, hanning(winlen), overlap, winlen, fs);
[Pxy,f2] = cpsd(x, y, hanning(winlen), overlap, winlen, fs);
figure(1);
subplot(312);
plot(f1, 20*log10(abs(Pxy ./ Pxx)));
title("幅频曲线(自带的pwelch、cpsd函数)");xlabel("频率");ylabel("db");
%% 功率谱密度估计函数
function Pe = myCpsd(x, y, window, nfft)
if ~isempty(y) % 互功率谱密度
z = x .* window' + 1i .* y .* window'; % x、y序列构造为1个复数序列z
Z = fft(z, nfft);
X = zeros(1, nfft);
Y = zeros(1, nfft);
% x、y的fft
R = real(Z);
I = imag(Z);
X(1) = R(1);
Y(1) = I(1);
for k = 2:nfft
X(k) = 1/2*(Z(k) + conj(Z(nfft+2-k)));
Y(k) = -1i/2*(Z(k) - conj(Z(nfft+2-k)));
end
Pe = X .* conj(Y);
else % 自功率谱密度
X = fft(x .* window', nfft);
Pe = X .* conj(X);
end
Pe = Pe(1:floor(nfft/2)+1)';
end