graficar_respuesta_del_barrido_en_frecuencia_con_python
Differences
This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
| graficar_respuesta_del_barrido_en_frecuencia_con_python [2024/06/01 23:28] – created oso | graficar_respuesta_del_barrido_en_frecuencia_con_python [2024/10/17 21:42] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 90: | Line 90: | ||
| 4. **Ploteo de la respuesta en frecuencia**: | 4. **Ploteo de la respuesta en frecuencia**: | ||
| * `plot_response` grafica la magnitud de la FFT en función de la frecuencia, utilizando una escala logarítmica para el eje de las frecuencias. | * `plot_response` grafica la magnitud de la FFT en función de la frecuencia, utilizando una escala logarítmica para el eje de las frecuencias. | ||
| + | |||
| + | {{: | ||
| ===== Notas Adicionales ===== | ===== Notas Adicionales ===== | ||
| Line 99: | 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: | ||
| + | |||
| + | ===== 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, | ||
| + | t = np.linspace(0, | ||
| + | sweep = np.sin(2 * np.pi * np.logspace(np.log10(start_freq), | ||
| + | return t, sweep | ||
| + | |||
| + | def play_and_record(sweep, | ||
| + | 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, | ||
| + | | ||
| + | stream.stop_stream() | ||
| + | stream.close() | ||
| + | p.terminate() | ||
| + | | ||
| + | recorded_signal = np.hstack(recorded_frames) | ||
| + | return recorded_signal | ||
| + | |||
| + | def analyze_frequency_response(sweep, | ||
| + | t = np.linspace(0, | ||
| + | f = np.logspace(np.log10(20), | ||
| + | | ||
| + | # Calculate power of the recorded signal | ||
| + | power = recorded_signal ** 2 | ||
| + | | ||
| + | return f, power | ||
| + | |||
| + | def plot_frequency_response(frequencies, | ||
| + | plt.figure(figsize=(10, | ||
| + | plt.plot(frequencies, | ||
| + | plt.title(' | ||
| + | plt.xlabel(' | ||
| + | plt.ylabel(' | ||
| + | plt.xscale(' | ||
| + | plt.grid() | ||
| + | plt.show() | ||
| + | |||
| + | if __name__ == " | ||
| + | start_freq = 20 | ||
| + | end_freq = 20000 | ||
| + | duration = 10 # Duration in seconds | ||
| + | sample_rate = 44100 | ||
| + | | ||
| + | t, sweep = generate_sweep(start_freq, | ||
| + | recorded_signal = play_and_record(sweep, | ||
| + | frequencies, | ||
| + | plot_frequency_response(frequencies, | ||
| + | </ | ||
| + | |||
| + | ===== 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**: | ||
| + | |||
| + | * **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, | ||
| + | t = np.linspace(0, | ||
| + | sweep = np.sin(2 * np.pi * np.logspace(np.log10(start_freq), | ||
| + | return t, sweep | ||
| + | |||
| + | def play_and_record(sweep, | ||
| + | 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, | ||
| + | | ||
| + | stream.stop_stream() | ||
| + | stream.close() | ||
| + | p.terminate() | ||
| + | | ||
| + | recorded_signal = np.hstack(recorded_frames) | ||
| + | return recorded_signal | ||
| + | |||
| + | def analyze_frequency_response(sweep, | ||
| + | len_min = min(len(sweep), | ||
| + | sweep = sweep[: | ||
| + | recorded_signal = recorded_signal[: | ||
| + | | ||
| + | # Aplicar una ventana de Hann | ||
| + | window = get_window(' | ||
| + | yf = fft(recorded_signal * window) | ||
| + | xf = np.fft.fftfreq(len(recorded_signal), | ||
| + | | ||
| + | # Solo tomar la parte positiva del espectro | ||
| + | pos_freqs = xf[:len(xf) // 2] | ||
| + | power = np.abs(yf[: | ||
| + | | ||
| + | return pos_freqs, power | ||
| + | |||
| + | def moving_average(data, | ||
| + | return np.convolve(data, | ||
| + | |||
| + | def plot_frequency_response(frequencies, | ||
| + | power += 1e-10 # Agregar un valor pequeño para evitar log10(0) | ||
| + | smoothed_power = moving_average(10 * np.log10(power), | ||
| + | |||
| + | plt.figure(figsize=(10, | ||
| + | plt.plot(frequencies[: | ||
| + | plt.plot(frequencies, | ||
| + | plt.title(' | ||
| + | plt.xlabel(' | ||
| + | plt.ylabel(' | ||
| + | plt.xscale(' | ||
| + | plt.grid() | ||
| + | plt.legend() | ||
| + | plt.show() | ||
| + | |||
| + | if __name__ == " | ||
| + | start_freq = 20 | ||
| + | end_freq = 20000 | ||
| + | duration = 10 # Duración en segundos | ||
| + | sample_rate = 44100 | ||
| + | | ||
| + | t, sweep = generate_sweep(start_freq, | ||
| + | recorded_signals = [play_and_record(sweep, | ||
| + | | ||
| + | # Promediar las respuestas en frecuencia | ||
| + | power_avg = np.zeros(len(recorded_signals[0]) // 2) | ||
| + | for recorded_signal in recorded_signals: | ||
| + | frequencies, | ||
| + | power_avg += power | ||
| + | power_avg /= len(recorded_signals) | ||
| + | | ||
| + | plot_frequency_response(frequencies, | ||
| + | </ | ||
| + | |||
| + | ==== Resultado ==== | ||
| + | |||
| + | {{: | ||
| + | |||
| + | {{: | ||
graficar_respuesta_del_barrido_en_frecuencia_con_python.1717284526.txt.gz · Last modified: 2024/10/17 21:42 (external edit)
