FFT ที่มีหน้าต่างแบบอสมมาตรหรือไม่?


17

ฟังก์ชั่นหน้าต่างที่ไม่ใช่สี่เหลี่ยมทั่วไปดูเหมือนว่าจะสมมาตร เคยมีกรณีบ้างไหมที่จะต้องการใช้ฟังก์ชั่นหน้าต่างที่ไม่สมมาตรต่อหน้า FFT? (พูดว่าข้อมูลที่อยู่ด้านหนึ่งของรูรับแสง FFT นั้นถือว่าสำคัญกว่าข้อมูลอื่น ๆ หรือมีเสียงรบกวนน้อยกว่า ฯลฯ )

ถ้าเป็นเช่นนั้นฟังก์ชั่นหน้าต่างแบบอสมมาตรชนิดใดที่ได้รับการศึกษาและจะมีผลต่อการตอบสนองความถี่อย่างไรเมื่อเทียบกับหน้าต่างออฟเซ็ตแบบสมมาตร


2
โดยทั่วไปจะใช้ windows เพราะ FFT ทำงานกับสัญญาณขนาดเล็กพยายามที่จะทำให้ดูเหมือนสัญญาณภายในเครื่อง ดังนั้นจึงไม่มี "ด้าน" ให้เลือกสัญญาณจะถือว่าเหมือนกันตลอด
endolith

4
ในอัลกอริธึมการวิเคราะห์เสียงที่ทำงานกับข้อมูลสดและมีความล่าช้าในการรับส่งข้อมูลบางครั้งหน้าต่างการออกแบบที่ไม่สมมาตรสามารถออกแบบให้มีความล่าช้าที่มีประสิทธิภาพน้อยกว่าหน้าต่างแบบสมมาตรที่มีความยาวเท่ากัน หากลักษณะการทำงานของหน้าต่างอสมมาตรนี้ (ซึ่งทราบล่วงหน้า) มีผลต่อพารามิเตอร์เอาต์พุตของการวิเคราะห์เสียงนี้ในลักษณะที่รู้จักพารามิเตอร์เหล่านั้นสามารถชดเชยได้และคุณยังคงรักษาความได้เปรียบของความล่าช้าที่ลดลง
robert bristow-johnson

คำตอบ:


9

ฉันจะใช้หน้าต่างชวเลขสำหรับ "ฟังก์ชั่นหน้าต่าง"

ด้วยเสียงการประมวลผลใด ๆ ที่สร้างสิ่งที่คล้ายกับเสียงกริ่งหรือเสียงก้องล่วงหน้าจะให้เสียงที่ลื่นเหมือน MP3 ที่อัตราบิตต่ำ สิ่งนี้เกิดขึ้นเมื่อพลังงานที่ถูกแปลของพลังงานชั่วคราวหรือแรงกระตุ้นถูกกระจายไปข้างหลังในเวลาตัวอย่างเช่นโดยการดัดแปลงข้อมูลสเปกตรัมในการแปลงแบบ lapped เช่นการแปลงโคไซน์ที่ไม่ต่อเนื่องแบบดัดแปลง ในการประมวลผลเช่นนั้นเสียงจะถูกเรียงซ้อนโดยหน้าต่างการวิเคราะห์ที่ซ้อนทับกันแปลงประมวลผลในโดเมนความถี่ (เช่นบีบอัดข้อมูลเป็นบิตเรตที่เล็กกว่า) หน้าต่างอีกครั้งด้วยหน้าต่างการสังเคราะห์และรวมกลับมารวมกัน ผลิตภัณฑ์ของหน้าต่างการวิเคราะห์และการสังเคราะห์ต้องเป็นหน้าต่างที่ซ้อนทับกันเพื่อรวมเป็นหนึ่ง

แต่เดิมฟังก์ชั่นหน้าต่างที่ใช้มีความสมมาตรและความกว้างของมันได้รับการประนีประนอมระหว่างการเลือกความถี่ (หน้าต่างยาว) และการหลีกเลี่ยงสิ่งประดิษฐ์โดเมนเวลา (หน้าต่างสั้น) ยิ่งหน้าต่างกว้างมากเท่าไรเวลาที่การประมวลผลก็จะสามารถแพร่สัญญาณได้มากขึ้น วิธีแก้ปัญหาล่าสุดคือการใช้หน้าต่างอสมมาตร หน้าต่างสองบานที่ใช้สามารถสะท้อนภาพของกันและกัน หน้าต่างการวิเคราะห์จะลดลงจากจุดสูงสุดเป็นศูนย์อย่างรวดเร็วเพื่อไม่ให้มีการตรวจพบแรงกระตุ้นล่วงหน้าและหน้าต่าง syntheis จะเพิ่มขึ้นจากศูนย์ถึงจุดสูงสุดอย่างรวดเร็วดังนั้นผลของการประมวลผลใด ๆ จะไม่กระจายไปด้านหลังมากนัก ข้อดีอีกอย่างของความล่าช้านี้คือ หน้าต่างแบบอสมมาตรสามารถเลือกความถี่ได้ดีและสามารถแทนที่หน้าต่างแบบสมมาตรขนาดแปรผันในการบีบอัดสัญญาณเสียงได้เหมือนวิธีรักษาแบบทั้งหมด ดูM. Schnell, M. Schmidt, M. Jander, T. Albert, R. Geiger, V. Ruoppila, P. Ekstrand, M. Lutzky, B. Grill, “ MPEG-4 Enhanced Low Delay AAC - มาตรฐานใหม่สำหรับความแรงสูง การสื่อสารที่มีคุณภาพ” , การประชุม AES ครั้งที่ 125, ซานฟรานซิสโก, แคลิฟอร์เนีย, สหรัฐอเมริกา, พิมพ์ล่วงหน้า 7503, ต.ค. 2551และเอกสารการประชุมอีกฉบับหนึ่งที่แสดงถึงขนาดของการแปลงฟูริเยร์ที่หน้าต่าง: Schnell, M. , et al. 2550. AAC ที่เพิ่มความล่าช้าต่ำระดับ MPEG-4 - การสื่อสารคุณภาพสูงบิตเรตต่ำ ในการประชุม 122th AES

ภาพประกอบของการวิเคราะห์การประมวลผลแบบ lapped โดยใช้ windows asymmetrical
รูปที่ 1 ภาพประกอบการใช้หน้าต่างอสมมาตรในการวิเคราะห์การประมวลผลแบบ lapped ผลิตภัณฑ์ (เส้นประสีดำ) ของหน้าต่างการวิเคราะห์ (สีน้ำเงิน) และหน้าต่างการสังเคราะห์ (สีส้มเหลือง) จะรวมเป็นหนึ่งกับหน้าต่างจากกรอบก่อนหน้า (เส้นประสีเทา) จำเป็นต้องมีข้อ จำกัด เพิ่มเติมเพื่อรับประกันการสร้างใหม่ที่สมบูรณ์แบบเมื่อใช้ MDCT

การแปลงฟูริเยร์แบบไม่ต่อเนื่อง (DFT, FFT) สามารถนำมาใช้แทน MDCT ได้ แต่ในบริบทดังกล่าวจะให้ข้อมูลสเปกตรัมซ้ำซ้อน เมื่อเทียบกับ DFT MDCT จะให้ข้อมูลสเปกตรัมเพียงครึ่งเดียวในขณะที่ยังคงสามารถสร้างใหม่ได้อย่างสมบูรณ์แบบหากเลือกหน้าต่างที่เหมาะสม

นี่คือการออกแบบหน้าต่างอสมมาตรของฉันเอง (รูปที่ 2) เหมาะสำหรับการสังเคราะห์การประมวลผลการวิเคราะห์แบบ lapped โดยใช้ DFT แต่ไม่ใช่ MDCT ซึ่งไม่ได้สร้างขึ้นมาใหม่อย่างสมบูรณ์แบบ หน้าต่างพยายามลดผลิตภัณฑ์ของแบนด์วิดท์เวลาเฉลี่ยและความถี่แบนด์วิดท์ (คล้ายกับหน้าต่าง Gaussian ที่ จำกัด ) ในขณะที่ยังคงรักษาคุณสมบัติโดเมนเวลาที่มีประโยชน์: nonnegative, unimodal กับจุดสูงสุดที่ "time zero" ซึ่งการวิเคราะห์และสังเคราะห์ windows เป็นภาพสะท้อนของกันและกันฟังก์ชันและความต่อเนื่องของอนุพันธ์อันดับแรกศูนย์ - ค่าเฉลี่ยเมื่อฟังก์ชันสี่เหลี่ยมของหน้าต่างถูกตีความว่าเป็นฟังก์ชันความหนาแน่นของความน่าจะเป็นแบบผิดปกติ หน้าต่างที่ได้รับการเพิ่มประสิทธิภาพการใช้วิวัฒนาการค่า

หน้าต่างอสมมาตรและโคไซน์
รูปที่ 2 ด้านซ้าย: หน้าต่างการวิเคราะห์แบบอสมมาตรเหมาะสำหรับการวิเคราะห์การประมวลผลและการสังเคราะห์ซ้ำที่ซ้อนทับกันพร้อมกับหน้าต่างการสังเคราะห์คู่ที่กลับด้านเวลา ขวา: หน้าต่างโคไซน์โดยมีเวลาแฝงเท่ากับหน้าต่างอสมมาตร

การแปลงฟูริเยร์ของหน้าต่าง
รูปที่ 3 ขนาดของการแปลงฟูริเยร์ของหน้าต่างโคไซน์ (สีฟ้า) และหน้าต่างอสมมาตร (สีส้ม) ของรูปที่ 2. หน้าต่างอสมมาตรแสดงการเลือกความถี่ที่ดีขึ้น

นี่คือซอร์สโค้ดของ Octave สำหรับพล็อตและสำหรับหน้าต่างอสมมาตร รหัสพล็อตมาจากวิกิพีเดีย บน Linux ผมขอแนะนำให้ติดตั้งgnuplot, epstool, pstoedit, transfigครั้งแรกและสำหรับการดูการใช้librsvg2-bindisplay

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

คุณอาจต้องการใช้ตัวอย่างหน้าต่างทุกวินาทีเท่านั้นเนื่องจากหน้าต่างจะเริ่มต้นและสิ้นสุดที่ศูนย์ รหัส C ++ ต่อไปนี้เป็นเช่นนั้นสำหรับคุณดังนั้นคุณจะไม่ได้รับตัวอย่างใด ๆ เป็นศูนย์ยกเว้นในหนึ่งในสี่ของหน้าต่างที่เป็นศูนย์ทุก สำหรับหน้าต่างการวิเคราะห์นี่เป็นไตรมาสแรกและสำหรับหน้าต่างการสังเคราะห์นี่เป็นไตรมาสสุดท้าย ช่วงครึ่งหลังของหน้าต่างการวิเคราะห์ควรจัดชิดกับครึ่งแรกของหน้าต่างการสังเคราะห์เพื่อคำนวณผลิตภัณฑ์ รหัสยังทดสอบค่าเฉลี่ยของหน้าต่าง (เป็นฟังก์ชันความหนาแน่นของความน่าจะเป็น) และนำเสนอความเรียบของการสร้างที่ซ้อนทับกัน

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

และซอร์สโค้ดสำหรับฟังก์ชั่นต้นทุนการเพิ่มประสิทธิภาพที่จะใช้กับKiss FFTและไลบรารีการปรับให้เหมาะสม :

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

3

มันขึ้นอยู่กับบริบทของหน้าต่าง Windowing ตามที่ได้รับการพัฒนาแบบดั้งเดิมมีไว้สำหรับวิธี Blackman-Tukey ของความหนาแน่นสเปกตรัมพลังงานของการประมาณค่า นี่เป็นรูปแบบทั่วไปของวิธี correlogram โดยที่ทฤษฎีบท Wiener-Khinchin ไม่ต่อเนื่องถูกใช้ประโยชน์ จำได้ว่าสิ่งนี้เกี่ยวข้องกับลำดับความสัมพันธ์ของออโตคอร์เรชันกับความหนาแน่นสเปกตรัมพลังงานผ่านการแปลงฟูริเยร์แบบแยกเวลา

ดังนั้นหน้าต่างจึงถูกออกแบบโดยคำนึงถึงเกณฑ์หลายประการ ก่อนอื่นพวกเขาจะต้องได้รับเอกภาพจากจุดกำเนิด นี่คือเพื่อรักษาพลังงานในลำดับสัญญาณอัตโนมัติของสัมพัทธ์ขณะที่ rxx [0] สามารถคิดได้ว่าเป็นพลังงานตัวอย่าง ถัดไปหน้าต่างควรเรียวจากจุดเริ่มต้น นี่คือเหตุผลหลายประการ ก่อนอื่นเพื่อให้เป็นลำดับการสัมพันธ์อัตโนมัติที่ถูกต้องล่าช้าอื่น ๆ ทั้งหมดจะต้องน้อยกว่าหรือเท่ากับต้นกำเนิด ประการที่สองสิ่งนี้ได้รับอนุญาตสำหรับการถ่วงน้ำหนักที่ต่ำกว่าของ lags ที่ต่ำกว่าซึ่งได้รับการคำนวณด้วยความมั่นใจอย่างมากโดยใช้ตัวอย่างส่วนใหญ่และการถ่วงน้ำหนักของ lags ที่สูงขึ้นเล็กน้อยหรือเป็นศูนย์ซึ่งมีความแปรปรวนที่เพิ่มขึ้น การคำนวณ ท้ายที่สุดนี้ส่งผลให้กลีบหลักกว้างขึ้นและลดความละเอียดในการประมาณ PSD

ในที่สุดมันก็เป็นที่ต้องการอย่างมากหากหน้าต่างมีคลื่นความถี่ลบ นี่เป็นเพราะด้วยวิธี Blackman-Tukey คุณสามารถคิดถึงอคติของการประเมินขั้นสุดท้ายเนื่องจากความหนาแน่นสเปกตรัมพลังงานที่แท้จริงที่เกิดจากสเปกตรัมของหน้าต่าง หากสเปกตรัมหน้าต่างนี้มีพื้นที่เป็นลบก็เป็นไปได้ที่จะมีพื้นที่ติดลบในการประมาณความหนาแน่นสเปกตรัมพลังงานของคุณ เห็นได้ชัดว่าไม่พึงประสงค์เนื่องจากมีความหมายทางกายภาพเล็กน้อยในบริบทนี้ นอกจากนี้คุณจะทราบว่าไม่มีการดำเนินการยกกำลังสองขนาดในวิธี Blackman-Tukey นี่เป็นเพราะด้วยลำดับความสัมพันธ์ที่แท้จริงและแบบอัตโนมัติคูณด้วยหน้าต่างจริงและแม้กระทั่งการแปลงฟูริเยร์ที่ไม่ต่อเนื่องก็จะเป็นจริงและสม่ำเสมอ ในทางปฏิบัติคุณจะพบองค์ประกอบเชิงลบที่มีขนาดเล็กมากซึ่งมักจะถูกหาปริมาณ

ด้วยเหตุผลเหล่านี้ windows จึงมีความยาวเป็นเลขคี่เนื่องจากลำดับการหาค่าสัมพันธ์ที่ถูกต้องทั้งหมดนั้นมีเช่นกัน ทีนี้สิ่งที่ยังคงสามารถทำได้ (และเสร็จสิ้น) ก็คือการทำวินโดว์ในบริบทของวิธีการของ periodogram นั่นคือหน้าต่างข้อมูลแล้วนำขนาดกำลังสองของข้อมูลหน้าต่าง สิ่งนี้ไม่เทียบเท่ากับวิธี Blackman-Tukey คุณสามารถค้นหาได้โดยผ่านการอนุมานทางสถิติว่าพวกมันมีพฤติกรรมคล้ายกันโดยเฉลี่ยแต่ไม่ใช่โดยทั่วไป ตัวอย่างเช่นเป็นเรื่องปกติที่จะใช้หน้าต่างสำหรับแต่ละเซ็กเมนต์ในวิธีของ Welch หรือ Bartlett เพื่อลดความแปรปรวนของการประมาณ ดังนั้นในสาระสำคัญด้วยวิธีการเหล่านี้แรงจูงใจอยู่ในส่วนเดียวกัน แต่แตกต่างกัน กำลังงานได้รับการปรับให้เป็นมาตรฐานในวิธีการเหล่านี้โดยการแบ่งพลังงานหน้าต่างออกมาแทนการใช้น้ำหนักอย่างระมัดระวังของหน้าต่างล่าช้า

หวังว่านี่จะเป็นบริบทของหน้าต่างและต้นกำเนิดของมันและทำไมมันถึงมีความสมมาตร หากคุณอยากรู้ว่าทำไมคนเราถึงเลือกหน้าต่างแบบอสมมาตรได้ให้ลองพิจารณาผลกระทบของคุณสมบัติความเป็นคู่ของการแปลงฟูริเยร์และสิ่งที่ทำให้การประมาณค่าความหนาแน่นสเปกตรัมพลังงานของคุณมีความหมายสำหรับการใช้งานของคุณ ไชโย


1

จุดเดิมของหน้าต่างคือการทำให้แน่ใจว่าสัญญาณ (สันนิษฐานว่าเป็นระยะโดย DFT) ไม่มีจุดเปลี่ยนที่คมชัดเมื่อเทียบกับจุดสิ้นสุด ค่าใช้จ่ายคือความถี่ไปทางศูนย์กลางของหน้าต่าง (สมมาตร) จะมีน้ำหนักมากกว่าและแสดงใน DFT ที่ตามมา

ด้วยทั้งหมดที่อยู่ในพื้นหลังฉันสามารถจินตนาการได้ว่าใครคนหนึ่งต้องการใช้หน้าต่างแบบอสมมาตรเพื่อเน้นคุณลักษณะของเวลาท้องถิ่นในสัญญาณที่กำลังวิเคราะห์ผ่านทาง DFT อย่างไรก็ตามสิ่งนี้อาจมาพร้อมกับความกว้างของกลีบที่กว้างขึ้นในช่วง DFT ถ้าจุดสิ้นสุดของสัญญาณของคุณไม่กว้างเท่ากับแอมพลิจูดหลังการเรียงหน้าต่าง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.