¿Cómo obtener la frecuencia fundamental de una señal mediante autocorrelación?

4

Estoy tratando de obtener la frecuencia fundamental de una señal que solo tiene un solo tono. Codifiqué la función de autocorrelación usando FFT y ya obtuve el resultado de autocorrelación. Desafortunadamente, no sé cómo obtener la frecuencia fundamental del resultado de autocorrelación. ¿Alguien me puede ayudar? Mi código está abajo:

public double getPitch(double[] buffer, int firstSample, int lastSample, double sampleRate)
{
    int lengthOfFFTWindow=lastSample-firstSample;
    double[] input_buffer=new double[lengthOfFFTWindow];
    DoubleFFT_1D fft = new DoubleFFT_1D(lengthOfFFTWindow);
    double[] autocorrelation_values=new double[lengthOfFFTWindow];
    double[] fftData = new double[lengthOfFFTWindow * 2];
    double max=-1;
    double max_i=-1;
    //FFT on each sample in each window
    for (int i = 0; i < lengthOfFFTWindow; i++) {
        // copying audio data to the fft data buffer, imaginary part is 0
        fftData[2 * i] = buffer[i+firstSample];
        fftData[2 * i + 1] = 0;
    }
    fft.complexForward(fftData);
    for (int i = 0,j=0; i < fftData.length; i += 2,j++) {
        // complex numbers -> vectors, so we compute the length of the vector, which is sqrt(realpart^2+imaginarypart^2)
        autocorrelation_values[j] = Math.sqrt((fftData[i] * fftData[i]) + (fftData[i + 1] * fftData[i + 1]));
    }
    fft.complexInverse(fftData, false);
    for(int i=0;i<fftData.length;i++)
    {
        if(max<fftData[i])
        {
            max=fftData[i];
            max_i=i;
        }
    }
    return (max_i * 44100 )/ lengthOfFFTWindow;
}

¿Es correcto devolver el valor máximo de autocorrelación dividido por 2 como la frecuencia fundamental? Sigo recibiendo respuestas incorrectas cuando hago eso.

EDITAR: Un ejemplo del archivo de prueba de un solo paso: enlace

    
pregunta Sakura

4 respuestas

3

La autocorrellación es una forma de ayudar a encontrar la frecuencia dominante de una señal, pero no veo qué tiene que ver una FFT con eso. La autocorrellación producirá picos con el período de cualquier componente de frecuencia fuerte. Si luego toma la FFT de eso para encontrar la frecuencia de esos picos, podría también tomar la FFT de la señal original en primer lugar.

En lugar de mostrarnos el código, muéstrenos los datos en varias etapas de su proceso. Los detalles del código son su problema y están separados de los procesos conceptuales de pasar por las distintas circunvoluciones, filtros o lo que sea.

Dice que su señal solo tiene un solo tono, lo que significa que es una onda sinusoidal pura. En ese caso, realmente no veo la ventaja de un pase de autocorrellación. Puede encontrar el período directamente mirando el tiempo entre cruces por cero.

En el pasado, he tenido que encontrar la frecuencia fundamental de la mayoría de las señales repetidas con un ruido significativo en ellas. Lo que solía hacer era aplicar varias etapas de filtrado de paso bajo. Una gran ventaja del filtrado digital es que las señales más pequeñas que salen de un filtro no significan menos relación señal / ruido, siempre y cuando se sigan agregando los bits necesarios en el extremo inferior. El uso de punto flotante, por ejemplo, lo hace automáticamente. Luego, puede pasar agresivamente un filtro de paso bajo de una señal tal que sería solo µV en analógico, pero aún le quedarán los mismos bits significativos al final.

Cada paso de LPF atenúa los armónicos en relación con el fundamental. Después de suficientes pases, te quedas con la mayoría fundamental. Una vez que haya atenuado los armónicos lo suficiente como para garantizar solo dos cruces por ciclo por cero, observe el período de cruce por cero, tal vez aplique un poco de filtro de paso bajo a los sucesivos e infiera la frecuencia a partir de ahí.

Añadido:

Ahora que ha proporcionado algunos datos, podemos ver lo que realmente está sucediendo:

Parecequetienesaproximadamenteunaseñalde440Hz,peroestoestáclaramentelejosdeun"tono único" ya que la forma está lejos de ser un seno. Solo por inspección podemos ver que el segundo armónico es particularmente fuerte. Puede ser tan fuerte que se perciba que esta "nota" es de 880 Hz en lugar de la fundamental de 440 Hz.

En este caso, ¿cuál es la respuesta que quieres que sea, 440 Hz o 880 Hz? Con suficiente filtrado de paso bajo, al final obtienes la mayoría fundamental y medir 440 Hz no debería ser tan difícil. Si desea que la respuesta sea el tono posiblemente perceptivo de 880 Hz, entonces las cosas se complican mucho más. Una posibilidad sería identificar lo fundamental en todos los casos. Una vez que tienes eso, es fácil encontrar la amplitud relativa de los primeros armónicos. Luego, puede decidir en función de la fuerza de esos armónicos si desea informar uno de ellos o el fundamental.

    
respondido por el Olin Lathrop
2

Veo un par de cosas que podrían ser problemas en tu código.

Primero, para usar la FFT para calcular una autocorrelación, hay tres pasos:

  1. transforme la señal de entrada, \ $ F_R (f) = \ mathrm {FFT} (x [t]) \ $
  2. calcular el espectro de potencia, \ $ S (f) = F_R (f) F_R ^ * (f) \ $
  3. transformación inversa para obtener la autocorrelación, \ $ R [\ tau] = \ mathrm {IFFT} (S (f)) \ $

Te veo haciendo el paso 1 y el paso 2, pero luego haces algo completamente diferente en el paso 3.

Tenga en cuenta que si tomó la autocorrelación, los picos indicarían el período de su señal, no la frecuencia.

Lo que su código parece estar intentando hacer es tomar la FFT, luego buscar en los contenedores el pico más alto y tomarlo como la frecuencia de la señal. También calcula el IFFT, pero luego tira el resultado de ese cálculo.

Se puede usar una búsqueda de picos en el espectro de potencia para estimar la frecuencia de la señal, pero ahí tienes otro error más básico:

for(int i=0;i<autocorrelation_values.length;i++)
{
    if(max<autocorrelation_values[i])
    {
        max=autocorrelation_values[i];
        max_i=i;
    }
}
return max/2;

Su max está almacenando la amplitud máxima de los valores del espectro de potencia (que denominó confusamente autocorrelation_values ). max_i está almacenando el número de bin donde se encontró la amplitud máxima. Debería devolver algo basado en max_i en lugar de max . Debe utilizar la frecuencia de muestreo y la cantidad de muestras utilizadas en la FFT para escalar el número de contenedor en una frecuencia real.

Editar También recomendaría buscar solo en la primera mitad del espectro de potencia en busca de picos.

for(int i=0; i < 0.5 * autocorrelation_values.length; i++)
   ...

Los intervalos más altos corresponden a un alias del espectro en las frecuencias negativas, por lo que no contienen ninguna información nueva.

    
respondido por el The Photon
0

¿Por qué no lo hace de la manera antigua, probada y probada, utilizando una versión de la señal con retraso de tiempo? Multiplique la señal por versión demorada de sí mismo y almacene el resultado. Para una ventana determinada en el tiempo que contiene (por ejemplo) 1000 muestras, deberá almacenar el mayor resultado de las multiplicaciones individuales.

Luego vuelva a realizar la prueba con un rango de diferentes retrasos. El resultado que es numéricamente mayor corresponde con el período de la señal. Tenga en cuenta que una vez que el retraso haya encontrado el "período" de la señal, también encontrará un período de "imagen" al doble del retraso (porque los picos de la señal coinciden una vez más).

Alternativamente, use un detector de cruce por cero y mida el tiempo. Usted dijo que la señal tiene solo un solo tono, por lo que debería estar perfectamente bien - tenga cuidado con el ruido de cruce cero promediando unos pocos ciclos - cuanto más mejor.

    
respondido por el Andy aka
0

Quiero encontrar la frecuencia fundamental con el método de autocorrelación. Leí su código pero creo que la última parte del código podría ser así:

int max=0; 

for (int i=0; i < autocorrelation_value.length; i++) {
  if (autocorrelation_value[i]>autocorrelation_value[max]) {
    max = i;
  }
  return 1/autocorrelation_value[max]
}

porque el máximo de autocorrelación es el período fundamental y, por lo tanto, en la "devolución" calculo el inverso del máximo de autocorrelación.

Y después no entiendo qué significa "max_i".

    
respondido por el user3582433

Lea otras preguntas en las etiquetas