Subversion Repositories shark

Rev

Blame | Last modification | View Log | RSS feed

/*
 * Project: S.Ha.R.K.
 *
 * Coordinators:
 *   Giorgio Buttazzo    <giorgio@sssup.it>
 *   Paolo Gai           <pj@gandalf.sssup.it>
 *
 * Authors     :
 *   Paolo Gai           <pj@gandalf.sssup.it>
 *   Massimiliano Giorgi <massy@gandalf.sssup.it>
 *   Luca Abeni          <luca@gandalf.sssup.it>
 *   (see the web pages for full authors list)
 *
 * ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy)
 *
 * http://www.sssup.it
 * http://retis.sssup.it
 * http://shark.sssup.it
 */



/**
 ------------
 CVS :        $Id: sensor.c,v 1.1.1.1 2002-09-02 09:37:42 pj Exp $

 File:        $File$
 Revision:    $Revision: 1.1.1.1 $
 Last update: $Date: 2002-09-02 09:37:42 $
 ------------
**/


/*
 * Copyright (C) 2000 Marco Dallera and Marco Fiocca
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */


/*
 *              AUTO
 *
 * Another Unuseful Track simulatOr
 *
 * Authors: Marco Dallera
 *          Marco Fiocca
 *
 */


/* ------------ */
/*  Camera car  */
/* ------------ */

#include "include/auto.h"
#include "include/const.h"
#include "include/utils.h"
#include <drivers/glib.h>

#define RADIUS                  1
#define TOLL_FACTOR         4.0
#define MIN_DIFF        3
#define ROAD_COLOR              27
#define OFFROAD_COLOR   0
#define DEBUG_COLOR             GREEN
#define OUT_OF_TRACK    230
#define STRIP_DEFAULT   TEL_WIDTH+1

extern sem_t grx_mutex;
extern car_params cars[MAX_CAR_NUMBER];

void scanline(point_f *buffer, float x1, float y1, float x2, float y2, int factor);
void get_camera_car(BYTE *buffer, car_status status);
int find_track(BYTE *image, road_info *info);
int find_opps(BYTE *image, road_info *info);
void find_collision(BYTE *image, road_info *info, car_status status);

/* ----------------------------------------------------------------- */

TASK sensor(int index) {
  car_status actual_car_status;         // the actual car state
  road_info actual_road_info;           // the actual road informations
  BYTE image[TEL_WIDTH*TEL_HEIGHT];     // buffer contenente la visione della telecamera ruotata

  point sq1, sq2;
  point im1, im2;

  CAB road_status_cab = cars[index].road_status_cab;
  CAB car_status_cab  = cars[index].car_status_cab;

  // Draws display squares
  sq1.x = TRACK_X1 + TRACK_WIDTH + TEL_HEIGHT;
  sq1.y = TEL_HEIGHT*index + 1 + index;
  sq2.x = sq1.x + TEL_WIDTH + 1;
  sq2.y = sq1.y + TEL_HEIGHT + 1;

  // Sets viewer coords
  im1.x = sq1.x + 1;
  im1.y = sq1.y + 1;
  im2.x = im1.x + TEL_WIDTH - 1;
  im2.y = im1.y + TEL_HEIGHT - 1;

  // Inizializza il cab della strada
  actual_road_info.left_border = TEL_WIDTH/2;
  actual_road_info.right_border = TEL_WIDTH/2;
  actual_road_info.distance = TEL_HEIGHT;
  actual_road_info.curve = 0.0;
  actual_road_info.opps_number = 0;
  actual_road_info.flag = ROAD_OK;
  actual_road_info.collision = NO_COLL;
  set_road_info(actual_road_info, road_status_cab);

  task_endcycle();

  while(1) {
    /* reads car informations */
    actual_car_status = get_car_status(car_status_cab);

    /* implementazione riconoscimento strada (sti cazzi!!!) */
    get_camera_car(image, actual_car_status);
    find_track(image, &actual_road_info);
    find_opps(image, &actual_road_info);
    find_collision(image, &actual_road_info, actual_car_status);

    sem_wait(&grx_mutex);
    grx_rect(sq1.x, sq1.y, sq2.x, sq2.y, cars[index].color);
    grx_putimage(im1.x, im1.y, im2.x, im2.y, image);
    sem_post(&grx_mutex);

    /* sends informations to control task */
    set_road_info(actual_road_info, road_status_cab);
    task_endcycle();
  }

}

/* ----------------------------------------------------------------- */

void find_collision(BYTE *image, road_info *info, car_status status)
{
  point_f lt,rt,lb,rb,ct;
  /* the lines used to scan the quadrilate */
  point_f height_line[CAR_HEIGHT];
  int i;
  BYTE pix;

  info->collision = NO_COLL;

  ct.x = status.pos.x + (float)(CAR_HEIGHT/2 + 1) * cos(degree_to_rad(status.orient));
  ct.y = status.pos.y - (float)(CAR_WIDTH/2 + 1) * sin(degree_to_rad(status.orient));

  lb.x = ct.x - (float)(CAR_WIDTH/2.0) * cos(degree_to_rad(90-status.orient));
  rb.x = ct.x + (float)(CAR_WIDTH/2.0) * cos(degree_to_rad(90-status.orient));
  lt.x = lb.x - (float)(CAR_HEIGHT) * cos(degree_to_rad(status.orient));
  rt.x = rb.x - (float)(CAR_HEIGHT) * cos(degree_to_rad(status.orient));

  lb.y = ct.y - (float)(CAR_WIDTH/2.0) * sin(degree_to_rad(90-status.orient));
  rb.y = ct.y + (float)(CAR_WIDTH/2.0) * sin(degree_to_rad(90-status.orient));
  lt.y = lb.y + (float)(CAR_HEIGHT) * sin(degree_to_rad(status.orient));
  rt.y = rb.y + (float)(CAR_HEIGHT) * sin(degree_to_rad(status.orient));

  sem_wait(&grx_mutex);
  scanline(height_line,rb.x,rb.y,rt.x,rt.y,CAR_HEIGHT);
  for (i=0; i < CAR_HEIGHT; i++) {
    pix = grx_getpixel(round(height_line[i].x),round(height_line[i].y));
    if (pix != OFFROAD_COLOR && pix != ROAD_COLOR)
      info->collision = COLLISION_RIGHT;
  }
  scanline(height_line,lb.x,lb.y,lt.x,lt.y,CAR_HEIGHT);
  for (i=0; i < CAR_HEIGHT; i++) {
    pix = grx_getpixel(round(height_line[i].x),round(height_line[i].y));
    if (pix != OFFROAD_COLOR && pix != ROAD_COLOR)
      info->collision = COLLISION_LEFT;
  }
  sem_post(&grx_mutex);

}

/* ----------------------------------------------------------------- */

/* Puts in a buffer the image viewed from the camera according to the car status */
void get_camera_car(BYTE *buffer, car_status status) {
  /*
   *  lt ------ rt
   *   |            |
   *   |            |
   *  lb --ct-- rb
   */

  point_f lt,rt,lb,rb,ct;
  /* the lines used to scan the quadrilate */
  point_f width_line[TEL_WIDTH];
  point_f height_line[TEL_HEIGHT];
  int i,j;
  int tmpx,tmpy,tx,ty;
  float x,y;
  BYTE *row;

  ct.x = status.pos.x + (float)(CAR_WIDTH/2 + 1) * cos(degree_to_rad(status.orient));
  ct.y = status.pos.y - (float)(CAR_WIDTH/2 + 1) * sin(degree_to_rad(status.orient));

  /* for now the visual starts from the center of the car */
  lb.x = ct.x - (float)(TEL_WIDTH/2.0) * cos(degree_to_rad(90-status.orient));
  rb.x = ct.x + (float)(TEL_WIDTH/2.0) * cos(degree_to_rad(90-status.orient));
  lt.x = lb.x + (float)(TEL_HEIGHT) * cos(degree_to_rad(status.orient));
  rt.x = rb.x + (float)(TEL_HEIGHT) * cos(degree_to_rad(status.orient));

  lb.y = ct.y - (float)(TEL_WIDTH/2.0) * sin(degree_to_rad(90-status.orient));
  rb.y = ct.y + (float)(TEL_WIDTH/2.0) * sin(degree_to_rad(90-status.orient));
  lt.y = lb.y - (float)(TEL_HEIGHT) * sin(degree_to_rad(status.orient));
  rt.y = rb.y - (float)(TEL_HEIGHT) * sin(degree_to_rad(status.orient));

  /* scan only 2 sense thanks to parallelism */
  /*  ^ <-(height)
   *  ! lt
   *  !  |
   *  !  |
   *  ! lb ------ rb )
   *  0 -------------> <-(width
   */

  /* the width_line coordinates are relative, so in the for cycle
     they are summarized to absolute height_line values */

  scanline(width_line,0,0,rb.x - lb.x,rb.y - lb.y,TEL_WIDTH);
  scanline(height_line,lb.x,lb.y,lt.x,lt.y,TEL_HEIGHT);

  /* First fill row TEL_HEIGHT-1 of image and so on until row 0  */
  sem_wait(&grx_mutex);
  for (i=0; i <  TEL_HEIGHT; i++) {
    x = height_line[i].x;
    y = height_line[i].y;
    row = &buffer[TEL_WIDTH * (TEL_HEIGHT-1-i)];
    for (j=0; j <  TEL_WIDTH; j++) {
      tmpx = round(x + width_line[j].x);
      tmpy = round(y + width_line[j].y);

      // Test rispetto ai bordi
      if (tmpx >= 0 && tmpx < SCREEN_WIDTH && tmpy >= 0 && tmpy < SCREEN_HEIGHT) {
        tx = tmpx - TRACK_X1-1;
        ty = tmpy - TRACK_Y1-1;
        if (tx >= 0 && tx < TRACK_WIDTH && ty >= 0 && ty < TRACK_HEIGHT)
          row[j] = grx_getpixel(tmpx,tmpy);
        else
          row[j] = OUT_OF_TRACK;
      } else {
        row[j] = OUT_OF_TRACK;
      }
    }
  }
  sem_post(&grx_mutex);
}

/* ----------------------------------------------------------------- */

void scanline(point_f *buffer, float x1, float y1, float x2, float y2, int factor) {
  int i;
  float x,y;
  float xstep,ystep;

  xstep = (x2 - x1) / factor;
  ystep = (y2 - y1) / factor;

  // Start scan at <x1, y1>
  x = x1;
  y = y1;

  for (i = 0; i < factor; i++) {
    buffer[i].x = x;
    buffer[i].y = y;
    x += xstep;
    y += ystep;
  }
}

/* ----------------------------------------------------------------- */

/*
 * Approssima la curva quando non è più possibile farlo con la scansione
 * orizzontale
 * - image
 * - index              l'indice da cui cominciare la ricerca della curva
 * - distance   la distanza dalla curva
 */

float approx_curve(BYTE *image, int index, int distance, int d_center, road_strip *strips) {
  BYTE *p,tmp;
  int i,j,k,ctrl=0;
  point ms_center,ct;

  // Decide se la curva e' verso sinistra o verso destra
  p = &image[(TEL_HEIGHT - index - 1) * TEL_WIDTH];

  ms_center.x = strips[index-1].left;
  for (i = 0; i < min(3,strips[index-1].left); i++) {
    // La curva è a destra
    tmp = *(p + round(ms_center.x) - i);
    if (tmp != ROAD_COLOR) {
      ms_center.x = strips[index-1].right;
      break;
    }
  }

  p += round(ms_center.x);
  // Scandisce la colonna fino alla fine della strada
  for (i = index; i < TEL_HEIGHT; i++, p -= TEL_WIDTH) {
    if (*p != ROAD_COLOR)       break;
  }

  // Imposta il nuovo max_scost empirico
  ms_center.y = (i-1 + index) / 2;

  // i-1 è l'indice dell'ultima riga bianca
  p = &image[(TEL_HEIGHT - (i-1) - 1) * TEL_WIDTH + round(ms_center.x)];
  // scandisce da destra a sinistra
  if (round(ms_center.x) > 0) {
    for (j = 0; j < (round(ms_center.x) - strips[index-1].left); j++, p--) {
      if (*p != ROAD_COLOR)     break;
      // scandisce dall'alto in basso finchè non trova nero
      for (k = i-1; k > index; k--) {
        tmp = *(p + (i-1-k)*TEL_WIDTH);
        if (tmp != ROAD_COLOR) {
          ms_center.y = (k + index) / 2;
          ctrl = 1;
          break;
        }
      }
      if (ctrl) break;
    }
    ct.x = ms_center.x - j/2;
    ct.y = ms_center.y;
  } else {
    // scandisce da sinistra a destra
    for (j = 0; j < (strips[index-1].right - round(ms_center.x)); j++, p++) {
      if (*p != ROAD_COLOR)     break;
      // scandisce dall'alto in basso finchè non trova nero
      for (k = i-1; k > index; k--) {
        tmp = *(p + (i-1-k)*TEL_WIDTH);
        if (tmp != ROAD_COLOR) {
          ms_center.y = (k + index) / 2;
          ctrl = 1;
          break;
        }
      }
      if (ctrl) break;
    }
    ct.x = ms_center.x + j/2;
    ct.y = ms_center.y;
  }

  return rad_to_degree(atan2((double)(ct.y - distance),(double)(ct.x - d_center)));
}

/* ----------------------------------------------------------------- */

int find_distance(road_strip *strips, int max_scost, int rett_tol) {
  int i, distance;
  int center_car = TEL_WIDTH/2;

  // La distanza non puo' essere maggiore del max_scost
  distance = max_scost;

  for (i=1; i < max_scost; i++) {
    // Calcola la distanza dalla curva
    if (abs(strips[i].center - center_car) > rett_tol) {
      distance = i;
      break;
    }
  }
  return distance;
}

/* ----------------------------------------------------------------- */

/* Scandisce la linea row e inserisce nella strip index le informazioni.
 * Ritorna 1 se trova entrambi i bordi, altrimenti 0
 */

int scan_strip(BYTE *row, int index, road_strip *strips) {
  // Tiene conto della posizione dei bordi precedenti per trattare
  // meglio il caso di curve molto strette, dove possono esistere
  // 2 bordi destri o sinistri

  // Per evitare problemi nei casi in cui siano possibili 2
  // bordi destri o sinistri
  int lfound = 0;
  int rfound = 0;
  int center_car = TEL_WIDTH/2;
  int i;

  // Parte dal secondo pixel e finisce al penultimo
  for(i=1; i < (TEL_WIDTH-1); i++) {
    // Se trova la strada controlla se si tratta di bordo
    // destro o sinistro
    if (row[i] == ROAD_COLOR) {
      // bordo sinistro
      if (row[i - 1] != ROAD_COLOR) {
                                // Se ho già trovato questo tipo di bordo cerco
                                // quello + plausibile ...
        if (lfound) {
          // Test sulla striscia precedente
          if (index > 0) {
            if (abs(i - strips[index-1].left) <
                abs(strips[index].left - strips[index-1].left))
              strips[index].left = i;
          } else {
            // Test sul centro della macchina
            if (abs(i - center_car) < abs(strips[0].left - center_car))
              strips[index].left = i;
          }
          lfound++;
        } else {
          // ... altrimenti lo inizializzo
          strips[index].left = i;
          lfound = 1;
        }
                                // bordo destro
      } else if ((row[i + 1] != ROAD_COLOR) && lfound) {
                                // Se ho già trovato questo tipo di bordo cerco
                                // quello + plausibile ...
        if (rfound) {
          // Test sulla striscia precedente
          if (index > 0) {
            if (abs(i - strips[index-1].right) <
                abs(strips[index].right - strips[index-1].right))
              strips[index].right = i;
          } else {
            // Test sul centro della macchina
            if (abs(i - center_car) < abs(strips[0].right - center_car))
              strips[index].right = i;
          }
          rfound++;
        } else {
          // ... altrimenti lo inizializzo
          strips[index].right = i;
          rfound = 1;
        }
      }
    }
  }

  // Calcola il centro della strada se trova i bordi
  if (lfound && rfound)
    strips[index].center = (strips[index].left + strips[index].right) / 2;

  return (lfound && rfound);
}

/* ----------------------------------------------------------------- */

void where_am_i(BYTE *row, road_strip strip, road_info *info) {
  int i;
  int lfound = 0;
  int rfound = 0;
  int car_center = TEL_WIDTH/2;

  for(i=1; i < (TEL_WIDTH-1); i++) {
    // Se trova la strada controlla se si tratta di bordo
    // destro o sinistro
    if (row[i] == ROAD_COLOR) {
      // bordo sinistro
      if (row[i - 1] != ROAD_COLOR) {
        strip.left = i;
        lfound = 1;
        break;
      } else if (row[i + 1] != ROAD_COLOR) {
        strip.right = i;
        rfound = 1;
        break;
      }
    }
  }

  if (lfound && rfound) {
    info->flag = ROAD_OK;
  }
  else if (lfound && !rfound)   info->flag = LEFT_ONLY;
  else if (!lfound && rfound)    info->flag = RIGHT_ONLY;
  else {
    if (row[car_center] == OFFROAD_COLOR) info->flag = NO_ROAD;
    else    info->flag = ROAD_OK;
  }
  info->left_border = car_center - strip.left;
  info->right_border = strip.right - car_center;
}

/* ----------------------------------------------------------------- */

/* Analizza l'immagine vista dalla telecamera inserendo le informazioni
 * necessarie in info, ovvero la distanza dai bordi destro e sinistro,
 * la distanza dalla prossima curva e il suo angolo di curvatura.
 * Per il momento ritorna -1 se non riesce a trovare entrambi i bordi della
 * strada; in futuro gestiremo anche questa situazione.
 */

int find_track(BYTE *image, road_info *info) {
  // vettore contenente le informazioni su ogni striscia
  road_strip strips[TEL_HEIGHT];
  BYTE *row;
  int car_center = TEL_WIDTH/2;
  int max_scost=0;
  int once_scost=0;
  int j, rett_tol=0;

  info->flag = ROAD_OK;

  strips[0].left = STRIP_DEFAULT;
  strips[0].right = STRIP_DEFAULT;
  /* Tratta in modo personalizzato il primo tratto di strada per
     inizializzare alcune variabili e per capire situazioni particolari */

  row = &image[(TEL_HEIGHT - 1)*TEL_WIDTH];
  // Se trova i bordi ...
  if (scan_strip(row,0,strips)) {
    // ... calcola i bordi della strada e ...
    info->left_border = car_center - strips[0].left;
    info->right_border = strips[0].right - car_center;
    // ... inizializza la tolleranza per il rettilineo.
    rett_tol = (strips[0].right - strips[0].left) / TOLL_FACTOR;
  } else {
    // Se non trova il bordo ...
    // Mi sono perso, cercami la strada
    where_am_i(row,strips[0],info);
    return 1;
  }

  /* Ora controlla le altre strisce della strada */

  for(j=1; j < TEL_HEIGHT ; j++) {
    row = &image[(TEL_HEIGHT - j - 1)*TEL_WIDTH];
    // Se trova i bordi ...
    if (scan_strip(row, j, strips)) {
      // ... aggiorna l'indice di max scostamento.
      if (abs(strips[j].center - car_center) >
          abs(strips[max_scost].center - car_center)
          && abs(strips[j].center - car_center) > rett_tol) {

        max_scost = j;
        once_scost=1; // ha trovato almeno un max scost
      }
    } else {
      // Se max_scost non è mai stato settato, prendo il max
      if (!once_scost) {
        max_scost = j-1;
        once_scost = 1;
      }

      // se la distanza e il max_scost sono troppo vicini approssima la curva
      info->distance = find_distance(strips, max_scost, rett_tol);
      if ((max_scost - info->distance) < MIN_DIFF) {
        info->curve = approx_curve(image, j, info->distance,
                                   strips[info->distance].center,strips) - 90.0;
        return 1;
      }
      break;
    }
  }

  // Se max_scost non è mai stato settato, prendo il max
  if (!once_scost)      max_scost = TEL_HEIGHT-1;
  max_scost = min(max_scost,TEL_HEIGHT-1);

  info->distance = find_distance(strips,max_scost,rett_tol);

  // calcola l'angolo di curvatura solo se il conto e' attendibile
  if ((info->distance < (TEL_HEIGHT*0.9)) &&
      ((max_scost - info->distance) >= MIN_DIFF))
    info->curve = rad_to_degree(atan2((double)(max_scost - info->distance),
                                      (double)(strips[max_scost].center - strips[info->distance].center)));
  else {
    // se i conti sono empirici, si guarda la posizione dei due centri
    if (strips[max_scost].center > strips[info->distance].center)
      info->curve = 88.0;
    else if (strips[max_scost].center < strips[info->distance].center)
      info->curve = 92.0;
    else
      info->curve = 90.0;
  }
  info->curve -= 90.0;
  return 0;
}

/* ----------------------------------------------------------------- */

int find_opps(BYTE *image, road_info *info)
{
  int i, j;
  int l_limit, r_limit;         // the opposite empiric limits
  BYTE *row;
  int car_center = TEL_WIDTH/2;
  int opps_number = 0;

  // by default it needs to scan the entire row
  l_limit = TEL_WIDTH - 1;
  r_limit = 0;

  // Scans one row at a time
  for(j=1; j<TEL_HEIGHT; j++) {
    row = &image[(TEL_HEIGHT - j - 1) * TEL_WIDTH];

    // if some of the previous row has been identified as a car...
    if (opps_number > 0 &&
        j < (info->opps_list[opps_number - 1].y + CAR_HEIGHT + 1)) {
      // excludes CAR_HEIGHT lines from the bottom if the car
      l_limit = (car_center + info->opps_list[opps_number - 1].x) - CAR_WIDTH;
      r_limit = (car_center + info->opps_list[opps_number - 1].x) + CAR_WIDTH;
    }
    else {
      // it needs to scan the entire row
      l_limit = TEL_WIDTH - 1;
      r_limit = 0;
    }

    // Scans the current row
    for (i=0; i<TEL_WIDTH; i++) {
      if( (i < l_limit || i > r_limit) &&
          row[i] != ROAD_COLOR && row[i] != OFFROAD_COLOR &&
          row[i] != DEBUG_COLOR && row[i] != OUT_OF_TRACK) {
                                // there is an opponent car!!
                                // stores its position
        info->opps_list[opps_number].x = i - car_center;                // left -, right +
        info->opps_list[opps_number].y = j;

                                // increases number of opposites car in the sensor view range
        opps_number++;
      }
    }
  }

  info->opps_number = opps_number;

  return 0;
}