Señales & SDR

Radio FM con Python + Algoritmo Genético: tu SDR que se sintoniza sola

En este tutorial vas a construir una radio FM en Python que no solo escucha el aire: usará un Algoritmo Genético para “evolucionar” y encontrar, por sí sola, la mejor configuración de …

Contenido del tutorial

¿Alguna vez giraste el dial de una radio antigua buscando esa frecuencia perfecta entre ruido y estática? Requiere paciencia y buena puntería. Ahora imagina enseñarle al computador a hacerlo por ti usando los principios de la evolución de Charles Darwin.

En este tutorial construiremos una radio FM en Python que no solo capta señales del aire, sino que utiliza un algoritmo genético para “evolucionar” hasta dar con la configuración que ofrece una recepción limpia. Vamos a mezclar hardware, procesamiento de señales y un toque de IA para crear una radio que se sintoniza sola.

Los ingredientes para esta aventura 📻

Dongle USB RTL-SDR económico: pieza clave del proyecto.
Figura 1: El RTL-SDR, nuestra puerta de entrada al mundo de las ondas de radio.

Antes de escribir una sola línea de código, asegúrate de tener lo necesario. Este proyecto combina hardware y software.

Vas a necesitar un dongle USB RTL-SDR como base de hardware y Python 3 instalado. Luego, abre tu terminal e instala las librerías con: pip install numpy scipy sounddevice pyrtlsdr.

Anatomía del código: el ADN de nuestra radio inteligente

Todo buen script arranca con una breve documentación: qué hace, de qué depende y cómo se ejecuta. Luego importamos las herramientas —nuestros “genes”— que darán vida al programa: numpy para cálculos, scipy para señales, sounddevice para audio, rtlsdr para hablar con el hardware y algunas utilidades de Python.

Encabezado e importaciones del script

import numpy as np
import random
from scipy.signal import decimate, resample_poly
import sounddevice as sd
from rtlsdr import RtlSdr
from threading import Thread
import queue
import argparse

El traductor: de ondas a sonido básico

La señal que llega del SDR está modulada en frecuencia (FM), así que no es audio listo para escuchar. La función fm_demodulate hace la traducción: toma las muestras complejas y las convierte en una señal que ya representa el audio original.

Función fm_demodulate

def fm_demodulate(samples):
    """Demodula una señal FM usando la derivada de fase."""
    return np.angle(samples[1:] * np.conj(samples[:-1]))

La voz: reproducción fluida en segundo plano

Para evitar cortes cuando el procesador está ocupado, dedicamos un hilo (Thread) solo a reproducir. La función audio_stream corre en paralelo, recibe bloques por una cola (Queue) y los envía al dispositivo de audio sin interrupciones.

Función audio_stream

def audio_stream(q, sample_rate):
    """Reproduce audio desde una cola."""
    with sd.OutputStream(channels=1, samplerate=int(sample_rate), dtype='float32') as stream:
        while True:
            data = q.get()
            if data is None: # Señal para terminar el hilo
                break
            stream.write(data)

El juez: la función de aptitud (fitness)

Esta función decide qué tan buena es cada configuración. Sintoniza con una frecuencia, ganancia y tasa de muestreo, escucha un instante y calcula una medida de “energía” de la señal. Una emisora clara tiene más energía; el ruido, menos.

💡 Analogía evolutiva: get_signal_quality es el “ambiente”. Las configuraciones con más puntuación “sobreviven”; las que rinden mal, desaparecen.

Función get_signal_quality

def get_signal_quality(sdr, frequency, gain, sample_rate):
    """Obtiene una métrica de calidad de la señal para una frecuencia, ganancia y tasa de muestreo dadas."""
    try:
        sdr.center_freq = frequency
        sdr.gain = gain
        sdr.sample_rate = sample_rate
        samples = sdr.read_samples(256 * 1024)
        demodulated = fm_demodulate(samples)
        signal_energy = np.sum(np.abs(demodulated) ** 2)
        return signal_energy
    except Exception as e:
        print(f"Error al obtener la calidad de la señal: {e}")
        return -np.inf  # Devolver un valor muy bajo si hay error

El motor evolutivo: el ciclo de la vida

Aquí es donde ocurre la magia. optimize_parameters_ga crea una población inicial al azar y, durante varias generaciones, evalúa individuos, selecciona a los mejores y genera descendencia mediante cruzamiento y mutación. Con cada ciclo, la población mejora y nos acercamos a la mejor configuración.

Función optimize_parameters_ga

Estás viendo solo el 60% del contenido. Hazte Premium para acceder al tutorial completo.

Comunidad

Comentarios y valoraciones

No hay comentarios aún. ¡Sé el primero en opinar!