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

El Meetup de noviembre en... CaliSharp!

Si, Xamarin Cali ahora es CaliSharp y en noviembre también tuvimos Meetup!

Este fue muy especial porque nos acompañó Luis Beltrán, Microsoft MVP de México, con la charla "Apps móviles inteligentes con Azure Cognitive Services y Xamarin". Quiero agradecerle a Luis su gran aporte e invitarlos a visitar su blog. Además, la charla quedó grabada en YouTube! Pueden ver el video aquí o más abajo en este post.

Los invito a unirse al Meetup y a estar muy pendientes de las charlas, el penúltimo martes de cada mes.



2018/11/02

El Meetup de octubre en Xamarin Cali

En octubre también tuvimos Meetup de Xamarin Cali! Edward Linares nos contó su experiencia con "MVVM y Local Storage", dos de las "vitaminas" que nos había mostrado en el meetup de agosto.

Edward es un integrante activo de la comunidad, con mucha experiencia desarrollando aplicaciones móviles con Xamarin.

El Meetup de septiembre en Xamarin Cali

En septiembre fue mi turno para compartir en el Meetup de Xamarin Cali; hablé de "Plugins para Visual Studio Code". Aproveché para mostrar algunas aplicaciones SDR (Software Defined Radio) y cómo se pueden desarrollar plugins para necesidades muy específicas.

2018/08/24

El Meetup de agosto en Xamarin Cali

En agosto tuvimos otro Meetup de Xamarin Cali. Edward Linares nos habló de Xamarin durante su charla "Hola mundo con vitaminas" y Manuel Zapata nos mostró cómo asegurar aplicaciones .Net Core con Auth0. Aprendimos mucho durante la jornada y conocimos más desarrolladores .Net de la ciudad.

Ese día compartimos una noticia que nos alegra mucho: el patrocinio del meetup por parte de la .NET Foundation. Ya aparecemos en el sitio web y nos enteramos que fuimos el primer patrocinado en Colombia!

Si tienen un meetup sobre tecnologías .Net, pueden ingresar a http://dotnetfoundation.org/ y llenar el formulario para acceder al beneficio de Meetup Pro en modalidad Sponsorship. Pero lo mejor es aparecer en el sitio web y en el buscador de meetups de la fundación.

2018/07/26

Xamarin - Lecciones aprendidas. Cierre

La serie de lecciones aprendidas terminó con una presentación general en el Meetup de Xamarin Cali. En el evento también participó Manuel Zapata con su charla "Desarrollo multiplataforma con .Net Core".

Lo mejor: conocimos a muchos desarrolladores .Net de la ciudad.

2018/05/26

Xamarin - Lecciones aprendidas 4

Esta es la cuarta parte de las lecciones aprendidas con Xamarin. Espero que puedan tomar algo útil de esta serie de contenidos.

A veces es útil el “uso desmedido” de los Converters:

Los Converters son componentes que permiten establecer una propiedad de un elemento xaml, a partir de un valor que podría no ser del mismo tipo que el requerido por la propiedad. Yo los uso para poner logos (Image) dependiendo del tipo de item (int/string), colores de fondo (Color) dependiendo de si el elemento está seleccionado o no (bool), etc.

Hace unos días tuve que hacer una app a contrarreloj: una calculadora de tasas de espectro, que sirve para definir el valor del espectro radioeléctrico dependiendo de ciertas variables de uso y de los servicios que se van a prestar, basado en el documento de UIT-D “Guidelines for the review of spectrum pricing methodologies and the preparation of spectrum fee schedules”.

Lo importante era velocidad (10 días), pero la app debía tener cierta interacción porque las variables para el cálculo dependen del servicio… velocidad + interacciones = código spaghetti. Entonces decidí, en lugar de usar muchas condicionales, usar muchos Converters.

El código de la Page se ve así:

https://github.com/i2tResearch/SpectrumFees/blob/master/SpectrumFees/Pages/SpectrumFeesPage.xaml

Pero el ViewModel quedó muy simple:

https://github.com/i2tResearch/SpectrumFees/blob/master/SpectrumFees/ViewModels/MainViewModel.cs

Cambiar la versión del assembly antes de (intentar) publicar:

Todas las aplicaciones Android tienen un archivo AndroidManifest.xml que contiene las propiedades de la aplicación, incluyendo versionCode (version number en Visual Studio) y versionName (version name en Visual Studio). El primero es un número secuencial interno que determina si una versión es más reciente que otra. Y el segundo es el número que se le muestra al usuario final (ej. 1.1.4).

Eso debe ser conocido por todos los desarrolladores Android. Lo importante es que en Xamarin también tenemos el archivo AssemblyInfo.cs, cuya propiedad AssemblyVersion debe coincidir con versionName.

[assembly: AssemblyVersion("1.1.4")]

Si los valores no coinciden, la aplicación no se puede publicar.

2018/03/07

Xamarin - Lecciones aprendidas 3

Esta es la tercera parte de las lecciones aprendidas con Xamarin. Aquí están la primera y la segunda.

Quiero insistir, especialmente en este post, que no quiero hablar de "buenas prácticas" sino de cosas que me han ayudado a solucionar situaciones específicas. Esta vez son los códigos QR como opción para "iniciar sesión" en las apps.

Autorización usando QR.

Algunas apps tienen un portal para mostrar reportes, modificar parámetros, registrar usuarios, etc. que ya está protegido por contraseña. Ese portal se puede aprovechar para mostrar un código QR que contiene el token de autorización para consumir servicios web. Es como "implementar la segunda parte de OAuth2" (aquí está bien explicado todo el protocolo OAuth2). Esto fue lo que hice:


Genero el código siguiendo la documentación de asp.net core sobre Hash Codes y me aseguro que sólo el usuario autorizado pueda verlo, usando Authorization Handlers. Tiene nombre de la app, el id del recurso y el token de autorización. Del lado del servidor, almaceno el token y valido cada solicitud del API.

Por ejemplo, si suponemos que el usuario tiene un único ítem y que el token es una propiedad del ítem y no del usuario, el código quedaría así:

[HttpGet]
public async Task<IActionResult> Get(int id)
{
    var appCode = Request.Headers["<SomeHeaderName>"].ToString();
    var (isAuthorized, item) = await CodeIsValid(appCode, id);
    if (!isAuthorized)
    {
        return BadRequest();  // Puede ser Unauthorized();
    }

    return Ok(item);
}

...

protected async Task<(bool, Item)> CodeIsValid(string appCode, int itemId)
{
    var item = await Context.Item.SingleOrDefaultAsync(

        i => i.Id == itemId &&
        i.AppCode == appCode);
    return (item != null, item);
}



Dependiendo de la aplicación, el token puede ser una propiedad del usuario, del perfil, de un ítem de más bajo nivel, etc. Se puede generar usando el Id del usuario, el SecurityStamp o un GUID aleatorio. Se debe regenerar cada vez que el usuario desee o cuando cambie la contraseña. Y se deben desconectar todas las aplicaciones que no tengan un código válido.

Del lado del cliente uso ZXing.Net.Mobile.Forms para escanear el QR y Xam.Plugins.Settings para almacenar el token.

2018/02/01

Xamarin - Lecciones aprendidas 2

Esta es la segunda parte de las lecciones aprendidas con Xamarin. Aquí está la primera parte.

API Keys, URLs y otras variables importantes:

Las API Keys, App URLs, Client IDs, etc. se deben manejar con mucho cuidado, especialmente para no publicarlas en repositorios de git, svn o tfs. Ese error es todo un dolor de cabeza.

Todavía estoy buscando la forma de utilizar variables de entorno en proyectos Xamarin desde Visual Studio for Mac. La idea es definir variables por consola (ej. export MY_AZURE_URL=https://myazureurl.azurewebsites.net) y luego usarlas en la aplicación (ej. var myAzureUrl = Environment.GetEnvironmentVariable("MY_AZURE_URL");), como en cualquier framework web decente.

En Xamarin es distinto porque Environment.GetEnvironmentVariable() lee variables locales, en este caso del teléfono, en lugar de cargarlas durante la compilación. Y Visual Studio for Mac todavía no tiene funciones "tan avanzadas".

Por ahora hago lo siguiente:
  1. Creo una clase estática como DemoApp.Infrastructure.AzureConfig para poner las variables, inicialmente con valores falsos.
  2. Subo el archivo al VCS (git en este caso).
  3. Ignoro todos los cambios futuros usando el comando git update-index --skip-worktree AzureConfig.cs. Aquí está la documentación del comando y aquí un ejemplo rápido.
Es importante que esas variables queden en una clase separada que se modifique muy de vez en cuando. Al principio las tenía directamente en el servicio de acceso a datos (ej. DataService.cs), que por supuesto editaba todo el tiempo... eso alcanzó a generarme un par de problemas.

2018/01/27

Xamarin - Lecciones aprendidas 1

Llevaba un tiempo haciendo aplicaciones nativas para Android y hace más o menos un año empecé a utilizar Xamarin (Forms) para casi todos mis desarrollos móviles. Aquí voy a documentar algunas lecciones aprendidas de cosas que quizá sean muy básicas y a veces obvias, pero que me pusieron en apuros. No es mi intención hablar de soluciones ideales ni de buenas prácticas, sino dar pistas de la forma en que abordé cada situación.

Ponerle atención al tamaño de las imágenes:

Para una app con muchos íconos y botones redondos, utilicé el plugin Xam.Plugins.Forms.ImageCircle. En algún ejemplo vi que usaban imágenes de 500x500px dentro de un marco de 700x700px, que da un borde de 200px por cada lado, así:

Parking by Arthur Shlain from the Noun Project

Esa relación de aspecto es ideal para las imágenes, porque al ponerles fondo transparente y ubicarlas en el ImageCircle, se ven muy bien:

check in by Chinnaking from the Noun Project
checkout by Chinnaking from the Noun Project

El problema está en el tamaño; 700x700px es demasiado grande para una Tablet Android de bajo costo (ej. Lenovo Tab3 7 Essential) y en general para cualquier ícono, incluso en grandes resoluciones. Todo parecía funcionar bien porque estaba probando en un iPhone y en un emulador acelerado por hardware, pero cuando probé en la Tablet, el problema de rendimiento fue evidente.

144x144 es más que suficiente para que los botones se vean bien tanto en iPhone como en Android, sin sacrificar el rendimiento.

Tener muy claro MVVM:

MVVM es un patrón arquitectural muy usado en Xamarin.Forms, que permite separar la interfaz de usuario (View) y el modelo (Model) utilizando una capa intermedia (ViewModel). Este video me ayudó mucho a entender el concepto.

Los que no entendemos bien MVVM tendemos a complicarnos con la implementación (OCD, que llaman), agregando complejidad innecesaria y haciéndonos preguntas un tanto existenciales como esta:

¿Si asigno el BindingContext (ViewModel) en el xaml (ej. BindingContext="{Binding Main, Source={StaticResource Locator}}"), entonces mi code behind debería estar siempre limpio, es decir, es una mala práctica invocar métodos del ViewModel desde el code behind (ej. ((MainViewModel)BindingContext).DoSomething();)?

Es totalmente válido interactuar con el ViewModel desde el code behind, de hecho, eso es lo que hace el xaml todo el tiempo "tras bambalinas". Lo que no está bien es que el ViewModel invoque métodos de la vista. Esto es importante para manejar métodos del ciclo de vida como OnAppearing() y OnDisappearing(); muchos desarrolladores utilizan el MessagingCenter, pero creo que esa herramienta se creó para tareas más interactivas.

Solía decir que el que entendía los Intents, entendía Android. Ahora digo que el que entiende MVVM, entiende Xamarin.Forms. Yo todavía no llego a ese punto.

Estar pendiente de liberar la memoria ociosa:

Una práctica común en Xamarin es crear un MainViewModel que controla la navegación y que tiene referencias de los otros ViewModels, que se van inicializando en la medida en que el usuario usa la aplicación. De nuevo, esto puede generar problemas de rendimiento cuando se usan aparatos de bajo costo.

Cuando la navegación es por Tabs, esos ViewModels permanecen en la memoria casi todo el tiempo. No sé cómo manejar ese caso porque nunca he hecho una aplicación así. Pero si la aplicación tiene navegación "tradicional" por páginas, algo que suelo hacer es poner en null los ViewModels que no estoy utilizando.

Esta no es una "buena práctica", pero es un remedio efectivo contra la frustración del usuario. Siempre depende del manejo de los datos, de qué tan frecuente navega el usuario de una página a otra, de qué tan livianos son los ViewModels, etc. Si se realizan muchas tareas en background cada que se inicializa un ViewModel, esto seguramente va a ser contraproducente.

La idea al final es ofrecerle al usuario una experiencia libre de frustración. Cualquier mejora en el rendimiento siempre será bien recibida.