@lvbernal

2018/12/22

.NET Core en reinos lejanos: hardware, radios y señales

El objetivo de este artículo es brindarles un punto de partida para trabajar con hardware y, en general, con librerías externas desde .NET Core. Encontrarán varios enlaces a documentación y a otros recursos complementarios.

Primero el contexto, que es lo más complejo:

En un Radio Software, Software Defined Radio o SDR, los componentes de procesamiento de señales se implementan en software, aprovechando los procesadores de propósito general, en lugar de utilizar hardware dedicado. Son esenciales para la Radio Cognitiva.

La bladeRF es un SDR fabricado por Nuand, que está disponible como hardware libre. Me gané una durante la GNU Radio Conference de 2012 y desde entonces he participado en varios proyectos que la incluyen. Además de la bladeRF, usaremos un transverter XB-200, que básicamente amplía el rango de frecuencias de la bladeRF.

Abajo la bladeRF y arriba el XB-200

GNU Radio es un toolkit que tiene bloques de procesamiento para desarrollar aplicaciones de Radio Software. Los bloques se "crean" con C++ y se "conectan" con Python. El bloque que se comunica con la bladeRF, utiliza la librería libbladeRF.

En este artículo usaremos la librería libbladeRF desde .NET Core, en un Mac.

El hardware:

Los drivers de la bladeRF incluyen la utilidad bladeRF-cli y varias librerías. Lo primero es conectar el dispositivo, cargar la FPGA, comprobar que esté operando correctamente y activar el XB-200. Los detalles de los comandos no son importantes.

bladeRF con el XB-200 instalado
Podemos comprobar el estado de la tarjeta usando bladeRF-cli

El proceso:

Debemos ubicar la librería y referenciarla en nuestra aplicación por consola de .NET Core. Una opción rápida es copiarla desde /opt/local/lib/libbladeRF.dylib al directorio del proyecto, configurando el .csproj. La ubicación depende de la plataforma (Windows/Linux/Mac) y hay opciones que nos permiten buscar en distintos directorios o usar variables de entorno según el caso.

<ItemGroup>
<Content Include="/opt/local/lib/libbladeRF.dylib">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

El primer paso es buscar los dispositivos conectados, para esto debemos usar el método bladerf_open de libbladeRF. Como se ve en la documentación, el método recibe como parámetros un device que se pasa como referencia y que, si todo sale bien, contendrá el handle del dispositivo, y un device_identifier que dejaremos en null para activar la detección automática; y retorna un código de estado.

Para llamar ese método desde C#, usamos InteropServices y creamos un conector, binding o fachada con la librería.

using System.Runtime.InteropServices;

[DllImport(dllName: "libbladeRF", EntryPoint="bladerf_open")]
public static extern int BladeRFOpen(out IntPtr handle, string id);

De aquí quiero resaltar 2 cosas: la propiedad EntryPoint, que especifica el nombre del método que queremos llamar. Y el tipo de dato IntPtr, que representa un apuntador y es una de las mejores herramientas que tenemos para interactuar con librerías C++. Podemos llamar el método así:

public static IntPtr GetDeviceHandle()
{
IntPtr handle;
BladeRFOpen(out handle, null);
return handle;
}

Luego debemos establecer la tasa de muestreo usando el método bladerf_set_sample_rate de libbladeRF. El método recibe el handle, el canal (transmisión o recepción), el valor y una referencia que tendrá el estado después del proceso; y retorna un código de estado.

[DllImport(dllName: "libbladeRF", EntryPoint="bladerf_set_sample_rate")]
public static extern int BladeRFSetSampleRate(IntPtr handle, int channel, int value, out int current);

public static bool SetSampleRate(IntPtr handle, int channel, int sampleRate)
{
int current;
BladeRFSetSampleRate(handle, channel, sampleRate, out current);
return current == sampleRate;
}

Debemos hacer lo mismo con el método  bladerf_set_frequency, que recibe como parámetros el handle, el canal y el valor; y retorna el código de estado. A diferencia de bladerf_set_sample_rate, este no retorna el valor final de la variable.

[DllImport(dllName: "libbladeRF", EntryPoint="bladerf_set_frequency")]
public static extern int BladeRFSetFrequency(IntPtr handle, int channel, int value);

public static bool SetCenterFreq(IntPtr handle, int channel, int centerFreq)
{
int result = BladeRFSetFrequency(handle, channel, centerFreq);
return result == 0;
}

Otros métodos sirven para habilitar el XB-200 y para desactivar sus filtros físicos. El primer proceso es importante porque queremos sintonizar 107.5MHz pero la bladeRF va de 300MHz a 3.8GHz. El transverter agrega de 60kHz a 300MHz. El segundo es por simplicidad del ejercicio.

[DllImport(dllName: "libbladeRF", EntryPoint="bladerf_expansion_attach")]
public static extern int BladeRFExpansionAttach(IntPtr handle, int xb);

[DllImport(dllName: "libbladeRF", EntryPoint="bladerf_xb200_set_filterbank")]
public static extern int BladeRFXB200SetFilterbank(IntPtr handle, int channel, int filter);

public static void AttachXB200(IntPtr handle)
{
BladeRFExpansionAttach(handle, 2);
BladeRFXB200SetFilterbank(handle, 0, 3);
}

Para terminar, unimos las partes en el Main.

static void Main(string[] args)
{
var handle = GetDeviceHandle();
Console.WriteLine("Device handle: " + handle);

if (handle == IntPtr.Zero)
{
Console.WriteLine("No device detected");
return;
}

AttachXB200(handle);

var sampleRate = 5_000_000;
var srResult = SetSampleRate(handle, 0, (int)sampleRate);
Console.WriteLine("Set sample rate " + sampleRate + ": " + srResult);

var centerFreq = 107_500_000;
var cfResult = SetCenterFreq(handle, 0, (int)centerFreq);
Console.WriteLine("Set center frequency " + centerFreq + ": " + cfResult);
}

Luego de compilar y correr el programa, se puede ver el resultado en la consola.

Resultado del programa
A través de la librería podemos obtener muestras para después procesarlas y generar gráficos como los de Gqrx, que es un referente de la comunidad, o para crear otras aplicaciones relacionadas con SDR y procesamiento digital de señales.

Este es un reino dominado por C++ y Python, pero C# puede tener un protagonismo interesante gracias a .NET Core. En mi opinión, cada vez más lenguajes fuertemente tipados van a ganar espacio en análisis y procesamiento de datos, conforme mejore el rendimientos y el soporte multiplataforma.

Espero que este artículo les ayude con proyectos que requieren interacción con hardware y con librerías externas. Lo más complejo es interpretar el API externo para evitar errores graves, pero afortunadamente los entornos de ejecución están cada vez más aislados y eso nos evita muchas pantallas azules.

En otra ocasión les mostraré herramientas de visualización usando el mismo hardware. Pueden consultar el código de este ejemplo en GitHub.

Este es un artículo para el primer calendario de adviento de C# en español.
Los invito a revisar los otros artículos de la iniciativa.

Oops!:

Son inevitables cuando trabajamos con hardware y librerías externas...

No verás alertas ni problemas de compilación, sólo programas rotos

2018/12/20

El Meetup de diciembre en CaliSharp

El 18 de diciembre tuvimos el último meetup de 2018. En esta ocasión nos acompañó Julio Avellaneda, MVP de Colombia, con una charla muy interesante sobre Azure App Services. Le agradecemos a Julio su gran aporte para fortalecer la comunidad y esperamos seguir aprendiendo de su contenido. Pueden ver la charla en YouTube o más abajo en este post.

Este ha sido un año muy especial para CaliSharp. Quiero agradecerle a todos los integrantes de la comunidad por acompañarnos siempre, a los speakers que han aportado a esta iniciativa, a nuestros amigos de Carmii y Modularis que sirvieron de sponsors en algunas ocasiones y a Manuel por su ayuda organizando esta vuelta.

Esperamos que nos sigan acompañando durante 2019, el penúltimo martes de cada mes.


2018/12/13

.NET Conf CO 2018


Entre el 8 y el 10 de noviembre tuve la oportunidad de asistir a la .NET Conf CO, uno de los eventos de tecnología más importantes del país, organizado por comunidades. Allá, Manuel Zapata y yo dictamos el taller "Desarrollo de funciones AWS Lambda con .NET Core". Los invito a revisar un buen post sobre los temas del evento, que Manuel escribió.

Para mí, además de las charlas, lo más importante fue compartir con referentes de la comunidad tecnológica de la talla de Rodrigo Díaz, Ricardo Gonzalez, Walter Novoa, Juan Carlos Ruiz, Sorey García, Yohanna Ramírez, Pablo Di Loreto, Miguel Mendoza, Freddy Angarita, Luis Linares, Guillermo Bellmann, Julie Lerman, Marco Dal Pino, Dan Fernandez y otro montón de personas entre conferencistas, organizadores y asistentes.

En el canal de YouTube de la conferencia encuentran entrevistas, el video de la keynote y de otras conferencias. Y aquí algunas fotos que tomé durante los 3 días.

Quiero agradecerle a Manuel por la invitación, a Sorey y su equipo por hacer posible la conferencia y a todas las personas con las que compartimos en Medellín. También a los integrantes de la comunidad CaliSharp que estuvieron allá representando a Cali durante el evento.

CaliSharp presente en la conferencia
Con Manuel, en el segundo día de conferencias
Compartiendo con los speakers
.NET Conf CO 2018