Agrupación de FFT en bandas de un tercio de octava

Estoy tratando de entender cómo tomar los resultados de un cálculo FFT y agruparlos en bandas de un tercio de octava . Como mis preguntas no son sobre el código, sino sobre la comprensión fundamental de los argumentos y los resultados de FFT, asumo que esta pregunta encaja mejor en la electrónica que en stackoverflow.

Comienzo calculando el punto central y el rango inferior/superior de cada banda de un tercio de octava en Python:

freqs = [10**(0.1 * i) for i in range(12, 43)] # center frequency for 31 bands
fd = 10 ** 0.05 # 1.12201845
freqs_l = [freq / fd for freq in freqs] # lower
freqs_u = [freq * fd for freq in freqs] # upper

Los valores mínimo ( freqs_l[0] == 14.125375446227551) y máximo ( freqs_u[-1] == 17782.794100389234) aquí me dan el rango general de señales que me interesan. Luego leo muestras de mi archivo WAV de 16 bits y 48 kHz usando houndy ejecuto rustfftla FFT directa en un segundo (48000 muestras) de datos:

const SAMPLE_RATE : usize = 48_000;
const NUM_BANDS : usize = 31; // this is the len(freqs) of the Python freqs above

let mut planner = FFTplanner::new(false);
let fft = planner.plan_fft(SAMPLE_RATE);

let reader = hound::WavReader::open(filename).unwrap();

let mut signal = reader
    .samples::<i16>()
    .take(SAMPLE_RATE) // just take one second of data for now
    .map(|samp| num::complex::Complex::new(samp.unwrap() as f32, 0f32))
    .collect::<Vec<_>>();

assert_eq!(signal.len(), SAMPLE_RATE);

// Convert time-domain signal to frequency-domain spectrum
let mut spectrum = signal.clone();
self.fft.process(&mut signal[..], &mut spectrum);

Mi pregunta principal aquí es sobre la granularidad de la frecuencia spectrumy cómo puedo agrupar estos valores en bandas de un tercio de octava. Ambos signaly spectrumson de tamaño 48000, pero ¿cubre esto el rango completo de frecuencia de Nyquist (0 - 24000 Hz), haciendo que cada valor tenga un ancho de 0,5 Hz? Para cada entrada, establecí un valor imaginario de 0 (porque todos los ejemplos que he visto hacen esto), pero no tengo idea si esto afecta la salida de alguna manera importante.

Al tratar de encontrar información sobre esto, leí esta pregunta SO sobre cómo encontrar el valor promedio de FFT de cada banda de frecuencia y vi un código que apuntaba a numpy.fft.fftfreqque genera frecuencias FFT para la longitud de la muestra y el espaciado de la muestra. De lo primero, parece que calcularía:

calcular d.

Si conecto esto en NumPy, numpy.fft.fftfreq(n=48000, d=0.14129655308810293)obtengo 48000 valores, la primera mitad no es negativa (porque es simétrica); el valor máximo aquí es 3.538; Esperaba que estuviera más cerca de la frecuencia de Nyquist.

Estoy seguro de que estoy lejos de aquí. ¿Cómo debo entender y ajustar las frecuencias de salida de FFT?

No, el "espaciado de muestra" es solo una frecuencia de muestreo inversa. Ha dicho fftfreqque está tomando una longitud de 48000 FFT en una señal que se muestrea a aproximadamente 7,077 Hz, y le ha dicho que los contenedores se ejecutarán de 1,4 milihercios a 3,538 hercios, pero eso es basura dentro, basura fuera.
Prueba de cordura. Si hay alguna duda sobre la interpretación de la teoría, o sobre el software, FFT una frecuencia de entrada conocida y ver qué contenedores contienen energía.
Esto no aborda su pregunta principal, pero es posible que desee ver la función numpy, rfftque es un poco más eficiente para señales de dominio de tiempo de valor real (no requiere que guarde las palmaditas imaginarias de un montón de números complejos cuyas partes reales son todas cero, y solo genera la parte positiva del espectro porque se sabe que la parte negativa es solo el conjugado c de la parte positiva)

Respuestas (2)

Para una FFT, su número de puntos de datos debe ser una potencia de 2. No necesita capturar durante un segundo completo, de hecho, su velocidad de actualización será muy lenta si captura durante 1 segundo. Si se trata de una visualización en tiempo real, los valores siguientes dan una velocidad de actualización de 3 Hz, todavía muy lenta.

Necesitas hacer algunos compromisos. Si este es un analizador de espectro divertido, no es tan crítico como si estuvieras haciendo un instrumento científico. Las bandas más bajas tendrán algún error de cuantización. Si está mostrando en decibelios, esto no será tanto como podría pensar.

Mi divertido analizador de espectro solo realiza una FFT de 512 puntos, tuve que hacer muchos compromisos.

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

"Para una FFT, su número de puntos de datos debe ser una potencia de 2". Otros factores primos además de dos también funcionan, por lo que no son realmente necesarios. Ver: dsp.stackexchange.com/questions/10043/…

:-) para comenzar desde el final, en su fórmula ha calculado la cantidad de décadas (órdenes decimales) necesarias para abarcar... no su espectro de audio deseado, sino el ancho de banda teórico de la grabación digital, lo que significa que usted Desperdicié algunos de los contenedores en frecuencias subsónicas que ningún equipo de audio puede reproducir de todos modos ... y lo dividió por el número deseado de "contenedores de escala de frecuencia logarítmica en su pantalla". Entonces, si sabe que desea mostrar 31 contenedores de frecuencia de registro, los calcularía a partir de 20 kHz / 20 Hz = inserte aproximadamente 1000 en lugar de 24000 en log10 (). Eso es claramente 3 décadas, /30 es aproximadamente 10 contenedores por década. Tenga en cuenta que 1000x también tiene alrededor de 10 octavas (2^10 = 1024), por lo que 30 es una cantidad bastante buena de log.bins, si apunta a 3 bins por octava.

Ahora... la FFT toma de manera óptima 2^n muestras en el dominio del tiempo y produce 2^n líneas de frecuencia (contenedores). Si tiene una entrada solo real (sin componente imaginario = no complejo), creo que la salida solo contendrá datos significativos en la mitad inferior de los contenedores (que seguirán siendo números complejos), y los "contenedores por encima de Nyquist" deberían ser óptimos contienen ceros, como prueba de que ha filtrado su entrada analógica lo suficiente, lo que probablemente no podrá lograr al 100% (es decir, por debajo del nivel de ruido de cuantificación de su ADC, para mantenerse completamente libre de imágenes especulares).

Los contenedores de salida complejos significan "amplitud y cambio de fase". Antes de intentar tomar un promedio, le sugiero que primero calcule un valor absoluto de cada contenedor de salida complejo, para obtener las amplitudes (e ignore la información de fase). Después de eso, tenga en cuenta que la FFT produce contenedores de salida espaciados uniformemente (2^n de ellos), y desea una escala logarítmica para la visualización. Por lo tanto, cada uno de sus contenedores de visualización de registro abarcará una cantidad diferente de contenedores de salida de FFT sin procesar. Lo dejaré en sus manos si su promedio contendrá algún tipo de ponderación logarítmica, tal vez incluso incluyendo fracciones de "contenedores en el borde" (como los bordes no encajarán en los límites de recuento de enteros de sus bandejas de salida FFT sin procesar).

Mientras juegas con las matemáticas, no tengas miedo de ser creativo, pero trata de entender lo que estás haciendo :-) Te deseo buena suerte.

Leí tu respuesta varias veces y jugué con el código; parece que necesito obtener mis contenedores de frecuencia como bins = numpy.fft.fftfreq(1000, 1/48000). Esto me da 1000 contenedores con 48 Hz entre cada uno. Usando la primera mitad (que no es negativa), este es un rango de 24000. El último cubre bandas de ~76,2 × 48 Hz de (14125, 17782), por lo que necesito agregar los valores de salida de 76 FFT para cubrir ese contenedor. Dado que solo necesito frecuencias de 1,58 Hz a 15,8 kHz, no necesito todos los valores de salida de 48000 FFT, y esta primera mitad es todo lo que puedo usar de todos modos si estoy usando los primeros 500 contenedores de frecuencia fft. ¿Está bien?
@user655321: No volví a hacer todos los cálculos, pero parece correcto, sí :-) Mattman944 ​​ha subido una hoja de cálculo ordenada en su respuesta.