sábado, 2 de marzo de 2019

Transceptor de señales débiles, bit a bit [Parte 3]

Siguiendo con la linea de tiempo del proyecto la baliza estuvo operando durante varios meses donde progresivamente fui trabajando en su estabilidad y confiabilidad haciendo muchas pruebas, la mayor parte sin resultados significativos como para compartirlos. Pero lo cierto es que  llegó el momento de querer experimentar con promover la baliza a una estación de monitoreo de WSPR, es decir que pudiera operar como baliza (y ser reportada) y como estación receptora (y reportar). Una elección inicial obvia fue buscar la respuesta en el programa wsjt-x trabajando con alguno de los transceptores de la estación, pero a poco de pensarlo me quedó claro que no era ese el propósito de la experimentación.
Es claro que para operar como receptor el RBPi necesita hardware adicional. Para eso es ideal utilizar un dongle RTL-SDR, el cual representa un buen compromiso entre funcionalidad y costo. Este dispositivo puede ser configurado como un procesador SDR capaz de sintonizar en el rango nominal de 28 MHz a 1.5 GHz proveyendo una gama de posibilidades en su tasa de muestreo (y por lo tanto la anchura de la banda base). En una cuenta rápida, si la tasa de muestreo es 1200000 muestras/seg el teorema de Nyquist nos ayuda a comprender que se pueden sintonizar segmentos de algo menos de 600 KHz por vez, y el RTL-SDR puede manejar el doble de eso (cómodamente) e incluso llegar a anchos banda de hasta 4 MHz (ver especificación). En realidad se necesita muchisimo menos que eso, el máximo ancho de banda que se requiere para modos digitales de señales débiles es el de un transceptor de SSB en fonía, es decir 2.5 KHz, la enorme cantidad de muestras puede hasta ser una molestia innecesaria, aunque termina agregando "resolución espectral" al proceso de demodulación que es muy útil. El dongle trabaja en un rango de frecuencias muy interesante pero que comienza en el HF lejano (28 MHz) y se extiende bien dentro del rango de VHF y UHF. Sin embargo mi principal interés es operar señales débiles en el rango de bandas bajas de HF. Para extender el rango del dongle lo recomendado es utilizar un "upconverter" que convierta las frecuencias "hacia arriba" desde HF a alguna banda dentro del rango de funcionamiento del RTL-SDR, hay dispositivos comerciales (caros...) o puede hacerse uno casero pues son relativamente simples [link]. Sin embargo los modelos mas recientes del RTL-SDR tienen una función que se denomina "direct sampling" (muestreo directo), que si bien degrada un poco la performance [link]; si bien no experimenté con upconverter (el cual objetivamente es una mejor solución técnica) los resultados con muestreo directo fueron suficientemente buenos como para adoptarlos. En algunos modelos antiguos de RTL-SDR hay que realizar una modificación de hardware para habilitar el muestreo directo, lo que es confuso y luce limitante, pero en los modelos mas recientes es una función que se habilita por software y soportado por la versión actualizada de los drivers.
Para poder un utilizar dongle RTL-SDR  en una RBPi hay que instalar drivers adecuados, uno de los cuales es la librerías denominada librtlsdr. Para evitarnos tener que desarrollar la aplicación que implemente las funciones de la librería es útil recurrir a programas ya hechos, y uno de los más útiles que encontré fue el paquete rtl-sdr
Hay varias versiones ("branches") en desarrollo, algunas mas estables que otras, y otras de naturaleza mas experimental. La que utilizo es una experimental porque tiene algunas funciones que me son útiles. Para instalar ambas librerías:

#!/bin/sh
#*----- Install librtlsdr

sudo apt-get update
sudo apt-get install git build-essential cmake libfftw3-dev pulseaudio pavucontrol mplayer
sudo apt-get install  libusb-1.0-0-dev curl libcurl4-gnutls-dev ntp
sudo git clone https://github.com/steve-m/librtlsdr 
cd /home/pi/librtlsdr
sudo mkdir build
cd build/
sudo cmake ../
sudo make
sudo make install
sudo ldconfig

Y para instalar el paquete rtl-sdr

#!/bin/sh
git clone https://github.com/keenerd/rtl-sdr
cd rtl-sdr/
mkdir build
cd build
cmake ../ -DINSTALL_UDEV_RULES=ON -DDETACH_KERNEL_DRIVER=ON
make
sudo make install
sudo cp ../rtl-sdr.rules /etc/udev/rules.d/
sudo ldconfig

sudo reboot

Este paquete permite explorar distintas alternativas para construir una cadena receptora completa.

Usando rtl_fm (método #1)

Como parte del paquete está el programa rtl_fm, originalmente concebido para poder demodular señales de FM de banda ancha (comercial) y posteriormente expandido para recibir prácticamente todos los modos. Es parte de los programas compilados al construir el paquete rtl-sdr y su funcionamiento consiste en leer muestras desde el dongle, filtrarlas de acuerdo al modo que se esté demodulando, diezmarlas (decimarlas) para retener solo un número de muestras consistentes con el ancho de banda del modo que se está demodulando (desde centenares de KHz a un par de ellos) y producir una señal "cruda"  de audio digital que puede ser reproducida por un reproductor multimedia como aplay o mplayer. El primero está normalmente instalado por defecto en la distribución Raspbian, mientras que el segundo fue instalado como parte de los paquetes desplegados en el paso previo.
El script típico de ejecución será usando mplayer:

rtl_fm -M usb -g 29 -s 1200000 -f 14095600 -r 48k -l 0 -E direct \
    | mplayer -nocache -rawaudio samplesize=2:channels=1:rate=48000 -demuxer rawaudio -

Usando aplay hay que cambiar la segunda parte de la cadena poniendo parámetros similares correspondientes. En sucesivos experimentos he tenido mejores resultados (mas continuos, menos interrupciones) con mplayer, probablemente debido a que mplayer prioriza la continuidad de la reproducción y por lo tanto establece buffers para asegurarla. El precio es que tiene un retardo de entre 2 y 3 segundos en su ejecución, lo que en modos digitales de baja señal terminan siendo críticos y deben ser compensados, veremos como mas tarde.
Desafortunadamente, el programa rtl_fm opera en modo direct sampling con el canal 1 (I) y para éste uso se debe habilitar el canal 2 (Q), la modificación es simple, hay que editar

nano /home/pi/rtl-sdr/rtl_fm.c

Y cambiar

if (strcmp("direct",  optarg) == 0) {
dongle.direct_sampling = 1;}

por

if (strcmp("direct",  optarg) == 0) {
dongle.direct_sampling = 2;}

Luego del cambio hay que salvar el fuente y construir nuevamente el paquete para lo cual hay que ejecutar

cd /home/pi/build
cmake ../ -DINSTALL_UDEV_RULES=ON -DDETACH_KERNEL_DRIVER=ON
make
sudo make install
sudo cp ../rtl-sdr.rules /etc/udev/rules.d/
sudo ldconfig
sudo reboot

Si ejecutamos el script y abrimos el diálogo de pavucontrol veremos que hay una instancia de mplayer operando como "fuente", si asociamos a ella la salida de auricular de la RBPi o una placa de sonido USB y conectamos auriculares a ella "escucharemos" el receptor tal como lo haríamos en un transceptor común.
Podremos por supuesto cambiar la frecuencia y utilizar esta misma configuración con frecuencias de FT8 (14074.0 KHz), o CW, o SSB, o RTTY o PSK o SSTV, es un receptor SSB (USB) como cualquier otro. El único detalle es que tendremos que cancelar el script (con Ctl-C) para editar la frecuencia en cada caso, no muy ágil para "ruletear" la banda, pero mas que adecuado para "escuchar" un segmento cualquiera haciendo pruebas.
Parece muy poco cómodo pero si uno empieza a pensar un poco con criterio Linux empezará a ver los problemas como una sucesión de pequeños módulos que se conectan unos con otros para lograr un propósito, en esa dirección no sería descabellado inventar un programa que actuando de "pantalla" permita manejar los controles de un transceptor (receptor por ahora) en cuanto a frecuencia, modo, ganancia y otros para, una vez especificados, actuar en la "trastienda" deteniendo el script y lanzándolo nuevamente con los nuevos argumentos. Pero no hace falta inventar eso, ya está inventado, es el programa qtcsdr, solo que usa otras formas de implementar el transceptor (nada impide cambiarselas a éstas, pero eso, eso es otro proyecto).
Hay un buen número de cuestiones prácticas que resolver para integrar un "transceptor" virtual implementado a partir del dongle RTL-SDR (u otro similar) con wsjt-x de forma de utilizarlo en modos de señales débiles, primariamente WSPR y FT8.
Mucho de lo que viene a continuación toma ideas originalmente expuestas en un artículo fantástico denominado "TUTORIAL: SETTING UP A LOW COST QRP (FT8, JT9, WSPR ETC) MONITORING STATION WITH AN RTL-SDR V3 AND RASPBERRY PI 3" [link] cuya lectura es altamente recomendable.
El primero es como lograr que el "audio" generado por el transceptor pueda ser utilizado por wsjt-x.
No es difícil pensar en una solución mas bien de fuerza bruta, con pavucontrol (previamente instalado) conectar la ejecución de mplayer a la salida de una placa USB de sonido, conectar con un cable la salida a la entrada de MIC, configurar wsjt-x para que tome como su entrada a esa y ajustar los niveles de audio para que no haya distorsión. Todo lo que no tiene de elegante el método lo tiene de efectivo, por cierto que anda.
Pero es cierto también que hay formas mas elegantes de hacer lo mismo, y sin necesidad de una placa física USB, el procesamiento de ese audio por esa placa dista de ser gratis en términos de CPU y en la RBPi la CPU no sobra....
Para salvar esta situación se recurre a un "virtual cable" o cualquiera de los nombre con que se lo encuentra; se trata de un programa que tiene la habilidad de comportarse como una placa de sonido (virtual) que opera como un "cable", todo lo que se le coloca en una entrada (virtual) sale por la otra (virtual también). La carga de CPU es enormemente menor, en realidad los datos casi no sea procesan, solo se mueven punteros en memoria muy rápida y eficientemente.
Hay al menos dos formas de implementarlo en forma mas o menos sencilla, una es usando snd-loop y la otra pulseaudio. Ambos son "cables virtuales", el primero viene ya en la distribución RSBPi pero hay que activarlo, al segundo hay que instalarlo.
Para activar y configurar snd-loop hay que seguir los siguientes pasos:

[activar snd-loop]
[configurar  snd-loop]

Para instalar y configurar pulseaudio hay que seguir los siguientes pasos (hechos parcialmente en pasos anteriores pero que no daña repetir):

#!/bin/sh
sudo apt-get update
sudo apt-get pulseaudio pavucontrol

Hay que editar el archivo de configuración

sudo nano /etc/pulse/default.pa

y agregar las siguientes lineas al final del archivo

load-module module-null-sink sink_name=Virtual0 sink_properties=device.description="Virtual0"
load-module module-null-sink sink_name=Virtual1 sink_properties=device.description="Virtual1"

Finalmente hay que optimizar el funcionamiento del pulseaudio limitando el logging que hace, hay que editar el archivo daemon.conf

sudo nano /etc/pulse/daemon.conf

Buscar "log-level" (con Ctl-W) y cambiar a "log-level = error" y remover el comentario para que quede como:

; log-target = auto
log-level = error
; log-meta = no

Salvando el archivo hay que relanzar pulseaudio, lo que se puede hacer con un reboot (sudo reboot) o con "pulseaudio -k".
Si hicimos las cosas bien veremos que en pavucontrol podemos asociar un dispositivo "Virtual0" o "Virtual1" a mplayer. Asignemos "Virtual0". Ejecutando wsjtx tenemos que ir a configuración y designar como dispositivo de entrada "Virtual0.monitor" (el otro extremo del cable virtual), con lo que veremos que el medidor de sonido se "enciende". Hay que trabajar un poco con los controles de nivel para asegurar que el mismo está entre 50 y 70 dB, en todo caso en color "verde".
Es probable que si bien wsjtx en apariencia funcione bien aún así no decodifique correctamente señales, lo haga en forma errática o tenga muchos errores; puede ser que algo de todo lo anterior no anda del todo bien. Pero la causa mas habitual en mi experiencia con ésta configuración es que simplemente hay un problema de sincronismo en el tiempo.
Previamente se dijo que los modos de señales débiles requieren una sincronización de tiempo muy estricta, en incluso se instaló un paquete denominado ntp para asegurar que el reloj esté correcto. Sin embargo el procesamiento de la señal no es instantáneo, tarda entre 2 y 3 segundos, eso significa que cuando la señal es decodificada por wsjtx está "corrida" respecto a la ventana en esa magnitud, muy pequeña pero suficientemente crítica. Para evitar ese problema hay que "correr" las sincronización para que la hora del RBPi "atrase" un par de segundos respecto a la sincronización perfecta; ntp no permite hacer eso, pero un paquete similar llamado "chrony" si.
Para instalarlo

sudo apt-get update
sudo apt-get install chrony

Chrony cuando se instala desinstala automáticamente a ntp, al que reemplaza completamente. Editar el archivo de configuración chrony.conf

sudo nano /etc/chrony/chrony.conf

Y agregar

pool 2.debian.pool.ntp.org iburst offset -2.5

Luego reciclar chrony con

sudo invoke-rc.d chrony restart

Se podrá percibir que el reloj de la máquina ahora atrasa aproximadamente 2 segundos; esto a su debido tiempo generará sus propios problemas, pero por ahora ayudará a que todo funcione.
El programa wsjtx podrá recibir (sintonizando en las frecuencias y modos correctos) recibir señales y decodificarlas normalmente. Recordar que no hay conexión alguna entre la frecuencia que wsjtx asume que está y la que realmente está "escuchando" el transceptor, asegurarse que son las mismas (error común olvidarse de verificar que lo sean).

Maravilla de maravillas, ¡la frambuesita tiene oidos ahora!

2 comentarios:

  1. Saludos Pedro. Que te parece si se usa un rasberry Zero?

    ResponderEliminar
  2. Hola! Tengo una en mi cluster (ecrux), tiene una performance substancialmente menor (un núcleo vs. cuatro de la 3B+), pero creo que es viable como beacon emisor pues ésta función consume 15% en un núcleo. Habría que probar pero creería que dá. Eso si, tiene que ser la W (con WiFi) pues la conexión a Internet es necesaria para sincronizar la frecuencia y el reloj en modos débiles. La diferencia de precio no es tal una vez que a la "lisa" hay que comprarle un dongle WiFi, un hub USB y sus cables asociados. Saludos, Pedro LU7DID

    ResponderEliminar