在考虑了 Marcus Müller 的建议后,我开发了一种算法,该算法仍然依赖于全局能量阈值,但也依赖于对本底噪声的估计。它可以通过多种方式进行改进,但由于其最简单的实现已经提供了可接受的真实世界捕获结果(同样,L 波段捕获 250 ksps),我相信它可能是其他人开始的良好基础。
该算法的中心思想是基于对噪声功率的连续估计来计算能量阈值,并随着频谱图的每次更新而更新它。在所有 FFT 运行期间,此估计跟踪每个 FFT bin 获得的最大和最小级别,使用它们来估计该 bin 中的 PSD 并丢弃异常值(即通道、杂散...)。该算法将每隔固定数量的样本执行一次。更正式地说:
算法参数:
int N /* spectrogram length (FFT bins) */
complex x[N] /* last N samples */
float alpha /* spectrogram smoothing factor between 0 (infinite smoothing,
spectrogram will never be updated) and 1 (no smoothing at all) */
float beta /* level decay factor between 0 (levels will never decay) and 1
(levels will be equal to the spectrogram). */
float gamma /* smooth factor applied to updates of the current noise estimation
between 0 (no updates allowed) and 1 /* no smoothing */
float SNR /* detection threshold above noise, in dB */
和的推荐值alpha
分别是beta
和。gamma
1e-2
1e-3
.5
变量:
complex dft[N] /* Fourier transform of the last N samples */
float spect[N] /* smoothed spectrogram */
float spmin[N] /* lower levels for spectrogram bins */
float spmax[N] /* upper levels for spectrogram bins */
int runs /* FFT run counter (initially zero) */
float N0 /* Current noise level estimation */
float new_N0 /* New noise level estimation */
float min_pwr /* Minimum power density found */
int min_pwr_bin /* FFT bin where min_pwr is */
int valid /* Number of valid bins for noise estimation */
int min_runs /* Minimum number of runs required to detect channels */
算法:
min_runs = max(2. / alpha, 1. / beta)
dft = FFT(x);
++runs;
if (runs == 1) then /* First FFT run */
spect = dft * conj(dft) /* |FFT(x)|^2 */
spmin = spect /* Copy spect to spmin */
spmax = spect /* Copy spect to spmax */
N0 = min(spect); /* First noise estimation */
else
/* Smooth spectrogram w.r.t the previous run */
spect += alpha * (dft * conj(dft) - spect)
/* Update levels. This has to be performed element-wise */
new_N0 = 0
valid = 0
min_pwr = INFINITY
min_pwr_bin = -1
for (int i = 0; i < N; ++i)
/* Update current lower levels or raise them */
if (spect[i] < spmin[i]) then
spmin[i] = spect[i]
else
spmin[i] += beta * (spect[i] - spmin[i]);
end
/* Update current upper levels or decrease them */
if (spect[i] > spmax[i]) then
spmax[i] = spect[i]
else
spmax[i] += beta * (spect[i] - spmax[i]);
end
if (runs > min_runs) then
/* Use previous N0 estimation to detect outliers */
if (spmin[i] < N0 or N0 < spmax[i]) then
new_N0 += spect[i]
++valid
end
/* Update current minimum power */
if (spect[i] < min_pwr) then
min_pwr = spect[i]
min_pwr_bin = i
end
end
end
/*
* Check whether levels have stabilized and update noise
* estimation accordingly
*/
if (runs > min_runs) then
/*
* This is a key step: if the number of valid bins is
* 0 this means that our previous estimation was
* absolutely wrong. We reset it with a cruder estimation
* based on where the minimum value of the current
* spectrogram was found
*/
if (valid == 0) then
N0 = .5 * (spmin[min_pwr_bin] + spmax[min_pwr_bin])
else
N0 += gamma * (new_N0 / valid - N0)
end
/*
* Detect channels based on this threshold (trivial,
* not detailed)
*/
detect_channels(spect, 10^(SNR / 10) * N0)
end
end
尽管该算法强烈假设本底噪声是平坦的(这在大多数情况下是错误的,因为在现实世界的收音机中,调谐器输出通过响应不平坦的低通滤波器),即使在这种情况下它仍然有效不成立。这些是不同 alpha 值和 的一些算法N = 4096
结果SNR = 3 dB
。噪声估计用黄色标记,通道阈值用绿色标记,高电平用红色标记,频谱图用白色标记,低电平用青色标记。我还提供了N0
每次 FFT 运行后估计的演变:
结果alpha = 1e-1
:
结果为alpha = 1e-2
。请注意,随着频谱图变得更加清晰,有效 bin 的数量是如何减少的:
结果为alpha = 1e-3
。在这种情况下,电平非常紧凑,本底噪声非常明显不平坦,以至于从一个 FFT 运行到另一个 FFT 存在无效的 bin。在这种情况下,我们回退到寻找具有最低功率密度的 bin 的粗略估计:
计算min_runs
很关键。为了防止噪声电平上升(即跟随通道而不是本底噪声),我们必须至少等待2. / alpha
FFT 运行,然后才能信任信号电平。这个值是通过实验找到的:在我之前的实现中,我直观地使用1. / alpha
which 惨遭失败alpha = 1e-3
:
我还没有在其他场景(如突发传输)上对此进行测试,由于最小/最大级别的持久性,该算法可能无法像连续通道那样执行,并且它可能无法将突发传输检测为异常值。但是,鉴于我正在使用的渠道类型,目前这不是优先事项。