FFT mit asymmetrischer Fensterung?

17

Gemeinsame nicht rechteckige Fensterfunktionen scheinen alle symmetrisch zu sein. Gibt es jemals einen Fall, in dem man vor einer FFT eine unsymmetrische Fensterfunktion verwenden möchte? (Sagen Sie, wenn die Daten auf einer Seite der FFT-Blende als etwas wichtiger als die Daten auf der anderen Seite oder als weniger verrauscht usw. angesehen wurden.)

Wenn ja, welche Arten von asymmetrischen Fensterfunktionen wurden untersucht und wie würden sie den Frequenzgang im Vergleich zu einem (verlustbehafteten?) Symmetrischen Versatzfenster beeinflussen?

hotpaw2
quelle
2
Im Allgemeinen werden Fenster verwendet, weil die FFT kleine Signalabschnitte verarbeitet und versucht, sie lokal wie ein stationäres Signal aussehen zu lassen. Damit es keine "Seite" zu bevorzugen gibt, wird angenommen, dass das Signal durchgehend einheitlich ist.
Endolith
4
In einem Audioanalysealgorithmus, der an Live-Daten arbeitet und eine zu befürchtende Durchsatzverzögerung aufweist, kann manchmal ein asymmetrisches Fenster entworfen werden, das eine geringere effektive Verzögerung aufweist als das symmetrische Fenster derselben Länge. Wenn das Verhalten dieses asymmetrischen Fensters (das im Voraus bekannt ist) die Ausgabeparameter dieser Audioanalyse in bekannter Weise beeinflusst, können diese Parameter kompensiert werden, und Sie behalten den Vorteil einer verringerten Verzögerung.
Robert Bristow-Johnson

Antworten:

9

Ich benutze das Shorthand- Fenster für die "Fensterfunktion".

Mit Audio klingt jede Verarbeitung, die so etwas wie ein Pre-Ringing oder ein Pre-Echo erzeugt, wie eine MP3-Datei mit niedriger Bitrate. Dies geschieht, wenn die lokalisierte Energie eines Transienten oder eines Impulses zeitlich rückwärts gestreut wird, beispielsweise durch Modifikation der Spektraldaten in überlappten Transformationen wie der überlappten modifizierten diskreten Cosinustransformation (MDCT). Bei einer solchen Verarbeitung wird Audio durch überlappende Analysefenster gefenstert , transformiert, im Frequenzbereich verarbeitet (wie auf eine kleinere Bitrate komprimierte Daten), erneut mit einem Synthesefenster gefenstert und wieder zusammenaddiert. Das Produkt des Analyse- und Synthesefensters muss so sein, dass sich die überlappenden Fenster zu einer Einheit summieren.

Traditionell waren die verwendeten Fensterfunktionen symmetrisch und ihre Breite war ein Kompromiss zwischen Frequenzselektivität (langes Fenster) und Vermeidung von Zeitbereichsartefakten (kurzes Fenster). Je breiter das Fenster, desto später kann die Verarbeitung das Signal verbreiten. Eine neuere Lösung ist die Verwendung eines asymmetrischen Fensters. Die beiden verwendeten Fenster können Spiegelbilder voneinander sein. Das Analysefenster fällt schnell von der Spitze auf Null ab, so dass Impulse nicht viel im Voraus "erkannt" werden, und das Syntheisfenster steigt schnell von Null auf die Spitze an, so dass sich die Auswirkungen einer Verarbeitung nicht viel zeitlich nach hinten ausbreiten. Ein weiterer Vorteil ist die geringe Latenz. Die asymmetrischen Fenster können eine gute Frequenzselektivität aufweisen und symmetrische Fenster mit variabler Größe bei der Audiokomprimierung ersetzen, wie eine Art Allheilmittel. SehenM. Schnell, M. Schmidt, M. Jander, T. Albert, R. Geiger, V. Ruoppila, P. Ekstrand, M. Lutzky, B. Grill, „MPEG-4 Enhanced Low Delay AAC - ein neuer Standard für High Qualitätskommunikation “ , 125. AES Convention, San Francisco, CA, USA, Preprint 7503, Okt. 2008 und ein weiteres Konferenzpapier, in dem sie auch das Ausmaß der Fourier-Transformation ihres Fensters zeigen: Schnell, M., et al. 2007. Verbesserte MPEG-4-AAC mit geringer Verzögerung - Kommunikation mit niedriger Bitrate und hoher Qualität. In 122. AES-Konvention .

Darstellung der überlappten Analyse-Verarbeitung-Synthese mit asymmetrischen Fenstern
Abbildung 1. Verwendungsbeispiele für asymmetrische Fenster in der überlappenden Analyse-Verarbeitung-Synthese. Das Produkt (schwarz gestrichelt) des Analysefensters (blau) und des Synthesefensters (gelblich-orange) summiert sich mit dem Fenster des vorherigen Rahmens (grau gestrichelt) zu einer Einheit. Weitere Einschränkungen sind erforderlich, um eine perfekte Rekonstruktion bei der Verwendung von MDCT zu gewährleisten.

Diskrete Fouriertransformation (DFT, FFT) könnte anstelle von MDCT verwendet werden, liefert jedoch in solchen Zusammenhängen redundante Spektraldaten. Im Vergleich zu DFT liefert MDCT nur die Hälfte der Spektraldaten und ermöglicht dennoch eine perfekte Rekonstruktion, wenn geeignete Fenster ausgewählt werden.

Hier ist mein eigenes asymmetrisches Fensterdesign (Abb. 2), das für überlappte Analyse-Verarbeitung-Synthese mit DFT, aber nicht mit MDCT geeignet ist und bei dem keine perfekte Rekonstruktion möglich ist. Das Fenster versucht, das Produkt der mittleren quadratischen Zeit- und Frequenzbandbreiten (ähnlich dem begrenzten Gaußschen Fenster ) zu minimieren, während einige potenziell nützliche Zeitdomäneneigenschaften beibehalten werden: nichtnegativ, unimodal mit dem Peak zum "Zeitpunkt Null", um den die Analyse und Synthese erfolgt Fenster sind Spiegelbilder voneinander, Funktion und Kontinuität der ersten Ableitung, Mittelwert Null, wenn das Quadrat der Fensterfunktion als eine nicht normalisierte Wahrscheinlichkeitsdichtefunktion interpretiert wird. Das Fenster wurde mit Differential Evolution optimiert .

Asymmetrisches und Kosinusfenster
Abbildung 2. Links: Ein asymmetrisches Analysefenster, das sich zusammen mit seinem zeitlich umgekehrten Gegenstücksynthesefenster für die überlappende Analyseverarbeitung eignet. Rechts: Cosine-Fenster mit der gleichen Latenz wie das asymmetrische Fenster

Fouriertransformationen der Fenster
Abbildung 3. Größe der Fourier-Transformationen des Kosinusfensters (blau) und des asymmetrischen Fensters (orange) in Abbildung 2. Das asymmetrische Fenster zeigt eine bessere Frequenzselektivität.

Hier ist der Octave-Quellcode für die Diagramme und für das asymmetrische Fenster. Der Plotcode stammt von Wikimedia Commons . Unter Linux empfehle ich die Installation gnuplot, epstool, pstoedit, transfigerste und librsvg2-binfür die Anzeige verwendet 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");

Möglicherweise möchten Sie nur jedes zweite Beispiel des Fensters verwenden, da es bei Null beginnt und endet. Der folgende C ++ - Code erledigt das für Sie, damit Sie keine Null-Samples erhalten, außer in einem Viertel des Fensters, das überall Null ist. Für das Analysefenster ist dies das erste Quartal und für das Synthesefenster das letzte Quartal. Die zweite Hälfte des Analysefensters sollte zur Berechnung ihres Produkts an der ersten Hälfte des Synthesefensters ausgerichtet sein. Der Code testet auch den Mittelwert des Fensters (als Wahrscheinlichkeitsdichtefunktion) und zeigt die Ebenheit der überlappenden Rekonstruktion.

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

Und der Quellcode für die Optimierungskostenfunktion, die mit Kiss FFT und einer Optimierungsbibliothek verwendet werden soll :

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
quelle
3

Es kommt auf den Kontext des Fensters an. Das Fenster wurde traditionell für die Blackman-Tukey-Methode zur Bestimmung der spektralen Leistungsdichte entwickelt. Dies ist die allgemeine Form der Korrelogrammmethoden, wobei das zeitdiskrete Wiener-Khinchin-Theorem ausgenutzt wird. Man erinnere sich, dass dies die Autokorrelationssequenz mit der spektralen Leistungsdichte durch die zeitdiskrete Fouriertransformation in Beziehung setzt.

Daher wurden Fenster nach verschiedenen Kriterien entworfen. Erstens mussten sie am Ursprung Einheitsgewinn haben. Dies sollte die Leistung in der Autokorrelationssequenz des Signals erhalten, da rxx [0] als die Abtastleistung angesehen werden kann. Als nächstes sollte sich das Fenster vom Ursprung aus verjüngen. Dies hat mehrere Gründe. Erstens müssen alle anderen Verzögerungen kleiner oder gleich dem Ursprung sein, um eine gültige Autokorrelationssequenz zu sein. Zweitens ermöglichte dies eine höhere Gewichtung der niedrigeren Verzögerungen, die mit großer Sicherheit unter Verwendung der meisten Stichproben berechnet wurden, und eine geringe oder Nullgewichtung der höheren Verzögerungen, die aufgrund der abnehmenden Menge der für sie verfügbaren Datenstichproben eine zunehmende Varianz aufweisen Berechnung. Dies führt letztendlich zu einer breiteren Hauptkeule und anschließend zu einer geringeren Auflösung in der PSD-Schätzung.

Schließlich ist es auch sehr erwünscht, wenn die Fenster ein nicht negatives Spektrum aufweisen. Dies liegt daran, dass Sie sich mit der Blackman-Tukey-Methode die Abweichung der endgültigen Schätzung als die wahre spektrale Leistungsdichte vorstellen können, die sich aus dem Fensterspektrum ergibt. Wenn dieses Fensterspektrum negative Bereiche aufweist, können negative Bereiche in Ihrer Schätzung der spektralen Leistungsdichte enthalten sein. Dies ist offensichtlich unerwünscht, da es in diesem Zusammenhang wenig physikalische Bedeutung hat. Darüber hinaus werden Sie feststellen, dass es bei der Blackman-Tukey-Methode keine Operation im Quadrat der Größe gibt. Dies liegt daran, dass mit einer reellen und geraden Autokorrelationssequenz multipliziert mit einem reellen und geraden Fenster die diskrete Fouriertransformation auch reell und gerade ist. In der Praxis finden Sie sehr kleine negative Komponenten, die normalerweise quantisiert werden.

Aus diesen Gründen haben Fenster auch eine ungerade Länge, da dies auch für alle gültigen Autokorrelationssequenzen gilt. Was jetzt noch getan werden kann (und getan wird), wird im Kontext der Periodogrammmethoden angezeigt. Das heißt, Fenster die Daten, und nehmen Sie dann die Größe der Fensterdaten im Quadrat. Dies entspricht nicht der Blackman-Tukey-Methode. Sie können anhand einiger statistischer Ableitungen feststellen, dass sie sich im Durchschnitt ähnlich verhalten , jedoch nicht im Allgemeinen. Beispielsweise ist es durchaus üblich, bei der Welch- oder Bartlett-Methode die Fensterung für jedes Segment zu verwenden, um die Varianz der Schätzungen zu verringern. Im Wesentlichen ist bei diesen Methoden die Motivation zum Teil dieselbe, aber unterschiedlich. Bei diesen Verfahren wird die Leistung normalisiert, indem beispielsweise die Fensternergie aufgeteilt wird, anstatt die Fensterverzögerungen sorgfältig zu gewichten.

Hoffentlich kontextualisiert dies also Fenster und ihre Ursprünge und warum sie symmetrisch sind. Wenn Sie neugierig sind, warum man ein asymmetrisches Fenster wählen kann, sollten Sie die Auswirkungen der Dualitätseigenschaft der Fouriertransformation und die Bedeutung der Schätzung der spektralen Leistungsdichte für Ihre Anwendung berücksichtigen. Prost.

Bryan
quelle
1

Der ursprüngliche Fensterungspunkt besteht darin, sicherzustellen, dass das (von der DFT angenommene periodische) Signal zu Beginn im Vergleich zum Ende keine scharfen Übergänge aufweist. Die Kosten sind, dass Frequenzen in Richtung der Mitte des (symmetrischen) Fensters stärker gewichtet und in der nachfolgenden DFT dargestellt werden.

Mit all dem im Hintergrund kann ich mir vorstellen, dass man ein asymmetrisches Fenster verwenden möchte, um lokale zeitliche Merkmale in dem über die DFT analysierten Signal hervorzuheben. Dies kann jedoch zu Lasten einer größeren Keulenbreite während der DFT gehen, wenn die Endpunkte Ihres Signals nach dem Fenstern nicht ungefähr die gleiche Amplitude haben.

Spacey
quelle