lunes, 20 de noviembre de 2017

Generación de una señal coseno discreta

Podemos generar una función senoidal o cosenoidal de forma discreta mediante una ecuación de diferencias que es obtenida a partir de la transformada Z de una función senoidal o cosenoidal según sea el caso. Esta ecuación de diferencias es fácilmente implementable en un microcontrolador o FPGA. En este ejemplo crearemos un generador de señal cosenoidal. Partimos de la transformada Z de una función coseno discreto considerándola como la respuesta al impulso de la función de transferencia de un sistema Y(z)/X(z):
Dónde W0 es la frecuencia angular en tiempo continuo y Ts es el tiempo de muestreo [ver mi entrada sobre el concepto de frecuencia en tiempo discreto]. Multiplicamos ahora por z^-2/z^-2 para obtener un modelo físicamente sintetizable (debido a que los valores positivos en los exponentes de z implican muestras desde el futuro):
Multiplicamos ambos lados por el denominador de la función de transferencia y despejamos para Y(z):
Aplicamos transformada Z inversa para obtener finalmente nuestra ecuación de diferencias:
Para simular el generador utilizaremos Xcos de Scilab que es una alternativa de código abierto de Simulink [si no están familiarizados con esta herramienta aquí hay un video curso en español]. Antes de colocar los bloques debemos ir a menú superior en Simulation>Set Context y definimos las siguientes variables:

Ts = 0.1
W0 = 2*%pi/10
k0 = cos(W0*Ts)
k1 = 2*k0

 
El diagrama en Xcos es el siguiente:
Recordemos que hemos supuesto desde el principio que x[n] = delta[n]. La función Delta de Kronecker (impulso discreto) es un super bloque formado por el siguiente subsistema:
Dónde el retardo 1/z está inicializado a 1. Obsérvese que en el diagrama principal estamos enviando los resultados de la simulación al workspace. Personalmente, las gráficas nativas Scilab me parecen feas. Para hacer una grafica en Python exportaremos los datos de la simulación a un archivo CSV con el siguiente comando en la consola de Scilab después de ejecutar la simulación:

csvWrite([A.time,A.values],'cos_data.csv',',')

Teniendo el archivo podemos generar una gráfica en Python con el siguiente código:

import numpy as np
import matplotlib.pyplot as plt

A = np.loadtxt('cos_data.csv',delimiter = ',')

plt.stem(A[:,0],A[:,1])
plt.ylim([-1.5,1.5])
plt.grid(True)
plt.xlabel('Ts*n')
plt.ylabel('y[n]')
plt.show()



  Se deja como ejercicio construir un generador para función seno.

jueves, 16 de noviembre de 2017

Multiplicación en punto fijo [formato fixdt(signed,n,l)]

Aunque al principio pueda chocar con la intuición, es posible realizar de forma digital una multiplicación de dos números con parte entera y decimal utilizando únicamente números enteros. De hecho, para aplicaciones de instrumentación científica o simulaciones numéricas dónde la precisión sea la prioridad es incluso aconsejable evitar el uso del famoso punto flotante. Recomiendo enormemente ver este capítulo del canal PBS Infinite Series para entender desde el punto de vista matemático el problema fundamental de los tipos double y single. En pocas palabras este problema radica en la en las limitaciones en la accesibilidad sobre la recta numérica computable. La resolución del punto variable no es homogénea a lo largo de todo su rango. Una segunda limitación de las operaciones de punto flotante tiene que ver a su costo de implementación en hardware; construir circuitos digitales de punto flotante consumen más recursos en un FPGA.

El formato de punto fijo con el trabajaremos en esta entrada es:

 fixdt(signed,n,l)
  • signed : toma el valor de 1 si el entero almacenado es signado y 0 en caso contrario
  • n : longitud en bits del entero almacenado 
  • l : longitud en bits de la parte fraccional
En esta convención un número con parte entera y decimal se representa de la siguiente manera:
Los parámetros n y signed nos da el rango del formato:
A partir de la relación entre el Valor del Mundo Real (como se le llama en la jerga de punto fijo y denotaremos como Val) y el entero almacenado (que denotaremos por E) podemos definir la multiplicación de punto fijo de la siguiente forma:

Esto significa que la multiplicación se realiza como el producto de dos enteros mas un bitshift a la derecha de l locaciones. Para comprender mejor este formato realizaremos un ejemplo práctico concreto. Supongamos que queremos digitalizar una señal de voltaje sinusoidal de amplitud de 2 V con una frecuencia de 1 Hz mediante un ADC de 12 bits de resolución y un rango de entrada de 0-4 V. Supongamos que una vez digitalizada la señal queremos realizar un escalamiento de 0.321 y enviar esta nueva señal a un DAC de la misma resolución y rango. ¿Cómo resolveríamos el problema sin utilizar operaciones de punto flotante?

Bien, usaremos el formato fixdt(signed,n,l) para este ejemplo. Lo primero que debemos hacer es establecer los parámetros del formato. Ya que nuestro ADC no puede leer voltajes negativos, el parámetro signed será igual a 0. Dado que la resolución es de 12 bits entonces n = 12. Sabiendo que el voltaje máximo de lectura es 4 V encontramos el valor l de la siguiente manera:
Tenemos que el valor de l es 10 dando un rango de 0-3.99 V. Entonces el formato para nuestro ejemplo queda definido así: fixdt(0,12,10). El siguiente paso en convertir la ganancia de 0.321 en formato fixdt(0,12,10):
El equivalente de la ganancia resulta ser: 329. Podemos realizar ahora una simulación de nuestro ejemplo en Simulink y comparar el desempeño de la operación de punto fijo contra la de punto flotante:
Click para agrandar
El opam en el circuito añade el offset necesario para levantar la señal de la fuente y pueda ser adquirida por el ADC. Los bloques de ADC y DAC son simplemente subsistemas que realizan las operaciones de conversión de decimal a fixdt(1,12,10) y de muestro ZOH. Si bien Matlab/Simulink permite realizar las conversiones al formato fixdt(signed,n,l), decidí manejar uint16 para poder generalizar el ejemplo para ser implementado en microcontroladores y no sólo en FPGA's. Nuestro formato fixdt(0,12,10) cabe en un uint16 y eso también permite hacer una intuición más clara de que simplemente trabajamos con formatos enteros convencionales. La gráfica resultante con la comparativa es la siguiente:
Vemos que la correspondencia entre las operaciones de punto fijo y punto flotante son muy buenas. Podemos ahora a aventurarnos a escribir un codigo VHDL para nuestra operación de escalamiento de este ejemplo:

La simulación del módulo mostrando algunos valores de ejemplo para los enteros almacenados de entrada y salida es la siguiente:
 Click para agrandar
Para finalizar quisiera hacer un último comentario. Quizá notaron que tuvimos mucha suerte al calcular el valor del exponente l y nos dio un número que se ajustaba perfectamente al rango del ADC. No fue suerte, yo elegí el rango para que este ejemplo fuera más sencillo. En la practica los DAC's suelen tener rangos de 0-5.2 V o 0-3.3 V. ¿Que se hace en esos casos? Aquí es donde entra un nuevo formato: fixdt(signed,n,s,b). Pero hablaremos de él en otra entrada.

Referencias:

martes, 7 de noviembre de 2017

Convertir un modelo de Simulink a código VHDL con HDL Coder [Ejemplo 2]

En una entrada anterior exponía el como generar un código VHDL de una función booleana sencilla. Este será también un ejemplo sencillo pero mucho más interesante. Generaremos un controlador P para la posición de un motor DC con caja de engranes y encoder [este modelo en particular] alimentado por un driver Pololu Qik 2s12v10. El esquema del sistema es el siguiente:
 La posición del motor será leída mediante el encoder que viene integrado en el motor. El modelo de Simulink que convertiremos a código VHDL es el siguiente:

Bloques requeridos:
HDL Coder > Commonly Used Blocks > In1
HDL Coder > Commonly Used Blocks > Out1
HDL Coder > Sources > Constant
HDL Coder >  Math Operations > Divide
HDL Coder > Discontinuities > Saturation
HDL Coder > Signal Attributes > Data Type Conversion

Diagrama:
 La operación de división por números que no sean potencias de 2 no está disponible en todos los dispositivos por lo que en este caso usaremos 1/32 como aproximación de 0.03 [es posible implementar en hardware divisiones entre números arbitrarios pero serán tratados en otro ejemplo]. Es necesario establecer en todos los bloques el tipo de dato int16 dando doble click a cada bloque y cambiando el tipo de dato la siguiente forma:
 También sera necesario dar doble click en el bloque Divide, ir a Signal Attributes y activar el modo "Saturate on integer overflow" y verificar que el modo de redondeo sea "zero" o "simplest". Habiendo hecho todo lo anterior generemos el código VHDL yéndonos a la pestaña Code > HDL Code > Generate HDL. Les generará el siguiente código:

Para complementar el controlador usaremos los siguientes módulos VHDL que ya he publicado por acá:
Agregando todos los archivos .vhd requeridos al proyecto se puede proceder a conectar los bloques de forma esquemática en ISE de la siguiente manera:
Si no están familiarizados con la creación de proyectos esquemáticos en ISE aquí hay un video-tutorial en español muy bien explicado [utilizan Verilog pero el procedimiento es el mismo]. El bloque selector es simplemente una declaración when que envía una referencia de 0 si el switch esta apagado y de 2400 (90° en cuentas de encoder) cuando esta encendido. En este video muestro el funcionamiento del sistema implementado en una tarjeta Basys2:

domingo, 5 de noviembre de 2017

Modoulo VHDL para un encoder de cuadratura en modo X4

El código de este módulo es sólo una modificación del código de Dr Dew (Scilab Ninja). La única diferencia es que le he agregado el conteo de ascenso y descenso para un registro de 16 bits. National Instrumentes tiene una nota en español muy completa sobre el manejo de encoders de cuadratura. La ventaja del modo X4 es que permite tener la máxima resolución angular posible para un encoder.

sábado, 4 de noviembre de 2017

Parálisis

Acabo de tener la pesadilla/parálisis del sueño más extraña y aterradora hasta ahora. Escribiré los detalles mientras los tenga frescos. La fotografía que muestro es relevante y llegaré a ella en un momento. En la primera parte del sueño me encontraba con varias personas en un extraño tren de vapor que no tenía paredes ni techo ni piso, sólo una estructura o esqueleto en el teníamos que movernos con mucho cuidado para no caer. Teníamos que alimentar la caldera con carbón lo cual era muy difícil de hacer por la forma de los vagones. Empecé a cansarme y me que parado recargado en la estructura mirando el suelo y las vías moverse relativamente por debajo de mi. Comencé a hacer un ejercicio de mindfulness que estoy muy acostumbrado a hacer en la vida real. Esto hizo forzar el "renderizado" del sueño y los detalles eran cada vez más y más reales [esto ya me había ocurrido antes]. De pronto el tren se descarrila. Salgo volando y todo se apaga. En ese momento me di cuenta que estaba soñando y que había muerto en el sueño al caer del tren. Lo extraño era que ahora estaba en una especie de limbo en mi mente. De pronto pude ver una imagen. Era justo la de la fotografía, era lo que tenía frente a mi ojo izquierdo (el derecho estaba tapado por una almohada). Sabía ahora que estaba de regreso en el mundo real. Traté de moverme pero estaba paralizado. Estaba tranquilo porque ya me había pasado muchas veces antes. Espere un momento a que la parálisis pasara. Pero pasaron varios minutos sin que lo hiciera. Traté de moverme lo más fuerte que pude hasta que ocurrió algo muy extraño. Empecé a sentir mover mi brazo, pero era como mover un miembro fantasma. Más extraño aún era que podía tocar y sentir el celular y el libro, sentía que podía moverlos pero la imagen que veía o estaba congelada o los movimientos que hacía no eran reales. Estaba seguro que seguía despierto. Esperé varios minutos más a que este estado pasara, pero para mí horror no lo hacía. Traté de calmarme y pensar en qué hacer. Si mi vista se había congelado tal vez podía hacer una llamada y pedir ayuda. Lo intenté, sentía todos los movimientos de mi mano y el celular pero la imagen seguía congelada y no sabía lo que hacía. Me rendí y trate de pensar en otra cosa. Había comenzado a notar algunos cambios en mi vista con cierto retraso (como el "lag" en un vídeo en vivo). Se me ocurrió intentar dar la vuelta y sentí que lo hice. Unos segundos después la imagen cambio abruptamente al techo. Esto ya era demasiado extraño y de pronto me vino la idea de que tal vez algo me había pasado en la realidad, que tal vez mi casa se había venido abajo, tenía un daño cerebral y sólo estaba atrapado en este limbo esperando morir en cualquier momento. Había pasado cerca de media hora. Tenía mucho miedo, comencé a gritar y sacudirme para despertar pero no podía. De pronto volví a ver la imagen de la foto y ya estaba completamente despierto. Me levanté, verifiqué que podía moverme de nuevo. Lo único que se me ocurrió decir fue "the fuck was this?" y comencé a escribir esto para no olvidarlo. Ahora me pregunto si no estoy en una simulación y esto no fue más que un fallo en la matrix que quizá yo mismo induje al hacer mindfulness en un sueño.

 [Originalmente una nota de Facebook]