miércoles, 11 de diciembre de 2013

Servidor empotrado Linux/Android de control de monturas astronómicas.(II)

Estructura

Fonera 2.0 OpenWrt
La idea original de este mini proyecto parte de la posibilidad programar un servicio ligero  para  ejecutar en pequeños dispositivos de control , que sin saberlo , muchos tenemos en casa, olvidados en un cajón y que en el mejor de los casos terminan en un punto limpio o mas frecuentemente directamente en el cubo de la basura.

Me refiero al típico  punto de acceso  o router ADSL WIFI que nos "regala" el operador  de turno.

La mayoría de de estos dispositivos  funcionan  o es posible hacerlos funcionar bajo alguna versión de sistema  operativo GNU/Linux y disponen en la placa base de un conector serie o USB  que puede usarse para comunicación con un microcontrolador.


Colateralmente ,ya que la base  de funcionamiento de los dispositivos Android ,  es también Linux, este mismo servicio se pude reutilizar compilar y ejecutar en tablets y telefonos  inteligentes,sin utilizar el framework Android ni la Dalvik como máquina Java virtual si no como código compilado nativo de procesador, en la mayor parte de los casos un procesador con arquitectura ARM.

En esta aplicación el control de excitación ,velocidad  y posición los motores se confía al uso de microcontroladores PIC.

El calculo de coordenadas,aceleración desplazamientos y sincronización así como las comunicaciones (serie,bluetooth y TCP/IP ) y el procesamiento de protocolo de comunicación LX200 de los clientes  ,es tarea del servidor.
El servidor implementa:
  • Servidor TCP/IP multi hebra clásico.
  • Interprete de protocolo LX200 ( implementado con  FSMC RAGEL.)
  • Control de protocolo de bajo nivel comunicación serie <->PicGoto.
  • Control nivel intermedio velocidad aceleración, posición motores
  • Control básico de tracking, sincronización y desplazamientos (Go To).

Arrancando el servicio

El servidor como tal carece de interfaz gráfico .Se ejecuta  como un servicio del  sistema en segundo plano.
No obstante estoy programando una app para Android que posibilita  el arranque, parada y la configuración de los parámetros sin tener que iniciar un shell (terminal o interprete de comandos).

En la siguiente captura de terminal  Android iniciamos el servidor bautizado  andtelescope como una aplicación nativa a efectos de para capturar y mostrar el tráfico que se genera.

Se inicia con éxito el puerto serie /dev/telescope  que es un enlace simbólico a /dev/rfcomm0  físicamente en este caso un dongle Bluetooth ,también podría ser un conversor usb-serie en ese caso /dev/ttyUSB0.



A continuación se inicia una primera hebra que sera la  encargada calcular los parámetros  del control de velocidad y aceleración de los motores (20ms).

El segundo hilo de ejecución solicita a los microcontroladores  cada 200ms la posición de los motores y ejecuta un algoritmo de control posición/velocidad proporcional que gestiona el tracking y los desplazamientos.

El procedimiento  principal es el servidor de conexiones.Lanza una hebra independiente por cada conexión entrante y le asocia un interprete de comandos LX200, esto permite que distinta aplicaciones( Sky Safari ,Cartes du ciel ,Skymap..) desde distintos dispositivo (tablet, telefono,PC..) puedan acceder simultáneamente la posición y controles del telescopio.

Conectando con Sky Safari

Para Sky Safari que no trabaja un conexión persistente , sino que abre y cierra un socket  por cada petición la programación como servicio  multihebra es casi  obligada.
Como el servicio corre en el mismo Tablet configuramos Sky Safari  para que escuche en la dirección local  y el puerto 8888.
Elegimos el sistema Losmandy Gemini, que implementa una versión simplificada  de LX200.

Sky Safari ,Menú Configuración Telescopio

En esta captura vemos que la conexión entre la aplicación Sky Safari y el servidor  ha tenido éxito y podemos alinear el telescopio que apunta a una estrella conocida.

Ponemos el  tablet en visión nocturna  y empezamos la sesión de observación.



(continuará)




martes, 3 de diciembre de 2013

Servidor empotrado Linux/Android de control de monturas astronomicas.(I)

En esta entrada se describe  el sistema PicGoTo++  en actual estado  , tal  y como lo están empleando un  par de centenares  usuarios, que se lo han autoconstruido por  todo el mundo.

El siguiente paso es  habilitar de un sistema de control de  montura  ultra portable utilizando el veterano PicGoTo++  como controlador  electrónico motores y dispositivos Linux de bajo coste como tablets( Android) o routers wifi(OpenWRT,dd-wrt) como controlador de "alto nivel" (por calificarlo de alguna manera).

PicGoTo++ en su  configuración clásica ,es adecuado para un observatorio fijo,astrofotografía o salir al observar al campo si se dispone de un pequeño Netbook con Windows.

Sin embargo para la observación ocasional ,  quizás seas mas sencillo prescindir del ordenador y usar un Tablet normalito , incluso un smartphone.Esta segunda opción se  mostrará en la siguiente entrada.Como adelanto ya funciona en conexión con Sky Safari en mi Tablet  chino de combate   cutre y viejuno.
El servidor está escrito en C,aunque la primera aproximación fue  en C++,que facilita mucho la programación ,el tener que usar  librerías adicionales en algunos de routers pequeñitos era una limitación importante.

Sky Safari y Linux Android  Picgoto++ server

ANTECEDENTES

Circuito


Tanto  PicGoTo estándar como  PicGoTo++ son circuitos basados en el  microcontrolador PIC16F628A,  por si mismos no son suficientes  para el completocontrol automatizado de una  montura astronómica.
En principio solo acepta ordenes  para fijar la velocidades de giro y potencia en cada eje y devolver la posición registrada  en contador virtual de  cada motor  calculada en función la dirección y del numero de micropasos que los motores hayan completado En total acepta un juego de unas veinte ordenes, que permiten fijar distintos parámetros de la montura y preferencias, así como alterar y almacenar la tablas de micropasos en almacenados en EEPROM.

PicGoTo++  permite controlar hasta cuatro motores de pasos, dos para mover los ejes de la montura y otro  para el control automatizado del enfocador y un tercero que se puede usar para controlar una rueda de filtros o un rotador/desrotador de campo.


Picgoto Server :Servidor ASCOM/Windows.
La parte complementaria del proyecto es un servidor empotrado en una aplicación gráfica que se ejecuta en un PC con S.O. Windows,en el PC se realizan todos los cálculos necesarios para encontrar y mantener el seguimiento de los objetos celestes que se  indiquen. En la versión mas reciente permite el control de monturas ecuatoriales (aquellas cuyo eje vertical  es paralelo al eje de rotación terrestre) y monturas horizontales ó alt-azimutales cuyo eje vertical apunta a cénit).
En ambos tipos de montura son necesarios cálculos trigonométricos y/o matriciales que sobrepasan a la capacidades de un microcontrolador de 2Kb .

Sin embargo el microcontrolador es capaz de generar las señales de control y excitación de los motores con mayor precision  que un PC,ya que dispone dispositivosy temporizadores dedicados, con estas funciones y a diferencia de PC no ejecuta un sistema operativo que puede requerir y sustraer recursos y ciclos de programa para otras tareas.


Para poder interactuar con las aplicaciones de  software astrónomico mas populares,Cartas estelares/planetario,autoguiado captura de imágenes , ,implementa varios drivers ASCOM que es el estándar de facto para aplicaciones astronómicas que se ejecutan sobre  Windows.

El sistema en funcionamiento
Para ilustrar el funcionamiento de sistema clásico  ,de vez en cuando subo algún vídeo para que sirva de ejemplo a los compañeros que  usan el sistema. En este caso se muestra el sistema conectado al popular software astronómico Stellarium. El sistema se esta ejecutando en modo ecuatorial configurado para un par de motores de 400 pasos 1,2 Amperio que se van instalar en una montura de un telescopio de 600mm de apertura.


Yo también lo uso (a ver..)Mi Telescopio.
(continuará)




lunes, 22 de abril de 2013

Chatarra:Circuito con microcontrolador sencillo.(2)

Diseñando el circuito.
El tipo de circuitos integrados que hemos visto en la entrada anterior, son una combinación de lógica secuencial, amplificación y regulación de potencia.
Como la intención inicial era mostrar como se puede implementar un control de este tipo de motores, en principio proponemos  un pequeña plataforma hardware sobre la que probar el desarrollo.
Vamos a implementar algo parecido utilizando un microcontrolador  PIC asequible (3€) un pequeño integrado amplificador de corriente  (500mA) de ocho canales (0,60 €) y algunos componentes pasivos : resistencias, condensadores,diodos ...

Dice la canción: sin entrar en pormenores ,yo sé hacer cosas mejores. Pero no iremos mas lejos; esto va ser un ejemplo básico.Aún así seria suficiente para implementar un control de enfoque.
De hecho el PicGoto mas básico es este mismo circuito + adaptador de niveles TTL-RS232.


Esquemático del Circuito básico Diseñado con KiCad.




El PCB con el visor 3-D de KiCad.
El circuito se ha compuesto  usando la aplicación  KiCad . Funciona bajo Linux,Windows y OSX es  gratuita y de código abierto,  posibilita tanto el diseño esquemático de circuito y el diseño físico de la placa. Incluye una librerías de componentes  no demasiado extensas, aunque se pueden encontrar un gran número librerías adicionales en la red.

Por lo demás, con un poco de práctica ,es sencillo diseñar uno mismo los elementos que no estén disponibles y se pueden  necesitar. En este caso el  motor stepper lo he tenido componer  por mi cuenta, no me habrá llevado más diez minutos.Trae un visor 3-D  que permite visualizar el prototipo PCB mientras se está diseñando.

Posiblemente, para quién no tenga experiencia, confeccionar un circuito impreso puede parecer una tarea muy compleja,en la realidad para circuitos sencillos como este, bastan c algunos aparatos caseros: una impresora láser,papel fotográfico para impresoras de chorro de tinta satinado,una plancha,un mini taladro  tipo  dremmel (no hace falta que sea de marca) y sustancias químicas de uso común: H20 (agua corriente),H2O2 (agua oxigenada),y HCl (ácido clorhídrico, conocido también  como agua fuerte o salfumán) todas ellas en el formato y grado de disolución con el que se encuentran  en el supermercado de la esquina. Hay tropecientos vídeos en youtube que  enseñan el proceso.

Las opciones para armar el circuito , sin tener que fabricar un PCB especifico (circuito impreso)  pasan por usar una placa de pruebas  (inserción) o placa prototipo perforada (soldadura).

Elementos.

Microcontrolador 
Usaremos el PIC16F628A de Microchip, es  un dispositivo de arquitectura HARVARD que se caracteriza en que la memoria de programa y almacenamiento  variables están separadas.El dispositivo tiene 2Kb de Flash, 224 bytes de RAM, 144bytes de EEPROM. Una USART para comunicación serie , dos TIMER de 8 bit  y otro de 16 bits.
Aunque este IC lleva un oscilador interno,para hacerlo funciona a su velocidad máxima de 20Mhz incluimos en el  circuito  un oscilador de cuarzo.

Etapa potencia.
Como amplificador/etapa de potencia  el ULN2803A es un pequeño array de transistores en configuración Darlintong. Para motores bipolares puede usarse el L293D.Estos integrados soportan un amperaje máximo de 500mA. Los  motores con consumo superior pueden  utilizarse con etapas de transistores discretas o integrados de potencia como el L298.

Compilador Lenguaje  C.
Para compilar los ejemplos se puede  utilizar  la versión gratuita del compilador HiTech C. La forma de obtener el máximo rendimiento de estos dispositivos pasa por programar directamente en ensamblador de código máquina(juego de instrucciones propio de cada microcontrolador ).La curva de aprendizaje y el nivel de dificultad que esto conlleva, hace que el uso de un compilador de Lenguaje C sea más adecuado en la mayoría de los casos.Para partes criticas de la aplicación  siempre queda la posibilidad de incluir código máquina en linea.

Empezaremos con el código en la próxima.




jueves, 4 de abril de 2013

Chatarra (1)

Un paréntesis para hablar de motores de pasos.

Electromecánica


La opción mas económica para motorizar instrumentos astronómicos (monturas, enfocadores, ruedas de filtros ...) de construcción propio o comerciales,suele ser el empleo de steppers ,en español motores de pasos.
La economía, radica ,principalmente, en que es posible reciclarlos de máquinas obsoletas,como impresoras matriciales ,fotocopiadoras y tragaperras. Por desgracia ,esta fuente es cada vez mas escasa por recogerse actualmente estos equipos en los llamados "puntos verdes"en lugar de dejase al lado de contenedor de residuos urbanos.

Motor de pasos NEMA23 con citronella al fondo
También se pueden encontrar en  Ebay o comprar directamente a fabricantes chinos, a precios bastante razonables.No hace mucho tiempo un motor normalito podía costar entre 45€ y 60€,  mientras que uno de fabricación china puesto en casa puede costar unos 15€ ó 20€.

El motor de la imagen , es un modelo de tamaño NEMA23, de 400 pasos por vuelta (0,9º por paso) 7V 1A, 35Ncm de par que viene pesar unos 700 grs. Hace unos años hicimos un compra conjunta a China y después de portes ,aduanas y demás  impuestos no salieron a 22€ la pieza.

Electrónica.


A diferencia de un motor de corriente continua  convencional ( de tipo de esos que sacábamos destripando juguetes el día de reyes ),que gira con solo conectarlo a una fuente de tensión, en el caso de los steppers es necesario conmutar  secuencialmente el paso de corriente por los bobinados para avanzar o retroceder pasos y de esa forma llevar  el eje del motor a una posición determinada o hacerlo girar a la velocidad deseada.
Existen cientos circuitos integrados específicos para  controlar motores de pasos,tanto para generar la lógica de conmutación como la etapas de potencia.

Con  la proliferación de sistemas de mecanizado  CNC   e impresoras caseros es también bastante asequible conseguir una controladora ya montada. La controladora de tres ejes  y 3A optoacoplada  me salió por unos 38$ que es la tercera parte  de lo que me habría costado, solo los componentes, en la tienda de electrónica de la esquina.

Básicamente, lo que hace este tipo de integrados es generar un par de curvas de excitación seno/ coseno  amplificarlas y aplicarlas a los bobinados  del motor mediante un par de puentes H de transistores bi'polares ,DMOS ó MOSFET .
Como aproximación razonable, un motor de pasos bipolar eléctricamente  opera según el mismo principio  de funcionamiento que un  motor de corriente alterna síncrono bifásico. La diferencia es que un motor síncrono convencional no puede avanzar a pasos discontinuos , si se desfasa cierto angulo  de la frecuencia de la red  pierde el sincronismo , se bloquea y a veces hasta se quema .

Curvas de excitación para avance en medio paso y 1/8 paso.
La curvas  de excitación se generan partir de una tabla senoidal de niveles discretos,por lo que la suavidad del paso depende del numero de niveles de corriente que el integrado pueda generar. En el que caso de que solo pueda generar  dos niveles (V+  V-) tenemos el caso  clásico de avance a paso completo o medio paso.

Cuantos mas niveles sea capaz de generar el circuito , más aproximada es la curva de excitación a una curva continua y  mayor sera  la suavidad de giro .De igual forma el mayor número de niveles de corriente posibles permite aumentar la resolución del motor.Si tenemos un motor de doscientos pasos usando  un controlador que admita dieciséis niveles (ocho con valores  positivo y otros tantos negativos) podremos multiplicar la resolución ocho veces esto es mil seiscientos micropasos por vuelta.

Este tipo de drivers regulan la el valor de la corriente por conmutación utilizando técnicas de choppingmodulación de ancho de pulso en lugar de regulación lineal  por lo que su rendimiento energético es mayor  y el calentamiento de los componentes y los motores mucho mas reducido.

El problema de este tipo de dispositivos es que  por lo general, las curvas de excitación que traen grabadas   no se pueden modificar. Ese es el motivo de que la proporcionalidad en la longitud de los micropasos difiera según cada  modelo o las características del motor que queramos usar.

Esta pega  ,que para una máquina herramienta CNC puede  tener poca o nula  importancia,¡ (la técnica de excitación por micropasos se emplea principalmente para eliminar  resonancias que para alcanzar mayor precisión) ,en Astronomía ,si que tiene, sobretodo trabajando con reducciones muy bajas, una importancia real.

Si el dispositivo de control que vamos a emplear sí permite un calibrado de tablas, entonces podremos compensar estas diferencias y ajustar la calidad y longitud de micropaso.

Como ejemplo tenemos en el vídeo  podemos ver un modelo de motor Shinano Kenshi, empleado en plotter s , con un par de mantenimiento muy alto lo que hace que operado en modo micropaso senoidal su comportamiento sea bastante malo.


Utilizando como controlador un PIC16F628A  en lugar de un IC convencional, osciloscopio y un punterito láser la calidad del giro  puede mejorar notablemente.


En posteriores entradas veremos como se pueden programar estas cositas en microcontroladores o al menos como las programo yo.

martes, 12 de marzo de 2013

Reconociendo coordenadas (4)

En astronomía,  los sistemas clásicos de coordenadas empleados para determinar la posición de un objeto utilizan valores angulares ya sean sexagesimales ,  horarios o una combinación de ambos.

Sistemas de coordenadas que  utiliza  el protocolo:

  1. Coordenadas geográficas, correspondientes a la ubicación física de observatorio.Ambos se expresan en grados sexagesimales Longitud (de 0 a 360) y latitud ( de -90º a 90º).
  2. Coordenadas ecuatoriales (absolutas) especifican la posición de un punto determinado en la esfera celeste :Ascensión recta (expresada en angulo horario 0 a 24h) y declinación  en grados sexagesimales (expresados en grados de -90º a 90º).
  3. Coordenadas Altacimutales locales u horizontales,su valor está en función de la hora sideral local,y las coordenadas geográficas.Se expresan en grados sexagesimales son componentes son azimuth (de 0º a 360º) y altitud (-90º a 90º).
Sistemas de coordenadas ecuatoriales (celeste)y altacimutales(naranja) proyectados sobre la esfera celeste para una latitud de 36º43'12"

Grados Sexagesimales.
El formato es similar al que aprendimos en la escuela :grados <º> minutos < '> segundos<">.
En LX200 debemos indicar el signo y según la precisión actual podemos especificar o no los segundos.
Por  ejemplo,para el valor 45º20'30
    • Cadena LX200 precisión  alta:  +54ß20'30#
    • Cadena LX200 precisión baja:  +54ß20#

Angulo Horario.
También el habitual :  horas <:>minutos <:>segundos.
Cuando se usan como coordenada de ascensión recta en baja precisión ,los segundos se expresan como décimas de minuto.
Para el valor 18:42:30:

    • Cadena LX200 precisión alta : 18:42:30#
    • Cadena LX200 precision baja: 18:42.5#
Expresiones regulares
Ahora construiremos los bloques para reconocer ambos formatos.
En Ragel, un dígito decimal se puede especificar por la palabra clave "digit" que es equivalente a la expresión [0-9].
    • digit=[0-9]
Un entero de longitud arbitraria se especificaría, añadiendo el operador '+' que en indica una o más ocurrencias de la expresión inmediatamente a la izquierda
    • entero=digit+;
    • entero=[0-9]+;

En nuestro caso para minutos y segundo  necesitamos especificar que necesitamos un entero de dos dígitos entre 0 y 59:
    • minuto=[0-5]digit;
    • segundo=[0-5][0-9]; 
Coordenada en formato grados sexagesimales
En el caso de los grados distiguimos dos formatos modulo 360º ó cuadrante con signo.
    • grado =(([\+]|[\-])digit) digit{2};
En lenguaje natural ,grado acepta dos tipos cadenas:
  1.  Las empiecen por el signo '+' ó '-' seguidas de dos digitos.
  2. Cadenas de  tres digitos.
Para reconocer el formato aceptado por el protocolo la expresión seria la siguiente:
    • degformat=(([\+]|[\-])[0-3]) digit{2}'ß' [0-5]digit ([\`][0-5][0-9])?;
expresion equivalente a :
    • degformat=grado ß minuto ([\']segundo)? ;
Esta expresión reconocería cadenas tales como 359ß12'38 , +23ß40'00 , -00ß19'56 , +36ß43
y rechazaría cadenas mal formadas como +345ß12'80 ó 45ßa'7f


Para ser exactos la expresión que reconoce exactamente el formato seria bastante mas complicada que la propuesta para limitar las cadenas reconocidas a las que estrictamente son válidas, manteniendo los rangos de cada sub-expresión.por ejemplo +90ß36'12 es aceptada aunque  no esta en rango -90º, 90º.

FSM reconocimiento formato de grados sexagesimales
Tratándose de coordenadas  supondremos, como Descartes, un  programa cliente "bueno y no engañador" por lo que no es estrictamente necesario ya que podemos rechazar el resultado  de la conversión en  fuera del propio parser.

Coordenadas formato angulo horario
Lo explicado para el formato anterior es válido para el presente.
Solo hemos de añadir  el reconocimiento de horas de 0 a 23 y el redondeo de segundos si ha lugar, en baja precisión

    • hora =('2'[0-3]) |[0-1]digit;
    • hourformat= hora ':' minuto ((':' segundo)|('.'digit));


FSM reconocimiento formato angulo horario
Como siempre incluyo el código Ragel que genera estas fermosuras.


long lx200( char *str )
{
 char *p = str, *pe = str + strlen( str );
 int cs;
 char stcmd;
 char neg = 1;
//---------------------------------------------------------------------------------------- 
%%{
 machine lx200;
 write data;
}%%
 %%{
  #Acciones 

   
 #definicion sintaxis LX terminos auxiliares
 minuto=[0-5]digit;
 segundo=[0-5][0-9]; 
 grado = (([\+]|[\-])|[0-3]) digit{2};
 hora =('2'[0-3]) |[0-1]digit;
 degformat = grado 'ß' minuto ([\']segundo)? ;
 hourformat= hora ':' minuto ((':' segundo)|('.'digit));
 
 main := (degformat|hourformat) '#'; 


  # Initialize and execute.
  write init;
  write exec;
 }%%

//-----------------------------------------------------------------------------------------
 if ( cs < command_first_final )
 // fprintf( stderr, "LX command:  error\n" );

 return  neg;
};
      Combinamos  las partes comunes a las dos máquinas y listo.Solo nos restan añadir "acciones" como en la entrada anterior




sábado, 2 de marzo de 2013

Transiciones y acciones (3)

Tal y como quedó definida la máquina en la entrada anterior, conseguimos disponer de una función que después de consumir toda la cadena de entrada nos indica si esta pertenece al lenguaje o no.

Como el objetivo  es que el telescopio ,además, se mueva, tendremos que incluir de alguna manera , el código en lenguaje de programación convencional (C,C++,Java,Ruby)  en nuestra definición  Ragel .

Para ello definimos acciones, esto consiste simplemente en asignar un identificador a un trozo de código.
A modo ejemplo soso y tontorrón de libro de texto:
action print_hello {printf("hello action %d \r\n",number++);}
action print_bye {printf("bye action %d \r\n",++number);}
action do_for {int i;for (i=0;i<=100;i++) printf("el valor del contador es %d\r\n",i);}
Hemos definido tres acciones,las dos primeras imprimen un mensaje  e incrementan el valor de una variable y la tercera es un bucle for que imprime de cero a cien.

Es mejor que  añadamos algo que sea realmente  útil para variar  la cantidad de momento angular de nuestros cacharritos,para ello  definimos las siguientes acciones:
  • move_dir :Mover telescopio.
    • "action move_dir {move(stcmd);}"
  • change_rate:Cambiar velocidad seleccionada.
    • "action change_rate {rate(stcmd);}"
  • goto:Ir a coordenadas destino.
    •  "action goto {go_to;}"
  • storecmd:Almacenar carácter  que activa la transición.
    •  "action storecmd{stcmd=fc;}"
El código marcado en color naranja es el código en lenguaje C que se ejecutará cada vez  se invoque la acción nombrada en la declaración.En este caso move,rate y go_to son llamadas a funciones que tendrían que ser implementadas  aparte según  cada tipo de drivers,elementos auxiliares  o motores que se vaya a usar.
La asignación stcmd=fc;  es una simple sentencia en la cual stcmd seria una variable local de tipo char, en ella almacenamos para uso posterior el carácter de la transición en curso.

La definición de  la sintaxis de Move se modifica a:
Move = 'M' ([nswe]@storecmd %move_dir | 'S'%goto)

  1. La expresión @storecmd  indica que al recibir un carácter que pertenezca al conjunto ['n','s','w','e']se almacena  el carácter en la variable stcmd.
  2. La acción %move_dir invocara la función move ,el valor almacenado en stmdc es el parámetro que determina la dirección seleccionada.
  3. Del mismo modo se invocará la función go_to si el carácter recibido es 'S'.
Este el el digrafo resultante ,vemos como en cada transición se indica ademas la acción que se ejecuta:
La diferencia entre los operadores '@' y '% 'es que con @ se ejecuta siempre la acción correspondiente cuando se recibe un  carácter válido correspondiente a esa transición.Sin embargo con % se pospone la acción a la siguiente transición ,a condición de que el  siguiente carácter recibido sea válido.
En este caso lo hacemos así para que la acción se ejecute siempre que se reciba el carácter terminal '#'

El mismo análisis es  aplicable a la definición de las sintaxis y acciones de Rate

Para ilustrar porque es interesante utilizar Ragel como herramienta para este tipo de tareas   se muestra el código Ragel empotrado en la función que  procesará la entrada y a continuación el código en C  de la implementación  de un parser correspondiente a los  comandos básicos LX200 que se usa en el PicGoto estándar  el cual esta programado "a tecla" sin usar ninguna herramienta.
Creo que es evidente cual de ellas es mas fácil de  mantener y ampliar.

En posteriores entradas (si alguien llegó hasta aquí) veremos como procesar el formato coordenadas sexagesimales  y horarias.



long lx200( char *str )
{
 char *p = str, *pe = str + strlen( str );
 int cs;
 char stcmd;
 char neg = 1;
//---------------------------------------------------------------------------------------- 
%%{
 machine lx200;
 write data;
}%%
 %%{
  #Acciones 
  action move_dir {move(stcmd);}
  action change_rate{rate(stcmd);}
  action goto {GOTO;}
  action stop {;}
   
  #definicion sintaxis LX terminos auxiliares
  Move = 'M' ([nswe]@storecmd %dir | 'S'%Goto);
     Rate = 'R' [CGMS] %rate;
  main := ':' (Rate|Move) '#';


  # Initialize and execute.
  write init;
  write exec;
 }%%

//-----------------------------------------------------------------------------------------
 if ( cs < command_first_final )
 // fprintf( stderr, "LX command:  error\n" );

 return  neg;
};

Ahora el código que implementa lo mismo en C pelón, picado a mano.
 if (RCIF) {

            buffer = RCREG;
            switch (state) {

/* algunos estados despues
.
.
.
.
.
.
.
*/
  //Estado 8 comienzo detección comandos basicos LX200
            case 8:
            
                state = 0;
                if (buffer == ':')
                    state = 9;
                if (buffer == '#')
                    state = 8;

                break;
   //Estado 9 comienzo identificacion comandos basicos LX200
            case 9:
                state = 0;
                switch (buffer) {
                case 'M':
                    ra_speed = ra_current_speed;
                    dec_speed = dec_current_speed;
                    dire = 1;
     halt= 0;
                    state = 10;;
                    break;

                case 'Q':
                    ra_speed = 0;
                    dec_speed = 0;
                    dire = 0;
                    state = 10;
                    break;

                case 'R':
                    state = 10;
                    guide = 0;
                    break;
                case 'F':
                    state = 14;
                    break;
                }

                break;
             //Estado 10 lectura Comandos movimiento y seleccion de velocidad LX200
            case 10:
                state = 0;
                switch (buffer) {
                case 'w':
                    if (dire == 1)
                        ra_current_mstep_period = ra_speed;
                    else {
                        ra_current_mstep_period = ra_speeds[4];
                        dire = 1;
                    }
                    ;
                    ra_dir = dire;
     
                    break;
               
                case 'e':
                    if (dire == 1) {
                        ra_current_mstep_period = ra_speed;
                        if (guide){
                            ra_current_mstep_period = ra_speeds[5];
                            if (ra_speeds[5] <= ra_speeds[4])  dire=-1;
                        }
                    } else {
                        ra_current_mstep_period = ra_speeds[4];
                        dire = -1;
                    }
                    ra_dir = -dire;
                    break;

                case 'n':
                    dec_current_mstep_period = dec_speed;
                    dec_period_counter = 0;
                    dec_dir = dire;
                    if (dire == 0)
                        PORTB = (9 & frev);
       
                    break;

                case 's':
                    dec_current_mstep_period = dec_speed;
                    dec_period_counter = 0;
                    dec_dir = -dire;
                    if (dire == 0)
                        PORTB = (9 & frev);

                    break;

                case 'G':
                    ra_current_speed = ra_speeds[3];
                    dec_current_speed = dec_speeds[3];
                    guide = 1;
                    break;

                case 'S':
                    ra_current_speed = ra_speeds[0];
                    dec_current_speed = dec_speeds[0];
                    break;

                case 'M':
                    ra_current_speed = ra_speeds[1];
                    dec_current_speed = dec_speeds[1];
                    break;

                case 'C':
                    ra_current_speed = ra_speeds[2];
                    dec_current_speed = dec_speeds[2];
                    break;

                default:
                    state = 0;
                    break;
                }

                break;
            case 11:
                state = 0;
                break;
   //Estado 13 conmutacion manual medios pasos ,micropasos
            case 13:
                    RB3=led=(buffer<64) ;
    if (led)
    {half_ra_bit=half_de_bit=0;
     TMR2IE=1;
     frev = frev | 0x08;
     }
    else {half_ra_bit=half_de_bit=1;
     TMR2IE=0;
      frev=frev & 0xf7;}
    
                state = 0;
                break;
   //Estado 14 lectura Comandos Enfoque LX200
            case 14:
                state = 0;
                switch (buffer) {
                case '-':
                    focuser_run_bit = 1;
                    frev = 0xff;
                    RB0 = 1;
                    break;
                case '+':
                    focuser_run_bit = 1;
                    frev = 0xfe;
                    RB0 = 0;
                    break;
                case 'Q':
                    focuser_run_bit = 0;
                    break;
                case 'F':
                    focuSpeed = hifocuSpeed;
                    break;
                case 'S':
                    focuSpeed = lowfocuSpeed;
                    break;
                default:
                    state = 0;
                    break;
                }
                break;

jueves, 21 de febrero de 2013

Implementar LX200 (server) en C con Ragel (FSM compiler) sobre microcontroladores AVR (2)

Definiciones:

Empezamos por   lo mas sencillo seleccionar velocidad y mover el telescopio:
Comandos de selección de velocidad:
  • :RC# Seleccionar velocidad de Guiado (guide).
  • :RG#  Seleccionar velocidad de Centrado (center).
  • :RM# Seleccionar velocidad de Búsqueda.(find).
  • :RS# Seleccionar velocidad de Desplazamiento.(slew).
Comandos de movimiento y dirección:
  • :Me#  Mueve Telescopio al Este a velocidad seleccionada. 
  • :Mn#  Mueve Telescopio al Norte a velocidad seleccionada. 
  • :Ms#  Mueve Telescopio al Sur a velocidad seleccionada.
  • :Mw#  Mueve Telescopio al Oeste a velocidad seleccionada.
  • :MS# Mueve hasta  Objeto / Coordenadas Destino
    •  Devuelve:
    • 0  si el desplazamiento es posible
    • 1<cadena># Object Below Horizon
    • 2<scadena># Object Below Higher
Código Ragel mas simple que reconoce las expresiones:
%%{
 machine LX200;
 write data;
}%%
//Definicion comandos 
%%{
Rate ="R" [GCMS];
Move = "M" [enswS];
main := ':' (Rate|Move) '#';
# Initialize and execute.
  write init;
  write exec;
 }%%

Traducida a lenguaje natural:
  • Rate:
    • Acepto cualquier cadena que empieza por la letra 'R'seguida de una 'G' o 'C' o'M' o 'S' .
  • Move:
    • Acepto cualquier cadena que empieza por la letra 'M'seguida de una 'e' o 'n' o 's' o'w' o 'S' .
  • main:(Regla principal)
    • Acepto cualquier comando Rate o Move si van precedidos de ':' y les sigue '#';
Y aquí el grafo de la máquina de estado finito que reconoce el lenguaje.





Reconocer comandos está muy bien pero no suele ser suficiente,  además queremos que estos tengan algún efecto en el mundo real.
Veremos como en la próxima entrada.

martes, 12 de febrero de 2013

Implementar LX200 (server) en C con Ragel (FSM compiler) sobre microcontroladores AVR (1)


Mi intención es dedicar unas cuantas entradas a explicar la implementación si aprecio que existe algún interés ya que es mas trabajoso explicarlo que hacerlo. Últimamente los proyectos de cacharreo astronómico nacionales son muy  escasitos...Y no:  AVR != Arduino.

LX200  es un protocolo, propiedad de fabricante de  equipamiento astronómico Meade Instrumentscuyo objeto es el control de telescopios astronómicos.
Adopta   el  modelo cliente-servidor ,siendo el dispositivo cliente generalmente un PC  ,o Tablet ,el cual ejecuta software de planetario o adquisición de imágenes.
El servidor suele esta implementado  en el firmware del telescopio(montura).
En otros casos puede implementarse parcialmente un traductor en el lado del cliente como se hacia con la versión estándar de PicGoto.

Esto sería una conversación de inicio de sesión típica con su traducción al español   :

Cliente: :Ga:#:Gt# (¿Donde  estás?) 
Servidor: +36*50#-002*50#  (En Almería.)
Cliente:::Sg +36*43#:St-004*12# (Eso fue ayer.Ahora estás en Benajarafe.)
Servidor 1 (vale)
Cliente::SL23:15:00# (Son las once y cuarto.) 
Servidor 1 (vale)
Cliente:::gT#(Te enciendo el GPS por si no te fías.)
Servidor: 1 (Gracias,Ya estoy orientado.)  
Cliente: :GR#:GD# (¿Donde estás apuntado? )
Servidor:  :05:15:10#-08*11'14#  (A las coordenadas del Rigel.)
Cliente :  :Sr13:26:48#:Sd-47*29'00# (Te paso las  coordenadas de omega centauri.) 
Servidor:  00 (Está bajo el horizonte , prepara el detector de neutrinos.)
Cliente : :MS# ¡Apunta allí ahora !
Servidor:  1 (No, paso, lo del detector era broma ,no apunto ni loco, que me rompo)

Se puede consultar el conjunto completo de comandos y respuestas en LX200  


El objetivo es implementar un intérprete de LX200;n o por gusto,sino porque es el protocolo más extendido y por estar lo suficientemente mal diseñado como para que tenga sentido emplear un lenguaje de especificación de maquinas de estado finito  como  Ragel .

Frente  a otras herramienta muy extendidas como Lex o, el código generado es lo suficientemente  ligero y rápido  para ser usado con microcontrolador y pequeños sistemas embebidos.
El compilador de Ragel genera código sin dependencias de librerías externas  en C,D,Java o Ruby.

Las ventajas  sobre  programar directamente dicha  máquina de estados en C son varias:
  • Se usan expresiones regulares para describir la maquina y sus componentes.
  • Se asocian acciones (trozos de código nativo) a las transiciones entre estados
  • Es mas fácil de mantener, ampliar modificar y corregir.
  • Puede  generar diagramas gráficos de máquina que facilitan el análisis y la detección de errores.
El digrafo siguiente es la representación gráfica generada a partir de la maquina de estado que esta descrita en el bloque de código posterior. Es una implementación minimalista del protocolo LX200 incluyendo  comandos básicos de apuntado,guiado,puesta en hora,sincronización ...













Aunque cacho de código Ragel siguiente parezca  un truño infumable, lo es mucho menos que su implementación directa  en C. 

%%{
 machine command;
 write data;
}%%


        

 %%{
  #Acciones 
  action getgrads {ADD_DIGIT(deg,fc); }
  action getmin {ADD_DIGIT(min,fc); } 
  action getsec {ADD_DIGIT(sec,fc); } 
  action neg { neg=-1;} 
  action dir {move(stcmd);}
  action Goto {GOTO;}
  action stop {;}
  
  action rate { RATE;}
  
  action return_ra { lxprintra(mount.current_ra);}
  action return_dec {lxprintde( mount.current_dec);} 
  action return_ra_target { lxprintra(mount.ra_target);}
  action return_dec_target {lxprintde( mount.dec_target);} 
  
  action return_sid_time { ;}
  action sync {mount.current_dec=mount.dec_target;
        mount.current_ra=mount.ra_target;
                          rprintf("synced#");}
        
  action settargetra { set_target_ra( deg,min, sec);} 
  action settargetde { set_target_de( deg,min, sec,neg);}
        action rafrac {deg+=(fc-'0')*6;}
  action return_local_time { ltime();}
  action set_cmd_exec {set_cmd_exe(stcmd,(neg*(deg )));}
  action addmin {deg=deg*3600+min*60;}
        action addsec {deg+=sec;}
  action storecmd {stcmd=fc;}
  
  #definicion gramática  LX terminos auxiliares
     sexmin =  ([0-5][0-9])$getmin@addmin ;
     sex= ([0-5][0-9] )$getsec@addsec;
  deg =(([\+] | [\-]@neg)  |  digit @getgrads) (digit @getgrads){2} "ß"  sexmin ([:\']  sex)? ;
        RA = ([0-2] digit) $getgrads   ':' sexmin ('.'digit@rafrac | ':' sex) ;
  #Definicion gramática comandos
     Poll= 'G'( 'R'%return_ra | 'D'%return_dec |'r'%return_ra_target | 'd'%return_dec_target | 'L'%return_local_time |'S'%return_sid_time); 
     Move = 'M' ([nswe]@storecmd %dir | 'S'%Goto);
     Rate = 'R' [CGMS] %rate;
     Set='S' (([dazgt]@storecmd ' 'deg ) | ([rLS]@storecmd ' ' RA))%set_cmd_exec ;
  Sync = "CM"%sync; 
  Stop ='Q' (''|[nsew])@storecmd %stop;

   main := (':' (Set | Move | Stop|Rate | Sync | Poll)) '#'  ;


  # Initialize and execute.
  write init;
  write exec;
 }%%

Lo he compilado con AVR-GCC para un ATMEGA128L y pasa como un telescopio verdadero para la implementación ASCOM de LX200.


(continuará)