import numpy as np import rtlsdr import matplotlib.pyplot as plt from scipy.signal import welch, windows, freqz # -------------------------- RTL-SDR Configuration ------------------------------------------- sdr = rtlsdr.RtlSdr() # Inicializar el dispositivo RTL-SDR sample_rate = 2.4e6 # Tasa de muestreo en muestras por segundo (3MHz) # The maximum sample rate is 3.2 MS/s (mega samples per second). # However, the RTL-SDR is unstable at this rate and may drop samples. # The maximum sample rate that does not drop samples is 2.56 MS/s. # Configurar parámetros # Rafael Micro R820T/2/R860 24 – 1766 MHz (Can be improved to ~13 - 1864 MHz with experimental drivers) # Experimentalmente capta desde 12MHz sdr.sample_rate = sample_rate # Tasa de muestreo en muestras por segundo sdr.center_freq = 99.9e6 # Frecuencia central en Hz (99.9 MHz) sdr.gain = 42 # Ganancia del receptor output_file = "fm999.bin" # Nombre del archivo de salida con las muestras capturadas block_size = 1024 * 1024 # Tamaño de cada bloque de muestras (tasa de muestreo * duracion de la muestra) samples_buffer = [] # Buffer para almacenar las muestras # ------------------------ Captura y procesamiento de muestras en bloques --------------------------------- num_blocks = 1 # Capturar 1 bloques de muestras for _ in range(num_blocks): samples = sdr.read_samples(block_size) # Capturar bloque de muestras np.save(output_file, samples, allow_pickle=False) # Guardar las muestras en el archivo binario samples_buffer.extend(samples) # Agregar las muestras al buffer # Mantener el buffer dentro del tamaño deseado if len(samples_buffer) > block_size * num_blocks: samples_buffer = samples_buffer[-block_size * num_blocks:] sdr.close() # Cerrar el dispositivo RTL-SDR signal_energy = np.sum(np.abs(samples_buffer)**2) / len(samples_buffer) # Calcular la energía de la señal threshold = 1e-8 # Umbral de energía de la señal information_present = signal_energy > threshold # Verificar si hay información en las muestras if information_present: print("Se detectó información en las muestras.") else: print("No se detectó información en las muestras.") # -------------------------------------- Procesamiento para generar el espectro de potencia con Welch --------------------------------------------------------- # Parámetros de Welch m = 256 # Tamaño de cada segmento de datos k = 128 # Superposición entre segmentos ventana = np.bartlett(m) # Ventana de Bartlett # Cálculo del espectro de potencia mediante Welch N = len(samples_buffer) S = int(np.floor((N-m) / (m-k))) + 1 # Número total de segmentos Pxx_segments = np.zeros((S, m)) # Inicializar arreglo para almacenar los espectros de potencia de cada segmento # Calcular el espectro de potencia para cada segmento for i in range(S-1): start = i* (m-k) +1 end = start + m segment = samples_buffer[start:end] * ventana Pxx_segments[i, :] = np.abs(np.fft.fft(segment))**2 / (np.sum(ventana**2) * sample_rate) PSD = np.mean(Pxx_segments, axis=0) # Promediar los espectros de potencia de todos los segmentos frecuencias_welch = np.fft.fftfreq(m, d=1/sample_rate) # Frecuencias correspondientes a los segmentos de datos # -------------------------------------- Ploteos --------------------------------------------------------- # Muestras capturadas plt.plot(samples_buffer) plt.xlabel('Muestras') plt.ylabel('Amplitud') plt.title('Muestras capturadas') plt.show() # Muestras capturadas plt.plot(np.abs(samples_buffer)) plt.xlabel('Muestras') plt.ylabel('Amplitud') plt.title('Muestras capturadas absolutas') plt.show() # Transformada de Fourier y frecuencias correspondiente de las muestras en el dominio de la frecuencia fft_result = np.fft.fft(samples_buffer) magnitud_fft = np.abs(fft_result) frecuencias = np.fft.fftfreq(len(samples_buffer), d=1/sample_rate) # Grafica de la transformada de Fourier plt.figure(figsize=(10, 6)) plt.plot(frecuencias, magnitud_fft) plt.title('Transformada de Fourier') plt.xlabel('Frecuencia (Hz)') plt.ylabel('Magnitud') plt.show() # Espectro de frecuencia frequencies, spectrum = plt.psd(samples_buffer, NFFT=1024, Fs=sample_rate, scale_by_freq=True) plt.xlabel('Frecuencia (Hz)') plt.ylabel('Densidad espectral de potencia') plt.title('Espectro de frecuencia') plt.show() # Calcular la respuesta en frecuencia w, h = freqz(np.sqrt(PSD), worN=8000) # Utilizar la raíz cuadrada para obtener la magnitud w_normalized = w / np.pi # Convertir la frecuencia a radianes por segundo normalizada por pi magnitude_db = 20 * np.log10(np.abs(h)) # Calcular la magnitud en decibelios # Graficar la respuesta en magnitud plt.figure(figsize=(10, 6)) plt.plot(w_normalized, magnitude_db) plt.title('Estimación de la Respuesta en Magnitud') plt.xlabel('Frecuencia Normalizada (xπ rad/muestras)') plt.ylabel('Magnitud (dB)') plt.grid(True) plt.show() # Calcular las frecuencias normalizadas en unidades de omega/pi frecuencias_normalizadas = frecuencias_welch / np.pi # Graficar el espectro de potencia promedio en función de omega/pi plt.figure(figsize=(10, 6)) plt.semilogy(frecuencias_normalizadas, PSD) plt.text(0.5, 0.9, "Ventaneo con Bartlett", horizontalalignment='center', verticalalignment='center', transform=plt.gca().transAxes, bbox=dict(facecolor='white', alpha=0.8)) plt.title('Espectro de Potencia usando Welch') plt.xlabel('Frecuencia Normalizada (ω/π)') plt.ylabel('Densidad espectral de potencia') plt.grid(True) plt.show()