【信号分选】SDIF序列直方图的全脉冲数据分选的学习(附代码)
在网上寻找关于SDIF的文章介绍,不是AI写的就是非常水的文章,结果终于找到一篇,欣喜若狂,点开代码运行,发现所用方法为CDIF,并且信号剔除操作全是手工剔除,,,,白费我一次下载机会,结合自己的学习体会记录一篇,共同交流。
分选方法选择
在侦察信号处理流程里,脉冲去交错占据关键地位。其运作逻辑是,依托接收机所捕获的脉冲开展去交错操作,进而分离出独立的单个脉冲列 。而去交错算法的核心,在于对接收机接收脉冲时获取的各类参数展开剖析。像脉冲到达时间(TOA)、到达角(DOA)、脉冲幅度、脉宽(PW)以及载频(RF)等不同参数,均可作为构建不同去交错算法的依据。本实验主要采用SDIF变换法实现信号分选。
SDIF 分选信号法原理
CDIF先算各级脉冲时间差,统计成直方图,统计每级超过门限值的PRI结果,没超过就算累计到下一阶直方图;否则挑出对应的脉冲序列,一直进行下去直到脉冲数量不够或者阶数达到上限。优点是抗干扰、漏脉冲少,缺点是计算量大,脉冲丢多了容易误判假序列。
SDIF是由CDIF算法改进而来。与传统 CDIF 算法相比,SDIF无需累加各级直方图,而是逐阶处理差值,显著提升了分选效率,尤其适用于高脉冲密度和复杂信号环境。
SDIF其工作过程是:把收到的脉冲按到达时间排列,计算相邻脉冲的时间间隔,构成一个SDIF直方图;检查直方图里有没有明显的峰值,若有峰值超过阈值,按照对应间隔剔除对应的脉冲信号,若有多个超过阈值的峰值,在子谐波检验后进行序列检索;计算更高阶的SDIF直方图,选超过阈值的有效PRI继续剔除信号;挑出一批信号后,用剩下的脉冲重复上述步骤,直到脉冲数少于3个可分选或达到预设阶数为止。
SDIF门限函数
SDIF算法中的门限函数,本质上是一个 “筛选开关”,用于判断脉冲时间间隔统计结果是否属于真实的脉冲重复周期(PRI)。好的门限函数应该满足两个条件:
1.能有效区分真实PRI与噪声干扰:脉冲的TOA差通常具有规律性,而噪声或干扰脉冲的间隔往往随机分布。门限函数通过设定阈值,过滤掉随机波动的“伪峰值”,保留真实 PRI 对应的直方图峰值。
2.控制算法检测灵敏度:门限参数可调,可根据实际场景(如信噪比、脉冲密度)平衡“漏检”与“误检”,阈值过高易漏检真实信号,过低易引入虚假 PRI。 为满足上述两个要求,SDIF 常用的门限函数表达式为:

为检测门限值,随时间间隔τ变化;
为检测速度调节参数,x越大,阈值越高,检测越严格;
为门限调节参数,影响指数衰减速度;E为总脉冲数,C为当前直方图阶数,N为直方图小区间个数。
信号的稳定性
经典 SDIF 算法通过“TOA 差直方图找 PRI”,但未显式做稳定性校验,因此用稳定性指标衡量脉冲序列平均间隔周期和实际PRI(脉冲重复间隔)的偏差程度。若脉冲序列的 TOA 为
,实际PRI 为T,则期望
(允许小偏差)。
首先遍历分选出的脉冲序列的TOA,计算相邻脉冲的实际周期,再对比实际PRI,算出每个间隔的偏差率。对第 i 个脉冲间隔,定义偏差率

然后定义稳定性指标S

是二值函数:条件满足时为 1,否则为0。
SDIF算法流程介绍
根据SDIF的算法介绍,编写代码,可得到伪代码如下:
|
算法:SDIF |
|
|
输入: |
脉冲序列 |
|
输出: |
分离出的信号源列表 |
|
1. |
初始化,令结果列表=空列表,阶数C =1,当前脉冲序列=原始脉冲序列 |
|
2. |
若当前脉冲序列长度 ≤ 3,执行参差分析,将分析结果加入结果列表,跳转至结束步骤; |
|
3. |
按当前阶数C,计算脉冲序列的C级TOA差统计TOA差频次,生成SDIF直方图; |
|
4. |
用门限函数 |
|
5. |
若检测到脉冲丢失迹象,下次分析需作谐波检验 |
|
6. |
若C = 1且可能的PRI值列表长度> 1: 令C = C + 1,跳转至步骤 2,否则对可能的PRI值列表逐个执行序列检索: (1)若检索成功,将子序列作为信号源加入结果列表,并从当前脉冲序列移除该子序列,令C = 0,跳转至步骤 6 (2)若检索失败,令C = 0,跳转至步骤 6; |
|
7. |
若 C =0,令C = 1,跳转至步骤2; |
|
8. |
返回结果列表。 |
分离PRI序列信号算法
得到候选 PRI后,需要从剩余脉冲里找连续符合该周期的子序列,该算法允许一定脉冲丢失,并且把真实序列剔除出来。算法伪代码如下:
|
算法:分离PRI序列信号算法 |
|
|
输入: |
剩余 TOA 序列、候选 PRI、时间容差、最大丢失脉冲数 |
|
输出: |
有效脉冲索引列表、是否成功分离标记 |
|
1. |
初始化有效脉冲索引列表为空,遍历剩余TOA序列每个脉冲作为起始点 |
|
2. |
对每个起始点,初始化当前序列,计算第一个预期脉冲时间 |
|
3. |
往后搜索脉冲,若与预期脉冲时间偏差≤时间容差,加入序列并更新预期时间;否则计数丢失脉冲; |
|
4. |
若丢失脉冲数≤最大丢失脉冲数 且序列长度≥3,标记为有效序列; |
|
5. |
合并所有有效序列索引,去重排序,返回结果 |
算法核心思路就是从每个脉冲起步,按PRI预期时间找后续脉冲,允许丢失一定数量的脉冲,数量大于3个就当有效序列。最后合并去重,从而把候选PRI对应的真实脉冲序列剔除出来
子谐波检验算法
SDIF算法重要的一步就是筛掉候选 PRI 里的谐波,避免把倍数周期误认成新信号。算法伪代码如下:
|
算法: 子谐波检验算法 |
|
|
输入: |
候选 PRI 列表、分辨率 |
|
输出: |
剔除谐波后的有效 PRI 列表 |
|
1. |
排序候选PRI,从小到大遍历 |
|
2. |
对每个PRI,检查是否是之前 PRI的n倍(误差≤n×分辨率) |
|
3. |
若是谐波就跳过,否则加入有效列表 |
该算法核心就是先把PRI排序,从小到大检查每个PRI,看是不是前面PRI的整数倍,允许误差容忍,因为实际信号有抖动,如果是整数倍则剔除掉,不是则进行后续信号分选操作。
数据验证
验证数据有500个脉冲,传递给SDIF算法,共分选出2个PRI:102.00us,112.20us
表 分选信号结果表
|
PRI(us) |
脉冲数 |
稳定性 |
时间范围(us) |
均值(标准差) |
|
102.00 |
286 |
0.92 |
30.60 - 26652.60 |
93.41(28.38 ) |
|
112.20 |
214 |
0.89 |
0.00 - 26591.40 |
124.84(35.56) |

可以看到两部信号源均未达到100%的稳定性,对应PRI均值也与理论的PRI有偏差,说明脉冲序列并不是等间隔分布的。


将PRI为112.2us的序列图放大(第一幅图),发现脉冲串每9个脉冲丢失了一个脉冲,造成了脉冲PRI平均值的偏差,这也侧面反应出该算法能很好的解决丢失脉冲的问题。
画出PRI为112.2us的序列间隔分布(第二幅图),由于均匀丢失脉冲的缘故,在间隔为222.4us处有分布,222.4us刚好是两倍PRI,因此也可判断出均匀丢失一个脉冲。

画出PRI为102us的直方图,发现有脉冲相邻间隔几乎为0,说明可能有TOA一样的脉冲,第二幅图展示了到达时间一样的两个脉冲,两个脉冲到达时间都为26142.6us。说明这两个脉冲可能来自不同的发射机,而被算法误判来自同一信号源。这也说明该算法的缺陷:当到达时间趋近于一致时,仅仅依靠PRI间隔难以有效区分信号,后续改进可以融合脉冲宽度,到达角度等多特征进行综合分选信号,也可以用聚类,深度学习等方法利用数据多维度的特征来区分信号。
分选过程分析

第一次分选图出现两个峰值,但并未有超过阈值,说明一阶间隔直方图受干扰影响较大。
进入二阶分选,发现有两个PRI值超过阈值,分别是102us,112.2us,算法将剔除掉PRI为102us的信号,分选出信号个数为286个。剩余的信号进入下一次分选。
进入第三次分选,PRI值为112.2us超过了阈值,进行信号分选,图中还有另外一个峰值在PRI值为224.39us处,为PRI为112.2us的子谐波,是由于脉冲丢失引起的,未超过阈值,不进行分选。致此信号全部分选完毕。
算法代码
clc; clear; close all;
%% 1. 读取并预处理脉冲数据
file_name = '你自己的数据';
try
data = readmatrix(file_name);
catch
error('无法读取文件,请确保文件存在。');
end
toa_data = data(:, 1); % 提取TOA数据(第4列,单位:us)
toa_data = sort(toa_data); % 按时间排序
remaining_toa = toa_data; % 剩余待分选脉冲
sorted_pri = []; % 存储已确认PRI
pulse_missing_flag = false; % 脉冲丢失标记
sorted_sequences = {}; % 存储分选后的脉冲序列
seq_id = 0; % 序列ID计数器
%% 2. SDIF主分选循环
disp('========== 开始脉冲分选==========');
iter = 0;
while true
iter = iter + 1;
n = length(remaining_toa);
if n < 3, break; end % 脉冲数不足时终止
disp(['迭代', num2str(iter), ':剩余脉冲数 = ', num2str(n)]);
pri_removed = false; % 标记是否剔除PRI
% 动态调整最大阶数(适应脉冲丢失场景)
max_order = min(5, floor(n/2)); % 限制最大阶数为5,避免计算爆炸
for order = 1:max_order
% 计算t阶TOA差(支持脉冲丢失时的稀疏间隔)
if n - order < 1, continue; end
delta_toa = remaining_toa(order+1:end) - remaining_toa(1:end-order);
% 动态阈值计算
bin_res = 0.01; % 分辨率
bin_max = 2000; % 最大时间差
bin_edges = 0:bin_res:bin_max;
[counts, ~] = histcounts(delta_toa, bin_edges);
bin_centers = bin_edges(1:end-1);
% 改进门限函数:T(τ) = x(E-C)e^(-τ/kN) * (1+α*missing_pulse_flag)
x_val = 0.2; k_val = 150; % 门限参数
total_pulses = length(toa_data); % 总脉冲数
threshold = x_val*(total_pulses-order)* exp(-bin_centers/(k_val));
% 脉冲丢失时增强低阈值检测(α=0.5为经验系数)
if pulse_missing_flag
threshold = threshold * 0.5;
end
% 绘制带动态阈值的SDIF直方图
figure('Name', ['迭代', num2str(iter), '阶SDIF']);
stem(bin_centers, counts, 'Color', [0.8500, 0.3250, 0.0980], 'Marker', 'none'); hold on;
plot(bin_centers, threshold, 'Color', [0.4660, 0.6740, 0.1880], 'LineStyle', '--', 'LineWidth', 1.5);
title(['剩余脉冲数:', num2str(n), ' | 阶数:', num2str(order)], 'FontSize', 12);
xlabel('TOA差(us)', 'FontSize', 10); ylabel('计数', 'FontSize', 10); grid on;
set(gca, 'FontSize', 10, 'LineWidth', 1);
legend('直方图', '阈值', 'Location', 'best', 'FontSize', 8);
% 提取候选PRI(超过阈值且过滤谐波)
candidate_mask = counts > threshold;
if sum(candidate_mask) == 0, continue; end
candidate_pri = bin_centers(candidate_mask);
candidate_pri = unique(round(candidate_pri/bin_resolution)) * bin_res;
% 谐波检验:剔除PRI=2*真实PRI的虚假周期
if ~isempty(candidate_pri)
candidate_pri = harmonic_check(candidate_pri, bin_res);
end
disp(['阶数', num2str(order), ':候选PRI = ', num2str(candidate_pri'), 'us']);
% 多PRI序列匹配(支持混合信号与脉冲丢失)
for pri_idx = 1:length(candidate_pri)
pri_val = candidate_pri(pri_idx);
tol = 0.3; % 时间容差(us)
% 改进序列检索:允许最大10个脉冲丢失
valid_idx = find_pulse_sequence(remaining_toa, pri_val, tol, 20);
if length(valid_idx) < 3, continue; end % 至少3个脉冲才有效
% 验证PRI真实性(计算序列周期稳定性)
stab = check_period_stability(remaining_toa(valid_idx), pri_val, tol);
if stab < 0.7, continue; end % 稳定性阈值0.7
% 记录并剔除有效PRI序列
disp(['确认PRI:', num2str(pri_val), 'us(匹配', num2str(length(valid_idx)), '个脉冲)']);
sorted_pri = [sorted_pri; pri_val];
% 保存分选出来的脉冲序列
seq_id = seq_id + 1;
sorted_sequences{seq_id}.pri = pri_val;
sorted_sequences{seq_id}.indices = valid_idx;
sorted_sequences{seq_id}.toa = remaining_toa(valid_idx);
sorted_sequences{seq_id}.stability = stab;
sorted_sequences{seq_id}.iteration = iter;
sorted_sequences{seq_id}.order = order;
% 剔除脉冲并更新丢失标记
keep_mask = true(size(remaining_toa));
keep_mask(valid_idx) = false;
remaining_toa = remaining_toa(keep_mask);
% 标记脉冲丢失(后续降低阈值)
if length(valid_idx) < n/3, pulse_missing_flag = true; end
pri_removed = true;
break; % 找到有效PRI后跳出循环
end
if pri_removed, break; end % 本阶成功剔除PRI,进入下一迭代
end
% 未找到有效PRI时重置丢失标记
if ~pri_removed
pulse_missing_flag = false;
disp('本轮未找到有效PRI,重置脉冲丢失标记');
end
% 终止条件:无PRI剔除或脉冲数不足
if ~pri_removed || n < 3
disp('分选终止:无更多有效PRI可分离');
break;
end
end
%% 3. 输出分选结果
if isempty(sorted_pri)
disp('未检测到有效PRI');
else
[unique_pri, ~, idx] = unique(round(sorted_pri/bin_res)*bin_res);
counts = histcounts(idx, 1:(length(unique_pri)+1));
disp('================ 分选结果 ================');
for i = 1:length(unique_pri)
disp(['PRI = ', num2str(unique_pri(i)), 'us | 出现次数:', num2str(counts(i))]);
end
% 保存分选结果到文件
save('sorted_results.mat', 'sorted_sequences', 'sorted_pri', 'toa_data');
disp(['分选结果已保存到 sorted_results.mat 文件中,共包含 ', num2str(seq_id), ' 个脉冲序列']);
% 生成详细的结果报告
generate_result_report(sorted_sequences, toa_data);
end
%% 4. 辅助函数:谐波检验
function pri_list = harmonic_check(pri_list, resolution)
% 剔除PRI=2*真实PRI的谐波(如PRI=100可能是50的谐波)
sorted_pri = sort(pri_list);
pri_list = [];
for i = 1:length(sorted_pri)
is_harmonic = false;
for j = 1:i-1
if abs(sorted_pri(i) - 2*sorted_pri(j)) < 2*resolution
is_harmonic = true;
break;
end
end
if ~is_harmonic, pri_list = [pri_list; sorted_pri(i)]; end
end
end
%% 5. 辅助函数:查找脉冲序列(支持丢失脉冲)
function indices = find_pulse_sequence(toa, pri, tolerance, max_missing)
indices = [];
for start_idx = 1:length(toa)
current_seq = [start_idx];
exp_time = toa(start_idx) + pri;
miss_count = 0;
search_idx = start_idx + 1;
while search_idx <= length(toa) && miss_count <= max_missing
if abs(toa(search_idx) - exp_time) <= tolerance
current_seq = [current_seq; search_idx];
exp_time = toa(search_idx) + pri;
miss_count = 0;
else
miss_count = miss_count + 1;
end
search_idx = search_idx + 1;
end
if length(current_seq) >= 3
indices = [indices; current_seq];
end
end
% 合并重叠序列并去重
if ~isempty(indices)
indices= unique(sort(indices(:)));
end
end
%% 6. 辅助函数:检验周期稳定性
function stability = check_period_stability(toa_seq, pri, tolerance)
if length(toa_seq) < 3, stability = 0; return; end
% 计算实际周期与理论PRI的偏差率
deviations = zeros(1, length(toa_seq)-1);
for i = 1:length(toa_seq)-1
deviations(i) = abs((toa_seq(i+1)-toa_seq(i)) - pri) / pri;
end
% 稳定性指标:偏差率<10%的比例
stability = sum(deviations < 0.1) / length(deviations);
end
%% 7. 辅助函数:生成详细的结果报告
function generate_result_report(sequences, original_toa)
% 创建结果文件夹
result_folder = 'sorting_results';
if ~exist(result_folder, 'dir')
mkdir(result_folder);
end
% 创建报告文件
report_file = fullfile(result_folder, 'sorting_report.txt');
fid = fopen(report_file, 'w');
fprintf(fid, '脉冲分选结果报告\n');
fprintf(fid, '========================\n');
fprintf(fid, '总脉冲数: %d\n', length(original_toa));
fprintf(fid, '分选序列数: %d\n\n', length(sequences));
% 按PRI分组
pri_cell = cellfun(@(x) x.pri, sequences, 'UniformOutput', false);
pri_list = [pri_cell{:}];
unique_pri = unique(pri_list);
fprintf(fid, 'PRI分组统计:\n');
for i = 1:length(unique_pri)
pri_val = unique_pri(i);
seq_idx = find([pri_cell{:}] == pri_val);
seq_idx = cellfun(@(x) length([x.indices]), sequences(seq_idx), 'UniformOutput', false);
pulse_count = sum([seq_idx{:}]);
fprintf(fid, ' PRI = %.2f us: %d个脉冲\n', ...
pri_val, pulse_count);
end
fprintf(fid, '\n');
% 详细序列信息
fprintf(fid, '详细序列信息:\n');
for i = 1:length(sequences)
seq = sequences{i};
fprintf(fid, '序列 #%d:\n', i);
fprintf(fid, ' PRI = %.2f us\n', seq.pri);
fprintf(fid, ' 脉冲数 = %d\n', length(seq.indices));
fprintf(fid, ' 稳定性 = %.2f\n', seq.stability);
fprintf(fid, ' 发现于迭代 #%d, 阶数 %d\n', seq.iteration, seq.order);
fprintf(fid, ' 时间范围: %.2f us - %.2f us\n', ...
min(seq.toa), max(seq.toa));
% 计算实际PRI值的分布
if length(seq.toa) > 1
actual_pri = diff(seq.toa);
mean_pri = mean(actual_pri);
std_pri = std(actual_pri);
fprintf(fid, ' 实际PRI均值 = %.2f us, 标准差 = %.2f us\n', mean_pri, std_pri);
end
fprintf(fid, '\n');
% 保存序列脉冲到单独文件
seq_file = fullfile(result_folder, sprintf('sequence_%03d.csv', i));
dlmwrite(seq_file, seq.toa, 'delimiter', ',');
end
fclose(fid);
disp(['详细结果报告已保存到 ', report_file]);
% 绘制所有序列的TOA分布图
figure('Name', '所有分选序列的TOA分布');
hold on;
colors = lines(length(sequences));
for i = 1:length(sequences)
plot(sequences{i}.toa, i*ones(size(sequences{i}.toa)), 'o', ...
'Color', colors(i,:), 'MarkerSize', 6, 'LineWidth', 1);
end
xlabel('TOA (us)', 'FontSize', 10);
ylabel('序列编号', 'FontSize', 10);
title('所有分选序列的TOA分布', 'FontSize', 12);
grid on;
set(gca, 'FontSize', 10, 'LineWidth', 1);
legend(arrayfun(@(x) sprintf('序列 #%d (PRI=%.2f)', x, sequences{x}.pri), ...
1:length(sequences), 'UniformOutput', false), 'Location', 'best', 'FontSize', 8);
end
figure;
histogram(diff(sorted_sequences{1}.toa),200000);
set(gca, 'FontSize', 10, 'LineWidth', 1);
xlabel('PRI (us)', 'FontSize', 10);
ylabel('频数', 'FontSize', 10);
title('第一序列PRI分布直方图', 'FontSize', 12);
grid on;
更多推荐
计算检测阈值提取直方图中超阈值的峰值,作为可能的PRI值列表


所有评论(0)