FFT com janelas assimétricas?

17

Todas as funções comuns de janela não retangular parecem simétricas. Existe um caso em que alguém gostaria de usar uma função de janela não simétrica antes de uma FFT? (Digamos se os dados de um lado da abertura da FFT foram considerados um pouco mais importantes que os dados do outro, ou menos barulhentos etc.)

Nesse caso, que tipos de funções de janela assimétricas foram estudadas e como elas afetariam a resposta de frequência em comparação com uma janela simétrica de deslocamento (com mais perdas?)?

hotpaw2
fonte
2
Geralmente, as janelas são usadas porque a FFT está operando em pequenos trechos de um sinal, tentando fazer com que pareça localmente como um sinal estacionário. Portanto, não há "lado" a preferir, o sinal é considerado uniforme o tempo todo.
Endolith
4
em um algoritmo de análise de áudio que trabalha com dados ativos e tem um atraso na taxa de transferência para se preocupar, às vezes uma janela assimétrica pode ser projetada com um atraso menos efetivo do que a janela simétrica do mesmo comprimento. se o comportamento dessa janela assimétrica (que é conhecida antecipadamente) afetar os parâmetros de saída dessa análise de áudio de maneira conhecida, esses parâmetros poderão ser compensados ​​e você manterá a vantagem de um atraso reduzido.
Robert Bristow-johnson

Respostas:

9

Vou usar a janela abreviada para "função da janela".

Com o áudio, qualquer processamento que crie algo semelhante ao pré-toque ou pré-eco soará superficial como um mp3 de baixa taxa de bits. Isso acontece quando a energia localizada de um transiente ou de um impulso é propagada para trás no tempo, por exemplo, pela modificação dos dados espectrais nas transformações lapidadas, como a transformação discreta de cosseno discreto modificado (MDCT). Nesse processamento, o áudio é visualizado por janelas de análise sobrepostas , transformadas, processadas no domínio da frequência (como dados compactados para uma taxa de bits menor), visualizadas novamente com uma janela de síntese e somadas novamente. O produto da janela de análise e síntese deve ser tal que as janelas sobrepostas se somam à unidade.

Tradicionalmente, as funções de janela usadas são simétricas e sua largura tem sido um compromisso entre a seletividade de frequência (janela longa) e a prevenção de artefatos no domínio do tempo (janela curta). Quanto maior a janela, mais tempo no tempo o processamento pode espalhar o sinal. Uma solução mais recente é usar uma janela assimétrica. As duas janelas usadas podem ser imagens espelhadas uma da outra. A janela de análise cai do pico para zero rapidamente, para que os impulsos não sejam "detectados" com muita antecedência, e a janela da síntese sobe de zero para o pico rapidamente, para que os efeitos de qualquer processamento não se espalhem muito no tempo. Outra vantagem disso é a baixa latência. As janelas assimétricas podem ter boa seletividade de frequência e podem substituir janelas simétricas de tamanho variável na compactação de áudio, como uma espécie de cura total. VejoM. Schnell, M. Schmidt, M. Jander, T. Albert, R. Geiger, V. Ruoppila, P. Ekstrand, M. Lutzky, B. Grill, MPAC -4 Enhanced Low Delay AAC - um novo padrão para alta comunicação de qualidade ” , 125ª Convenção da AES, São Francisco, CA, EUA, pré-impressão 7503, outubro de 2008 e outro trabalho de conferência onde também mostram a magnitude da transformação de Fourier de sua janela: Schnell, M., et al. 2007. AAC MPEG-4 baixo atraso - Comunicação de alta qualidade e baixa taxa de bits. Na 122ª Convenção da AES .

Ilustração da análise-processamento-síntese lapidada usando janelas assimétricas
Figura 1. Ilustração do uso de janelas assimétricas na análise-processamento-síntese-lapped. O produto (preto tracejado) da janela de análise (azul) e a janela de síntese (laranja amarelada) somam-se à unidade com a janela do quadro anterior (cinza tracejado). São necessárias restrições adicionais para garantir a reconstrução perfeita ao usar o MDCT.

A transformada discreta de Fourier (DFT, FFT) pode ser usada no lugar da MDCT, mas em tais contextos fornecerá dados espectrais redundantes. Comparado ao DFT, o MDCT fornece apenas metade dos dados espectrais, enquanto ainda permite a reconstrução perfeita se janelas adequadas forem escolhidas.

Aqui está o meu próprio design de janela assimétrica (Fig. 2), adequado para síntese de processamento de análise por lapidação usando DFT, mas não MDCT, com o qual não proporciona uma reconstrução perfeita. A janela tenta minimizar o produto das larguras de banda de tempo e freqüência quadradas médias (semelhante à janela Gaussiana confinada ), mantendo algumas propriedades potencialmente úteis no domínio do tempo: não-negativas, unimodais com o pico no "tempo zero" em torno do qual a análise e síntese janelas são imagens espelhadas uma da outra, função e continuidade da primeira derivada, média zero quando o quadrado da função da janela é interpretado como uma função de densidade de probabilidade não normalizada. A janela foi otimizada usando evolução diferencial .

Janela assimétrica e cosseno
Figura 2. Esquerda: uma janela de análise assimétrica adequada para sobreposição de análise-processamento-ressíntese, juntamente com sua janela de síntese de contrapartida invertida no tempo. Direita: Janela cosseno, com a mesma latência que a janela assimétrica

Transformadas de Fourier das janelas
Figura 3. Magnitude das transformadas de Fourier da janela cosseno (azul) e da janela assimétrica (laranja) da Fig. 2. A janela assimétrica mostra melhor seletividade de frequência.

Aqui está o código-fonte do Oitava para os gráficos e para a janela assimétrica. O código de plotagem vem do Wikimedia Commons . No Linux eu recomendo a instalação gnuplot, epstool, pstoedit, transfigem primeiro lugar e librsvg2-binpara visualização usando display.

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");

Você pode usar apenas cada segundo exemplo da janela, pois inicia e termina em zero. O código C ++ a seguir faz isso para você, para que você não obtenha zero amostras, exceto em um quarto da janela que é zero em todos os lugares. Para a janela de análise, este é o primeiro trimestre e, para a janela de síntese, é o último trimestre. A segunda metade da janela de análise deve estar alinhada com a primeira metade da janela de síntese para o cálculo do seu produto. O código também testa a média da janela (como uma função de densidade de probabilidade) e mostra o nivelamento da reconstrução sobreposta.

#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;
}

E o código-fonte da função de custo de otimização a ser usado com o Kiss FFT e uma biblioteca de otimização :

class WinProblem : public Opti::Problem {
private:
  int numParams;
  double *min;
  double *max;
  kiss_fft_scalar *timeData;
  kiss_fft_cpx *freqData;
  int smallSize;
  int bigSize;
  kiss_fftr_cfg smallFFTR;
  kiss_fftr_cfg smallIFFTR;
  kiss_fftr_cfg bigFFTR;
  kiss_fftr_cfg bigIFFTR;

public:
  // numParams must be odd
  WinProblem(int numParams, int smallSize, int bigSize, double* candidate = NULL) : numParams(numParams), smallSize(smallSize), bigSize(bigSize) {
    min = new double[numParams];
    max = new double[numParams];
    if (candidate != NULL) {
      for (int i = 0; i < numParams; i++) {
        min[i] = candidate[i]-fabs(candidate[i])*(1.0/65536);
        max[i] = candidate[i]+fabs(candidate[i])*(1.0/65536);
      }
    } else {
      for (int i = 0; i < numParams; i++) {
        min[i] = -1;
        max[i] = 1;
      }
    }
    timeData = new kiss_fft_scalar[bigSize];
    freqData = new kiss_fft_cpx[bigSize/2+1];
    smallFFTR = kiss_fftr_alloc(smallSize, 0, NULL, NULL);
    smallIFFTR = kiss_fftr_alloc(smallSize, 1, NULL, NULL);
    bigFFTR = kiss_fftr_alloc(bigSize, 0, NULL, NULL);
    bigIFFTR = kiss_fftr_alloc(bigSize, 1, NULL, NULL);
  }

  double *getMin() {
    return min;
  }

  double *getMax() {
    return max;
  }

// ___                                                            __ 1     
// |  \    |       |       |       |       |       |       |     / |       
// |   \   |       |       |       |       |       |       |    /  |       
// |    \_ |       |       |       |       |       |       |   /   |
// |      \|__     |       |       |       |       |       |  /|   |       
// |       |  -----|_______|___    |       |       |       | / |   |       
// |       |       |       |   ----|       |       |       |/  |   |       
// --------------------------------x-----------------------x---|---- 0
// 0      1/8     2/8     3/8     4/8     5/8     6/8     7/8 15/16 
// |-------------------------------|                       |-------|
//            zeroStarts                                   winStarts
//
// f(x) = 0 if 4/8 < x < 7/8
// f(-x)f(x) + f(-x+1/8)f(x-1/8) = 1 if 0 < x < 1/8

  double costFunction(double *params, double compare, int print) {
    double penalty = 0;
    double accu = params[0]/2;
    for (int i = 1; i < numParams; i += 2) {
      accu += params[i];
    }
    if (print) {
      printf("%.20f", params[0]/2/accu);
      for (int i = 1; i < numParams; i += 2) {
        printf("+%.20fcos(%d pi x)", params[i]/accu, (i+1)/2);
        printf("+%.20fsin(%d pi x)", params[i+1]/accu, (i+1)/2);
      }
      printf("\n");
    }
    if (accu != 0) {
      for (int i = 0; i < numParams; i++) {
        params[i] /= accu;
      }
    }
    const int zeroStarts = 4; // Normally 4
    const int winStarts = 2; // Normally 1
    int i = 0;
    int j = 0;
    freqData[j].r = params[i++];
    freqData[j++].i = 0;
    for (; i < numParams;) {
      freqData[j].r = params[i++];
      freqData[j++].i = params[i++];
    }
    for (; j <= smallSize/2;) {
      freqData[j].r = 0;
      freqData[j++].i = 0;
    }
    kiss_fftri(smallIFFTR, freqData, timeData);
    double scale = 1.0/timeData[0];
    double tilt = 0;
    double tilt2 = 0;
    for (int i = 2; i < numParams; i += 2) {
      if ((i/2)%2) {
        tilt2 += (i/2)*params[i]*scale;
      } else {
        tilt2 -= (i/2)*params[i]*scale;
      }
      tilt += (i/2)*params[i]*scale;
    }
    penalty += fabs(tilt);
    penalty += fabs(tilt2);
    double accu2 = 0;
    for (int i = 0; i < smallSize; i++) {
      timeData[i] *= scale;
    }
    penalty += fabs(timeData[zeroStarts*smallSize/8]);
    penalty += fabs(timeData[winStarts*smallSize/16]*timeData[smallSize-winStarts*smallSize/16]-0.5);
    for (int i = 1; i < winStarts*smallSize/16; i++) {
      // Last 16th
      timeData[bigSize-winStarts*smallSize/16+i] = timeData[smallSize-winStarts*smallSize/16+i];
      accu2 += timeData[bigSize-winStarts*smallSize/16+i]*timeData[bigSize-winStarts*smallSize/16+i];
    }
    // f(-1/8+i)*f(1/8-i) + f(i)*f(-i) = 1
    // => f(-1/8+i) = (1 - f(i)*f(-i))/f(1/8-i)   
    // => f(-1/16) = (1 - f(1/16)*f(-1/16))/f(1/16)
    //             = 1/(2 f(1/16))
    for (int i = 1; i < winStarts*smallSize/16; i++) {
      // 2nd last 16th
      timeData[bigSize-winStarts*smallSize/8+i] = (1 - timeData[i]*timeData[bigSize-i])/timeData[winStarts*smallSize/8-i];
      accu2 += timeData[bigSize-winStarts*smallSize/8+i]*timeData[bigSize-winStarts*smallSize/8+i];
    }
    // Between 2nd last and last 16th
    timeData[bigSize-winStarts*smallSize/16] = 1/(2*timeData[winStarts*smallSize/16]);
    accu2 += timeData[bigSize-winStarts*smallSize/16]*timeData[bigSize-winStarts*smallSize/16];
    for (int i = zeroStarts*smallSize/8; i <= bigSize-winStarts*smallSize/8; i++) {
      timeData[i] = 0;
    }
    for (int i = 0; i < zeroStarts*smallSize/8; i++) {
      accu2 += timeData[i]*timeData[i];
    }
    if (print > 1) {
      printf("\n");
      for (int x = 0; x < bigSize; x++) {
        printf("%d,%f\n", x, timeData[x]);
      }
    }
    scale = 1/sqrt(accu2);
    if (print) {
      printf("sqrt(accu2) = %f\n", sqrt(accu2));
    }
    double tSpread = 0;
    timeData[0] *= scale;
    double tMean = 0;
    for (int i = 1; i <= zeroStarts*smallSize/8; i++) {
      timeData[i] *= scale;
      //      tSpread += ((double)i)*((double)i)*(timeData[i]*timeData[i]);
      double x_0 = timeData[i-1]*timeData[i-1];
      double x_1 = timeData[i]*timeData[i];
      tSpread += ((double)i)*((double)i)*(x_0 + x_1)*0.5 - ((double)i)*(2.0/3*x_0 + 1.0/3*x_1) + 0.25*x_0 + 1.0/12*x_1;
      double slope = timeData[i]-timeData[i-1];
      if (slope > 0) {
        penalty += slope+1;
      }
      tMean += x_1*i;
      if (timeData[i] < 0) {
        penalty -= timeData[i];
      }
    }
    double x_0 = timeData[0]*timeData[0];
    for (int i = 1; i <= winStarts*smallSize/8; i++) {
      timeData[bigSize-i] *= scale;
      double x_1 = timeData[bigSize-i]*timeData[bigSize-i];
      tSpread += ((double)i)*((double)i)*(x_0 + x_1)*0.5 - ((double)i)*(2.0/3*x_0 + 1.0/3*x_1) + 0.25*x_0 + 1.0/12*x_1;
      x_0 = x_1;        
      tMean += x_1*(-i);
    }
    tMean /= smallSize;
    penalty += fabs(tMean);
    if (tMean > 0) {
      penalty += 1;
    }
    tSpread /= ((double)smallSize)*((double)smallSize); 
    if (print) {
      printf("tSpread = %f\n", tSpread);
    }
    kiss_fftr(bigFFTR, timeData, freqData);
    double fSpread = 0;
    x_0 = freqData[0].r*freqData[0].r;
    for (int i = 1; i <= bigSize/2; i++) {
      double x_1 = freqData[i].r*freqData[i].r+freqData[i].i*freqData[i].i;
      fSpread += ((double)i)*((double)i)*(x_0 + x_1)*0.5 - ((double)i)*(2.0/3*x_0 + 1.0/3*x_1) + 0.25*x_0 + 1.0/12*x_1;
      x_0 = x_1;
    }
    if (print > 1) {
      for (int i = 0; i <= bigSize/2; i++) {
        printf("%d,%f,%f\n", i, freqData[i].r, freqData[i].i);
      }
    }
    fSpread /= bigSize; // Includes kiss_fft scaling
    if (print) {
      printf("fSpread = %f\n", fSpread);
      printf("%f,%f,%f\n", tSpread, fSpread, tSpread*fSpread);
    }
    return tSpread*fSpread + penalty;
  }

  double costFunction(double *params, double compare) {
    return costFunction(params, compare, false);
  }

  int getNumDimensions() {
    return numParams;
  }

  ~WinProblem() {
    delete[] min;
    delete[] max;
    delete[] timeData;
    delete[] freqData;
    KISS_FFT_FREE(smallFFTR);
    KISS_FFT_FREE(smallIFFTR);
    KISS_FFT_FREE(bigFFTR);
    KISS_FFT_FREE(bigIFFTR);
  }
};
Olli Niemitalo
fonte
3

Depende do contexto de janelas. O windowing, como era tradicionalmente desenvolvido, era destinado ao método Blackman-Tukey de densidade espectral de potência de estimativa. Essa é a forma geral dos métodos de correlograma, nos quais o teorema de Wiener-Khinchin em tempo discreto é explorado. Lembre-se de que isso relaciona a sequência de autocorrelação à densidade espectral de potência através da transformação de Fourier no tempo discreto.

Portanto, as janelas foram projetadas com vários critérios em mente. Primeiro, eles tinham que ter ganho de unidade na origem. Isso foi para preservar a energia na sequência de autocorrelação do sinal, já que rxx [0] pode ser considerado a potência da amostra. Em seguida, a janela deve diminuir da origem. Isso ocorre por várias razões. Primeiro, para ser uma sequência de autocorrelação válida, todos os outros atrasos devem ser menores ou iguais à origem. Segundo, isso permitiu maior ponderação dos atrasos mais baixos, que foram computados com grande confiança usando a maioria das amostras, e peso pequeno ou zero dos atrasos mais altos, que apresentam variação crescente devido à quantidade decrescente de amostras de dados disponíveis para seus grupos. Cálculo. Em última análise, isso resulta em um lobo principal mais amplo e subsequentemente na resolução diminuída na estimativa do PSD,

Finalmente, também é altamente desejado se as janelas tiverem um espectro não negativo. Isso ocorre porque, com o método Blackman-Tukey, você pode pensar no viés da estimativa final como a verdadeira densidade espectral de potência convoluida com o espectro da janela. Se esse espectro da janela tiver regiões negativas, é possível ter regiões negativas na estimativa de densidade espectral de potência. Obviamente, isso é indesejável, pois tem pouco significado físico nesse contexto. Além disso, você notará que não há operação de magnitude ao quadrado no método Blackman-Tukey. Isso ocorre porque, com uma sequência real e até de autocorrelação multiplicada por uma janela real e uniforme, a transformada discreta de Fourier também será real e uniforme. Na prática, você encontrará componentes negativos muito pequenos que geralmente são quantizados.

Por esses motivos, as janelas também têm um comprimento ímpar, porque todas as seqüências de autocorrelação válidas também são. Agora, o que ainda pode ser feito (e é feito) é de janelas no contexto dos métodos do periodograma. Ou seja, janela os dados e, em seguida, pegue a magnitude ao quadrado dos dados em janela. Isso não é equivalente ao método Blackman-Tukey. Você pode descobrir, por meio de algumas derivações estatísticas, que elas se comportam da mesma forma em média , mas não em geral. Por exemplo, é bastante comum usar a janela para cada segmento no método de Welch ou Bartlett para diminuir a variação das estimativas. Então, em essência, com esses métodos, a motivação é em parte a mesma, mas diferente. A energia é normalizada nesses métodos dividindo a energia da janela, por exemplo, em vez de ponderar cuidadosamente as defasagens da janela.

Portanto, espero que isso contextualize janelas e suas origens, e por que elas são simétricas. Se você está curioso para saber por que alguém pode escolher uma janela assimétrica, considere as implicações da propriedade de dualidade da transformada de Fourier e o que significa a convolução de sua estimativa de densidade espectral de potência para sua aplicação. Felicidades.

Bryan
fonte
1

O ponto original da janela é garantir que o sinal (assumido periodicamente pela DFT) não tenha transientes nítidos no início em comparação ao final. O custo é que as frequências em direção ao centro da janela (simétrica) serão mais ponderadas e representadas na DFT subsequente.

Com tudo isso em segundo plano, posso imaginar que alguém desejaria usar uma janela assimétrica para acentuar os recursos temporais locais no sinal analisado via DFT. No entanto, isso pode custar uma largura maior do lobo durante a DFT, se os pontos finais do seu sinal não tiverem aproximadamente a mesma amplitude após a janela.

Spacey
fonte