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;