常见的非矩形窗口函数似乎都是对称的。是否曾经有人希望在FFT之前使用非对称窗口函数? (假设是否认为FFT孔径一侧的数据比另一侧的数据更重要,或者噪声较小等)。进行了研究,与偏移对称窗口(损耗更大)相比,它们如何影响频率响应?

评论

通常使用窗口,因为FFT在信号的小块上运行,试图使其在本地看起来像固定信号。因此,没有“侧面”可取,信号被认为在整个过程中都是统一的。

在处理实时数据并需要处理吞吐量延迟的音频分析算法中,有时可以设计一个非对称窗口,该窗口的有效延迟要小于相同长度的对称窗口。如果此非对称窗口的行为(预先已知)以已知方式影响此音频分析的输出参数,则可以补偿这些参数,并保留减少延迟的优点。

#1 楼

我将为“窗口功能”使用简写窗口。

对于音频,任何产生类似于预振铃或预回声的处理听起来都像是低比特率的mp3。当瞬变或脉冲的局部能量在时间上向后扩展时,例如通过修改重叠变换(例如重叠修改的离散余弦变换(MDCT))中的频谱数据,就会发生这种情况。在这种处理中,音频通过重叠的分析窗口进行窗口化,变换,在频域中进行处理(如数据压缩到较小的比特率),再通过合成窗口进行窗口化并加在一起。分析和合成窗口的乘积必须使重叠的窗口求和成一个整体。

传统上使用的窗口函数是对称的,并且它们的宽度是频率选择性之间的折衷(长窗口)和时域伪像规避(短窗口)。窗口越宽,处理可以更及时地传播信号。最近的解决方案是使用非对称窗口。使用的两个窗口可以互为镜像。分析窗口从峰值快速下降到零,因此不会提前“检测”脉冲,而合成窗口从零快速上升到峰值,因此任何处理的效果都不会在时间上向后扩散。这样做的另一个优点是低延迟。非对称窗口可以具有良好的频率选择性,并且可以像音频疗法一样替代音频压缩中大小可变的对称窗口。参见M.Schnell,M.Schmidt,M.Jander,T.Albert,
R。 Geiger,V。Ruoppila,P。Ekstrand,
男。 Lutzky,B. Grill,“ MPEG-4增强型Low
延迟AAC-高质量
通信的新标准”,第125 AES Convention,San
美国加利福尼亚州弗朗西斯科市,预印本7503,2008年10月,以及另一篇会议论文,它们还显示了其窗的傅里叶变换的幅度:Schnell,M.等。 2007。增强的MPEG-4低延迟AAC –低比特率高质量通信。在第122届AES公约中。

图1.不对称窗口在重叠分析处理合成中的使用说明。分析窗口(蓝色)和合成窗口(黄橙色)的乘积(黑色虚线)与前一帧的窗口(灰色虚线)求和。在使用MDCT时,还需要进一步的约束来保证完美的重建。与DFT相比,MDCT仅提供一半的光谱数据,而如果选择合适的窗口,仍然可以实现完美的重建。

这是我自己的不对称窗口设计(图2),适用于重叠分析处理-使用DFT而不是MDCT进行合成,因此无法完美重建。该窗口尝试最小化均方时间和频率带宽的乘积(类似于受限的高斯窗口),同时保留一些潜在有用的时域属性:非负,单峰,在“时间零”处有峰,围绕该峰进行分析和合成窗口彼此互为镜像,函数和一阶导数连续性为零,当窗口函数的平方被解释为非归一化的概率密度函数时,为零。该窗口使用差分进化进行了优化。右:余弦窗口,其延迟与不对称窗口相同

图3.图2的余弦窗口(蓝色)和不对称窗口(橙色)的傅立叶变换的幅值。不对称窗口显示出更好的频率选择性。图和非对称窗口。绘图代码来自Wikimedia Commons。在Linux上,我建议先安装gnuplotepstoolpstoedittransfiglibrsvg2-bin以便使用display进行查看。它开始于零。下面的C ++代码为您做到了这一点,因此,除了四分之一的窗口在所有位置均为零之外,您不会得到任何零采样。对于分析窗口,这是第一季度,对于综合窗口,这是最后一个季度。分析窗口的后半部分应与合成窗口的前半部分对齐,以计算其乘积。该代码还测试了窗口的均值(作为概率密度函数),并展示了重叠重构的平坦度。

pkg load signal

graphics_toolkit gnuplot
set (0, "defaultaxesfontname", "sans-serif")
set (0, "defaultaxesfontsize", 12) 
set (0, "defaultaxeslinewidth", 1)

function plotWindow (w, wname, wfilename = "", wspecifier = "", wfilespecifier = "")

  M = 32; % Fourier transform size as multiple of window length
  Q = 512; % Number of samples in time domain plot
  P = 40; % Maximum bin index drawn
  dr = 130; % Maximum attenuation (dB) drawn in frequency domain plot

  N = length(w);
  B = N*sum(w.^2)/sum(w)^2 % noise bandwidth (bins)

  k = [0 : 1/Q : 1];
  w2 = interp1 ([0 : 1/(N-1) : 1], w, k);

  if (M/N < Q)
    Q = M/N;
  endif

  figure('position', [1 1 1200 600])
  subplot(1,2,1)
  area(k,w2,'FaceColor', [0 0.4 0.6], 'edgecolor', [0 0 0], 'linewidth', 1)
  if (min(w) >= -0.01)
    ylim([0 1.05])
    set(gca,'YTick', [0 : 0.1 : 1])
  else
    ylim([-1 5])
    set(gca,'YTick', [-1 : 1 : 5])
  endif
  ylabel('amplitude')
  set(gca,'XTick', [0 : 1/8 : 1])
  set(gca,'XTickLabel',[' 0'; ' '; ' '; ' '; ' '; ' '; ' '; ' '; 'N-1'])
  grid('on')
  set(gca,'gridlinestyle','-')
  xlabel('samples')
  if (strcmp (wspecifier, ""))
    title(cstrcat(wname,' window'), 'interpreter', 'none')
  else
    title(cstrcat(wname,' window (', wspecifier, ')'), 'interpreter', 'none')
  endif
  set(gca,'Position',[0.094 0.17 0.38 0.71])

  H = abs(fft([w zeros(1,(M-1)*N)]));
  H = fftshift(H);
  H = H/max(H);
  H = 20*log10(H);
  H = max(-dr,H);
  k = ([1:M*N]-1-M*N/2)/M;
  k2 = [-P : 1/M : P];
  H2 = interp1 (k, H, k2);

  subplot(1,2,2)
  set(gca,'FontSize',28)
  h = stem(k2,H2,'-');
  set(h,'BaseValue',-dr)
  xlim([-P P])
  ylim([-dr 6])
  set(gca,'YTick', [0 : -10 : -dr])
  set(findobj('Type','line'),'Marker','none','Color',[0.8710 0.49 0])
  grid('on')
  set(findobj('Type','gridline'),'Color',[.871 .49 0])
  set(gca,'gridlinestyle','-')
  ylabel('decibels')
  xlabel('bins')
  title('Fourier transform')
  set(gca,'Position',[0.595 0.17 0.385 0.71])

  if (strcmp (wfilename, ""))
    wfilename = wname;
  endif
  if (strcmp (wfilespecifier, ""))
    wfilespecifier = wspecifier;
  endif
  if (strcmp (wfilespecifier, ""))
    savetoname = cstrcat('Window function and frequency response - ', wfilename, '.svg');
  else
    savetoname = cstrcat('Window function and frequency response - ', wfilename, ' (', wfilespecifier, ').svg');
  endif
  print(savetoname, '-dsvg', '-S1200,600')
  close

endfunction

N=2^17; % Window length, B is equal for Triangular and Bartlett from 2^17
k=0:N-1;

w = -cos(2*pi*k/(N-1));
w .*= w > 0;
plotWindow(w, "Cosine")

freqData = [0.66697133904805994131, -0.20556692772918355727, 0.49267389481655493588, -0.25062332863369246594, -0.42388422228212319087, 0.42317609537724842905, -0.03930334287740060856, -0.11936153294075849129, 0.30201210285940127687, -0.15541616804857899536, -0.16208119255594669039, 0.12843871362286504723, -0.04470810646117385351, -0.00521885027256757845, 0.07185811583185619522, -0.02835116723496184862, -0.01393644785822748498, 0.00780746224568363342, -0.00748496824751256583, 0.00119325723511989282, 0.00194602547595042175];
freqData(1) /= 2;
scale = freqData(1) + sum(freqData.*not(mod(1:length(freqData), 2)));
freqData /= scale;
w = freqData(1)*ones(1, N);
for bin = 1:(length(freqData)/2)
  w += freqData(bin*2)*cos(2*pi*bin*((1:N)-1)/N);
  w += freqData(bin*2+1)*sin(2*pi*bin*((1:N)-1)/N);
endfor
w(N/4+1:N/2+1) = 0;
w(N/8+2:N/4) = (1 - w(N/8:-1:2).*w(7*N/8+2:N))./w(7*N/8:-1:6*N/8+2);
w = shift(w, -N/2);
plotWindow(w, "Asymmetrical");


以及优化成本的源代码与Kiss FFT和优化库一起使用的函数:

#include <stdio.h>
#include <math.h>

int main() {
  const int windowSize = 400;
  double *analysisWindow = new double[windowSize];
  double *synthesisWindow = new double[windowSize];
  for (int k = 0; k < windowSize/4; k++) {
    analysisWindow[k] = 0;
  }
  for (int k = windowSize/4; k < windowSize*7/8; k++) {
    double x = 2 * M_PI * ((k+0.5)/windowSize - 1.75);
    analysisWindow[k] = 2.57392230162633461887-1.58661480271141974718*cos(x)+3.80257516644523141380*sin(x)
      -1.93437090055110760822*cos(2*x)-3.27163999159752183488*sin(2*x)+3.26617449847621266201*cos(3*x)
      -0.30335261753524439543*sin(3*x)-0.92126091064427817479*cos(4*x)+2.33100177294084742741*sin(4*x)
      -1.19953922321306438725*cos(5*x)-1.25098147932225423062*sin(5*x)+0.99132076607048635886*cos(6*x)
      -0.34506787787355830410*sin(6*x)-0.04028033685700077582*cos(7*x)+0.55461815542612269425*sin(7*x)
      -0.21882110175036428856*cos(8*x)-0.10756484378756643594*sin(8*x)+0.06025986430527170007*cos(9*x)
      -0.05777077835678736534*sin(9*x)+0.00920984524892982936*cos(10*x)+0.01501989089735343216*sin(10*x);
  }
  for (int k = 0; k < windowSize/8; k++) {
    analysisWindow[windowSize-1-k] = (1 - analysisWindow[windowSize*3/4-1-k]*analysisWindow[windowSize*3/4+k])/analysisWindow[windowSize/2+k];
  }
  printf("Analysis window:\n");
  for (int k = 0; k < windowSize; k++) {
    printf("%d\t%.10f\n", k, analysisWindow[k]);
  }
  double accu, accu2;
  for (int k = 0; k < windowSize; k++) {
    accu += k*analysisWindow[k]*analysisWindow[k];
    accu2 += analysisWindow[k]*analysisWindow[k];
  }
  for (int k = 0; k < windowSize; k++) {
    synthesisWindow[k] = analysisWindow[windowSize-1-k];
  }
  printf("\nSynthesis window:\n");
  for (int k = 0; k < windowSize; k++) {
    printf("%d\t%.10f\n", k, synthesisWindow[k]);
  }
  printf("Mean of square of analysis window as probability density function:\n%f", accu/accu2);
  printf("\nProduct of analysis and synthesis windows:\n");
  for (int k = 0; k < windowSize/2; k++) {
    printf("%d\t%.10f\n", k, analysisWindow[windowSize/2+k]*synthesisWindow[k]);
  }
  printf("\nSum of overlapping products of windows:\n");
  for (int k = 0; k < windowSize/4; k++) {
    printf("%d\t%.10f\n", k, analysisWindow[windowSize/2+k]*synthesisWindow[k]+analysisWindow[windowSize/2+k+windowSize/4]*synthesisWindow[k+windowSize/4]);
  }
  delete[] analysisWindow;
  delete[] synthesisWindow;
}


#2 楼

这取决于窗口的上下文。正如传统上开发的那样,开窗用于功率谱密度估计的Blackman-Tukey方法。这是相关图法的一般形式,从而利用了离散时间Wiener-Khinchin定理。回想一下,这通过离散时间傅立叶变换将自相关序列与功率谱密度相关联。

因此,在设计窗口时要牢记几个标准。首先,他们必须在原点获得团结。这是为了保留信号自相关序列中的功率,因为​​rxx [0]可以被认为是采样功率。接下来,窗口应从原点开始逐渐变细。这是出于多种原因。首先,为了成为有效的自相关序列,所有其他滞后必须小于或等于原点。其次,这允许使用较低的滞后权重进行较高的加权(已使用大多数样本进行了高信度计算),并且对较高的滞后项进行了小加权或零加权,由于可用的数据样本量减少,方差增加了计算。这最终会导致主瓣变宽,从而导致PSD估计值的分辨率降低,但是旁瓣的显着减少可能会使正弦分量的主瓣偏斜。

最后,如果窗口具有非负光谱,则也非常需要。这是因为使用Blackman-Tukey方法,您可以将最终估计的偏差视为真实功率谱密度与窗谱卷积。如果此窗口频谱具有负区域,则功率谱密度估计中可能具有负区域。这显然是不希望的,因为在这种情况下它几乎没有物理意义。此外,您会注意到Blackman-Tukey方法中没有大小平方运算。这是因为,用实数和偶数自相关序列乘以实数和偶数窗口,离散傅里叶变换也将是实数和偶数。在实践中,您会发现通常会被量化的很小的负分量。

由于这些原因,窗口的长度也是奇数,因为所有有效的自相关序列也是如此。现在,在周期图方法的上下文中,仍然可以做(和完成)窗口化。即,将数据窗口化,然后取窗口数据的大小平方。这不等同于Blackman-Tukey方法。通过一些统计推导,您可以发现它们的平均表现类似,但总体上却不一样。例如,在韦尔奇(Welch)或巴特利特(Bartlett)方法中为每个段使用开窗以减小估计的方差是很常见的。因此,从本质上讲,使用这些方法,动机部分相同,但有所不同。在这些方法中,功率可以通过例如划分窗能量来标准化,而不是对窗滞后进行仔细加权。如果您对为什么选择不对称窗口感到好奇,请考虑傅立叶变换的对偶属性的含义,以及卷积功率谱密度估计对您的应用意味着什么。干杯。

#3 楼

开窗的原始点是要确保(DFT假定为周期性的)信号与开始时相比在开始时没有急剧的瞬变。代价是,朝着(对称)窗口中心的频率将得到更多加权,并在随后的DFT中表示出来。

在所有这些背景下,我可以想象一个人想要使用非对称窗口来强调通过DFT分析的信号中的局部时间特征。但是,如果开窗后信号的端点幅度大致不相同,则在DFT期间可能会以更宽的波瓣宽度为代价。