% HRDPD - Doppler
% Reference: High Resolution Localization of Narrowband Radio Emitters Based on Doppler Frequency Shifts
% written by Tom Tirer
clear; clc;
simulate_sensors_motion = 1;
use_sensors_motion_in_estimation = 0; % see: "An interesting observation..." in Section 4.1. Example 1.
L = 4; % number of sensors (receivers)
N = 128; % number of used time samples per interval
N_skip = 3;
N_toltal = N*N_skip+100; % number of time samples per interval
J = 3; % number of frequency bands (snapshots)
K = 10; % number of intervals, i.e. different locations of the sensors
SNR = 30; % [dB]
Q = 2; % number of emitters
fc = 2.5*10^9; % 2.5 GHz
fmax = 10*10^3;
fs = fmax;
fs_air = fmax*20;
N_air = N_toltal*40;
T_air = N_air/fs_air;
x0 = [-1.5,1.5]; % true x coordinate of emitters [km]
y0 = [0,0]; % true y coordinate of emitters [km]
x_sensors = zeros(L,K);
y_sensors = zeros(L,K);
vx_sensors = zeros(L,K);
vy_sensors = zeros(L,K);
radius = 22; % [km]
angles = linspace(0,360-360/K,K);
angles0 = [0,atand(1/radius),0,-atand(1/radius)]';
angles0 = angles0(1:L);
angles = repmat(angles,L,1) + repmat(angles0,1,K);
radii_sensors = repmat([radius-0.5,radius,radius+0.5,radius]',1,K);
radii_sensors = radii_sensors(1:L,:);
v_tangent = repmat([300,300,300,300]',1,K); % [m/sec]
v_tangent = v_tangent(1:L,:);
x_sensors = 0 + radii_sensors.*cosd(angles);
y_sensors = 0 + radii_sensors.*sind(angles);
vx_sensors = v_tangent.*sind(-angles);
vy_sensors = v_tangent.*cosd(angles);
if simulate_sensors_motion
% more accurate observations
angular_range = T_air * v_tangent./radii_sensors;
angles_phy = zeros(L,K,N_air);
for l=1:1:L
for k=1:1:K
angles_phy(l,k,:) = angles(l,k) + linspace(-angular_range(l,k)/2,angular_range(l,k)/2,N_air);
end
end
x_sensors_phy = 0 + repmat(radii_sensors,1,1,N_air).*cosd(angles_phy);
y_sensors_phy = 0 + repmat(radii_sensors,1,1,N_air).*sind(angles_phy);
vx_sensors_phy = repmat(v_tangent,1,1,N_air).*sind(-angles_phy);
vy_sensors_phy = repmat(v_tangent,1,1,N_air).*cosd(angles_phy);
end
C = 3*10^8; % m/sec
x0 = x0(1:Q);
y0 = y0(1:Q);
% create filters for DPD-HR
filterBank = cell(J,1);
[B,A] = cheby1(3,0.5,0.7);
filterBank{1} = [B;A];
[B,A] = cheby1(3,0.5,[0.15 0.85]);
filterBank{2} = [B;A];
[B,A] = cheby1(3,0.5,0.3,'high');
filterBank{3} = [B;A];
% create signals
signal_air = zeros(N_air,Q,K);
for k=1:1:K
for q=1:1:Q
temp = (randn(N_air,1)+1i*randn(N_air,1))/sqrt(2);
[B,A] = cheby1(10,0.5,fs/fs_air);
signal_air(:,q,k) = filter(B,A,temp); % narrowband signal
end
end
% take into account the delays between receivers
tau0_history = zeros(Q,L,K);
debug_max_shift = zeros(Q,K);
signal_air_delayed = cell(Q,K,L);
for k=1:1:K
for q=1:1:Q
for l=1:1:L
tau0 = (1/C) * 1000 * ((x0(q)-x_sensors(l,k)).^2+(y0(q)-y_sensors(l,k)).^2).^0.5;
tau0_history(q,l,k) = tau0;
tau0 = tau0 - tau0_history(q,1,k);
tau0_samples = tau0*fs_air;
debug_max_shift(q,k) = max(abs([debug_max_shift(q,k),tau0_samples]));
yyy_fft = fft(signal_air(:,q,k));
yyy_fft_exp = yyy_fft .* (exp(-1i*2*pi*tau0_samples/N_air*[0:1:(N_air/2-1),-N_air/2:1:-1])).';
yyyi = ifft(yyy_fft_exp);
signal_air_delayed{q,k,l} = yyyi;
end
end
end
X = zeros(N_toltal,L,K);
fd0_history = zeros(Q,L,K);
fac = 10^(SNR/20);
% create measurements
signal_unfilt = zeros(N_toltal,L,Q,K);
norm_fac = zeros(Q,K);
for k=1:1:K
for l=1:1:L
channel_phi = 2*pi*rand(Q,1) * 1;
channel_mag = 1+0.1*randn(Q,1);
alpha = channel_mag.*exp(1i*channel_phi); % same alpha during each interval (per sensor per emitter)
for q=1:1:Q
if ~simulate_sensors_motion
v_lk = [vx_sensors(l,k); vy_sensors(l,k)];
p0 = [x0(q);y0(q)];
p_lk = [x_sensors(l,k); y_sensors(l,k)];
mu = v_lk'*(p0-p_lk) / norm(p0-p_lk) / C;
else
v_lk = squeeze([vx_sensors_phy(l,k,:); vy_sensors_phy(l,k,:)]); % 2 x N_air
p0 = [x0(q);y0(q)];
p_lk = squeeze([x_sensors_phy(l,k,:); y_sensors_phy(l,k,:)]); % 2 x N_air
mu = sum(v_lk.*(repmat(p0,1,N_air)-p_lk),1) ./ sqrt(sum((repmat(p0,1,N_air)-p_lk).^2,1)) / C;
end
fd0 = fc*mu; % real doppler frequency
fd0_history(q,l,k) = mean(fd0);
if ~simulate_sensors_motion % for backward compatability
sampleOffset = 100; % keep sampleOffset > max(tau0)
signal_unfilt(:,l,q,k) = signal_air_delayed{q,k,l}(sampleOffset*2:fs_air/fs:sampleOffset*2+(N_toltal-1)*fs_air/fs);
else
signal_unfilt(:,l,q,k) = signal_air_delayed{q,k,l}(N_air/2-(N_toltal/2-1)*fs_air/fs :fs_air/fs: N_air/2+(N_toltal/2)*fs_air/fs);
end
if l==1
norm_fac(q,k) = norm(signal_unfilt(:,1,q,k));
end
signal_unfilt(:,l,q,k) = signal_unfilt(:,l,q,k)/norm_fac(q,k)*sqrt(N_toltal);
if ~simulate_sensors_motion
A_lk_vect = exp(1i*2*pi*fd0/fs).^(0:1:N_toltal-1).';
else
A_lk_vect = exp(1i*2*pi*cumsum(fd0)/fs_air); A_lk_vect = A_lk_vect(N_air/2-(N_toltal/2-1)*fs_air/fs :fs_air/fs: N_air/2+(N_toltal/2)*fs_air/fs);
end
X(:,l,k) = X(:,l,k) + fac * alpha(q) * signal_unfilt(:,l,q,k) .* A_lk_vect(:);
end
noise = (randn(N_toltal,1) + 1i*randn(N_toltal,1))./sqrt(2); %noise
X(:,l,k) = X(:,l,k) + noise;
end
end
%%
x_candidate = [-2.5:0.125:2.5];
y_candidate = [-2.5:0.125:2.5];
Q_ML = zeros(length(y_candidate),length(x_candidate));
Q_HR = zeros(length(y_candidate),length(x_candidate));
r_unfiltered = X(50:N_skip:50+(N-1)*N_skip,:,:); %(N,L,K);
r_unfiltered = permute(r_unfiltered,[2 1 3]); %(L,N,K)
r = zeros(L,J,N,K);
% filter the observations for DPD-HR
for k=1:1:K
for l=1:1:L
for j=1:1:J
B = filterBank{j}(1,:);
A = filterBank{j}(2,:);
temp = filter(B,A,X(:,l,k));
r(l,j,:,k) = temp(50:N_skip:50+(N-1)*N_skip);
end
end
end
R_tilde = zeros(L,L,N,K);
R_hat = zeros(L,L,N,K);
inv_R_hat = zeros(L,L,N,K);
tic
for k=1:1:K % invert all R_hat beforehand
for n=1:1:N
R_kn = r(:,:,n,k) * r(:,:,n,k)' / J;
R_hat(:,:,n,k) = R_kn;
inv_R_hat(:,:,n,k) = inv(R_hat(:,:,n,k)+diag(0.001*ones(1,L))); % adding diagonal loading
end
% do not filter the observations for ML (use the full frequency band)
for n=1:1:N
R_kn = r_unfiltered(:,n,k)*r_unfiltered(:,n,k)';
R_tilde(:,:,n,k) = R_kn;
end
end
for x_ind=1:1:length(x_candidate)
for y_ind=1:1:length(y_candidate)
x = x_candidate(x_ind);
y = y_candidate(y_ind);
D_ML = 0;
D_HR = 0;
for k=1:1:K
disp(['x_ind=',num2str(x_ind),', y_ind=',num2str(y_ind),', k=',num2str(k)]);
D_ML_k = zeros(L,L);
D_HR_k = zeros(L,L);
lamda_kn = zeros(L);
lamda_kn_phy = zeros(L,N);
for l=1:1:L
if ~use_sensors_motion_in_estimation
v_lk = [vx_sensors(l,k); vy_sensors(l,k)];
p = [x;y];
p_lk = [x_sensors(l,k); y_sensors(l,k)];
mu = v_lk'*(p-p_lk) / norm(p-p_lk) / C;
fd = fc*mu;
lamda_kn(l,l) = exp(1i*2*pi*fd/(fs/N_skip));
else
v_lk = squeeze([vx_sensors_phy(l,k,:); vy_sensors_phy(l,k,:)]); % 2 x N_air
p = [x;y];
p_lk = squeeze([x_sensors_phy(l,k,:); y_sensors_phy(l,k,:)]); % 2 x N_air
mu = sum(v_lk.*(repmat(p,1,N_air)-p_lk),1) ./ sqrt(sum((repmat(p,1,N_air)-p_lk).^2,1)) / C;
fd = fc*mu;
temp = exp(1i*2*pi*cumsum(fd)/fs_air);
temp = temp(N_air/2-(N_toltal/2-1)*fs_air/fs :fs_air/fs: N_air/2+(N_toltal/2)*fs_air/fs);
lamda_kn_phy(l,:) = temp(50:N_skip:50+(N-1)*N_skip);
end
end
if ~use_sensors_motion_in_estimation
% n=0 ==> Lambda = eye(L)
D_ML_k = D_ML_k + R_tilde(:,:,1,k);
D_HR_k = D_HR_k + inv_R_hat(:,:,1,k);
for n=2:1:N
Lambda = lamda_kn^(n-1);
D_ML_k = D_ML_k + Lambda'*R_tilde(:,:,n,k)*Lambda;
D_HR_k = D_HR_k + Lambda'*inv_R_hat(:,:,n,k)*Lambda;
end
else
for n=1:1:N
Lambda = diag(lamda_kn_phy(:,n));
D_ML_k = D_ML_k + Lambda'*R_tilde(:,:,n,k)*Lambda;
D_HR_k = D_HR_k + Lambda'*inv_R_hat(:,:,n,k)*Lambda;
end
end
D_ML = D_ML + max(real(eig(D_ML_k)));
D_HR = D_HR + min(real(eig(D_HR_k)));
end
Q_ML(y_ind,x_ind) = real(D_ML);
Q_HR(y_ind,x_ind) = real(inv(D_HR));
end
end
toc
figure; imagesc(x_candidate,y_candidate,Q_ML); title('Q_S_M_L'); set(gca,'YDir','normal'); colormap jet;
figure; imagesc(x_candidate,y_candidate,Q_HR); title('Q_H_R'); set(gca,'YDir','normal'); colormap jet;