graficar_respuesta_del_barrido_en_frecuencia_con_python

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
graficar_respuesta_del_barrido_en_frecuencia_con_python [2024/06/01 23:29] – [Explicación del Código] osograficar_respuesta_del_barrido_en_frecuencia_con_python [2024/10/17 21:42] (current) – external edit 127.0.0.1
Line 101: Line 101:
 Prueba este script y ajusta los parámetros según tus necesidades. ¡Buena suerte con tu análisis de la respuesta en frecuencia de tu dispositivo de sonido! Prueba este script y ajusta los parámetros según tus necesidades. ¡Buena suerte con tu análisis de la respuesta en frecuencia de tu dispositivo de sonido!
  
 +
 +====== Análisis de Respuesta en Frecuencia usando Barrido de Frecuencias ======
 +
 +
 +Tomar la FFT de una señal de barrido de frecuencias puede no ser la mejor manera de analizar la respuesta en frecuencia de un sistema.
 +
 +**Opción 1:** Tren de Deltas
 +
 +Un tren de deltas (o una señal de impulsos) es una buena forma de obtener la respuesta al impulso del sistema, y la FFT de esta respuesta te dará directamente la respuesta en frecuencia. Este método es efectivo, pero requiere que el sistema pueda responder a impulsos rápidos, lo que puede no ser ideal para todos los dispositivos de audio.
 +
 +**Opción 2:** Barrido de Frecuencias con Análisis de Potencia
 +
 +El método de barrido de frecuencias con análisis de la potencia de la señal recibida es más directo y puede ser más adecuado para sistemas de audio. Al saber la frecuencia de cada instante de tiempo, puedes medir directamente la respuesta en frecuencia sin necesidad de transformar la señal.
 +
 +Vamos a implementar la Opción 2: Barrido de Frecuencias con Análisis de Potencia.
 +
 +====== Implementación del Barrido de Frecuencias con Análisis de Potencia ======
 +
 +**Objetivo:** Este artículo detalla cómo medir y analizar la respuesta en frecuencia de un dispositivo de sonido utilizando Python. Usaremos un barrido de frecuencias y analizaremos la potencia de la señal recibida para obtener la respuesta en frecuencia.
 +
 +===== Script para Barrido de Frecuencias y Análisis de Potencia =====
 +<code python: main.py>
 +import numpy as np
 +import pyaudio
 +import matplotlib.pyplot as plt
 +
 +def generate_sweep(start_freq, end_freq, duration, sample_rate=44100):
 +    t = np.linspace(0, duration, int(sample_rate * duration), False)
 +    sweep = np.sin(2 * np.pi * np.logspace(np.log10(start_freq), np.log10(end_freq), t.size))
 +    return t, sweep
 +
 +def play_and_record(sweep, duration, sample_rate=44100, channels=1):
 +    p = pyaudio.PyAudio()
 +    
 +    stream = p.open(format=pyaudio.paFloat32,
 +                    channels=channels,
 +                    rate=sample_rate,
 +                    output=True,
 +                    input=True,
 +                    frames_per_buffer=1024)
 +    
 +    recorded_frames = []
 +    
 +    stream.start_stream()
 +    stream.write(sweep.astype(np.float32).tobytes())
 +    
 +    for _ in range(0, int(sample_rate / 1024 * duration)):
 +        data = stream.read(1024)
 +        recorded_frames.append(np.frombuffer(data, dtype=np.float32))
 +    
 +    stream.stop_stream()
 +    stream.close()
 +    p.terminate()
 +    
 +    recorded_signal = np.hstack(recorded_frames)
 +    return recorded_signal
 +
 +def analyze_frequency_response(sweep, recorded_signal, sample_rate=44100):
 +    t = np.linspace(0, len(sweep) / sample_rate, num=len(sweep))
 +    f = np.logspace(np.log10(20), np.log10(20000), len(sweep))
 +    
 +    # Calculate power of the recorded signal
 +    power = recorded_signal ** 2
 +    
 +    return f, power
 +
 +def plot_frequency_response(frequencies, power):
 +    plt.figure(figsize=(10, 6))
 +    plt.plot(frequencies, 10 * np.log10(power))
 +    plt.title('Frequency Response')
 +    plt.xlabel('Frequency (Hz)')
 +    plt.ylabel('Power (dB)')
 +    plt.xscale('log')
 +    plt.grid()
 +    plt.show()
 +
 +if __name__ == "__main__":
 +    start_freq = 20
 +    end_freq = 20000
 +    duration = 10  # Duration in seconds
 +    sample_rate = 44100
 +    
 +    t, sweep = generate_sweep(start_freq, end_freq, duration, sample_rate)
 +    recorded_signal = play_and_record(sweep, duration, sample_rate)
 +    frequencies, power = analyze_frequency_response(sweep, recorded_signal, sample_rate)
 +    plot_frequency_response(frequencies, power)
 +</code>
 +
 +===== Explicación del Código =====
 +
 +1. **Generación del barrido de frecuencias**:
 +   - `generate_sweep` crea un barrido logarítmico de frecuencias de `start_freq` a `end_freq` durante `duration` segundos.
 +   
 +2. **Reproducción y grabación**:
 +   - `play_and_record` reproduce el barrido de frecuencias y simultáneamente graba la señal de audio del micrófono.
 +   - La señal grabada se almacena en `recorded_frames` y se convierte en un array de `numpy`.
 +   
 +3. **Análisis de la respuesta en frecuencia**:
 +   - `analyze_frequency_response` calcula la potencia de la señal grabada para cada frecuencia del barrido. Este método asume que la frecuencia cambia logarítmicamente con el tiempo durante el barrido.
 +
 +4. **Ploteo de la respuesta en frecuencia**:
 +   - `plot_frequency_response` grafica la potencia en función de la frecuencia, utilizando una escala logarítmica para el eje de las frecuencias.
 +
 +===== Notas Adicionales =====
 +
 +  * **Compatibilidad de hardware**: Este script asume que el micrófono y los altavoces están bien calibrados y pueden manejar el rango de frecuencias de 20 Hz a 20 kHz. En la práctica, algunos dispositivos de audio pueden no tener una respuesta plana en todo este rango.
 +
 +  * **Latencia y sincronización**: La latencia de audio y posibles desincronizaciones pueden afectar la precisión del análisis. Este script es una aproximación simple y puede necesitar ajustes para aplicaciones más precisas.
 +
 +  * **Seguridad Auditiva**: Asegúrate de mantener el volumen a un nivel seguro para evitar daños auditivos, especialmente cuando trabajes con frecuencias altas y barridos de amplio rango.
 +
 +===== Ventana de Hann y media móvil =====
 +
 +<code python: sweep_Hanning_5x_movingavg.py>
 +import numpy as np
 +import pyaudio
 +import matplotlib.pyplot as plt
 +from scipy.signal import get_window, butter, filtfilt
 +from scipy.fft import fft
 +
 +def generate_sweep(start_freq, end_freq, duration, sample_rate=44100):
 +    t = np.linspace(0, duration, int(sample_rate * duration), False)
 +    sweep = np.sin(2 * np.pi * np.logspace(np.log10(start_freq), np.log10(end_freq), t.size))
 +    return t, sweep
 +
 +def play_and_record(sweep, duration, sample_rate=44100, channels=1):
 +    p = pyaudio.PyAudio()
 +    
 +    stream = p.open(format=pyaudio.paFloat32,
 +                    channels=channels,
 +                    rate=sample_rate,
 +                    output=True,
 +                    input=True,
 +                    frames_per_buffer=1024)
 +    
 +    recorded_frames = []
 +    
 +    stream.start_stream()
 +    stream.write(sweep.astype(np.float32).tobytes())
 +    
 +    for _ in range(0, int(sample_rate / 1024 * duration)):
 +        data = stream.read(1024)
 +        recorded_frames.append(np.frombuffer(data, dtype=np.float32))
 +    
 +    stream.stop_stream()
 +    stream.close()
 +    p.terminate()
 +    
 +    recorded_signal = np.hstack(recorded_frames)
 +    return recorded_signal
 +
 +def analyze_frequency_response(sweep, recorded_signal, sample_rate=44100):
 +    len_min = min(len(sweep), len(recorded_signal))
 +    sweep = sweep[:len_min]
 +    recorded_signal = recorded_signal[:len_min]
 +    
 +    # Aplicar una ventana de Hann
 +    window = get_window('hann', len(recorded_signal))
 +    yf = fft(recorded_signal * window)
 +    xf = np.fft.fftfreq(len(recorded_signal), 1 / sample_rate)
 +    
 +    # Solo tomar la parte positiva del espectro
 +    pos_freqs = xf[:len(xf) // 2]
 +    power = np.abs(yf[:len(yf) // 2]) ** 2
 +    
 +    return pos_freqs, power
 +
 +def moving_average(data, window_size):
 +    return np.convolve(data, np.ones(window_size)/window_size, mode='valid')
 +
 +def plot_frequency_response(frequencies, power):
 +    power += 1e-10  # Agregar un valor pequeño para evitar log10(0)
 +    smoothed_power = moving_average(10 * np.log10(power), 1000)  # Ajusta el tamaño de la ventana según sea necesario
 +
 +    plt.figure(figsize=(10, 6))
 +    plt.plot(frequencies[:len(smoothed_power)], smoothed_power, label='Smoothed Response')
 +    plt.plot(frequencies, 10 * np.log10(power), alpha=0.3, label='Original Response')
 +    plt.title('Frequency Response')
 +    plt.xlabel('Frequency (Hz)')
 +    plt.ylabel('Power (dB)')
 +    plt.xscale('log')
 +    plt.grid()
 +    plt.legend()
 +    plt.show()
 +
 +if __name__ == "__main__":
 +    start_freq = 20
 +    end_freq = 20000
 +    duration = 10  # Duración en segundos
 +    sample_rate = 44100
 +    
 +    t, sweep = generate_sweep(start_freq, end_freq, duration, sample_rate)
 +    recorded_signals = [play_and_record(sweep, duration, sample_rate) for _ in range(5)]
 +    
 +    # Promediar las respuestas en frecuencia
 +    power_avg = np.zeros(len(recorded_signals[0]) // 2)
 +    for recorded_signal in recorded_signals:
 +        frequencies, power = analyze_frequency_response(sweep, recorded_signal, sample_rate)
 +        power_avg += power
 +    power_avg /= len(recorded_signals)
 +    
 +    plot_frequency_response(frequencies, power_avg)
 +</code>
 +
 +==== Resultado ====
 +
 +{{:sweep_hann_window_test01.png?nolink|}}
 +
 +{{:figure_1_probing.png?nolink|}}
graficar_respuesta_del_barrido_en_frecuencia_con_python.1717284595.txt.gz · Last modified: 2024/10/17 21:42 (external edit)