获取特定频率的持续时间

Getting duration of a particular frequency

我需要一些帮助来分析 arduino 中的音频输入。这是我的代码(主要来自现有示例):

#include <arduinoFFT.h>
#include <PDM.h>
#include <Arduino_APDS9960.h>

#define SAMPLES 256 //Must be a power of 2
#define SAMPLING_FREQUENCY 16000 //On pin communication limit is 10k. Our microphone is on board

//Buffer for storing input values
short sampleBuffer[SAMPLES];
volatile int samplesRead;

//Timer variables
long t1=0;
long t2=-1;

//Fourier arrays
double vReal[SAMPLES];
double vImag[SAMPLES];

//Call back function
void onPDMdata(void);

//Led feedback
const int ledPin = 22; //red
const int ledPin2 = 23; //green
const int ledPin3 = 24; //blue

//Creating FFT obj
arduinoFFT FFT = arduinoFFT();

void setup()
{
  Serial.begin(115200);
  while (!Serial) 
  {
    ; // wait for serial port to connect. 
  }
  
  PDM.onReceive(onPDMdata);
  PDM.setBufferSize(SAMPLES);

  // PDM: single channel (mono) sampling at 16K
  if (!PDM.begin(1, SAMPLING_FREQUENCY)) 
  {
    Serial.println("Failed to start PDM!");
    while (1);
  }

   if (!APDS.begin()) 
   {
    Serial.println("Error initializing APDS9960 sensor!");
   }
  

  //Pin setup! 
  pinMode(ledPin, OUTPUT);
  pinMode(ledPin2 , OUTPUT);
  pinMode(ledPin3, OUTPUT);
  digitalWrite(ledPin, HIGH);
  digitalWrite(ledPin2, HIGH);

  lightOne();
}

void loop() 
{
  // put your main code here, to run repeatedly:

  //Check if i'm close to something
  if (APDS.proximityAvailable()) 
  {
    // read the proximity
    // - 0   => close
    // - 255 => far
    // - -1  => error
    int proximity = APDS.readProximity();

    /*print value to the Serial Monitor
    Serial.print("Proximity ");
    Serial.print(proximity);
    Serial.print("\n");
    */
      //If something is really near i'll check for sound
      //Apparently can't see black
      if(proximity < 200)
      {
              lightThree();
              
             //If we have something to read
            if (samplesRead) 
            {
                    for (int i = 0; i < SAMPLES; i++)
                    {
                      vReal[i] = sampleBuffer[i];
                      vImag[i] = 0;
                    }
                        //Magic Fourier transformation! 
                        FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_BLACKMAN_HARRIS, FFT_FORWARD); 
                        FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
                        FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
                        
                        //Pick peak frequency
                        double peak = FFT.MajorPeak(vReal, SAMPLES, SAMPLING_FREQUENCY);
                      
                        if (peak >2500 && peak < 2800 && t1==0 )
                        {
                          Serial.println(peak);
                          t1=millis();
                          
                          //lightTwo();
                          //delay(250);
                          //lightOne();
                        }
                        if(peak < 2500 && t2<0 && t1>0)
                        {
                          t2=millis();
                          
                          Serial.print("TIME SPAN ");
                          //long n=t2-t1;
                          Serial.print(t2-t1);
                          Serial.print("\n");

                          t1=0;
                          t2=-1;
                        }
                       
                
                        //Reset sample counts.
                        samplesRead = 0;
            }
        
      }
      else
      {
        lightOne();  
      }
  }


 

}

//Call back function, invoked when data is available to be read.
void onPDMdata()
{
  int bytesAvailable = PDM.available();
  PDM.read(sampleBuffer, bytesAvailable);
  samplesRead = bytesAvailable / 2;
}

//Led functions to get feedback
void lightOne() 
{
  digitalWrite(ledPin, LOW);
  digitalWrite(ledPin2, HIGH);
  digitalWrite(ledPin3, HIGH);
}
void lightTwo() 
{
  digitalWrite(ledPin, HIGH);
  digitalWrite(ledPin2, LOW);
  digitalWrite(ledPin3, HIGH);
}

void lightThree()
{
  digitalWrite(ledPin, HIGH);
  digitalWrite(ledPin2, HIGH);
  digitalWrite(ledPin3, LOW);
}

所以基本上这个小脚本使用 arduino 来捕获和转换音频检查我是否可以感知特定频率 (2550-2800)。 频率识别工作正常,但我也想知道这种频率峰值持续多长时间。 有人可以解决我的问题吗?我对傅里叶变换有非常基本的了解(我知道我们应该如何使用它以及为什么要使用它),所以即使您不能为我提供解决方案,我也很高兴有一些文档可以帮助我安排解决方案。

短期傅立叶变换部分地从时域转换到频域 - 您的时间分辨率从 16000 下降到 16000/256,但您仍然知道哪些帧有峰值,哪些帧没有。

我不完全确定你的 SAMPLESsamplesRead - 你的 Arduino 有没有可能有 samplesRead>0 但

否则,时机就很简单了。您每毫秒获得 16 个样本,因此您传递给 FFT 的每组 SAMPLES 代表 SAMPLES/16 毫秒。 FFT 以时间分辨率换取频率分辨率,因此您只能测量具有 16 毫秒粒度的峰值长度。您只需 计算 具有峰值的 FFT 帧数即可。

其他有用的提示:您可能想检查发现的主峰是否确实比其他值高得多。 10 或 20% 只是正常变化,您可能想要一个 10 倍平均值的峰值。