2011-07-29 32 views
1

我想採用一個大小爲70-80k的字節數組,並將它們從時域轉換到頻域(可能使用DFT)。到目前爲止,我一直在關注wiki並獲得此代碼。如何將一組離散的數據傳輸到頻域並返回(最好是無損地)

for (int k = 0; k < windows.length; k++) { 
     double imag = 0.0; 
     double real = 0.0; 
     for (int n = 0; n < data.length; n++) { 
      double val = (data[n]) 
        * Math.exp(-2.0 * Math.PI * n * k/data.length) 
        /128; 
      imag += Math.cos(val); 
      real += Math.sin(val); 
     } 
     windows[k] = Math.sqrt(imag * imag + real 
       * real); 
} 

並據我所知,它發現每個頻率窗口/ bin的幅度。然後,我穿過窗戶,找到最高級別的窗戶。我添加一個標誌到該頻率重建信號時使用。我檢查重建的信號是否與我的原始數據集匹配。如果沒有找到下一個最高頻率窗口並標記重建信號時要使用的頻率窗口。

這裏是我有用於重建其我大多一定的信號中的代碼是非常錯誤的(它應該執行IDFT):

for (int n = 0; n < data.length; n++) { 
     double imag = 0.0; 
     double real = 0.0; 
     sinValue[n] = 0; 
     for (int k = 0; k < freqUsed.length; k++) { 
      if (freqUsed[k]) { 
       double val = (windows[k] * Math.exp(2.0 * Math.PI * n 
         * k/data.length)); 
       imag += Math.cos(val); 
       real += Math.sin(val); 
      } 
     } 
     sinValue[n] = imag* imag + real * real; 
     sinValue[n] /= data.length; 
     newData[n] = (byte) (127 * sinValue[n]); 
} 

freqUsed用來標誌是否一個布爾陣列重建信號時不應使用頻率窗口。

無論如何,這裏是出現的問題:

  1. 即使所有頻率窗口的使用,信號不會重建。這可能是由於以下事實...
  2. 有時Math.exp()的值太高,因此返回無窮大。這使得難以精確計算。
  3. 雖然我一直在關注wiki作爲指導,但很難判斷我的數據是否有意義。這使得很難測試和識別問題。

副手從問題:

我是相當新的這一點,並沒有完全明白了一切。因此,任何幫助或見解是值得讚賞的。感謝您花時間閱讀所有內容,並提前感謝您提供的任何幫助。任何幫助真的會很好,即使你認爲我正在做這個最糟糕的可能的方式,我想知道。再次感謝。

-

編輯:

所以我更新了我的代碼看起來像:

for (int k = 0; k < windows.length; k++) { 
     double imag = 0.0; 
     double real = 0.0; 
     for (int n = 0; n < data.length; n++) { 
      double val = (-2.0 * Math.PI * n * k/data.length); 
      imag += data[n]*-Math.sin(val); 
      real += data[n]*Math.cos(val); 
     } 
     windows[k] = Math.sqrt(imag * imag + real 
       * real); 
} 

原始變換和:

for (int n = 0; n < data.length; n++) { 
    double imag = 0.0; 
    double real = 0.0; 
    sinValue[n] = 0; 
    for (int k = 0; k < freqUsed.length; k++) { 
     if (freqUsed[k]) { 
      double val = (2.0 * Math.PI * n 
        * k/data.length); 
      imag += windows[k]*-Math.sin(val); 
      real += windows[k]*Math.cos(val); 
     } 
    } 
    sinValue[n] = Math.sqrt(imag* imag + real * real); 
    sinValue[n] /= data.length; 
    newData[n] = (byte) (Math.floor(sinValue[n])); 
} 

的逆變換。雖然我仍然擔心它不能正常工作。我生成一個數組,持有一個單一的正弦波,它甚至不能分解/重建。任何有關我失蹤的信息?

+0

如果有任何更多信息可以幫助解決問題,請告訴我,我很樂意發佈。 – Matt

+0

我改變了我的代碼,以便它不再將值轉換成域[-1,1]並將它們留下[-128,127] ...它仍然不起作用。 – Matt

+0

如果你的正弦波相對於其核心頻率窗口是相移的或者它的週期不是任何頻率窗口週期的乘法,那麼你的DFT不能保證重構的有效數據集合是頻域信號重構的基本問題。爲了解決這個問題,你必須有足夠多的樣本,有時甚至可以在DFT/IDFT之前轉換信號來同步你的DFT/IDFT ... – Spektre

回答

3

是的,你的代碼(對於DFT和IDFT)都被破壞了。你在混淆如何使用指數的問題。 DFT可寫爲:

 N-1 
X[k] = SUM { x[n] . exp(-j * 2 * pi * n * k/N) } 
     n=0 

其中j是sqrt(-1)。這可以表示爲:

 N-1 
X[k] = SUM { (x_real[n] * cos(2*pi*n*k/N) + x_imag[n] * sin(2*pi*n*k/N)) 
     n=0 +j.(x_imag[n] * cos(2*pi*n*k/N) - x_real[n] * sin(2*pi*n*k/N)) } 

這反過來又可以分成:

  N-1 
X_real[k] = SUM { x_real[n] * cos(2*pi*n*k/N) + x_imag[n] * sin(2*pi*n*k/N) } 
      n=0 

      N-1 
X_imag[k] = SUM { x_imag[n] * cos(2*pi*n*k/N) - x_real[n] * sin(2*pi*n*k/N) } 
      n=0 

如果輸入的數據是真實的,只是,這簡化爲:

  N-1 
X_real[k] = SUM { x[n] * cos(2*pi*n*k/N) } 
      n=0 

      N-1 
X_imag[k] = SUM { x[n] * -sin(2*pi*n*k/N) } 
      n=0 

所以在總結,您不需要expcos/sin

+0

哇...好吧...首先,謝謝...第二,你如何將x [n]分解爲x_real [n]和x_imag [n]?第三,x_real [n]應該是一個單獨的數組,而不是x_real [k]?或者不是?它似乎遞歸定義... – Matt

+1

@Matt:啊,我區分時域('x')和頻域('X')(這個大寫差異在信號處理的東西中相當普遍)。如果你的數據是真實的(比如音頻錄音或者其他),那麼'x_real [n] = x [n]'和'x_imag [n] = 0',所以x_imag的所有術語消失。 –

+0

我的數據看似是隨機的,因此我不確定我的數據是否只是真實的。我的意思是......我只有一組數據,所以我認爲我可以把它看作是真實的,但我不確定這是否有複雜性。只有一個數組意味着我的數據是全真的? – Matt

1

除了@Oli正確設置的要點之外,您還對時域和頻域之間的轉換存在根本的誤解。您的實數輸入信號在頻域中變爲複數信號。你應該而不是正在取得這個幅度和轉換回時域(如果做得對,這實際上會給你時域自相關,但這不是你想要的)。如果你想能夠重建時域信號,那麼你必須保持複數頻域信號(即分離的實部/虛部),並且做一個複數到實數的IDFT以回到時域。

E.g.你正變換應該是這個樣子:

for (int k = 0; k < windows.length; k++) { 
     double imag = 0.0; 
     double real = 0.0; 
     for (int n = 0; n < data.length; n++) { 
      double val = (-2.0 * Math.PI * n * k/data.length); 
      imag += data[n]*-Math.sin(val); 
      real += data[n]*Math.cos(val); 
     } 
     windows[k].real = real; 
     windows[k].imag = image; 
} 

其中windows被定義爲復值的數組。

+0

有道理。那我該如何重構信號呢?向前轉變肯定有幫助,但這只是問題的一半。我想我可以從@ Oli的回答中找到答案。儘管感謝您的幫助。 – Matt

+1

逆變換與正變換非常相似,不同之處在於您有複雜的輸入數據,您只需忽略輸出數據的虛部(無論如何應接近於零)並使用輸出的實部。最大的區別是你需要執行復雜的乘法。 –

+0

通過複數乘法,你的意思是@Oli在答案中的含義是正確的?[下面的評論] – Matt

相關問題