MUSIC DF 算法中的非零特征值过多

信息处理 matlab 信号分析 算法
2022-02-21 09:04:41

我正在努力在 MATLAB 中模拟 MUSIC 算法以确定模拟信号的方位角到达。我的虚拟天线阵列由排列在一个平面上的 4 个天线组成。

虽然我的算法“工作”,因为 MUSIC 在很大程度上选择了对到达角的良好估计,但我注意到我的相关矩阵比算法声称的等级更高。这导致少一个非零特征值,因此对应于噪声子空间的特征向量少一个。此外,这个额外的非零特征值并不接近于零,并且将相应的特征向量作为噪声子空间的一部分会大大降低 MUSIC 的性能。

当我查看 2D 案例时,我注意到了同样的行为;即,在搜索方位角和仰角时。我在下面复制了我的代码。谁能阐明这里发生了什么?

% Assume 0 degrees due north, with positive clockwise orientation
% Positive phase difference indicates that antennna 1 recieves the waveform
% before the respective antenna (2,3, or 4)
% 
% Fix antenna 1 at the origin. From the x-axis, rotate antenna 2 60 degrees,
% rotate antenna 3 90 degrees, and rotate antenna 4 120 degrees. The
% optimal antenna spacing is still to be determined.
% 
%                         | 0 deg         --> +
%                         |
%                         |
%                         |
%             ---------- A1 ----------
%                   -60 ' | `  60
%                      '  |  `
%                     '   A3  `
%                  A4'         `A2


num_antennas = 4;
num_sig_sources = 1;
RF_freq = 600e6;        % freq of incoming signal
lambda = 3e8/RF_freq;   % wavelength of incoming signal
fs = 100e6;             % sampling frequency
samples = 200;          % number of snapshots
cos_freq = 1e6;         % freq to downconvert to

ant_spacings = [.25, .98, .91]; % antenna spacing

% generate_lookup_table just calculates a set of theoretical phase
% differences corresponding to some random AoA for the purpose of signal
% generation.
n = randi([0,360],1);
[AoAs, PDs1to2, PDs1to3, PDs1to4] = generate_lookup_table(-90, 90, .5, lambda, ant_spacings(1), ant_spacings(2), ant_spacings(3));
AoA = AoAs(n);

% generate signal
t = linspace(0,samples/fs,samples);
recived_signal_ant_1 = cos(2*pi.*t.*cos_freq);
recived_signal_ant_2 = cos(2*pi.*t.*cos_freq + PDs1to2(n));
recived_signal_ant_3 = cos(2*pi.*t.*cos_freq + PDs1to3(n));
recived_signal_ant_4 = cos(2*pi.*t.*cos_freq + PDs1to4(n));


% build sample correlation matrix
cov_mat = zeros(4,4);
for i = 1:samples
    X = [recived_signal_ant_1(i);
         recived_signal_ant_2(i); 
         recived_signal_ant_3(i);
         recived_signal_ant_4(i)];
    cov_mat = cov_mat + X*X';
end
cov_mat = cov_mat*(1/samples);

% grab eigenvalues and eigenvectors
[eig_vecs, eig_vals] = eig(cov_mat);

% generate matrix w/ noise eigenvector columns. An eigenvector corresponds
% to noise if the associated eigenvalue is close to 0. 
col_num = 1;
for i = 1:4
    if eig_vals(i,i) < 1e-4
        noise_vecs(:,col_num) = eig_vecs(:,i);
        col_num = col_num + 1;
    end
end

% walk t
theta = linspace(-90,90,200);
spectrum_func = zeros(1,100);
for i = 1:length(theta)

    % steering vector - only taking real part as of now (Re(e^i*phasediff) = cos(phase_diff))
    steering_vec = [1;
        cos((-2*pi*ant_spacings(1)/lambda)*sin(theta(i)*(pi/180) - pi/3));
        cos((-2*pi*ant_spacings(2)/lambda)*sin(theta(i)*(pi/180) - pi/2));
        cos((-2*pi*ant_spacings(3)/lambda)*sin(theta(i)*(pi/180) - 2*pi/3))];
    spectrum_func(i) = 1/(((steering_vec'*noise_vecs)*noise_vecs')*steering_vec);
end

% grab theta corresponding to highest peak, round to nearest half integer
[mx,argmax] = max(spectrum_func);
estimated_AoA = floor(2*theta(argmax)+.5)/2;
fprintf("Estimated AoA: %.1f \t Hard-coded AoA: %.1f.\n", estimated_AoA, AoA)

plot(theta,spectrum_func)
title('MUSIC Psuedospectrum')
xlabel('Angle of Arrival')





编辑:我添加了 generate_lookup_table() 的源代码:


function [AoAs, PDs1to2, PDs1to3, PDs1to4] = generate_lookup_table(start, stop, step, lambda, d1to2, d1to3, d1to4)
% d1to2 = spacing between antennas 1 and 2

num_pts = (stop-start)/step + 1;
AoAs = zeros(1,num_pts);
PDs1to2 = zeros(1,num_pts);
PDs1to3 = zeros(1,num_pts);
PDs1to4 = zeros(1,num_pts);

for i = 1:num_pts 
    AoAs(i) = start + (i-1)*step;

    % phase differences
    value_to_push_to_2 = (-2*pi*d1to2/lambda)*sin(AoAs(i)*(pi/180) - pi/3);
    value_to_push_to_3 = (-2*pi*d1to3/lambda)*sin(AoAs(i)*(pi/180) - pi/2);
    value_to_push_to_4 = (-2*pi*d1to4/lambda)*sin(AoAs(i)*(pi/180) - 2*pi/3);

    % increment/decrement until in -180 to 180
    if value_to_push_to_2 > 2*pi
        n = ceil(value_to_push_to_2/(2*pi) - 1);
        value_to_push_to_2 = value_to_push_to_2 - 2*pi*n;
    end

    if value_to_push_to_2 > pi
        value_to_push_to_2 = value_to_push_to_2 - 2*pi;
    end

    if value_to_push_to_3 > 2*pi
        n = ceil(value_to_push_to_3/(2*pi) - 1);
        value_to_push_to_3 = value_to_push_to_3 - 2*pi*n;
    end

    if value_to_push_to_3 > pi
        value_to_push_to_3 = value_to_push_to_3 - 2*pi;
    end

    if value_to_push_to_4 > 2*pi
        n = ceil(value_to_push_to_4/(2*pi) - 1);
        value_to_push_to_4 = value_to_push_to_4 - 2*pi*n;
    end

    if value_to_push_to_4 > pi
        value_to_push_to_4 = value_to_push_to_4 - 2*pi;
    end

    if value_to_push_to_2 < -2*pi
        n = ceil(-1 - value_to_push_to_2/(2*pi));
        value_to_push_to_2 = value_to_push_to_2 + 2*pi*n;
    end

    if value_to_push_to_2 < -pi
        value_to_push_to_2 = value_to_push_to_2 + 2*pi;
    end

    if value_to_push_to_3 < -2*pi
        n = ceil(-1 - value_to_push_to_3/(2*pi));
        value_to_push_to_3 = value_to_push_to_3 + 2*pi*n;
    end

    if value_to_push_to_3 < -pi
        value_to_push_to_3 = value_to_push_to_3 + 2*pi;
    end
    if value_to_push_to_4 < -2*pi
        n = ceil(-1 - value_to_push_to_4/(2*pi));
        value_to_push_to_4 = value_to_push_to_4 + 2*pi*n;
    end

    if value_to_push_to_4 < -pi
        value_to_push_to_4 = value_to_push_to_4 + 2*pi;
    end

    if value_to_push_to_4 < -2*pi
        n = ceil(-1 - value_to_push_to_4/(2*pi));
        value_to_push_to_4 = value_to_push_to_4 + 2*pi*n;
    end

    if value_to_push_to_4 < -pi
        value_to_push_to_4 = value_to_push_to_4 + 2*pi;
    end

    PDs1to2(i) = value_to_push_to_2;
    PDs1to3(i) = value_to_push_to_3;
    PDs1to4(i) = value_to_push_to_4;
end
end
```
1个回答

你真的很亲近!将信号和转向矢量更改为复杂的。特别是对于转向矢量,这些系数旨在充当相移。使用真正的正弦曲线会在相反的角度方向引入相移项,这是您不想要的。单独做这件事,你会看到你的伪谱有所改善。

关于额外的非零特征值:

您选择的跨越噪声子空间的特征向量是基于对接近零的相关特征值的定义。这当然是主观的,但可以根据情况适当。走这条路时要小心。

更通用的解决方案是根据特征值的降序对特征向量进行排序。MATLABsvd()会为您做到这一点。然后选择 M+1 到 N 个特征向量来跨越噪声子空间,其中 M 是预期信号的数量,N 是天线元件的数量。在您的情况下,M = 1 和 N = 4,因此您将选择第 2、3 和 4 列的特征向量。

一些快速的结果:

在将信号和转向矢量更改为复杂并svd()如上所述使用后,运行会给出以下特征值:

 [4.00000007.141101400006.0258101400002.55271014]

在选择噪声特征向量后,这些将产生一个很好的伪谱。鉴于您很接近,以下是您的代码的编辑版本:

% Assume 0 degrees due north, with positive clockwise orientation
% Positive phase difference indicates that antennna 1 recieves the waveform
% before the respective antenna (2,3, or 4)
% 
% Fix antenna 1 at the origin. From the x-axis, rotate antenna 2 60 degrees,
% rotate antenna 3 90 degrees, and rotate antenna 4 120 degrees. The
% optimal antenna spacing is still to be determined.
% 
%                         | 0 deg         --> +
%                         |
%                         |
%                         |
%             ---------- A1 ----------
%                   -60 ' | `  60
%                      '  |  `
%                     '   A3  `
%                  A4'         `A2

clearvars;
close all;

%%

num_antennas = 40;
num_sig_sources = 1;
RF_freq = 600e6;        % freq of incoming signal
lambda = 3e8/RF_freq;   % wavelength of incoming signal
fs = 100e6;             % sampling frequency
samples = 2000;          % number of snapshots
cos_freq = 1e6;         % freq to downconvert to

ant_spacings = [.25, .98, .91]; % antenna spacing

% generate_lookup_table just calculates a set of theoretical phase
% differences corresponding to some random AoA for the purpose of signal
% generation.
n = randi([0,360],1);
[AoAs, PDs1to2, PDs1to3, PDs1to4] = generate_lookup_table(-90, 90, .5, lambda, ant_spacings(1), ant_spacings(2), ant_spacings(3));
AoA = AoAs(n);

% generate signal
t = linspace(0,samples/fs,samples);
% recived_signal_ant_1 = cos(2*pi.*t.*cos_freq);
% recived_signal_ant_2 = cos(2*pi.*t.*cos_freq + PDs1to2(n));
% recived_signal_ant_3 = cos(2*pi.*t.*cos_freq + PDs1to3(n));
% recived_signal_ant_4 = cos(2*pi.*t.*cos_freq + PDs1to4(n));

% Change signals to be complex
recived_signal_ant_1 = exp(1i*2*pi.*t.*cos_freq);
recived_signal_ant_2 = exp(1i*(2*pi.*t.*cos_freq + PDs1to2(n)));
recived_signal_ant_3 = exp(1i*(2*pi.*t.*cos_freq + PDs1to3(n)));
recived_signal_ant_4 = exp(1i*(2*pi.*t.*cos_freq + PDs1to4(n)));


% build sample correlation matrix
cov_mat = zeros(4,4);
for i = 1:samples
    X = [recived_signal_ant_1(i);
         recived_signal_ant_2(i); 
         recived_signal_ant_3(i);
         recived_signal_ant_4(i)];
    cov_mat = cov_mat + X*X';
end

cov_mat = cov_mat*(1/samples);

% grab eigenvalues and eigenvectors
% [eig_vecs, eig_vals] = eig(cov_mat);

% generate matrix w/ noise eigenvector columns. An eigenvector corresponds
% to noise if the associated eigenvalue is close to 0. 
% col_num = 1;
% for i = 1:4
%     if eig_vals(i,i) < 1e-4
%         noise_vecs(:,col_num) = eig_vecs(:,i);
%         col_num = col_num + 1;
%     end
% end

% Use svd() instead
[eig_vecs, eig_vals] = svd(cov_mat);
noise_vecs = eig_vecs(:,num_sig_sources+1:end);

% walk t
theta = linspace(-90,90,200);
spectrum_func = zeros(1,100);
for i = 1:length(theta)

    % steering vector - only taking real part as of now (Re(e^i*phasediff) = cos(phase_diff))
    steering_vec = [1;
%         cos((-2*pi*ant_spacings(1)/lambda)*sin(theta(i)*(pi/180) - pi/3));
%         cos((-2*pi*ant_spacings(2)/lambda)*sin(theta(i)*(pi/180) - pi/2));
%         cos((-2*pi*ant_spacings(3)/lambda)*sin(theta(i)*(pi/180) - 2*pi/3))];

    % Make the steering vectors complex
    exp((1i*(-2*pi*ant_spacings(1)/lambda)*sin(theta(i)*(pi/180) - pi/3)));
        exp((1i*(-2*pi*ant_spacings(2)/lambda)*sin(theta(i)*(pi/180) - pi/2)));
        exp((1i*(-2*pi*ant_spacings(3)/lambda)*sin(theta(i)*(pi/180) - 2*pi/3)))];
    spectrum_func(i) = 1/((steering_vec'*noise_vecs)*noise_vecs'*steering_vec);
end

% grab theta corresponding to highest peak, round to nearest half integer
[mx,argmax] = max(spectrum_func);
estimated_AoA = floor(2*theta(argmax)+.5)/2;
fprintf("Estimated AoA: %.1f \t Hard-coded AoA: %.1f.\n", estimated_AoA, AoA)

plot(theta,20*log10(abs((spectrum_func))))
title('MUSIC Psuedospectrum')
xlabel('Angle of Arrival')

```