2014-10-31 99 views
3

我想要從任何Android設備獲取超聲波,例如頻率在18KHz和19KHz之間的超聲波。從Android獲取超聲波使用頻率

我使用下面的代碼來計算頻率,但它似乎並沒有得到我正確的頻率。我得到的頻率停留在11 KHz和13KHz之間。

private void  calculateFrequency() 
{ 
    // 1 - Initialize audio 
    int channel_config = AudioFormat.CHANNEL_CONFIGURATION_MONO; 
    int format = AudioFormat.ENCODING_PCM_16BIT; 
    int sampleRate = 8000; 
    int bufferSize = 2048; 

    if (bufferSize < AudioRecord.getMinBufferSize(sampleRate, channel_config, format)) 
     bufferSize = AudioRecord.getMinBufferSize(sampleRate, channel_config, format); 
    AudioRecord audioInput = new AudioRecord(AudioSource.MIC, sampleRate, channel_config, format, bufferSize); 

    // 2 - Get sound 
    byte[] audioBuffer = new byte[bufferSize]; 
    audioInput.startRecording(); 
    int nbRead = audioInput.read(audioBuffer, 0, bufferSize); 
    audioInput.stop(); 
    audioInput.release(); 

    // 3 - Transform to double array 
    double[] micBufferData = new double[bufferSize]; 
    final int bytesPerSample = 2; // As it is 16bit PCM 
    final double amplification = 100.0; // choose a number as you like 
    for (int index = 0, floatIndex = 0; index < nbRead - bytesPerSample + 1; index += bytesPerSample, floatIndex++) { 
     double sample = 0; 
     for (int b = 0; b < bytesPerSample; b++) { 
      int v = audioBuffer[index + b]; 
      if (b < bytesPerSample - 1 || bytesPerSample == 1) { 
       v &= 0xFF; 
      } 
      sample += v << (b * 8); 
     } 
     double sample32 = amplification * (sample/32768.0); 
     micBufferData[floatIndex] = sample32; 
    } 

    // 4 - Create complex array 
    Complex[] fftTempArray = new Complex[bufferSize]; 
    for (int i=0; i<bufferSize; i++) 
    { 
     fftTempArray[i] = new Complex(micBufferData[i], 0); 
    } 

    // 5 - Calculate FFT 
    Complex[] fftArray = FFT.fft(fftTempArray); 

    // 6 - Calculate magnitude 
    double[] magnitude = new double[bufferSize/2]; 
    for (int i = 0; i < (bufferSize/2); i++) 
    { 
     magnitude[i] = Math.sqrt(fftArray[i*2].re() * fftArray[i*2].re() + fftArray[i*2].im() * fftArray[i*2].im()); 
    } 

    // 7 - Get maximum magnitude 
    double max_magnitude = -1; 
    for (int i = 0; i < bufferSize/2; i++) 
    { 
     if (magnitude[i] > max_magnitude) 
     { 
      max_magnitude = magnitude[i]; 
     } 
    } 

    // 8 - Calculate frequency 
    int freq = (int)(max_magnitude * sampleRate/bufferSize); 

    ((TextView) findViewById(R.id.textView1)).setText("FREQUENCY = " + freq + "Hz"); 
} 

我使用兩部手機:一個發送超聲波與this app,另一個得到這個超聲波。 我用這個question作爲起始點,我接受了FFT複雜類。

我的代碼有什麼問題?

+0

不錯的主意。你能否爲我解釋一下:'int freq =(int)(max_magnitude * sampleRate/bufferSize);'。 – FelixMarcus 2014-10-31 15:59:12

+0

我用這個答案[鏈接](http://stackoverflow.com/a/7675171/1770833):請參閱代碼的最後一行 – Drakkin 2014-10-31 16:02:35

+0

錯誤在步驟7和8 - 您需要最大量級的*索引*以確定頻率,而不是幅度本身。更仔細地看鏈接答案中的僞代碼。 – 2014-11-01 08:37:54

回答

4

爲了得到正確的無鋸齒頻率估計值,必須使用比音頻輸入中的最高頻率高兩倍的採樣率(可能大10%至20%以避免濾波器滾降),因此是您想要找到的最高頻率的兩倍以上。

這是由於採樣定理所需的奈奎斯特率。

所以,如果你想找到一個19千赫的信號,就需要一個接近48000的採樣率。

3

步驟7和8是不完全正確 - 你需要爲了與最大的幅度爲使用FFT倉的指數確定頻率:

// 7 - Get maximum magnitude 
    double max_magnitude = -1; 
    int max_magnitude_index = -1; 
    for (int i = 0; i < bufferSize/2; i++) 
    { 
     if (magnitude[i] > max_magnitude) 
     { 
      max_magnitude = magnitude[i]; 
      max_magnitude_index = i; 
     } 
    } 

    // 8 - Calculate frequency 
    int freq = (int)(max_magnitude_imdex * sampleRate/bufferSize); 

並經@ hotpaw2,指出您的採樣率在8 kHz時太低 - 至少需要44.1 kHz,最好是48 kHz。

+0

謝謝你的回答。我將採樣率設置爲48kHz,並按照您的說法改變步驟7和8。當我在沒有發出聲音的情況下嘗試時,頻率聽起來不錯,40 - 200 Hz。但是當我從第二部手機產生19kHz的超聲波時,我沒有得到好的數值,有時我得到23kHz。你知道一部手機是否真的可以產生超聲嗎?你認爲從我的計算機揚聲器中產生超聲會更好嗎? – Drakkin 2014-11-03 08:33:27

+1

手機,電腦等中的大多數消費級傳感器(麥克風和揚聲器)在高於〜15 kHz的頻率下都很困難,因爲它們並非真正用於「高保真」應用。你可能會發現很難在這個頻率範圍內做任何可靠的事情。 – 2014-11-03 08:35:42

+0

@PaulR嘿哥們,你知道爲什麼有時候max_magnitude_index會返回很大的價值嗎?頻率然後不正確,就像我有500赫茲,應用程序有時計算它爲45000/49000和其他大數字? – Peter 2016-08-22 12:08:59