1. Computer interface

From the software's point of view, the grating has 2 devices:
   1. Stepper motor. Controlled using a PC38 on axis 'y'.
   2. Encoder, connected to serial port /dev/ttyS5

2. ic/Grating program

Cshell grating is controlled by the program ic/grating.
When the IC starts, the grating program open the devices
driver to the PC38 and encoder. The encoder is initialized
with the grating's scale factor using the command: "0sf25000\r:

The ic/grating performs 2 funtions during cshell operations: 
init, and step

2.1 init

   Sets up the pc38 y axis with velocity 5000, size 600000, backlash 250.

   Reads the encoder.
      EncoderPos is set to this position.
      The PC38 axis is set to EncoderPos.
      Grating.pos = pc38 position.

   opt_order is set to 28.
   user_order is set to 28.

2.2 step

  Move the PC38 axis to a step position.
  The pc38 moves the motor by output step and direction pulses. There is
  no feedback loop between the motor & pc38.

  At the end of the move the ic/grating program reads the encoder (encoderpos).

  if (Encoderpos - Grating.pos ) > 30
     a warning is printed on the XUI: WARNING Grating's encoder & motor out of sync by %ld steps

3. IC  Sofware Commands.

The IC provide text command to control the grating's position, they are:

3.1 cvfwlen wlen - move the cvf, but affects user_order.

   Used to move the cvf. The grating is not move the the user
   order is set using the wlen, grating angle:

   grating_angle = step2gangle( Grating.pos ).
   user_order =  gangle2order(wlen, grating_angle).

3.2  GratingPos - Position grating motor

  command to move the grating to a step position. Range is 100000 to 400000

3.3  grating_rmove - Relative move for grating motor

  move the grating relative to its current position. Range is -2000..+2000.

3.4. gwlen wlen - position grating by giving wlen.

    input is wlen, range is 0.93 to 6.0.

    order = wlengangle2order( wlen, BLAZE_ANGEL)   // find optinum order for wlen

    step = wlenorder2step( wlen, order )        // get step for this wlen & order.

    opt_order = order;
    user_order = order;

    move motor by issuing gratingpos command.

3.5 order num - specify the user order.
                can move grating to keep the same wavelen.

   user_order = num. Input range is 8..60.

   current_wlen = steporder2wlen(step, user_order )
   new_pos = wlenorder2step( current_wlen, user_order).

   move grating to new_pos using commmand: gratingpos new_pos.

3.6  wavelen - This command combines the CVFWlen and GWlen commands.

   issue gwlen  command.
   issue cvfwlen command.


4. The XUI

  Observing Parameter Screen:

  "WaveLen" textbox issues "WaveLen" command.
  The "cm-1" converts to wavelen and issues "WaveLen".
  The display value is: wlen =  steporder2wlen( grating_pos, user_order)

  "CvfWlen" textbox issues "CvfWlen" command.

  Motors details - provide details on the grating:

     Grating_Pos - step position of the pc38.
     GAngle      - Grating angle: gangle = step2gratingangle( Grating_pos)
     Order       - opt_order.
     User Order  - user_order.
     Wlen        - wavelen: wlen = steporder2wlen( Grating_pos, user_order).
     EncoderPos  - reports the last encoder query.

5.  FITS Header

  GPOS   is grating_pos. (Pc38 value)
  GANGLE is Grating Angle. Angle = step2gratingangle(grating_pos).  ORDER  is user_order.

6. low level C function to convert between step, order, wavelength.

      wlenorder2step()     : step  =  f( wavelength, order)
      steporder2wlen()     : wavelength  = f( step, order).
      wlengangle2order()   : "best" order = f( wavelength, grating_angle)
      gratingangle2step()  : step = f( grating_angle )
      step2gratingangle()  : grating_angle = f( step )

/************************************************************************
**
**   Conversion tables and routines for grating wlen to step position
**
*************************************************************************
*/

/* This is the coefficient to convert grating angle(A) to steps (N)
**   ie: N = -1055613 + 18634.8*A + 26.85632*A*A
*/
static double g_coeff[3] = { -1055613.0, 18634.80, 26.85632 };
#define GROOVE_SPACE 31.5195

/*----------------------------------------------------------------
**  grating_wlen2step() - determine step from wlen and order.
**----------------------------------------------------------------
*/
int grating_wlenorder2step( step_ref, wlen, order )
   long * step_ref;    /* reference to step variable */
   double wlen;        /* wlen in microns            */
   long   order;       /* order                      */
{
   int  rc;
   long step;
   double gangle;

   rc = ERR_NONE;
   step = 0;

   if( INRANGE( 0.93, wlen, 6.0 ) )
   {
     /* Find grating angle */
     gangle = asin( (wlen*(double)order) / (GROOVE_SPACE*2.0)) * (180.0/M_PI);
     rc = grating_gratingangle2step( &step, gangle );
   }
   else
      rc = ERR_INV_RNG;

   *step_ref = step;
   return rc;
}

/*----------------------------------------------------------------
**  grating_steporder2wlen() - determing wlen from step and order.
**----------------------------------------------------------------
*/
int grating_steporder2wlen( wlen_ref, step, order )
   double *wlen_ref; /* Returns wlen as float to this address */
   long   step;
   long   order;
{
   int rc;
   double wlen;
   double gangle;

   rc = ERR_NONE;
   wlen = 0;

   if( INRANGE( 100000L, step, 400000L ) )
   {
      if( ERR_NONE == (rc = grating_step2gratingangle( &gangle, step)) )
         wlen = (GROOVE_SPACE * 2.0 * sin(gangle*(M_PI/180.0))) / (double)order;
   }
   else
      rc = ERR_INV_RNG;

   *wlen_ref = wlen;
   return rc;
}

/*----------------------------------------------------------------
**  grating_wlengangle2order() - determines the best order to use for
**     wlen & grating angle.
**----------------------------------------------------------------
*/
int grating_wlengangle2order( order_ref, wlen, angle )
   long    *order_ref; /* Returns order as a long to this address */
   double  wlen;
   double  angle;
{
   int  rc;
   long order;

   rc = ERR_NONE;
   order = 0;
   if( INRANGE( 0.93, wlen, 6.0 ) && INRANGE( 55.0, angle, 71.0 ))
   {
      order = ((2.0*GROOVE_SPACE * sin(angle * (M_PI/180.0))) / wlen) + 0.5;
   }
   else
      rc = ERR_INV_RNG;
   *order_ref = order;
   return rc;
}


/*----------------------------------------------------------------
**  grating_step2gratingangle() - converts grating angel to steps
**----------------------------------------------------------------
*/
int grating_gratingangle2step( step_ref, ang )
   long   *step_ref; /* Returns step as a long to this address */
   double ang;
{
   int   rc;
   double step;

   rc = ERR_NONE;
   step = 0;
   if( INRANGE( 56.0, ang, 70.0))
      step = ypoly( (double) ang, (double *) &g_coeff, 3);
   else
      rc = ERR_INV_RNG;
   *step_ref = step;
   return rc;
}

/*----------------------------------------------------------------
**  grating_step2gratingangle(); convert steps to grating angle.
**----------------------------------------------------------------
*/
int grating_step2gratingangle( ang_ref, step )
   double *ang_ref; /* Returns the grating angle to this address */
   long   step;
{
   int    rc;
   double ang;
   double c;

   rc = ERR_NONE;
   ang = 0;
   if( INRANGE( 100000L, step, 400000L))
   {
      c = g_coeff[0] - step;
      ang  = (-g_coeff[1] + sqrt( (g_coeff[1]*g_coeff[1]) - (4*g_coeff[2]*c) )) /
             ( 2 * g_coeff[2] );
   }
   else
      rc = ERR_INV_RNG;
   *ang_ref = ang;
   return rc;
}