1. C Hardware & Módulos de Radio

Esta sección documenta los controladores de bajo nivel desarrollados en C99. Estos módulos constituyen la capa de abstracción de hardware (HAL) necesaria para la interacción directa con los periféricos críticos del sensor.

1.1. Arquitectura de Integración

La comunicación entre el procesador host y los componentes de radio se realiza mediante una arquitectura de bus de alto rendimiento, optimizada para minimizar la latencia en la captura de muestras.

  • Bus SPI: Utilizado para el streaming de tramas binarias del receptor GPS.

  • GPIO Dedicados: Control de líneas de interrupción, reset de hardware y gestión de estados del transceptor.

  • Flujo Binario: Se implementa un protocolo de tramas para maximizar el rendimiento del bus.

1.2. Controlador de GPS (Binario)

Gestión del módulo GPS para la obtención de coordenadas geográficas y, fundamentalmente, la señal de tiempo de precisión para la sincronización de capturas.

group GPS-LTE Binary

Logic and helper functions for the GPS-LTE module.

Manejadores de Hardware y UART

st_uart LTE

Estructura de control para la UART vinculada al módem LTE.

gp_uart GPS

Estructura de control para la UART vinculada al receptor GPS.

Estado y Datos Globales

bool LTE_open = false

Indica si el puerto serie LTE está abierto.

bool GPS_open = false

Indica si el puerto serie GPS está abierto.

bool GPSRDY = false

Bandera de sincronización para nueva trama GPS.

pthread_mutex_t gps_ready_mutex = PTHREAD_MUTEX_INITIALIZER

Indica si el puerto serie LTE está abierto.

pthread_cond_t gps_ready_cond = PTHREAD_COND_INITIALIZER

Indica si el puerto serie LTE está abierto.

Constantes de Buffer

CMD_BUF 256

Tamaño máximo para comandos de sistema.

IP_BUF 64

Tamaño del buffer para almacenar direcciones IPv4.

Functions

static bool gps_wait_for_ready(int timeout_ms)
void connection_LTE(void)

Gestiona la conexión a la red de datos mediante el demonio PPP.

Ejecuta el script de marcado “rnet”. Si la asignación de IP falla, intenta reiniciar la interfaz una vez tras un tiempo de espera. Utiliza get_ppp_ip para validar el éxito de la operación.

void run_cmd(const char *cmd)

Ejecuta un comando de sistema e imprime su traza en consola.

Parámetros:

cmd[in] Cadena de caracteres con el comando a ejecutar.

int get_wlan_ip(char *ip)

Obtiene la dirección IPv4 de la interfaz wlan0 (WiFi).

Parámetros:

ip[out] Buffer donde se copiará la dirección IP encontrada.

Devuelve:

1 si se obtuvo con éxito, 0 en caso contrario.

int get_eth_ip(char *ip)

Obtiene la dirección IPv4 de la interfaz eth0 (Ethernet).

Parámetros:

ip[out] Buffer donde se copiará la dirección IP encontrada.

Devuelve:

1 si se obtuvo con éxito, 0 en caso contrario.

int get_ppp_ip(char *ip)

Obtiene la dirección IPv4 de la interfaz ppp0 (Módem LTE).

Parámetros:

ip[out] Buffer donde se copiará la dirección IP encontrada.

Devuelve:

1 si se obtuvo con éxito, 0 en caso contrario.

Controlador para la adquisición y parseo de datos NMEA desde un receptor GPS.

Defines

UART_BUFFER_SIZE 120

Tamaño del buffer de lectura para tramas NMEA.

SERIAL_DEV_GPS "/dev/serial/by-id/usb-SimTech__Incorporated_SimTech__Incorporated_0123456789ABCDEF-if01-port0"

Ruta persistente del dispositivo USB-Serial para el GPS.

Typedefs

typedef struct gps_command_s GPSCommand

Estructura de datos para almacenar una trama GPGGA parseada.

  • Esta estructura contiene punteros a los segmentos de la cadena NMEA procesada en GPS_Track.

Functions

int8_t init_usart1(gp_uart *s_uart)

Inicializa el puerto serie del GPS y lanza el hilo de recepción.

Parámetros:

s_uart – Puntero a la estructura de control gp_uart.

Devuelve:

0 en éxito, -1 en caso de fallo.

void close_usart1(gp_uart *s_uart)

Detiene el hilo de recepción y cierra el descriptor de la UART.

Parámetros:

s_uart – Puntero a la estructura de control.

void GPS_Track(char *GPSData)

Tokeniza una cadena NMEA y asigna los valores a la estructura global GPSInfo.

Parámetros:

GPSData – Cadena de texto con la trama cruda recibida.

void *GPSIntHandler(void *arg)

Hilo de ejecución para la captura de datos del puerto serie.

Utiliza select() para monitoreo no bloqueante y dispara el parseo si la trama recibida tiene una longitud mínima válida.

Variables

char RESPONSE_BUFFER_GPS[UART_BUFFER_SIZE]

Buffer global donde el hilo deposita los datos crudos del GPS.

const char NMEA_DELIMITERS[3] = "$,"

Delimitadores estándar para tramas NMEA (Coma y símbolo de inicio).

bool GPS_run = false
struct gp_uart
#include <bacn_GPS.h>

Estructura de control para la interfaz UART del GPS.

Public Members

uint32_t serial_fd

Descriptor de archivo del dispositivo serie.

pthread_t th_recv

Hilo dedicado para la escucha del puerto.

int32_t recv_buff_cnt

Cantidad de bytes leídos en el último evento.

struct gps_command_s
#include <bacn_GPS.h>

Estructura de datos para almacenar una trama GPGGA parseada.

  • Esta estructura contiene punteros a los segmentos de la cadena NMEA procesada en GPS_Track.

Public Members

char *Header

Cabecera de la trama (ej. $GPGGA).

char *UTC_Time

Tiempo universal coordinado.

char *Latitude

Valor de latitud.

char *LatDir

Dirección de latitud (N/S).

char *Longitude

Valor de longitud.

char *LonDir

Dirección de longitud (E/W).

char *Quality

Calidad del fix GPS (0=invalido, 1=GPS fix).

char *Satelites

Número de satélites en uso.

char *HDOP

Dilución de precisión horizontal.

char *Altitude

Altitud sobre el nivel del mar.

char *Units_al

Unidades de altitud (M).

char *Undulation

Separación geoidal.

char *Units_un

Unidades de ondulación (M).

char *Age

Edad de los datos DGPS.

char *Cheksum

Checksum de la trama para validación.

Controlador para la comunicación con módulos LTE vía comandos AT.

Functions

static void deadline_from_now_ms(struct timespec *ts, uint32_t timeout_ms)
static int count_crlf_sequences(const char *buffer)
void Read_Response(void)

Bloquea la ejecución hasta que se procesa una respuesta completa o expira el tiempo.

void Start_Read_Response(void)

Inicia el ciclo de lectura de respuesta, reintentando si el estado es de espera.

bool WaitForExpectedResponse(const char *ExpectedResponse)

Espera a que el buffer contenga una cadena específica tras una recepción.

Parámetros:

ExpectedResponse – Cadena de texto que se busca (ej. «OK», «ERROR»).

Devuelve:

true Si se encontró la respuesta esperada.

Devuelve:

false Si hubo timeout o la respuesta no coincide.

bool SendATandExpectResponse(st_uart *s_uart, const char *ATCommand, const char *ExpectedResponse)

Envía un comando AT y verifica la respuesta esperada en un solo paso.

Parámetros:
  • s_uart – Puntero a la configuración de la UART.

  • ATCommand – Comando a enviar (ej. «AT\r»).

  • ExpectedResponse – Respuesta buscada (ej. «OK»).

Devuelve:

true Si la operación fue exitosa.

void LTE_SendString(st_uart *s_uart, const char *data)

Envía una cadena de datos formateada hacia el módulo LTE.

Parámetros:
  • s_uart – Puntero a la configuración de la UART.

  • data – Cadena de texto a enviar.

bool LTE_Start(st_uart *s_uart)

Inicializa la comunicación con el módulo LTE y desactiva el Eco (ATE0).

Parámetros:

s_uart – Puntero a la configuración de la UART.

Devuelve:

true Si el módulo responde correctamente tras los intentos.

int8_t init_usart(st_uart *s_uart)

Configura el puerto serie e inicia el hilo de recepción.

Parámetros:

s_uart – Puntero a la estructura donde se guardará el descriptor y el hilo.

Devuelve:

0 en éxito, -1 en caso de error de apertura o configuración.

void close_usart(st_uart *s_uart)

Finaliza el hilo de recepción y cierra el puerto serie.

Parámetros:

s_uart – Puntero a la configuración de la UART.

void *LTEIntHandler(void *arg)

Función ejecutada por el hilo para monitorear el puerto serie.

Parámetros:

arg – Puntero a st_uart pasado como argumento de hilo.

Variables

uint32_t TimeOut = 0
int8_t Response_Status
int8_t CRLF_COUNT = 0
int8_t Data_Count
volatile uint8_t OBDCount = 0
volatile uint8_t GPSCount = 0
char RESPONSE_BUFFER[UART_BUFFER_SIZE]

Buffer global donde el hilo deposita los datos recibidos.

bool LTERDY = false

Flag que indica que hay nuevos datos listos en el buffer.

bool LTE_run = false
static pthread_mutex_t lte_response_mutex = PTHREAD_MUTEX_INITIALIZER
static pthread_cond_t lte_response_cond = PTHREAD_COND_INITIALIZER

Utility functions for Environment, Network, and GPS HTTP POSTs.

Defines

MAX_URL_LENGTH 1024
MAX_JSON_LENGTH 256
MAC_ADDR_LENGTH 18

Functions

char *getenv_c_gps(const char *key)

Reads a specific key from a local .env file.

Parámetros:

key – The key to search for (e.g., «API_URL»).

Devuelve:

char* Dynamically allocated string containing the value. Caller must free() the result. Returns NULL if not found.

int get_wlan0_mac(char *mac_out)

Retrieves the MAC address of the wlan0 interface.

Parámetros:

mac_out – Buffer to store the result (must be at least MAC_ADDR_LENGTH).

Devuelve:

int 0 on success, -1 on failure.

double nmea_to_decimal(double raw_coord)
int post_gps_data(const char *base_api_url, const char *altitude_str, const char *latitude_str, const char *lat_dir_str, const char *longitude_str, const char *lon_dir_str)

Converts coordinates to JSON and POSTs them via HTTP.

Nota

Automatically retrieves the wlan0 MAC address to include in the JSON.

Nota

NMEA coordinate fields are unsigned; the hemisphere sign is applied from the direction indicators (S/W -> negative). If lon_dir_str is missing, a Western-hemisphere (Colombia) fallback is kept for longitude.

Parámetros:
  • base_api_url – The server URL (e.g., «http://myserver.com»).

  • altitude_str – Altitude as string.

  • latitude_str – Latitude as string (NMEA ddmm.mmmm, unsigned).

  • lat_dir_str – Latitude hemisphere indicator («N» or «S»), may be NULL/empty.

  • longitude_str – Longitude as string (NMEA dddmm.mmmm, unsigned).

  • lon_dir_str – Longitude hemisphere indicator («E» or «W»), may be NULL/empty.

Devuelve:

int 0 on success, non-zero on failure.

int shm_add_to_persistent_gps(const char *key, const char *value_text)

Adds or updates a key in /dev/shm/persistent.json (GPS-LTE variant).

Uses exclusive file lock + fsync for safe multi-process writes, matching Python ShmStore protection semantics.

Parámetros:
  • key – JSON key to insert/update.

  • value_text – Value as text. If valid JSON, it is stored typed; otherwise it is stored as JSON string.

Devuelve:

int 0 on success, -1 on error.

char *shm_consult_persistent_gps(const char *key)

Reads a key from /dev/shm/persistent.json (GPS-LTE variant).

Uses shared file lock for safe concurrent reads.

  • If JSON value is string: returns raw string content (no quotes).

  • Otherwise: returns unformatted JSON text.

Parámetros:

key – JSON key to query.

Devuelve:

char* Heap-allocated string (caller must free), or NULL on not found/error.

char *ashm_consult_persistent_gps(const char *key)

Compatibility alias for shm_consult_persistent_gps.

Parámetros:

key – JSON key to query.

Devuelve:

char* Same return contract as shm_consult_persistent_gps.

Importante

El módulo GPS requiere una antena externa con vista clara al cielo. La precisión de la marca de tiempo PPS es crítica para la integridad de los datos espectrales.

1.3. Módulo de Radio (RF)

Controlador principal para el hardware HackRF One y la lógica de procesamiento DSP (Digital Signal Processing). Este módulo se encarga de la sintonización, ganancia y transferencia de muestras de IQ.

group Binario RF

Lógica, Procesamiento de Señales Digitales (DSP) y transmisión de Audio para el módulo de Radio.

Configuración DSP

  • Constantes y selectores para el flujo de procesamiento de señales.

static int IQ_FILTER_ENABLE = 1

Interruptor para habilitar/deshabilitar el filtrado IIR en datos IQ crudos.

static float IQ_FILTER_BW_AM_HZ = 20000.0f

Ancho de banda en Hz para el pre-filtro de demodulación AM.

Comunicación y Manejadores de Hardware

Interfaces para mensajería de red y hardware físico SDR.

zpair_t *zmq_channel = NULL

Par de sockets ZMQ para comando y control de red/IPC.

hackrf_device *device = NULL

Puntero a la instancia inicializada del hardware HackRF.

Sistema de Buffers

Buffers circulares utilizados para desacoplar el muestreo de hardware de alta velocidad de los hilos de procesamiento.

ring_buffer_t rb

Buffer circular primario para muestras IQ crudas del HackRF.

ring_buffer_t audio_rb

Buffer circular para muestras de audio demoduladas, listas para transmitir.

Estado Global e Hilos (Threading)

Banderas y primitivas que gestionan el flujo de ejecución y la seguridad entre hilos.

atomic_bool audio_enabled = false

Bandera atómica que indica si el subsistema de audio está activo.

atomic_bool calibration_running = false

Indica calibración en curso para evitar cierre concurrente de HW.

volatile bool stop_streaming = true

Señal para detener la adquisición de datos del hardware.

volatile bool keep_running = true

Bandera maestra de salida para el bucle principal de la aplicación.

pthread_t audio_thread

Identificador del hilo para la tarea de red/audio Opus.

volatile bool audio_thread_running = false

Bandera de estado para el ciclo de vida del hilo de audio.

pthread_cond_t rb_cond = PTHREAD_COND_INITIALIZER

Bandera atómica que indica si el subsistema de audio está activo.

pthread_mutex_t rb_mutex = PTHREAD_MUTEX_INITIALIZER

Bandera atómica que indica si el subsistema de audio está activo.

pthread_cond_t audio_rb_cond = PTHREAD_COND_INITIALIZER

Bandera atómica que indica si el subsistema de audio está activo.

pthread_mutex_t audio_rb_mutex = PTHREAD_MUTEX_INITIALIZER

Bandera atómica que indica si el subsistema de audio está activo.

Estructuras de Configuración

Instantáneas de los ajustes deseados, de hardware y de procesamiento.

SDR_cfg_t current_hw_cfg = {0}

Estado actual real del hardware para comparaciones de sintonización optimizada.

Defines

RF_DEBUG_LOGS 0
RF_TRACE(...) do { if (0) printf(__VA_ARGS__); } while (0)

Functions

int rx_callback(hackrf_transfer *transfer)

Función de retorno (Callback) activada por libhackrf cuando hay nuevas muestras disponibles.

Esta función se ejecuta en el contexto del hilo del controlador HackRF. Realiza un procesamiento mínimo para evitar la pérdida de muestras:

  1. Escribe los datos IQ crudos en el buffer primario (rb).

  2. Si audio_enabled es verdadero, clona los datos en audio_rb.

Nota

Ejecución de alta frecuencia; evite llamadas bloqueantes o lógica pesada aquí.

Parámetros:

transfer[in] Puntero a la estructura hackrf_transfer que contiene los bytes crudos.

Devuelve:

0 para continuar la transmisión, distinto de cero para detenerla.

static int cmp_double_asc(const void *a, const void *b)
static void rf_workspace_release(rf_processing_workspace_t *ws)
static int rf_workspace_ensure(rf_processing_workspace_t *ws, size_t iq_bytes, int nperseg)
static int rf_workspace_ensure_scratch(rf_processing_workspace_t *ws, size_t count)
static int rf_workspace_ensure_aux_sig(rf_processing_workspace_t *ws, size_t samples)
static int rf_workspace_ensure_pcm(rf_processing_workspace_t *ws, size_t samples)
static double median_of_double_workspace(rf_processing_workspace_t *ws, const double *v, int n)
static inline float calibration_finish(float final_ppm)
static double interp_linear(const double *x, const double *y, int n, double xq)
static void invalidate_hackrf_state(const char *reason)
static int ensure_hackrf_session_is_healthy(void)
static float calibrate_hackrf(void)
static inline uint64_t now_ms(void)

Obtiene el tiempo actual del sistema en milisegundos.

Devuelve:

Entero de 64 bits sin signo que representa los milisegundos desde la Época (Epoch).

static inline void msleep_int(int ms)

Envoltura (Wrapper) de usleep para proporcionar precisión en milisegundos.

Parámetros:

ms[in] Tiempo a dormir en milisegundos.

static inline double resolve_demod_fs_hz(const audio_stream_ctx_t *ctx, int mode)

Resuelve la frecuencia de muestreo IQ para demodulación en tiempo de ejecución.

Prioriza el valor atómico actualizado por el hilo principal con la configuración activa de HackRF. Si aún no está disponible, reconstruye la tasa a partir del estado del demodulador (decimation_factor * audio_fs), evitando asumir 2 MS/s fijos.

void handle_sigint(int sig)

Manejador de señales para SIGINT (Ctrl+C).

Cambia la bandera global keep_running a falso para iniciar un apagado controlado.

Parámetros:

sig[in] El número de señal (ignorado).

int recover_hackrf(void)

Intenta recuperar el dispositivo HackRF tras una pérdida de conexión.

Esta función detiene las transmisiones existentes, cierra el dispositivo e intenta volver a abrirlo hasta 3 veces con un retraso de 1 segundo entre intentos.

Nota

Esta es una llamada bloqueante.

Devuelve:

0 si tiene éxito, -1 si el dispositivo no pudo recuperarse.

static int send_json_reply(cJSON *root)

Envía un objeto JSON ya construido como reply del request actual.

Parámetros:

root[in] Objeto cJSON a serializar.

Devuelve:

0 si el reply fue enviado, -1 si falló.

static int send_status_reply(const char *status, const char *reason)

Envía un reply de estado simple.

Parámetros:
  • status[in] Estado semántico del reply.

  • reason[in] Motivo opcional de error o contexto adicional.

Devuelve:

0 si el reply fue enviado, -1 si falló.

static void apply_runtime_request(DesiredCfg_t *desired, SDR_cfg_t *out_hack, PsdConfig_t *out_psd, RB_cfg_t *out_rb)

Aplica al request actual la configuración DSP/HW y el estado de audio.

Parámetros:
  • desired[inout] Configuración parseada desde el request.

  • out_hack[out] Configuración de hardware derivada.

  • out_psd[out] Configuración PSD derivada.

  • out_rb[out] Configuración de buffer derivada.

int publish_results(double *psd_array, int length, SDR_cfg_t *local_hack, uint64_t original_center_freq, int rf_mode, float am_depth, float fm_dev)

Serializa los datos de PSD y metadatos de RF en JSON y los envía vía ZMQ.

Utiliza cJSON para construir una carga útil que contiene los límites de frecuencia, métricas específicas del modo (profundidad AM o excursión FM) y el arreglo de PSD crudo.

Parámetros:
  • psd_array[in] Arreglo de valores de densidad espectral de potencia en doble precisión.

  • length[in] Tamaño del arreglo psd_array.

  • local_hack[in] Configuración actual del hardware para cálculos de frecuencia.

  • rf_mode[in] Modo de operación actual (ej. FM_MODE, AM_MODE, PSD_MODE).

  • am_depth[in] Profundidad de modulación AM calculada.

  • fm_dev[in] Desviación de frecuencia FM calculada.

Devuelve:

0 si el reply fue enviado, -1 si falló.

void *audio_thread_fn(void *arg)

Hilo principal de procesamiento y transmisión de audio.

Implementa el siguiente flujo de trabajo (pipeline):

Advertencia

Se bloquea en ensure_tx_with_retry si la red está caída.

Parámetros:

arg[inout] Puntero a una estructura audio_stream_ctx_t.

Devuelve:

NULL al finalizar el hilo.

Variables

static double g_request_cooldown_s = 1.0
static float g_last_cal_ppm = 0.0f
static int g_has_last_cal_ppm = 0
static rf_processing_workspace_t g_calibration_ws = {0}
struct rf_processing_workspace_t

Public Members

int8_t *linear_buffer
size_t linear_capacity_bytes
signal_iq_t sig
size_t sig_capacity_samples
double *freq
double *psd
int spectrum_capacity
double *scratch
size_t scratch_capacity
double complex * aux_sig
size_t aux_sig_capacity_samples
int16_t *pcm
size_t pcm_capacity_samples

Procesamiento de señales AM desde IQ a PCM.

Defines

AM_AUDIO_LPF_HZ 4000.0f

Frecuencia de corte del filtro pasa bajos de audio (estilo voz conservador).

AM_AUDIO_Q 0.707f

Factor de calidad Q para el filtro Biquad (0.707 = Butterworth).

DEPTH_EMA_ALPHA 0.20f

Factor alfa para el promedio móvil exponencial (EMA) de la profundidad de modulación.

Functions

static inline void am_depth_reset(am_depth_state_t *st)

Reinicia las métricas de envolvente para un nuevo cálculo de ventana.

Parámetros:

st[inout] Puntero al estado de métricas de profundidad AM.

static inline float update_am_depth_from_env_ctx(am_depth_state_t *st, float env_decimated)

Actualiza la métrica de profundidad AM utilizando la envolvente diezmada.

  • La profundidad de modulación m se calcula como:

    m = \frac{A_{max} - A_{min}}{A_{max} + A_{min}}

    El valor resultante se filtra mediante un promedio móvil exponencial (EMA).

Parámetros:
  • st[inout] Estado de métricas de profundidad AM.

  • env_decimated[in] Muestra de envolvente después del diezmado.

Devuelve:

float Profundidad de modulación suavizada actual (0.0 a 1.0).

void am_radio_init(am_radio_t *r, double fs, int audio_fs)

Configura el estado inicial del demodulador AM.

  • Calcula el factor de diezmado y pre-calcula los coeficientes de los filtros basándose en las frecuencias de entrada y salida.

Parámetros:
  • r[inout] Puntero a la instancia de la estructura de radio AM.

  • fs[in] Frecuencia de muestreo de la señal IQ de entrada (Hz).

  • audio_fs[in] Frecuencia de muestreo de audio deseada (Hz).

static void biquad_lowpass(am_radio_t *r, float fs, float fc, float Q)

Calcula los coeficientes de un filtro biquad pasa bajos.

  • Utiliza la arquitectura RBJ (Robert Bristow-Johnson) para un filtro Butterworth.

Parámetros:
  • r[inout] Puntero al estado del radio donde se guardarán los coeficientes.

  • fs[in] Frecuencia de muestreo (Hz).

  • fc[in] Frecuencia de corte (Hz).

  • Q[in] Factor de calidad.

static inline float biquad_process(am_radio_t *r, float x)

Procesa una muestra de audio a través de un filtro biquad.

  • Implementa la Forma Directa II Transpuesta:

    y[n] = b_0 x[n] + z_1[n-1]

    z_1[n] = b_1 x[n] - a_1 y[n] + z_2[n-1]

    z_2[n] = b_2 x[n] - a_2 y[n]

Parámetros:
  • r[inout] Puntero al estado del radio (contiene coeficientes y retardos).

  • x[in] Muestra de audio de entrada.

Devuelve:

float Muestra de audio filtrada.

static inline float dc_block_process(am_radio_t *r, float x)

Aplica un filtro de bloqueo de componente DC.

  • Sigue la ecuación en diferencias:

    y[n] = x[n] - x[n-1] + R \cdot y[n-1]

    Donde R controla la frecuencia de corte cercana a 0 Hz.

Parámetros:
  • r[inout] Puntero al estado del radio.

  • x[in] Entrada de audio con offset DC.

Devuelve:

float Salida de audio sin componente DC.

int am_radio_iq_to_pcm(am_radio_t *r, signal_iq_t *sig, int16_t *pcm_out, am_depth_state_t *depth_st)

Procesa un bloque de señal IQ y genera muestras de audio PCM16.

  • Realiza la detección de envolvente mediante:

    E[n] = \sqrt{I[n]^2 + Q[n]^2}

Parámetros:
  • r[inout] Puntero al estado del radio AM.

  • sig[in] Estructura con el buffer de señal IQ de entrada.

  • pcm_out[out] Buffer para almacenar las muestras de audio generadas.

  • depth_st[inout] Estado opcional para actualizar métricas de profundidad AM.

Devuelve:

int Número de muestras de audio generadas en esta llamada.

struct am_radio_t
#include <am_radio.h>

Estructura de estado para el demodulador AM.

  • Contiene los acumuladores para diezmado, estados del filtro DC-Blocker y coeficientes del filtro Biquad para la etapa de audio.

Public Members

double audio_acc

Acumulador para el promedio de muestras (decimation).

int samples_in_acc

Contador de muestras acumuladas.

int decim_factor

Factor de diezmado calculado M = \frac{f_s}{f_{audio}}.

float gain

Ganancia de salida para el ajuste de volumen PCM.

float dc_r

Coeficiente de realimentación del DC blocker.

float dc_x1

Estado anterior de la entrada x[n-1].

float dc_y1

Estado anterior de la salida y[n-1].

float b0
float b1
float b2
float a1
float a2

Coeficientes del filtro biquad.

float z1
float z2

Estados del filtro (Direct Form II Transposed).

int enable_dc_block

Flag para habilitar/deshabilitar el filtro DC blocker.

int enable_lpf

Flag para habilitar/deshabilitar el filtro pasa bajos.

Demodulador AM robusto con diezmado CIC, normalización y AGC.

Functions

static void am_biquad_lowpass(am_radio_local_t *r, float fs, float fc, float Q)

Calcula coeficientes Biquad para el filtro de audio.

Parámetros:
  • r – Puntero al estado.

  • fs – Frecuencia de muestreo.

  • fc – Frecuencia de corte.

  • Q – Factor de calidad.

static inline float am_biquad_process(am_radio_local_t *r, float x)

Procesa una muestra mediante el filtro Biquad (DFII-T).

static inline float am_dc_block_process(am_radio_local_t *r, float x)

Aplica un filtro de primer orden para remover offset DC.

static inline float update_am_local_depth_from_env_ctx(am_depth_state_t *st, float env_decimated)

Actualiza la métrica de profundidad de modulación AM (Índice de modulación).

  • La profundidad de modulación m se calcula utilizando los valores pico de la envolvente en una ventana de tiempo (definida por report_samples):

    m = \frac{A_{max} - A_{min}}{A_{max} + A_{min}}

    El resultado se suaviza mediante un filtro de promedio móvil exponencial (EMA) para evitar fluctuaciones por ruido.

Parámetros:
  • st[inout] Puntero al estado de métricas de profundidad.

  • env_decimated[in] Muestra de la envolvente después del diezmado (antes de normalizar).

Devuelve:

float Profundidad de modulación actual suavizada [0.0 a 1.0].

static inline double am_env_mag(double re, double im)

Calcula la magnitud de la muestra IQ.

Parámetros:
  • re – Parte real.

  • im – Parte imaginaria.

Devuelve:

Magnitud (envolvente).

static inline float am_cic2_decim_push(am_radio_local_t *r, double x, int R, int *ready)

Filtro CIC de orden 2 para diezmado eficiente.

  • Implementa la estructura Integrador-Integrador -> Diezmado -> Peine-Peine. La ganancia del filtro es R^2, la cual se normaliza internamente.

Parámetros:
  • r[inout] Estado del radio.

  • x[in] Muestra de entrada a alta tasa.

  • R[in] Factor de diezmado.

  • ready[out] Se pone a 1 cuando una nueva muestra diezmada está disponible.

Devuelve:

float Muestra diezmada (si ready=1).

static inline float am_update_env_mean(am_radio_local_t *r, float env_dec)

Actualiza el estimador del nivel de portadora (media lenta).

  • Utiliza un EMA de tiempo largo para identificar el nivel DC de la envolvente.

Parámetros:
  • r – Estado del radio.

  • env_dec – Muestra actual de la envolvente.

Devuelve:

Media actualizada.

static inline float am_agc_process(am_radio_local_t *r, float x)

Control Automático de Ganancia (AGC) basado en RMS.

  • Ajusta la ganancia de forma asimétrica (ataque rápido, liberación lenta) para mantener la señal en un nivel de volumen constante.

Parámetros:
  • r – Estado del radio.

  • x – Muestra de audio de entrada.

Devuelve:

Muestra de audio con ganancia aplicada.

void am_radio_local_init(am_radio_local_t *r, double fs_iq, int audio_fs)

Inicializa el estado del demodulador AM robusto.

  • Configura los filtros, el diezmador CIC y los parámetros del AGC basándose en las frecuencias de muestreo proporcionadas.

Parámetros:
  • r[out] Puntero a la estructura de estado.

  • fs_iq[in] Frecuencia de muestreo de la señal IQ de entrada.

  • audio_fs[in] Frecuencia de muestreo de audio de salida (típicamente 48000).

int am_radio_local_iq_to_pcm(am_radio_local_t *r, signal_iq_t *sig, int16_t *pcm_out, am_depth_state_t *depth_st)

Procesa un bloque IQ y produce audio PCM de 16 bits.

  • El proceso sigue la cadena: Detección -> CIC -> Normalización -> DC Block -> LPF -> AGC -> Gain.

Parámetros:
  • r[inout] Puntero al estado del radio.

  • sig[in] Buffer de entrada IQ.

  • pcm_out[out] Buffer de salida para audio PCM16.

  • depth_st[inout] Estado opcional para métricas de profundidad de modulación.

Devuelve:

int Cantidad de muestras escritas en pcm_out.

Módulo de gestión de audio, filtrado IIR y compresión Opus.

Constantes de Audio y PSD

AUDIO_CHUNK_SAMPLES 16384

Tamaño del bloque de procesamiento de audio.

PSD_SAMPLES_TOTAL 2097152

Total de muestras para el cálculo de la PSD.

AUDIO_FS 48000

Frecuencia de muestreo estándar para el codificador Opus (Hz).

Defaults de Streaming Opus

AUDIO_TCP_DEFAULT_HOST "127.0.0.1"

IP por defecto para el gateway de audio.

AUDIO_TCP_DEFAULT_PORT 9000

Puerto TCP por defecto.

OPUS_FRAME_MS_DEFAULT 20

Duración por defecto del frame Opus (ms).

OPUS_BITRATE_DEFAULT 32000

Bitrate por defecto para el stream de voz/audio (bps).

OPUS_COMPLEXITY_DEFAULT 5

Complejidad computacional del encoder (0-10).

OPUS_VBR_DEFAULT 0

Modo por defecto: CBR (0).

Defines

IQ_FILTER_BW_FM_HZ 200000.0f

Parámetros de diseño para el filtro de canal IQ. El ancho de banda para WBFM se define típicamente como.

BW = 200 \text{ kHz}

IQ_FILTER_ORDER 6

Orden del filtro Butterworth (par).

Typedefs

typedef struct audio_stream_ctx audio_stream_ctx_t

Estructura de contexto para el stream de audio.

  • Mantiene el estado de los demoduladores, la configuración de red y las métricas de calidad de señal (AM/FM).

Functions

void audio_stream_ctx_defaults(audio_stream_ctx_t *ctx, fm_radio_t *fm, am_radio_local_t *am)

Inicializa el contexto de audio con valores por defecto y variables de entorno.

  • Realiza la puesta a cero de la estructura y carga las configuraciones iniciales, priorizando las variables de entorno si están presentes.

Parámetros:
  • ctx[out] Puntero a la estructura de contexto a inicializar.

  • fm[in] Puntero a una instancia válida de fm_radio_t.

  • am[in] Puntero a una instancia válida de am_radio_local_t.

struct audio_stream_ctx
#include <audio_stream_ctx.h>

Estructura de contexto para el stream de audio.

  • Mantiene el estado de los demoduladores, la configuración de red y las métricas de calidad de señal (AM/FM).

Public Members

fm_radio_t *fm_radio

Instancia del demodulador FM.

am_radio_local_t *am_radio

Instancia del demodulador AM.

const char *tcp_host

Dirección del servidor TCP.

int tcp_port

Puerto del servidor TCP.

int opus_sample_rate

Tasa de muestreo de salida (usualmente 48kHz).

int opus_channels

Número de canales (1 = Mono).

int bitrate

Bitrate configurado para Opus.

int complexity

Nivel de complejidad del codificador.

int vbr

Flag de Variable Bitrate (1=VBR, 0=CBR).

int frame_ms

Latencia del frame en milisegundos.

_Atomic int current_mode

Modo RF actual (fm_mode_t casted to int).

_Atomic double current_fs_hz

Frecuencia de muestreo de entrada (IQ rate).

iq_iir_filter_t iqf

Filtro IIR para pre-procesamiento de señal IQ.

filter_audio_t iqf_cfg

Configuración del filtro IQ.

int iqf_ready

Flag que indica si el filtro está inicializado.

fm_dev_state_t fm_dev

Estado de la medición de desviación FM.

am_depth_state_t am_depth

Estado de la medición de profundidad AM.

Procesamiento espectral para aislamiento de señales.

Defines

CLAMPD(x, lo, hi) (((x)<(lo))?(lo):(((x)>(hi))?(hi):(x)))

Limita un valor doble entre un rango mínimo y máximo.

Functions

static inline double db_to_lin_amp_chan_filt(double db)

Convierte decibelios a amplitud lineal.

A_{lin} = 10^{\frac{dB}{20}}

Parámetros:

db – Decibelios.

Devuelve:

Amplitud lineal.

static inline double raised_cos_chan_filt(double t)

Función de Coseno Alzado para transiciones suaves.

f(t) = 0.5 - 0.5 \cdot \cos(\pi \cdot t)

Parámetros:

t – Parámetro de entrada (típicamente tiempo normalizado o fase).

Devuelve:

Valor suavizado entre 0.0 y 1.0.

const char *chan_filter_last_region(void)

Informa la ubicación espectral de la última banda filtrada.

Utilizado para depuración o lógica de decodificación que dependa de si la señal es puramente positiva, negativa o si cruza la frecuencia de 0 Hz (DC).

Devuelve:

const char* Cadena estática: «POSITIVE», «NEGATIVE», «CROSS_DC» o «UNKNOWN».

static void cache_free(void)

Libera los recursos de la caché global.

void chan_filter_free_cache(void)

Libera los planes FFTW y buffers de máscara precalculados.

Debe invocarse antes de cerrar la aplicación para limpiar la caché global de la biblioteca.

static int need_rebuild(int N, const filter_t *cfg, uint64_t fc, double fs)

Determina si la caché actual es inválida para los nuevos parámetros.

Parámetros:
  • N – Número de muestras actual.

  • cfg – Configuración del filtro.

  • fc – Frecuencia central actual (Hz).

  • fs – Frecuencia de muestreo actual (Hz).

Devuelve:

int 1 si requiere reconstrucción, 0 si la caché es reutilizable.

static int cmp_double(const void *a, const void *b)

Función de comparación para qsort.

Parámetros:
  • a – Puntero al primer elemento.

  • b – Puntero al segundo elemento.

Devuelve:

-1 si a < b, 0 si a == b, 1 si a > b.

static double median_of_array(double *v, int n)

Calcula la mediana de un array de doubles.

Parámetros:
  • v – Puntero al array.

  • n – Tamaño del array.

Devuelve:

Mediana del array.

int chan_filter_validate_cfg_abs(const filter_t *cfg, uint64_t fc_hz, double fs_hz, char *err, size_t err_sz)

Valida la configuración del filtro contra los límites físicos de Nyquist.

Valida los límites del filtro respecto al ancho de banda de captura.

Asegura que el rango solicitado [f_{start}, f_{end}] no exceda los límites de Nyquist definidos por la frecuencia central y de muestreo.

Parámetros:
  • cfg – Configuración del filtro.

  • fc_hz – Frecuencia central.

  • fs_hz – Frecuencia de muestreo.

  • err – Mensaje de error.

  • err_sz – Tamaño del buffer de error.

  • cfg[in] Parámetros de corte (frecuencias absolutas en Hz).

  • fc_hz[in] Frecuencia central del receptor (Hz).

  • fs_hz[in] Frecuencia de muestreo (Hz).

  • err[out] Buffer donde se escribirá el motivo del fallo.

  • err_sz[in] Capacidad del buffer de error.

Devuelve:

int 0 si la configuración es físicamente realizable, < 0 en caso contrario.

static int build_mask_and_plans(int N, const filter_t *cfg, uint64_t fc_hz, double fs_hz)

Inicializa planes FFTW y calcula la máscara de magnitud de la Etapa 2.

Parámetros:
  • N – Tamaño de la FFT.

  • cfg – Configuración del filtro.

  • fc_hz – Frecuencia central.

  • fs_hz – Frecuencia de muestreo.

int chan_filter_apply_inplace_abs(signal_iq_t *sig, const filter_t *cfg, uint64_t fc_hz, double fs_hz)

Filtra una señal IQ utilizando una máscara de frecuencia de dos etapas.

El procesamiento se realiza in-place siguiendo este flujo:

  1. Transformada Directa: Se proyecta la señal al dominio de la frecuencia (FFT).

  2. Etapa 1 (Anti-Blooming): Se calcula la mediana de magnitud fuera de la banda de paso. Los picos que exceden la mediana por un umbral dinámico son recortados.

  3. Etapa 2 (Máscara): Se aplica la ganancia de la banda de paso (1.0) y la atenuación de banda de parada con transiciones suaves (Raised Cosine).

  4. Transformada Inversa: Retorno al dominio del tiempo (IFFT) con normalización 1/N.

Parámetros:
  • sig[inout] Señal IQ de entrada/salida.

  • cfg[in] Definición de la banda de paso.

  • fc_hz[in] Frecuencia central (Hz).

  • fs_hz[in] Frecuencia de muestreo (Hz).

Devuelve:

int 0 en éxito, -1 si los datos son inválidos, -5 si falla la FFTW.

Variables

static const double OOB_REJECT_DB = -15.0

Suelo de rechazo fuera de banda (Etapa 2).

static const double TRANS_FRAC = 0.30

Fracción del ancho de banda usada para la transición.

static const double CAP_OOB_DB = 6.0

Umbral sobre la mediana para recorte de picos (Etapa 1).

static const double MIN_OOB_FRAC = 0.05

Porcentaje mínimo de bins OOB para activar Etapa 1.

static cache_t g = {0}
static const char *g_region = "UNKNOWN"
struct cache_t

Estructura interna para caché de planes FFT y máscara de frecuencia.

Public Members

int N

Tamaño de la FFT actual.

fftw_complex *in

Buffer de entrada para FFTW.

fftw_complex *out

Buffer de salida para FFTW.

fftw_plan fwd

Plan de FFT directa.

fftw_plan inv

Plan de FFT inversa.

double *mask_stage2

Valores precalculados de la máscara de magnitud.

double *oob_mag

Scratch reusable para magnitudes OOB.

uint64_t last_fc

Última frecuencia central procesada.

double last_fs

Última frecuencia de muestreo procesada.

int last_start

Última frecuencia de inicio.

int last_end

Última frecuencia de fin.

Tipos de datos globales y estructuras para el sistema SDR.

Enums

enum PsdWindowType_t

Tipos de ventanas de suavizado para el procesamiento espectral.

Values:

enumerator HAMMING_TYPE

Ventana Hamming.

enumerator HANN_TYPE

Ventana Hann.

enumerator RECTANGULAR_TYPE

Sin ventana (Rectangular).

enumerator BLACKMAN_TYPE

Ventana Blackman.

enumerator FLAT_TOP_TYPE

Ventana Flat Top (alta precisión de amplitud).

enumerator KAISER_TYPE

Ventana Kaiser.

enumerator TUKEY_TYPE

Ventana Tukey.

enumerator BARTLETT_TYPE

Ventana Bartlett.

enum Psd_method

Métodos disponibles para el cálculo de la Densidad Espectral de Potencia (PSD).

Values:

enumerator WELCH

Método de Welch (promediado de periodogramas).

enumerator PFB

Polyphase Filter Bank (Banco de filtros polifase).

enum type_filter_audio_t

Tipos de filtros de audio disponibles.

Values:

enumerator LOWPASS_TYPE

Filtro Paso Bajo.

enumerator HIGHPASS_TYPE

Filtro Paso Alto.

enumerator BANDPASS_TYPE

Filtro Paso Banda.

enum rf_mode_t

Modos de operación del receptor RF.

Values:

enumerator PSD_MODE

Modo espectrograma/visualización (sin audio).

enumerator FM_MODE

Demodulación de Frecuencia.

enumerator AM_MODE

Demodulación de Amplitud.

struct signal_iq_t
#include <datatypes.h>

Estructura para el manejo de señales en cuadratura (IQ).

Public Members

double _Complex *signal_iq

Puntero al buffer de muestras complejas.

size_t n_signal

Número total de muestras en el buffer.

struct PsdConfig_t
#include <datatypes.h>

Configuración de parámetros para el algoritmo PSD.

Public Members

PsdWindowType_t window_type

Tipo de ventana a aplicar.

double sample_rate

Frecuencia de muestreo del hardware (Hz).

int nperseg

Número de muestras por segmento.

int noverlap

Número de muestras solapadas entre segmentos.

struct RB_cfg_t
#include <datatypes.h>

Configuración del Ring Buffer y gestión de memoria.

Public Members

size_t total_bytes

Tamaño total asignado en bytes.

int rb_size

Número de elementos en el ring buffer.

struct filter_t
#include <datatypes.h>

Configuración de límites de frecuencia para filtrado digital.

Public Members

int start_freq_hz

Frecuencia de corte inferior (Hz).

int end_freq_hz

Frecuencia de corte superior (Hz).

struct filter_audio_t
#include <datatypes.h>

Estructura de configuración para filtros de audio.

Public Members

float bw_filter_hz

Ancho de banda del filtro (Hz).

type_filter_audio_t type_filter

Topología del filtro.

int order_fliter

Orden del filtro (número de polos).

struct DesiredCfg_t
#include <datatypes.h>

Configuración maestra deseada para el hardware y procesamiento.

Parámetros de Hardware

uint64_t center_freq

Frecuencia central de sintonía (Hz).

double sample_rate

Frecuencia de muestreo (Sps).

int lna_gain

Ganancia del amplificador de bajo ruido (LNA).

int vga_gain

Ganancia del amplificador de ganancia variable (VGA).

bool amp_enabled

Estado del amplificador de potencia interno.

int antenna_port

Puerto de antena seleccionado.

float ppm_error

Corrección de error del oscilador en PPM.

Parámetros de Análisis Espectral

int rbw

Resolution Bandwidth (Hz).

double overlap

Porcentaje de solapamiento (0.0 a 1.0).

PsdWindowType_t window_type

Ventana aplicada al PSD.

double cooldown_request

Cooldown entre requests/PSD en segundos.

bool cooldown_request_set

Indica si cooldown_request vino explícitamente en el último JSON.

Bloque de Filtrado

bool filter_enabled

Habilitación del filtro digital.

filter_t filter_cfg

Configuración de frecuencias de corte.

Public Members

rf_mode_t rf_mode

Modo de operación actual.

Psd_method method_psd

Algoritmo PSD seleccionado.

bool calibrate

Solicita ejecutar rutina de calibración sin adquirir.

struct am_depth_state_t
#include <datatypes.h>

Estado de las métricas de profundidad de modulación AM.

  • La profundidad de modulación m se calcula como:

    m = \frac{A_{max} - A_{min}}{A_{max} + A_{min}}

Public Members

float env_min

Amplitud mínima de la envolvente detectada.

float env_max

Amplitud máxima de la envolvente detectada.

uint32_t counter

Contador de muestras procesadas en la ventana actual.

uint32_t report_samples

Tamaño de la ventana de reporte a tasa de audio.

float depth_ema

Profundidad de modulación suavizada por EMA.

struct fm_dev_state_t
#include <datatypes.h>

Estado de las métricas de desviación de frecuencia FM.

  • La desviación se estima a partir de la frecuencia instantánea f_i:

    EMA_n = (1 - \alpha) \cdot EMA_{n-1} + \alpha \cdot f_i

Public Members

float dev_max_hz

Desviación pico registrada en la ventana actual (Hz).

float dev_ema_hz

Desviación promedio suavizada (EMA) en Hz.

uint32_t counter

Contador de muestras procesadas.

Demodulador FM y procesamiento de audio para señales IQ.

Defines

DEV_EMA_ALPHA 0.10f

Factor de suavizado para la métrica de desviación.

Functions

static void biquad_lowpass(fm_radio_t *r, float fs, float fc, float Q)

Diseño de filtro Biquad paso bajo (RBJ).

  • Calcula coeficientes para una transferencia:

    H(z) = \frac{b_0 + b_1 z^{-1} + b_2 z^{-2}}{a_0 + a_1 z^{-1} + a_2 z^{-2}}

Parámetros:
  • r[out] Estado del radio donde se guardarán los coeficientes.

  • fs[in] Frecuencia de muestreo (Hz).

  • fc[in] Frecuencia de corte (Hz).

  • Q[in] Factor de calidad.

static inline float biquad_process(fm_radio_t *r, float x)

Filtro Biquad en Forma Directa II Transpuesta.

  • Implementa las ecuaciones de estado:

    \begin{aligned}
y[n] &= b_0 x[n] + z_1[n-1] \\
z_1[n] &= b_1 x[n] - a_1 y[n] + z_2[n-1] \\
z_2[n] &= b_2 x[n] - a_2 y[n]
\end{aligned}

Parámetros:
  • r[inout] Estado con coeficientes y registros.

  • x[in] Muestra de entrada.

Devuelve:

float Muestra filtrada.

static inline float dc_block_process(fm_radio_t *r, float x)

Bloqueador de componente DC.

  • Aplica la ecuación diferencial:

    y[n] = x[n] - x[n-1] + r \cdot y[n-1]

Parámetros:
  • r[inout] Estado del radio.

  • x[in] Muestra de audio.

Devuelve:

float Audio sin componente DC.

static inline float phase_diff_to_hz_local(float phase_diff_rad, int fs_demod)
static inline float update_fm_deviation_ctx(fm_dev_state_t *st, float phase_diff_rad, int fs_demod)

Actualiza la métrica de desviación de frecuencia.

  • La frecuencia instantánea se calcula como:

    f_i = \Delta\phi \cdot \frac{f_{demod}}{2\pi}

Parámetros:
  • st[inout] Estado de desviación.

  • phase_diff_rad[in] Fase instantánea en radianes.

  • fs_demod[in] Tasa de muestreo.

Devuelve:

float Desviación suavizada (EMA) en Hz.

void fm_radio_init(fm_radio_t *radio, double fs, int audio_fs, int deemph_us)

Inicializa el estado del radio y calcula coeficientes de filtrado.

Parámetros:
  • radio – Puntero a la estructura de estado.

  • fs – Frecuencia de muestreo de entrada (Hz).

  • audio_fs – Frecuencia de muestreo de audio deseada (Hz).

  • deemph_us – Constante de tiempo de de-énfasis ( \mu s).

int fm_radio_iq_to_pcm(fm_radio_t *radio, signal_iq_t *sig, int16_t *pcm_out, fm_dev_state_t *dev_st, int fs_demod)

Procesa un bloque IQ y genera muestras de audio PCM de 16 bits.

Parámetros:
  • radio[inout] Contexto de estado del radio.

  • sig[in] Buffer de señal IQ de entrada.

  • pcm_out[out] Buffer de salida para muestras PCM16.

  • dev_st[inout] Métricas de desviación FM (opcional).

  • fs_demod[in] Tasa de muestreo de la etapa de demodulación.

Devuelve:

int Número de muestras de audio escritas en pcm_out.

struct fm_radio_t
#include <fm_radio.h>

Estructura de estado del demodulador FM.

  • Mantiene los registros necesarios para el discriminador de fase y la cadena de filtrado.

DC Blocker

float dc_r

Radio del polo r.

float dc_x1

Estado de entrada x[n-1].

float dc_y1

Estado de salida y[n-1].

Biquad LPF

float b0

Coeficientes del numerador del biquad.

float b1

Coeficientes del numerador del biquad.

float b2

Coeficientes del numerador del biquad.

float a1

Coeficientes del numerador del biquad.

float a2

Coeficientes del denominador (con a_0 = 1).

float z1

Coeficientes del numerador del biquad.

float z2

Registros de estado de la Forma Directa II Transpuesta.

Public Members

double _Complex prev_sample

Almacena la muestra anterior para el cálculo de \Delta\phi.

double _Complex iq_acc

Acumulador complejo para pre-diezmado IQ.

int pre_decim_factor

Factor de pre-diezmado IQ antes del discriminador FM.

int pre_samples_in_acc

Contador de muestras acumuladas para pre-diezmado.

double demod_fs_hz

Tasa efectiva de demodulación tras pre-diezmado.

double audio_acc

Acumulador para diezmado.

int samples_in_acc

Contador de muestras acumuladas.

int decim_factor

Factor de diezmado M = f_{in} / f_{out}.

float deemph_acc

Estado del filtro de de-énfasis.

float deemph_alpha

Coeficiente \alpha del de-énfasis.

float gain

Escalamiento para salida PCM16.

int enable_dc_block

Flag de activación del bloqueador de DC.

int enable_lpf

Flag de activación del filtro paso bajo.

Filtro IIR Butterworth de precisión para señales en cuadratura (IQ).

Functions

static int clamp_int(int v, int lo, int hi)

Restringe un valor entero dentro de un rango determinado.

Parámetros:
  • v[in] Valor a evaluar.

  • lo[in] Límite inferior.

  • hi[in] Límite superior.

Devuelve:

int Valor truncado al rango [lo, hi].

static double clamp_double(double v, double lo, double hi)

Restringe un valor de punto flotante doble dentro de un rango determinado.

Parámetros:
  • v[in] Valor a evaluar.

  • lo[in] Límite inferior.

  • hi[in] Límite superior.

Devuelve:

double Valor truncado al rango [lo, hi].

static inline float dc_block_1p(float x, float *x1, float *y1, float r)

Filtro DC Blocker de un solo polo.

La transferencia en el dominio Z es:

H(z) = \frac{1 - z^{-1}}{1 - r \cdot z^{-1}}

Donde r suele ser \approx 0.995. Esto crea un cero en DC y un polo muy cercano que cancela el efecto en el resto de la banda.

Parámetros:
  • x[in] Muestra de entrada actual.

  • x1[inout] Puntero al estado de la muestra de entrada anterior.

  • y1[inout] Puntero al estado de la muestra de salida anterior.

  • r[in] Factor de radio de polo (determina el ancho de la muesca).

Devuelve:

float Muestra filtrada.

static void rbj_lowpass(float fs, float fc, float Q, float *b0, float *b1, float *b2, float *a1, float *a2)

Diseño de filtros Robert Bristow-Johnson (RBJ). Convierte los parámetros de frecuencia y Q en coeficientes de transferencia:

H(z) = \frac{b_0 + b_1 z^{-1} + b_2 z^{-2}}{1 + a_1 z^{-1} + a_2 z^{-2}}

Parámetros:
  • fs[in] Frecuencia de muestreo.

  • fc[in] Frecuencia de corte.

  • Q[in] Factor de calidad (determina la respuesta en la esquina).

  • b0[out] Numerador 0.

  • b1[out] Numerador 1.

  • b2[out] Numerador 2.

  • a1[out] Denominador 1 (normalizado).

  • a2[out] Denominador 2 (normalizado).

static float butterworth_Q(int N, int k)

Cálculo de factor de calidad para Butterworth.

  • Para un orden N, los polos se distribuyen uniformemente en el semiplano izquierdo del plano S. El valor de Q para la sección k es:

    Q_k = \frac{1}{-2 \cos(\frac{(2k + N + 1)\pi}{2N})}

Parámetros:
  • N[in] Orden total del filtro.

  • k[in] Índice de la sección (0 a N/2 - 1).

Devuelve:

float Valor de Q correspondiente.

static int alloc_sections(iq_iir_filter_t *st, int sections)

Gestiona la asignación y liberación de memoria para las secciones del filtro.

Parámetros:
  • st[inout] Puntero al estado del filtro.

  • sections[in] Cantidad de secciones biquad a alojar.

Devuelve:

int 0 en éxito, -1 si falló el sistema de memoria.

int iq_iir_filter_init(iq_iir_filter_t *st, double fs_hz, const filter_audio_t *cfg, int enable_dc_block)

Inicializa la estructura del filtro y reserva memoria.

Parámetros:
  • st[out] Puntero a la estructura de estado del filtro.

  • fs_hz[in] Frecuencia de muestreo del sistema.

  • cfg[in] Configuración de audio (contiene orden y ancho de banda).

  • enable_dc_block[in] Indica si se debe activar el filtro eliminador de DC.

Devuelve:

int 0 en caso de éxito, -1 si falla la reserva de memoria.

int iq_iir_filter_config(iq_iir_filter_t *st, double fs_hz, const filter_audio_t *cfg)

Reconfigura dinámicamente los parámetros del filtro.

  • Calcula nuevos coeficientes si cambian la frecuencia de muestreo, el orden o el ancho de banda. Si el orden cambia, se reasigna memoria automáticamente.

Parámetros:
  • st[inout] Puntero al estado del filtro.

  • fs_hz[in] Nueva frecuencia de muestreo.

  • cfg[in] Nueva configuración de filtro.

Devuelve:

int 0 en caso de éxito, -1 si los parámetros son inválidos.

void iq_iir_filter_reset(iq_iir_filter_t *st)

Reinicia los registros de estado (historia) del filtro.

  • Pone a cero las memorias internas del filtro para evitar transitorios, sin modificar los coeficientes ni la configuración.

Parámetros:

st[inout] Puntero al estado del filtro.

void iq_iir_filter_free(iq_iir_filter_t *st)

Libera toda la memoria dinámica asociada al filtro.

Parámetros:

st[inout] Puntero al estado del filtro. Se limpia la estructura tras liberar.

static inline float biquad_df2t(float x, float b0, float b1, float b2, float a1, float a2, float *z1, float *z2)

Núcleo de la Forma Directa II Transpuesta (DF2T).

A diferencia de la Forma Directa I, la DF2T minimiza los requerimientos de almacenamiento y es numéricamente superior para implementaciones en punto flotante.

Las ecuaciones de estado que gobiernan cada sección son:

\begin{aligned}
y[n]   &= b_0 x[n] + z_1[n-1] \\
z_1[n] &= b_1 x[n] - a_1 y[n] + z_2[n-1] \\
z_2[n] &= b_2 x[n] - a_2 y[n]
\end{aligned}

Parámetros:
  • x[in] Muestra de entrada.

  • b0[in] Coeficiente numerador.

  • b1[in] Coeficiente numerador.

  • b2[in] Coeficiente numerador.

  • a1[in] Coeficiente denominador.

  • a2[in] Coeficiente denominador.

  • z1[inout] Registro de estado 1.

  • z2[inout] Registro de estado 2.

Devuelve:

float Muestra filtrada resultante.

void iq_iir_filter_apply_inplace(iq_iir_filter_t *st, signal_iq_t *sig)

Procesa un bloque de muestras IQ «in-place».

  • El procesamiento sigue el flujo:

  1. Eliminación de DC (si aplica).

  2. Cascada de k secciones Biquad:

    y_k[n] = b_{0,k}x_k[n] + z_{1,k}[n-1]

Parámetros:
  • st[inout] Contexto del filtro.

  • sig[inout] Estructura con el buffer de muestras complejas.

Módulo para la transmision de audio sobre TCP.

Defines

MSG_NOSIGNAL 0

Flag para evitar señales SIGPIPE en sistemas que no lo soportan nativamente.

RECONNECT_DELAY_MS 2000

Retraso estándar entre intentos de reconexión en milisegundos.

Functions

static int set_sock_timeouts(int fd, int snd_ms, int rcv_ms)

Configura los tiempos de espera (timeouts) para operaciones de envío y recepción.

Parámetros:
  • fd[in] Descriptor del socket.

  • snd_ms[in] Tiempo de espera para envío en milisegundos.

  • rcv_ms[in] Tiempo de espera para recepción en milisegundos.

Devuelve:

int 0 en éxito, -1 si falló setsockopt.

static void enable_tcp_keepalive(int fd)

Habilita y configura el mecanismo de Keep-Alive de TCP.

  • Configura el socket para enviar sondas de mantenimiento de conexión, permitiendo detectar desconexiones «silenciosas» o caídas de red de forma proactiva.

Nota

Los tiempos están hardcodeados para detectar fallos en aproximadamente 19 segundos (10s idle + 3 probes * 3s).

Parámetros:

fd[in] Descriptor del socket.

int connect_tcp_net_audio(const char *host, int port)

Establece una conexión TCP de forma bloqueante con resolución de nombres.

  • Utiliza getaddrinfo para soportar IPv4/IPv6 y configura el socket con timeouts y Keep-Alive antes de intentar la conexión.

Parámetros:
  • host[in] Cadena con la dirección IP o nombre del host.

  • port[in] Puerto TCP de destino.

Devuelve:

int Descriptor del socket (fd) si tiene éxito, o -1 en caso de error.

int send_all_net_audio(int fd, const void *buf, size_t len)

Envía un bloque de datos completo, manejando envíos parciales e interrupciones.

  • Itera sobre la llamada send hasta que todos los bytes solicitados hayan sido transmitidos o ocurra un error irrecuperable.

Parámetros:
  • fd[in] Descriptor del socket activo.

  • buf[in] Puntero al buffer de datos.

  • len[in] Longitud en bytes de los datos a enviar.

Devuelve:

int 0 si se envió todo el bloque, -1 en caso de error o desconexión.

void sleep_cancelable_ms(int total_ms, volatile bool *running_flag)

Realiza una pausa en la ejecución que puede ser interrumpida externamente.

  • Divide el tiempo de espera en pequeños intervalos para verificar frecuentemente el estado de un flag de control, permitiendo una finalización rápida del hilo.

Parámetros:
  • total_ms[in] Tiempo total de espera en milisegundos.

  • running_flag[in] Puntero volátil a una bandera de control; si cambia a false, el sueño termina.

int ensure_tx_with_retry(audio_stream_ctx_t *ctx, opus_tx_t **ptx, volatile bool *running_flag)

Asegura que el transmisor Opus esté conectado, reintentando si es necesario.

  • Si el puntero al transmisor (*ptx) es nulo, entra en un bucle de reintento hasta que logra establecer la conexión o hasta que el flag de ejecución se apague.

Parámetros:
  • ctx[in] Contexto que contiene los parámetros de audio y red.

  • ptx[out] Doble puntero donde se almacenará la instancia de opus_tx_t creada.

  • running_flag[in] Bandera que controla la continuidad del bucle de reintentos.

Devuelve:

int 0 si el transmisor está listo/conectado, -1 si el proceso fue cancelado.

Modulo para la transmision de audio codificado en Opus sobre TCP }.

Typedefs

typedef struct opus_tx opus_tx_t

Estructura opaca que representa el contexto del transmisor Opus.

Functions

static int send_all(int fd, const void *buf, size_t n)

Garantiza el envío de un bloque completo de datos a través de TCP.

  • Debido a la naturaleza de los sockets de flujo, una llamada a send puede no enviar todos los bytes solicitados. Esta función itera hasta completar el envío.

Parámetros:
  • fd[in] Descriptor del socket.

  • buf[in] Puntero a los datos a enviar.

  • n[in] Cantidad de bytes a transmitir.

Devuelve:

int 0 en caso de éxito, -1 si la conexión se cierra o falla.

static int connect_tcp(const char *host, int port)

Crea un socket TCP y se conecta al destino especificado.

  • Realiza la resolución de dirección (vía aton/pton) y establece la comunicación.

Parámetros:
  • host[in] Cadena con la dirección IP de destino.

  • port[in] Puerto de destino.

Devuelve:

int Descriptor del socket conectado, o -1 en caso de error.

opus_tx_t *opus_tx_create(const char *host, int port, const opus_tx_cfg_t *cfg)

Crea una instancia del transmisor e inicia la conexión de red.

  • Reserva memoria para el contexto, inicializa el motor Opus con la configuración proporcionada e intenta establecer una conexión TCP con el host remoto.

Nota

La memoria retornada debe ser liberada con opus_tx_destroy().

Parámetros:
  • host[in] Dirección IP o nombre de dominio del servidor destino.

  • port[in] Puerto TCP de destino.

  • cfg[in] Puntero a la estructura de configuración del codificador.

Devuelve:

opus_tx_t* Puntero al contexto creado, o NULL en caso de error (red, memoria o parámetros).

int opus_tx_send_frame(opus_tx_t *tx, const int16_t *pcm, int frame_samples)

Codifica y envía una trama de audio PCM.

  • Toma una muestra de audio en crudo, la comprime usando el formato Opus y la transmite a través del socket TCP precedida por una cabecera de protocolo OpusFrameHeader.

Parámetros:
  • tx[inout] Contexto del transmisor.

  • pcm[in] Puntero al buffer con muestras de audio (int16_t).

  • frame_samples[in] Número de muestras por canal (ej. 960 para 20ms a 48kHz).

Devuelve:

int 0 si la operación fue exitosa, -1 si ocurrió un error en la codificación o envío.

void opus_tx_destroy(opus_tx_t *tx)

Cierra la conexión y libera los recursos asociados.

  • Finaliza la conexión TCP, destruye el codificador interno de Opus y libera la memoria del contexto.

Parámetros:

tx[in] Contexto a destruir. Si es NULL, la función no hace nada.

int opus_tx_fd(const opus_tx_t *tx)

Obtiene el descriptor de archivo (socket) asociado al transmisor.

  • Útil para integrar el transmisor en bucles de eventos (select/poll/epoll) o para configurar opciones de socket adicionales.

Parámetros:

tx[in] Contexto del transmisor.

Devuelve:

int Descriptor del socket, o -1 si el contexto es inválido.

struct OpusFrameHeader

Cabecera de red para tramas Opus.

  • Se envía de forma binaria antes de cada payload Opus para permitir la sincronización y reconstrucción en el receptor.

Public Members

uint32_t magic

Identificador único “OPU0” (0x4F505530).

uint32_t seq

Número de secuencia incremental para detectar pérdidas.

uint32_t sample_rate

Frecuencia de muestreo de la trama.

uint16_t channels

Cantidad de canales de audio.

uint16_t payload_len

Tamaño en bytes de los datos codificados que siguen.

struct opus_tx

Contexto interno del transmisor.

  • Mantiene el estado de la conexión, el contador de secuencia y el estado del codificador.

Public Members

int sock_fd

Descriptor del socket TCP.

uint32_t seq

Contador para el número de secuencia.

OpusEncoder *enc

Puntero al estado del codificador Opus.

opus_tx_cfg_t cfg

Copia local de la configuración.

struct opus_tx_cfg_t
#include <opus_tx.h>

Configuración para el codificador Opus.

  • Define los parámetros de calidad y comportamiento del flujo de audio.

Public Members

int sample_rate

Frecuencia de muestreo en Hz (8000, 12000, 16000, 24000, 48000).

int channels

Número de canales (1 para mono, 2 para estéreo).

int bitrate

Tasa de bits en bps (ej. 64000).

int complexity

Complejidad computacional (0-10).

int vbr

Variable Bitrate: 1 para habilitar, 0 para CBR (Constant Bitrate).

Funciones para la deserialización y validación de parámetros del sistema.

Functions

static PsdWindowType_t resolve_window_enum(const char *window_str_lower)

Mapea cadenas de texto normalizadas a valores del enumerado PsdWindowType_t.

Parámetros:

window_str_lower – Cadena de texto en minúsculas.

Devuelve:

Enumerado correspondiente al tipo de ventana.

char *strdup_lowercase(const char *str)

Duplica una cadena convirtiendo todos los caracteres a minúsculas.

Advertencia

El usuario es responsable de liberar la memoria mediante free().

Parámetros:

str[in] Cadena de origen.

Devuelve:

Nueva cadena en minúsculas asignada en el heap, o NULL si falla el malloc.

static void set_default_config(DesiredCfg_t *target)

Establece los valores de fábrica/seguridad antes del análisis del JSON.

Parámetros:

target – Puntero a la estructura DesiredCfg_t.

int parse_config_rf(const char *json_string, DesiredCfg_t *target)

Analiza una cadena JSON y puebla una estructura DesiredCfg_t.

  • Sigue un flujo lógico de tres etapas:

  1. Inicialización: Aplica valores por defecto hardcoded.

  2. Extracción: Sobrescribe parámetros con los datos encontrados en el JSON.

  3. Validación (Clamping): Ajusta las frecuencias de filtrado para que no excedan el ancho de banda de Nyquist definido por la frecuencia central y el sample rate.

Nota

Si el JSON es inválido, la función retorna 0 pero mantiene los valores por defecto.

Parámetros:
  • json_string[in] Cadena JSON cruda recibida por la interfaz de comunicación.

  • target[out] Puntero a la estructura de configuración donde se guardarán los datos.

Devuelve:

0 si el proceso fue exitoso, -1 si los punteros de entrada son nulos.

void print_config_summary_DEBUG(DesiredCfg_t *des, SDR_cfg_t *hw, PsdConfig_t *psd, RB_cfg_t *rb)

Imprime un resumen detallado del estado del sistema en formato tabla ASCII.

Parámetros:
  • des – Configuración deseada por el usuario.

  • hw – Estado actual del hardware SDR.

  • psd – Configuración del motor de densidad espectral (PSD).

  • rb – Estado del buffer circular (Ring Buffer).

void print_config_summary_DEPLOY(DesiredCfg_t *des, SDR_cfg_t *hw, PsdConfig_t *psd, RB_cfg_t *rb)

Imprime un registro compacto de una sola línea de la configuración. Ideal para logs de producción (deployment) y monitoreo de terminal en tiempo real.

Algoritmos avanzados de estimación espectral (Welch y PFB).

Defines

PFB_TAPS_PER_CHANNEL 8

Número de taps (coeficientes) por canal en el Banco de Filtros Polifásicos. Un valor de 8 ofrece un compromiso óptimo entre la selectividad del filtro y la carga computacional (latencia).

KAISER_BETA 8.6

Parámetro Beta para la generación de la ventana Kaiser. Un valor de 8.6 resulta en una atenuación de lóbulos laterales de aproximadamente 80 dB, minimizando drásticamente el leakage espectral en señales de gran rango dinámico.

IMPEDANCE_50_OHM 50.0

Impedancia de referencia del sistema (Ohmios). Utilizada para la conversión de la potencia digital a unidades físicas (Watts), asumiendo que el front-end de radio está acoplado a 50 \Omega.

POWER_FLOOR_WATTS 1.0e-20

Suelo de potencia mínimo (Watts) para evitar inestabilidad numérica. Se utiliza como «clamp» antes del cálculo logarítmico para prevenir \log(0) o valores de dBm excesivamente negativos en ausencia de señal.

Functions

int load_iq_into_signal(const int8_t *buffer, size_t buffer_size, signal_iq_t *signal_data)

Convierte un búfer IQ interleaved dentro de una estructura ya asignada.

Parámetros:
  • buffer – Puntero a datos [I0, Q0, I1, Q1, …].

  • buffer_size – Tamaño total en bytes.

  • signal_data – Estructura destino con capacidad suficiente en signal_iq.

Devuelve:

0 en éxito, -1 si hay error o la capacidad no alcanza.

signal_iq_t *load_iq_from_buffer(const int8_t *buffer, size_t buffer_size)

Carga y convierte un búfer de bytes interleaved en señal compleja IQ.

Parámetros:
  • buffer – Puntero a datos [I0, Q0, I1, Q1, …].

  • buffer_size – Tamaño total en bytes.

Devuelve:

Puntero a estructura signal_iq_t con datos en double complex.

void iq_compensation(signal_iq_t *signal_data)

Compensación ciega básica de IQ imbalance por bloque.

Compensación de desequilibrios IQ (IQ Imbalance Compensation).

Esta rutina realiza: 1) Remoción de DC en I y Q 2) Balance de ganancia entre ramas I/Q 3) Decorrelación lineal de Q respecto de I

Es una compensación global, ciega y de segundo orden. No corrige efectos dependientes de frecuencia.

Esta función corrige defectos comunes introducidos por el front-end analógico y el proceso de digitalización de señales complejas IQ. La corrección se realiza in-situ y consta de tres etapas secuenciales:

1. Eliminación de DC Offset

Se calcula y elimina la componente continua (DC) de los canales I y Q, reduciendo el pico central en el espectro:

I'_{n} = I_{n} - \frac{1}{N}\sum_{k=0}^{N-1} I_k,\quad
Q'_{n} = Q_{n} - \frac{1}{N}\sum_{k=0}^{N-1} Q_k

2. Corrección de Desequilibrio de Ganancia

Se ajusta la ganancia del canal Q para igualar su potencia media con la del canal I:

G = \sqrt{\frac{\sum I_n^2}{\sum Q_n^2}},\quad
Q''_{n} = G \cdot Q'_{n}

3. Corrección de Fase (Decorrelación Lineal)

Se elimina la proyección lineal del canal I sobre Q, corrigiendo la falta de ortogonalidad entre ambos canales.

\rho = \frac{\sum I_n Q_n}{\sum I_n^2} \implies Q^{final}_{n} = Q''_{n} - \rho \cdot I'_{n}

Nota

Esta compensación mejora significativamente el rechazo de la imagen espectral, pero no sustituye una calibración analógica completa del receptor.

Parámetros:
  • signal_data – Puntero a la estructura con las muestras IQ.

  • signal_data[inout] Puntero a la estructura que contiene el búfer de muestras complejas y el número de muestras. Los datos se modifican directamente en memoria (in-place).

void free_signal_iq(signal_iq_t *signal)

Libera la memoria utilizada por una estructura signal_iq_t.

Parámetros:

signal – Estructura a liberar.

static inline double clampd(double x, double lo, double hi)

Restringe un valor de punto flotante a un rango específico [lo, hi].

Parámetros:
  • x – Valor de entrada a evaluar.

  • lo – Límite inferior permitido.

  • hi – Límite superior permitido.

Devuelve:

El valor x si está dentro del rango, de lo contrario devuelve el límite excedido.

static inline double db_to_lin_amp(double db)

Convierte un valor de ganancia/amplitud de decibelios (dB) a escala lineal. La conversión sigue la fórmula de amplitud:

A_{lineal} = 10^{\frac{dB}{20}}

Parámetros:

db – Valor en decibelios.

Devuelve:

Amplitud en escala lineal.

static inline double raised_cos(double t)

Calcula una función de coseno alzado (Raised Cosine) en el intervalo [0, 1]. Esta función genera una transición suave (suavizado) entre 0 y 1, útil para funciones de ventana o desvanecimientos (fading).

  • La fórmula aplicada es:

    f(t) = 0.5 - 0.5 \cdot \cos(\pi \cdot t)

    donde t se restringe internamente al rango [0, 1].

Parámetros:

t – Parámetro de entrada (típicamente tiempo normalizado o fase).

Devuelve:

Valor suavizado entre 0.0 y 1.0.

int find_params_psd(DesiredCfg_t desired, SDR_cfg_t *hack_cfg, PsdConfig_t *psd_cfg, RB_cfg_t *rb_cfg)

Determina parámetros óptimos de PSD a partir de un RBW deseado.

Calcula el tamaño de FFT necesario para alcanzar una resolución espectral aproximada (RBW) considerando el ancho de banda equivalente al ruido (ENBW) de la ventana seleccionada.

El tamaño de segmento se fuerza a la siguiente potencia de dos por eficiencia computacional en FFT:

N_{perseg} = 2^{\lceil \log_2(\text{ENBW} \cdot F_s / \text{RBW}) \rceil}

Nota

El RBW resultante es aproximado y depende del tipo de ventana seleccionada.

Parámetros:
  • desired[in] Configuración deseada por el usuario.

  • hack_cfg[out] Configuración resultante para el hardware SDR.

  • psd_cfg[out] Configuración del algoritmo PSD.

  • rb_cfg[out] Configuración del búfer circular de adquisición.

Devuelve:

0 en caso de éxito.

static void convert_to_dbm_inplace(double *psd, int length)

Conversión de densidad de potencia lineal a dBm.

Convierte valores de potencia normalizados (W/Hz) a escala logarítmica dBm, asumiendo una impedancia de carga de 50 Ω:

P_{dBm} = 10 \log_{10}(P_{W} \cdot 1000)

Nota

Esta conversión asume que la señal IQ está correctamente escalada. Los valores obtenidos representan potencia relativa al ADC y no potencia RF absoluta sin una calibración del sistema.

double get_window_enbw_factor(PsdWindowType_t type)

Calcula el factor ENBW (Equivalent Noise Bandwidth) de una ventana.

Parámetros:

type – Tipo de ventana.

Devuelve:

Factor multiplicativo (ej. 1.5 para Hann).

static void generate_window(PsdWindowType_t window_type, double *window_buffer, int window_length)

Genera los coeficientes de la función de ventana seleccionada.

  • Las funciones de ventana se utilizan para reducir el «spectral leakage» (filtración espectral) al truncar la señal en el tiempo antes de aplicar la FFT. Para todas las fórmulas, se define M = L - 1, donde L es la longitud de la ventana.

  • Dependiendo del window_type, se aplica una de las siguientes ecuaciones para 0 \le n \le M:

  • - Rectangular: No aplica atenuación.

    w[n] = 1

  • Hann: Excelente para propósitos generales y buena resolución de frecuencia.

    w[n] = 0.5 \left( 1 - \cos\left( \frac{2\pi n}{M} \right) \right)

  • Hamming: Optimiza la cancelación del primer lóbulo lateral.

    w[n] = 0.54 - 0.46 \cos\left( \frac{2\pi n}{M} \right)

  • Blackman: Mayor atenuación de lóbulos laterales a costa de un lóbulo principal más ancho.

    w[n] = 0.42 - 0.5 \cos\left( \frac{2\pi n}{M} \right) + 0.08 \cos\left( \frac{4\pi n}{M} \right)

  • Bartlett (Triangular):

    w[n] = 1 - \left| \frac{n - M/2}{M/2} \right|

  • Flat Top: Diseñada para una medición precisa de la amplitud de los picos.

    w[n] = a_0 - a_1 \cos\left(\frac{2\pi n}{M}\right) + a_2 \cos\left(\frac{4\pi n}{M}\right) - a_3 \cos\left(\frac{6\pi n}{M}\right) + a_4 \cos\left(\frac{8\pi n}{M}\right)

    Donde: a_0=1, a_1=1.93, a_2=1.29, a_3=0.388, a_4=0.032.

Parámetros:
  • window_type – Identificador de la ventana (PsdWindowType_t).

  • window_buffer – Búfer donde se almacenarán los window_length coeficientes calculados.

  • window_length – Número total de puntos de la ventana (típicamente NPERSEG).

static void fftshift(double *data, int n)

Realiza un desplazamiento circular para centrar la frecuencia cero (DC).

  • Los algoritmos de FFT devuelven los datos en el orden estándar de salida: [0 a Fs/2] seguido de [-Fs/2 a 0]. Esta función intercambia la primera mitad del búfer con la segunda para obtener un eje de frecuencias ordenado de:

    [-F_s/2, \dots, 0, \dots, F_s/2]

  • La operación consiste en un swap de bloques:

  • El bloque [0, \frac{n}{2}-1] se mueve al final.

  • El bloque [\frac{n}{2}, n-1] se mueve al principio.

Nota

Utiliza asignación dinámica temporal mediante malloc para evitar el desbordamiento de pila (stack overflow) en FFTs de gran tamaño, a diferencia de alloca.

Parámetros:
  • data – Puntero al arreglo de datos (double) que se desea desplazar.

  • n – Número de elementos en el arreglo (debe coincidir con el tamaño de la FFT).

void execute_welch_psd(signal_iq_t *signal_data, const PsdConfig_t *config, double *f_out, double *p_out)

Estimación de la Densidad Espectral de Potencia mediante el método de Welch.

El método de Welch divide la señal en segmentos solapados, aplica una ventana temporal a cada uno, calcula su periodograma y promedia los resultados para reducir la varianza del estimador.

static double bessi0(double x)

Función de Bessel de primera especie de orden cero modificada I_0(x).

  • Esta función calcula una aproximación numérica de la función de Bessel mediante su expansión en serie de potencias:

    I_0(x) = \sum_{k=0}^{\infty} \frac{(\frac{1}{4}x^2)^k}{(k!)^2}

  • Se utiliza específicamente para el diseño de la Ventana de Kaiser, la cual es óptima para maximizar la energía en el lóbulo principal.

Parámetros:

x – Valor de entrada (argumento de la función).

Devuelve:

La aproximación de I_0(x). La iteración se detiene cuando el término incremental es menor a 10^{-12} para garantizar precisión de doble flotante.

static void generate_kaiser_proto(double *h, int len, double beta)

Genera los coeficientes de una ventana Kaiser para el filtro prototipo del PFB.

  • Esta función implementa la ventana de Kaiser, la cual es una aproximación a la función de onda esferoidal alargada que maximiza la concentración de energía en el lóbulo principal. Se utiliza como filtro prototipo en la arquitectura PFB.

  • La ventana se define mediante la fórmula:

    w[n] = \frac{I_0 \left( \beta \sqrt{1 - \left( \frac{2n}{L-1} - 1 \right)^2} \right)}{I_0(\beta)}

    donde L es la longitud total del filtro y I_0 es la función de Bessel modificada de primera especie y orden cero.

  • **Impacto del parámetro Beta ( \beta):**

  • \beta = 0: Equivale a una ventana Rectangular.

  • \beta = 5.0: Similar a una ventana Hamming.

  • \beta = 8.6: Valor por defecto en este módulo, proporciona ~80 dB de rechazo.

Parámetros:
  • h – Búfer de salida donde se almacenarán los coeficientes (tamaño len).

  • len – Longitud total del filtro (calculada como M \cdot T).

  • beta – Parámetro de forma que controla la relación entre el ancho del lóbulo y la atenuación.

void execute_pfb_psd(signal_iq_t *signal_data, const PsdConfig_t *config, double *f_out, double *p_out)

Estimación de PSD mediante Banco de Filtros Polifásicos (PFB).

Este método utiliza un filtro FIR prototipo de longitud L = M \cdot T (ventana Kaiser) descompuesto en T ramas polifásicas de longitud M.

Para cada bloque b, la entrada a la FFT se calcula como:

X_{fft}[m] =
\sum_{t=0}^{T-1}
x[bM + tM + m] \cdot h[tM + m]

donde:

  • M: número de canales (bins FFT).

  • T: taps por canal.

  • h[tM + m]: coeficientes del filtro prototipo reorganizados en componentes polifásicas.

struct welch_window_cache_t

Public Members

int nperseg
PsdWindowType_t window_type
double *window
double u_norm

Implementación de un búfer circular (Ring Buffer) seguro para hilos.

Functions

void rb_init(ring_buffer_t *rb, size_t size)

Inicializa el búfer circular y su mutex.

Parámetros:
  • rb – Puntero a la estructura del búfer.

  • size – Capacidad deseada en bytes.

void rb_free(ring_buffer_t *rb)

Libera la memoria y destruye el mutex.

Nota

Realiza un borrado seguro de los datos (memset a 0) antes de liberar.

Parámetros:

rb – Puntero a la estructura a liberar.

void rb_reset(ring_buffer_t *rb)

Reinicia los índices y limpia el contenido del búfer.

Parámetros:

rb – Puntero al búfer.

void rb_discard_all(ring_buffer_t *rb)

Descarta de forma atómica todos los bytes pendientes de lectura.

No toca el contenido físico del búfer, solo adelanta el cursor de lectura para que futuras lecturas usen únicamente datos nuevos.

Parámetros:

rb – Puntero al búfer.

size_t rb_write(ring_buffer_t *rb, const void *data, size_t len)

Escribe datos en el búfer.

size_t rb_read(ring_buffer_t *rb, void *data, size_t len)

Lee datos del búfer.

size_t rb_available(ring_buffer_t *rb)

Devuelve la cantidad de bytes disponibles para lectura.

Parámetros:

rb – Puntero al búfer.

Devuelve:

size_t Bytes listos para ser leídos.

Capa de Abstracción de Hardware (HAL) para dispositivos HackRF.

Functions

static void tune_freq_with_ppm(hackrf_device *dev, uint64_t target_freq, float ppm_error)

Calcula y aplica la frecuencia corregida según el error de reloj. La fórmula utilizada es: $f_{corregida} = f_{objetivo} \times (1 + \frac{PPM}{1,000,000})$.

Parámetros:
  • dev – Puntero al dispositivo.

  • target_freq – Frecuencia deseada en Hz.

  • ppm_error – Error de cristal en partes por millón.

void hackrf_apply_cfg(hackrf_device *dev, SDR_cfg_t *cfg)

Aplica una configuración completa al dispositivo HackRF.

  • Esta función centraliza las llamadas a libhackrf para establecer ganancias, frecuencia y frecuencia de muestreo de una sola vez.

Parámetros:
  • dev – Puntero al dispositivo HackRF abierto.

  • cfg – Puntero a la estructura de configuración que se desea aplicar.

Utilidades varias.

Functions

char *getenv_c(const char *key)

Lee el valor de una clave específica desde un archivo .env local.

  • Busca en el archivo «.env» una línea que comience con la clave y el signo “=”. Útil para cargar configuraciones sin depender de las variables de entorno del sistema.

Nota

El llamador es responsable de liberar (free()) la memoria del resultado.

Parámetros:

key – La clave que se desea buscar (ej. «API_URL»).

Valores devueltos:

NULL – Si el archivo no existe o la clave no se encuentra.

Devuelve:

char* Cadena de caracteres con el valor asignado.

int shm_add_to_persistent(const char *key, const char *value_text)

Agrega/actualiza una clave en /dev/shm/persistent.json de forma segura.

Implementa protección concurrente con file-lock exclusivo (flock) y persistencia con fsync, emulando el patrón de ShmStore en Python.

Parámetros:
  • key – Clave JSON a insertar/actualizar.

  • value_text – Valor en texto. Si es JSON válido (número, bool, objeto, array, string con comillas), se guarda tipado. Si no, se guarda como string.

Devuelve:

int 0 en éxito, -1 en error.

char *shm_consult_persistent(const char *key)

Consulta una clave en /dev/shm/persistent.json de forma segura.

Usa lock compartido (flock) para lectura concurrente segura.

  • Si el valor es string JSON, retorna el contenido sin comillas.

  • En otros tipos (número/bool/objeto/array), retorna JSON serializado.

Parámetros:

key – Clave JSON a consultar.

Devuelve:

char* Memoria dinámica con el valor; liberar con free(). Retorna NULL si no existe o hay error.

char *ashm_consult_persistent(const char *key)

Alias de compatibilidad para shm_consult_persistent().

Parámetros:

key – Clave JSON a consultar.

Devuelve:

char* Igual que shm_consult_persistent(). Liberar con free().

Utilidad de sockets ZeroMQ REP para comunicación síncrona de payloads JSON.

Defines

ZBUF_SIZE 65536

Tamaño máximo del búfer de mensajes.

Functions

static int internal_connect(zpair_t *pair)

Ayudante interno para configurar opciones de socket y conectar. Configura un socket REP con timeouts cortos, HWM mínimo y reconexión automática.

Parámetros:

pair – La instancia a configurar.

Devuelve:

Código de resultado ZMQ (0 éxito, -1 error).

zpair_t *zpair_init(const char *ipc_addr, int verbose)

Reserva memoria e inicializa una nueva conexión ZMQ REP.

Parámetros:
  • ipc_addr – Dirección de conexión (ej. «ipc:///tmp/feed.ipc»).

  • verbose – Habilita o deshabilita la salida de errores por consola.

Devuelve:

Puntero a zpair_t si tiene éxito, NULL en caso de fallo de memoria.

int zpair_recv(zpair_t *pair)

Espera un request y lo guarda en pair->buffer.

Parámetros:

pair – Puntero a la instancia de zpair_t inicializada.

Devuelve:

Número de bytes recibidos; 0 si venció el timeout; -1 si falló.

int zpair_reconnect(zpair_t *pair)

Recrea el socket para resetear el estado interno REQ/REP tras errores.

Parámetros:

pair – Puntero a la instancia activa de zpair_t.

Devuelve:

0 en éxito, -1 en error.

int zpair_send(zpair_t *pair, const char *json_payload)

Envía un payload JSON como reply del request actual.

Parámetros:
  • pair – Puntero a la instancia activa de zpair_t.

  • json_payload – Cadena de texto a transmitir.

Devuelve:

Número de bytes enviados, o -1 en caso de fallo.

void zpair_close(zpair_t *pair)

Cierra sockets y libera memoria.

Parámetros:

pair – Puntero a la instancia de zpair_t a destruir.

struct zpair_t
#include <zmq_util.h>

Estructura de gestión para una conexión ZMQ REP síncrona.

Public Members

void *context

Manejador del contexto ZeroMQ.

void *socket

Manejador del socket ZeroMQ.

char *addr

Cadena con la dirección del endpoint.

char buffer[ZBUF_SIZE]

Búfer interno para datos entrantes.

int verbose

Bandera para habilitar logs por stderr.

Nota

Este módulo incluye funciones internas de gestión de buffers que son vitales para evitar el desbordamiento de datos durante barridos de frecuencia rápidos.

1.4. Control de GPIO (BACN)

group GPIO

Interfaz de control GPIO para el módulo LTE y selección de antenas.

Definición de Pines (Offsets)

PWR_MODULE 4

Pin para control de encendido del módulo.

RST_MODULE 27

Pin para el reset físico del módulo.

ANTENNA_SEL1 23

Selector de RF para la Antena 1.

ANTENNA_SEL2 22

Selector de RF para la Antena 2.

ANTENNA_SEL3 10

Selector de RF para la Antena 3.

ANTENNA_SEL4 24

Selector de RF para la Antena 4.

STATUS 18

Pin de entrada para verificar estado del módulo.

RF1 1

Valor lógico para RF Activo.

RF2 0

Valor lógico para RF Inactivo.

Functions

static struct gpiod_line_request *request_output_line(const char *chip_path, unsigned int offset, enum gpiod_line_value value, const char *consumer)

Solicita y configura una línea de GPIO como salida.

Parámetros:
  • chip_path – Ruta del dispositivo de chip GPIO (ej. «/dev/gpiochip0»).

  • offset – Número del pin dentro del chip.

  • value – Valor inicial de la salida (activo/inactivo).

  • consumer – Etiqueta para identificar quién usa la línea en el sistema.

Devuelve:

struct gpiod_line_request* Puntero al objeto de solicitud, NULL si falla.

static struct gpiod_line_request *request_input_line(const char *chip_path, unsigned int offset, const char *consumer)

Solicita y configura una línea de GPIO como entrada.

Parámetros:
  • chip_path – Ruta del dispositivo de chip GPIO.

  • offset – Número del pin dentro del chip.

  • consumer – Etiqueta para identificar el consumidor.

Devuelve:

struct gpiod_line_request* Puntero al objeto de solicitud, NULL si falla.

uint8_t status_LTE(void)

Obtiene el estado actual del módulo LTE.

Devuelve:

uint8_t Valor leído del pin STATUS (0 o 1).

uint8_t power_ON_LTE(void)

Realiza la secuencia de encendido del módulo LTE.

Devuelve:

uint8_t EXIT_SUCCESS si fue exitoso, EXIT_FAILURE en caso de error.

uint8_t power_OFF_LTE(void)

Realiza la secuencia de apagado controlado del módulo LTE.

Devuelve:

uint8_t EXIT_SUCCESS si fue exitoso, EXIT_FAILURE en caso de error.

uint8_t reset_LTE(void)

Envía un pulso de reset al módulo LTE.

Devuelve:

uint8_t EXIT_SUCCESS si fue exitoso, EXIT_FAILURE en caso de error.

uint8_t select_ANTENNA(uint8_t ANTENNA)

Selecciona una antena específica desactivando las demás.

Parámetros:

ANTENNA – Número de antena a seleccionar (1-4).

Devuelve:

uint8_t Estado de la operación.

uint8_t switch_ANTENNA1(bool RF)

Controla el switch de la Antena 1.

Parámetros:

RF – true para activar, false para desactivar.

Devuelve:

uint8_t EXIT_SUCCESS o EXIT_FAILURE.

uint8_t switch_ANTENNA2(bool RF)

Controla el switch de la Antena 2.

Parámetros:

RF – true para activar, false para desactivar.

Devuelve:

uint8_t EXIT_SUCCESS o EXIT_FAILURE.

uint8_t switch_ANTENNA3(bool RF)

Controla el switch de la Antena 3.

Parámetros:

RF – true para activar, false para desactivar.

Devuelve:

uint8_t EXIT_SUCCESS o EXIT_FAILURE.

uint8_t switch_ANTENNA4(bool RF)

Controla el switch de la Antena 4.

Parámetros:

RF – true para activar, false para desactivar.

Devuelve:

uint8_t EXIT_SUCCESS o EXIT_FAILURE.

uint8_t real_time(void)

Genera un pulso rápido en el pin 16 para pruebas de tiempo real.

Devuelve:

uint8_t EXIT_SUCCESS o EXIT_FAILURE.

Truco

Consulte este módulo si necesita modificar la asignación de pines para una nueva revisión de la PCB o realizar depuración de señales lógicas.