/*
 * c't-Sim - Robotersimulator fuer den c't-Bot
 * 
 * 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.
 * 
 */

/*! @file 	sensor.c  
 * @brief 	Architekturunabhaengiger Teil der Sensorsteuerung
 * @author 	Benjamin Benz (bbe@heise.de)
 * @date 	15.01.05
*/
#include <stdio.h>
#include "ct-Bot.h"
#include "timer.h"
#include "bot-local.h"
#include "math.h"
#include "sensor_correction.h"
#ifdef SRF10_AVAILABLE
	#include "srf10.h"
#endif

// Defines einiger, haeufiger benoetigter Konstanten
#define DEG2RAD (2*M_PI/360)


int16 sensLDRL=0;		/*!< Lichtsensor links */
int16 sensLDRR=0;		/*!< Lichtsensor rechts */

int16 sensDistL=1023;		/*!< Distanz linker IR-Sensor in [mm], wenn korrekt umgerechnet wird */
int16 sensDistR=1023;		/*!< Distanz rechter IR-Sensor in [mm], wenn korrekt umgerechnet wird */

int16 sensBorderL=0;	/*!< Abgrundsensor links */
int16 sensBorderR=0;	/*!< Abgrundsensor rechts */

int16 sensLineL=0;	/*!< Lininensensor links */
int16 sensLineR=0;	/*!< Lininensensor rechts */

uint8 sensTrans=0;		/*!< Sensor Ueberwachung Transportfach */

uint8 sensDoor=0;		/*!< Sensor Ueberwachung Klappe */

uint8 sensError=0;		/*!< Ueberwachung Motor oder Batteriefehler */

#ifdef MAUS_AVAILABLE

	int8 sensMouseDX;		/*!< Maussensor Delta X, positive Werte zeigen querab der Fahrtrichtung nach rechts */
	int8 sensMouseDY;		/*!< Maussensor Delta Y, positive Werte zeigen in Fahrtrichtung */
	
	int16 sensMouseX;		/*!< Mausposition X, positive Werte zeigen querab der Fahrtrichtung nach rechts */
	int16 sensMouseY;		/*!< Mausposition Y, positive Werte zeigen in Fahrtrichtung  */
#endif

volatile int16 sensEncL=0;		/*!< Encoder linkes Rad */
volatile int16 sensEncR=0;		/*!< Encoder rechtes Rad */
float heading_enc=0;	/*!< Blickrichtung aus Encodern */
float x_enc=0;		/*!< X-Koordinate aus Encodern [mm] */
float y_enc=0;		/*!< Y-Koordinate aus Encodern [mm] */
float v_enc_left=0;	/*!< Abrollgeschwindigkeit des linken Rades in [mm/s] [-128 bis 127] relaisitisch [-50 bis 50] */
float v_enc_right=0;	/*!< Abrollgeschwindigkeit des linken Rades in [mm/s] [-128 bis 127] relaisitisch [-50 bis 50] */
float v_enc_center=0;	/*!< Schnittgeschwindigkeit ueber beide Raeder */

#ifdef PC
	uint16 simultime=0;	/*! Simulierte Zeit */
#endif

#ifdef MEASURE_MOUSE_AVAILABLE
	float heading_mou=0;		/*!< Aktuelle Blickrichtung relativ zur Startposition aus Mausmessungen */
	float x_mou=0;			/*!< Aktuelle X-Koordinate in mm relativ zur Startposition aus Mausmessungen */
	float y_mou=0;			/*!< Aktuelle Y-Koordinate in mm relativ zur Startposition aus Mausmessungen */
	float v_mou_center=0;		/*!< Geschwindigkeit in mm/s ausschliesslich aus den Maussensorwerten berechnet */
	float v_mou_left=0;		/*!< ...aufgeteilt auf linkes Rad */
	float v_mou_right=0;		/*!< ...aufgeteilt auf rechtes Rad */
#endif

float heading=0;			/*!< Aktuelle Blickrichtung aus Encoder-, Maus- oder gekoppelten Werten */
float x_pos=0;			/*!< Aktuelle X-Position aus Encoder-, Maus- oder gekoppelten Werten */
float y_pos=0;			/*!< Aktuelle Y-Position aus Encoder-, Maus- oder gekoppelten Werten */
float v_left=0;			/*!< Geschwindigkeit linkes Rad aus Encoder-, Maus- oder gekoppelten Werten */
float v_right=0;			/*!< Geschwindigkeit rechtes Rad aus Encoder-, Maus- oder gekoppelten Werten */
float v_center=0;			/*!< Geschwindigkeit im Zentrum des Bots aus Encoder-, Maus- oder gekoppelten Werten */

int8 sensors_initialized = 0;	/*!< Wird 1 sobald die Sensorwerte zur Verfügung stehen */

#ifdef SRF10_AVAILABLE
	uint16 sensSRF10;	/*!< Messergebniss Ultraschallsensor */
#endif


/*! Linearisiert die Sensorwerte
 * @param left Linker Rohwert [0-1023]
 * @param right Rechter Rohwert [0-1023]
 */
void sensor_abstand(uint16 left, uint16 right){
	if (left  == SENSDISTOFFSETLEFT)  // Vermeidet Div/0
		left++;	
	sensDistL = SENSDISTSLOPELEFT / (left - SENSDISTOFFSETLEFT);
	// Korrigieren, wenn ungueltiger Wert
	if (sensDistL > SENS_IR_MAX_DIST || sensDistL<=0)
		sensDistL=SENS_IR_INFINITE;

	if (right == SENSDISTOFFSETRIGHT) // Vermeidet Div/0
		right++;
	sensDistR = SENSDISTSLOPERIGHT / (right - SENSDISTOFFSETRIGHT);
	// Korrigieren, wenn ungueltiger Wert
	if (sensDistR > SENS_IR_MAX_DIST || sensDistR<=0)
		sensDistR=SENS_IR_INFINITE;
}

/*! Sensor_update
* Kuemmert sich um die Weiterverarbeitung der rohen Sensordaten 
*/
void sensor_update(void){
	static uint16 old_pos=0;			/*!< Ticks fuer Positionsberechnungsschleife */
	static uint16 old_speed=0;			/*!< Ticks fuer Geschwindigkeitsberechnungsschleife */
	#ifdef MEASURE_MOUSE_AVAILABLE
			static int16 lastMouseX=0;		/*!< letzter Mauswert X fuer Positionsberechnung */
			static int16 lastMouseY=0;		/*!< letzter Mauswert Y fuer Positionsberechnung */
			static float lastDistance=0;	/*!< letzte gefahrene Strecke */
			static float lastHead=0;		/*!< letzter gedrehter Winkel */
			static float oldHead=0;		/*!< Winkel aus dem letzten Durchgang */
			static float old_x=0;			/*!< Position X aus dem letzten Durchgang */
			static float old_y=0;			/*!< Position Y aus dem letzten Durchgang */
			float radius=0;				/*!< errechneter Radius des Drehkreises */
			float s1=0;					/*!< Steigung der Achsengerade aus dem letzten Durchgang */
			float s2=0;					/*!< Steigung der aktuellen Achsengerade */
			float a1=0;					/*!< Y-Achsenabschnitt der Achsengerade aus dem letzten Durchgang */
			float a2=0;					/*!< Y-Achsenabschnitt der aktuellen Achsengerade */
			float xd=0;					/*!< X-Koordinate Drehpunkt */
			float yd=0;					/*!< Y-Koordinate Drehpunkt */
			float right_radius=0;			/*!< Radius des Drehkreises des rechten Rads */
			float left_radius=0;			/*!< Radius des Drehkreises des linken Rads */
	#endif
	static int16 lastEncL =0;		/*!< letzter Encoderwert links fuer Positionsberechnung */
	static int16 lastEncR =0;		/*!< letzter Encoderwert rechts fuer Positionsberechnung */
	static int16 lastEncL1=0;		/*!< letzter Encoderwert links fuer Geschwindigkeitsberechnung */
	static int16 lastEncR1=0;		/*!< letzter Encoderwert rechts fuer Geschwindigkeitsberechnung */
	float dHead=0;					/*!< Winkeldifferenz aus Encodern */
	float deltaY=0;				/*!< errechneter Betrag Richtungsvektor aus Encodern */
	int16 diffEncL;					/*!< Differenzbildung linker Encoder */
	int16 diffEncR;					/*!< Differenzbildung rechter Encoder */
	float sl;						/*!< gefahrene Strecke linkes Rad */
	float sr;						/*!< gefahrene Strecke rechtes Rad */
	#ifdef MEASURE_MOUSE_AVAILABLE
		int16 dX;						/*!< Differenz der X-Mauswerte */
		int16 dY;						/*!< Differenz der Y-Mauswerte */
		int8 modifiedAngles=False;		/*!< Wird True, wenn aufgrund 90 Grad oder 270 Grad die Winkel veraendert werden mussten */
	
		sensMouseY += sensMouseDY;		/*!< Mausdelta Y aufaddieren */
		sensMouseX += sensMouseDX;		/*!< Mausdelta X aufaddieren */
	#endif	
	
	register uint16 ticks = TIMER_GET_TICKCOUNT_16;
	if (ticks-old_pos > MS_TO_TICKS(50)){
		old_pos = ticks;
		/* Gefahrene Boegen aus Encodern berechnen */
		diffEncL=sensEncL-lastEncL;
		diffEncR=sensEncR-lastEncR;
		lastEncL=sensEncL;
		lastEncR=sensEncR;
		sl=(float)diffEncL*WHEEL_PERIMETER/ENCODER_MARKS;
		sr=(float)diffEncR*WHEEL_PERIMETER/ENCODER_MARKS;
		/* Winkel berechnen */
		dHead=(float)(sr-sl)/(2*WHEEL_DISTANCE);
		/* Winkel ist hier noch im Bogenmass */
		/* Position berechnen */
		/* dazu Betrag des Vektors berechnen */
		if (dHead==0) {
			/* Geradeausfahrt, deltaY=diffEncL=diffEncR */
			deltaY=sl;
		} else {
			/* Laenge berechnen aus alpha/2 */
			deltaY=(sl+sr)*sin(dHead/2)/dHead;
		}
		/* Winkel in Grad umrechnen */
		dHead=dHead/DEG2RAD;
		
		/* neue Positionen berechnen */
		heading_enc+=dHead;
		while (heading_enc>=360) heading_enc=heading_enc-360;
		while (heading_enc<0) heading_enc=heading_enc+360;
		
		x_enc+=(float)deltaY*cos(heading_enc*DEG2RAD);
		y_enc+=(float)deltaY*sin(heading_enc*DEG2RAD);	
		#ifdef MEASURE_MOUSE_AVAILABLE
			dX=sensMouseX-lastMouseX;
			/* heading berechnen */
			dHead=(float)dX*360/MOUSE_FULL_TURN;
			heading_mou+=dHead;
			lastHead+=dHead;
			if (heading_mou>=360) heading_mou=heading_mou-360;
			if (heading_mou<0) heading_mou=heading_mou+360;
			/* x/y pos berechnen */
			dY=sensMouseY-lastMouseY;
			x_mou+=(float)dY*cos(heading_mou*DEG2RAD)*25.4/MOUSE_CPI;
			lastDistance+=dY*25.4/MOUSE_CPI;
			y_mou+=(float)dY*sin(heading_mou*DEG2RAD)*25.4/MOUSE_CPI;

			lastMouseX=sensMouseX;
			lastMouseY=sensMouseY;
		#endif
		#ifdef MEASURE_COUPLED_AVAILABLE
			/* Werte der Encoder und der Maus mit dem Faktor G_POS verrechnen */
			x_pos=G_POS*x_mou+(1-G_POS)*x_enc;
			y_pos=G_POS*y_mou+(1-G_POS)*y_enc;
			/* Korrektur, falls mou und enc zu unterschiedlichen Seiten zeigen */
			if (fabs(heading_mou-heading_enc) > 180) {
				/* wir nutzen zum Rechnen zwei Drehrichtungen */
				heading = heading_mou<=180 ? heading_mou*G_POS : (heading_mou-360)*G_POS;
				heading += heading_enc<=180 ? heading_enc*(1-G_POS) : (heading_enc-360)*(1-G_POS);
				/* wieder auf eine Drehrichtung zurueck */
				if (heading < 0) heading += 360;					
			} else 
				heading = G_POS*heading_mou + (1-G_POS)*heading_enc;
			if (heading >= 360) heading -= 360;
		#else
			#ifdef MEASURE_MOUSE_AVAILABLE
				/* Mauswerte als Standardwerte benutzen */
				heading=heading_mou;
				x_pos=x_mou;
				y_pos=y_mou;
			#else
				/* Encoderwerte als Standardwerte benutzen */
				heading=heading_enc;
				x_pos=x_enc;
				y_pos=y_enc;
			#endif
		#endif	
	}
	ticks = TIMER_GET_TICKCOUNT_16;
	if (ticks-old_speed > MS_TO_TICKS(250)){	
		old_speed=ticks;
		v_enc_left=  (((sensEncL - lastEncL1) * WHEEL_PERIMETER) / ENCODER_MARKS)*4;
		v_enc_right= (((sensEncR - lastEncR1) * WHEEL_PERIMETER) / ENCODER_MARKS)*4;
		v_enc_center=(v_enc_left+v_enc_right)/2;
		lastEncL1= sensEncL;
		lastEncR1= sensEncR;
		#ifdef MEASURE_MOUSE_AVAILABLE
			/* Speed aufgrund Maussensormessungen */
			v_mou_center=lastDistance*4;
			/* Aufteilung auf die Raeder zum Vergleich mit den Radencodern */
			/* Sonderfaelle pruefen */
			if (oldHead==90 || oldHead==270 || heading_mou==90 || heading_mou==270) {
				float temp;
				/* winkel um 90 Grad vergroessern */
				oldHead+=90;
				heading_mou+=90;
				// Koordinaten anpassen
				temp=old_x;
				old_x=old_y;
				old_y=-temp;
				temp=x_mou;
				x_mou=y_mou;
				y_mou=-temp;
				modifiedAngles=True;
			} 
			
			/* Steigungen berechnen */
			s1=-tan(oldHead*DEG2RAD);
			s2=-tan(heading_mou*DEG2RAD);
			
			/* Geradeausfahrt? (s1==s2) */
			if (s1==s2) {
				/* Bei Geradeausfahrt ist v_left==v_right==v_center */
				v_mou_left=v_mou_right=v_mou_center;
			} else {
				/* y-Achsenabschnitte berechnen */
				a1=old_x-s1*old_y;
				a2=x_mou-s2*y_mou;
				/* Schnittpunkt berechnen */
				yd=(a2-a1)/(s1-s2);	
				xd=s2*yd+a2;
				/* Radius ermitteln */
				radius=sqrt((x_mou-xd)*(x_mou-xd)+(y_mou-yd)*(y_mou-yd));
				/* Vorzeichen des Radius feststellen */
				if (lastHead<0) {
					/* Drehung rechts, Drehpunkt liegt rechts vom Mittelpunkt
					 * daher negativer Radius */
					 radius=-radius;
				}
				if (v_mou_center<0) {
					/* rueckwaerts => links und rechts vertauscht, daher VZ vom Radius umdrehen */
					radius=-radius;
				}
				/* Geschwindigkeiten berechnen */
				right_radius=radius+WHEEL_DISTANCE;
				left_radius=radius-WHEEL_DISTANCE;
				v_mou_right=lastHead/360*2*M_PI*right_radius*4;
				v_mou_left=lastHead/360*2*M_PI*left_radius*4;
			}
			/* Falls Koordinaten/Winkel angepasst wurden, nun wieder korrigieren */
			if (modifiedAngles){
				float temp;
				/* Winkel wieder um 90 Grad reduzieren */
				oldHead-=90;
				heading_mou-=90;
				/* Koordinaten wieder korrigieren */
				temp=old_x;
				old_x=-old_y;
				old_y=temp;
				temp=x_mou;
				x_mou=-y_mou;
				y_mou=temp;	
			}
			lastDistance=0;
			lastHead=0;
			old_x=x_mou;
			old_y=y_mou;
			oldHead=heading_mou;
		#endif
		#ifdef MEASURE_COUPLED_AVAILABLE
			v_left=G_SPEED*v_mou_left+(1-G_SPEED)*v_enc_left;
			v_right=G_SPEED*v_mou_right+(1-G_SPEED)*v_enc_left;
			v_center=G_SPEED*v_mou_center+(1-G_SPEED)*v_enc_center;
		#else
			#ifdef MEASURE_MOUSE_AVAILABLE
				/* Mauswerte als Standardwerte benutzen */
				v_left=v_mou_left;
				v_right=v_mou_right;
				v_center=v_mou_center;
			#else
				/* Encoderwerte als Standardwerte benutzen */
				v_left=v_enc_left;
				v_right=v_enc_right;
				v_center=v_enc_center;
			#endif			
		#endif
		#ifdef SRF10_AVAILABLE
			sensSRF10 = srf10_get_measure();	/*!< Messung Ultraschallsensor */
		#endif	
	}
	
	sensors_initialized=1;
}

