Files
ARSS/04. Signal Processing/detect_targets_cfar.m
T

141 lines
4.2 KiB
Matlab

function [det_mask, threshold_map, detections] = detect_targets_cfar(rd_map, RadarParams)
% CFAR target detection for range-doppler map
% - method: 'CA' or 'OS'
% - dimension: '1D' or '2D'
% - axis (for 1D): 'range' or 'doppler'
%
% Input:
% rd_map : [Ndoppler x Nrange] complex or real map
% RadarParams : struct containing SP.CFAR options
%
% Output:
% det_mask : logical detection mask [Ndoppler x Nrange]
% threshold_map : threshold map [Ndoppler x Nrange]
% detections : [Ndet x 2] = [doppler_bin, range_bin]
cfg = RadarParams.SP.CFAR;
method = upper(string(cfg.method));
dim_mode = upper(string(cfg.dimension));
axis_mode = lower(string(cfg.axis));
pfa = cfg.pfa;
train = cfg.train;
guard = cfg.guard;
if numel(train) == 1
train = [train, train];
end
if numel(guard) == 1
guard = [guard, guard];
end
os_rank_ratio = cfg.rank;
os_scale = cfg.os_scale;
rd_power = abs(rd_map).^2;
[n_dop, n_rng] = size(rd_power);
det_mask = false(n_dop, n_rng);
threshold_map = nan(n_dop, n_rng);
switch dim_mode
case "2D"
td = train(1); tr = train(2);
gd = guard(1); gr = guard(2);
for d = (td+gd+1):(n_dop-(td+gd))
for r = (tr+gr+1):(n_rng-(tr+gr))
d_idx = (d-(td+gd)):(d+(td+gd));
r_idx = (r-(tr+gr)):(r+(tr+gr));
win = rd_power(d_idx, r_idx);
cut_d = td+gd+1;
cut_r = tr+gr+1;
guard_mask = false(size(win));
guard_mask((cut_d-gd):(cut_d+gd), (cut_r-gr):(cut_r+gr)) = true;
train_cells = win(~guard_mask);
th = local_cfar_threshold(train_cells, method, pfa, os_rank_ratio, os_scale);
threshold_map(d, r) = th;
det_mask(d, r) = rd_power(d, r) > th;
end
end
case "1D"
switch axis_mode
case "range"
tr = train(2); gr = guard(2);
for d = 1:n_dop
[det_row, th_row] = cfar_1d_line(rd_power(d, :), tr, gr, method, pfa, os_rank_ratio, os_scale);
det_mask(d, :) = det_row;
threshold_map(d, :) = th_row;
end
case "doppler"
td = train(1); gd = guard(1);
for r = 1:n_rng
[det_col, th_col] = cfar_1d_line(rd_power(:, r).', td, gd, method, pfa, os_rank_ratio, os_scale);
det_mask(:, r) = det_col.';
threshold_map(:, r) = th_col.';
end
otherwise
error('CFAR axis must be ''range'' or ''doppler'' when dimension is 1D.');
end
otherwise
error('CFAR dimension must be ''1D'' or ''2D''.');
end
[d_idx, r_idx] = find(det_mask);
detections = [d_idx, r_idx];
end
function [det_line, th_line] = cfar_1d_line(x, t, g, method, pfa, rank_ratio, os_scale)
n = numel(x);
det_line = false(1, n);
th_line = nan(1, n);
left = t + g;
right = t + g;
for i = (left+1):(n-right)
l_train = x((i-g-t):(i-g-1));
r_train = x((i+g+1):(i+g+t));
train_cells = [l_train, r_train];
th = local_cfar_threshold(train_cells, method, pfa, rank_ratio, os_scale);
th_line(i) = th;
det_line(i) = x(i) > th;
end
end
function th = local_cfar_threshold(train_cells, method, pfa, rank_ratio, os_scale)
train_cells = train_cells(:);
n_train = numel(train_cells);
if n_train == 0
th = inf;
return;
end
switch method
case "CA"
noise_hat = mean(train_cells);
alpha = n_train * (pfa^(-1/n_train) - 1);
th = alpha * noise_hat;
case "OS"
sorted_cells = sort(train_cells, 'ascend');
k = max(1, min(n_train, round(rank_ratio * n_train)));
noise_hat = sorted_cells(k);
th = os_scale * noise_hat;
otherwise
error('CFAR method must be ''CA'' or ''OS''.');
end
end