int gyro_type = 300;        /* 150 for ADXRS150, 300 for ADXRS300 */
float lsb_ms_per_deg;       /* nominal scale factor depending on gyro type */
float scale_factor = 1.017; /* adjust this for your particular gyro! */
float theta;                /* current angle, units explained in update_angle() */
float offset;               /* gyro offset */
int gyro_port = 3;          /* gyro analog input port number */
int num_avgs = 800;         /* number of averages for calibration */
long time_ms;               /* system time the last gyro reading was taken */

int left = 3;               /* motor ports */
int right = 1;

/*
void main(){

  if (gyro_type==300) 
    lsb_ms_per_deg = 256.0;
  else if (gyro_type==150) 
    lsb_ms_per_deg = 640.0;

  lsb_ms_per_deg *= scale_factor;

  calibrate_gyro();
  start_process(update_angle()); */
  /* Don't set theta, just read it! */
/*  start_process(display_angle());*/

  /* Make an Isoceles right triangle */
/*  go_straight(0.0,   80, 4000L);
  go_straight(135.0, 80, 2657L);   
  go_straight(270.0, 80, 4000L);
  go_to_angle(360.0); 
*/
  /* Make another one, but obstruct the robot for the demo */
/*  start_press();
  msleep( 500L );
  go_straight(360.0, 80, 4000L);
  go_straight(495.0, 80, 5657L); 
  go_straight(630.0, 80, 4000L);
  go_to_angle(720.0); 
*/
  /* Drive straight in current direction under gyro control... */
 /* start_press(); 
  msleep(500L);*/
  /* theta/lsb_ms_per_deg is the current angle in degrees */
 /* go_straight( theta/lsb_ms_per_deg, 80, 12000L );*/

  /* ... turn around ... */
/*  go_to_angle ( theta/lsb_ms_per_deg + 180.0 );*/

  /* ... and drive back open-loop (without gyro control). */
/*  go_open_loop( 80, 12000L ); */
/*
} main() */


void calibrate_gyro(){
/*
   Averaging a number of readings only works when the gyro 
   noise is greater than the converter resolution.  Since 
   the xrs300 noise is far lower than the 20mV resolution 
   of the 8-bit converter, a noise source has been added 
   to the gyro board.  You should average for about 5 
   seconds to minimize the effects of the added noise.
*/

  int samples;
  float sum;
  long start_time_ms;
  long int_time_ms = 5000L;

  printf("Place robot,    press start.\n");
  start_press();
  printf("Stabilizing...\n");
  msleep(500L); /* Wait for robot to stabilize mechanically */
  printf("Calibrating     offset...\n");

  /* average some samples for offset */
  sum = 0.0;
  samples = 0;
  start_time_ms = mseconds();
  while( ( mseconds() - start_time_ms ) < int_time_ms ){
    sum += (float)analog(gyro_port);
    samples++;
  }
  offset = sum / (float)samples;

  theta = 0.0;

} /* calibrate_gyro() */


void update_angle(){
/* The units of theta are converter LSBs * ms
   1 deg/s = 5mV = 0.256 LSB for ADXRS300, 12.5mV = 0.64LSB for ADXRS150
   1 deg = 0.256LSB * 1000ms = 256 (stored in lsb_ms_per_deg)
   To convert theta to degrees, divide by lsb_ms_per_deg. */

  long delta_t_ms, new_time_ms;
  int i;

  time_ms = mseconds();

  for(;;){
    new_time_ms = mseconds();
    delta_t_ms = (new_time_ms - time_ms);

    /* This does the Riemann sum; CCW gyro output polarity is negative
       when the gyro is visible from the top of the robot. */
    theta -= ((float)analog(gyro_port) - offset) * (float)delta_t_ms; 

    time_ms = new_time_ms;
    defer();
  }
} /* update_angle() */


void display_angle(){
  for(;;){
    printf("%d degrees\n",(int)(theta / lsb_ms_per_deg)); 
    msleep(200L);
    defer();
  }
} /* display_angle() */


void go_to_angle(float angle) {
/* To convert degrees to units of theta, multiply by lsb_ms_per_deg.
   Demo robot turns at about 120 deg/s when speed = 100 */

  float angle_target;
  float error, tolerance;
  int speed;

  tolerance = 1.0 * lsb_ms_per_deg;
  angle_target = angle * lsb_ms_per_deg;
  error = theta - angle_target;

  if ( fabs(error) > tolerance ) {

    spin(100 * -sign(error));
    while(fabs(error) > (80.0 * lsb_ms_per_deg)){
      error = theta - angle_target;
    }

    spin(80 * -sign(error));
    while(fabs(error) > (60.0 * lsb_ms_per_deg)){
      error = theta - angle_target;
    }

    spin(70 * -sign(error));
    while(fabs(error) > (10.0 * lsb_ms_per_deg)){
      error = theta - angle_target;
    }

    /* Slam on the brakes */
    spin(100 * sign(error));
    msleep(10L);

    /* Fine adjust either direction as necessary */
    spin(70 * -sign(error));
    while(fabs(error) > tolerance){
      error = (theta - angle_target);
      spin(70 * -sign(error));
    }

    spin(0);  

  } /* if ( fabs(error)... */

} /* go_to_angle() */


void spin(int speed){
  motor(left, -speed);
  motor(right, speed);
} /* spin() */


void back_straight(float angle, int speed, long duration_ms){

/* The robot turns to the specified angle, then goes straight for 
   the specified duration. Note that speed must be greater than tweak, 
   and less than 100 - tweak. */

  long start_time;
  int tweak = 20;
  float angle_target;
  float tolerance;

  tolerance = lsb_ms_per_deg * 0.5; /* +/-0.5 degree accuracy */

  /* first turn the robot */
  go_to_angle(angle) ;

  /* then go forward under gyro control for the specified time */
  start_time = mseconds();
  angle_target = angle * lsb_ms_per_deg; 
  
  while ((mseconds() - start_time) < duration_ms){
    if (theta > (angle_target - tolerance)) {
      /* Turn CCW a little */
      motor(left, speed - tweak);
      motor(right, speed + tweak);
    } else if (theta < (angle_target + tolerance)){
      /* Turn CW a little */
      motor(left, speed + tweak);
      motor(right, speed - tweak);
    } else {
      motor(left, speed);
      motor(right, speed);
    }  
    /* You can check a sensor here and terminate the while loop */
  } /* while */
  
  /* Throw it in reverse briefly to stop quickly */
  motor(left, -100);
  motor(right, -100);
  msleep(20L);  
  motor(left, 0);
  motor(right, 0);
  
} /* void back_straight() */

void go_straight(float angle, int speed, long duration_ms){

/* The robot turns to the specified angle, then goes straight for 
   the specified duration. Note that speed must be greater than tweak, 
   and less than 100 - tweak. */

  long start_time;
  int tweak = 20;
  float angle_target;
  float tolerance;

  tolerance = lsb_ms_per_deg * 0.5; /* +/-0.5 degree accuracy */

  /* first turn the robot */
  go_to_angle(angle) ;

  /* then go forward under gyro control for the specified time */
  start_time = mseconds();
  angle_target = angle * lsb_ms_per_deg; 
  
  while ((mseconds() - start_time) < duration_ms){
    if (theta < (angle_target - tolerance)) {
      /* Turn CCW a little */
      motor(left, speed - tweak);
      motor(right, speed + tweak);
    } else if (theta > (angle_target + tolerance)){
      /* Turn CW a little */
      motor(left, speed + tweak);
      motor(right, speed - tweak);
    } else {
      motor(left, speed);
      motor(right, speed);
    }  
    /* You can check a sensor here and terminate the while loop */
  } /* while */
  
  /* Throw it in reverse briefly to stop quickly */
  motor(left, -100);
  motor(right, -100);
  msleep(20L);  
  motor(left, 0);
  motor(right, 0);
  
} /* void go_straight() */


void go_open_loop( int speed, long duration_ms ){

/* Drive both motors at a specified speed and duration. */

  long start_time;

  start_time = mseconds();
  
  while ((mseconds() - start_time) < duration_ms){
    motor(left, speed);
    motor(right, speed); 
    /* You can check a sensor here and terminate the while loop if necessary */
  } /* while */
  
  /* Throw it in reverse briefly to stop quickly */
  motor(left, -100);
  motor(right, -100);
  msleep(20L);  
  motor(left, 0);
  motor(right, 0);
  
} /* void go_open_loop() */


int sign(float a){
  if ( a > 0.0 ) return 1;
  else return -1;
} /* sign() */


float fabs(float a){
  if ( a > 0.0 ) return a;
  else return -a;
} /* fabs() */


