with class

Dependencies:   ISR_Mini-explorer mbed

Fork of VirtualForces by Georgios Tsamis

MiniExplorerCoimbra.cpp

Committer:
Ludwigfr
Date:
2017-06-16
Revision:
39:890439b495e3
Parent:
38:5ed7c79fb724

File content as of revision 39:890439b495e3:

#include "MiniExplorerCoimbra.hpp"
#include "robot.h"

#define PI 3.14159

MiniExplorerCoimbra::MiniExplorerCoimbra(float defaultXWorld, float defaultYWorld, float defaultThetaWorld):map(200,200,20,20),sonarLeft(10*PI/36,-4,4),sonarFront(0,0,5),sonarRight(-10*PI/36,4,4){
    i2c1.frequency(100000);
    initRobot(); //Initializing the robot
    pc.baud(9600); // baud for the pc communication

    measure_always_on();//TODO check if needed

    this->setXYThetaAndXYThetaWorld(defaultXWorld,defaultYWorld,defaultThetaWorld);
    this->radiusWheels=3.25;
    this->distanceWheels=7.2; 
    
    this->khro=12;
    this->ka=30;
    this->kb=-13;
    this->kv=200;
    this->kh=200;

    this->rangeForce=30;
    this->attractionConstantForce=10000;
    this->repulsionConstantForce=1;
} 

void MiniExplorerCoimbra::setXYThetaAndXYThetaWorld(float defaultXWorld, float defaultYWorld, float defaultThetaWorld){
    this->xWorld=defaultXWorld;
    this->yWorld=defaultYWorld;
    this->thetaWorld=defaultThetaWorld;
    X=defaultYWorld;
    Y=-defaultXWorld;
	if(defaultThetaWorld < -PI/2)
		theta=PI/2+PI-defaultThetaWorld;
	else
		theta=defaultThetaWorld-PI/2;
}

void MiniExplorerCoimbra::myOdometria(){
	Odometria();
	this->xWorld=-Y;
	this->yWorld=X;
	if(theta >PI/2)
		this->thetaWorld=-PI+(theta-PI/2);
	else
		this->thetaWorld=theta+PI/2;
}

//generate a position randomly and makes the robot go there while updating the map
void MiniExplorerCoimbra::randomize_and_map() {
    //TODO check that it's aurelien's work
    float movementOnX=rand()%(int)(this->map.widthRealMap/2);
    float movementOnY=rand()%(int)(this->map.heightRealMap/2);
    
    float signOfX=rand()%2;
    if(signOfX < 1)
        signOfX=-1;
    float signOfY=rand()%2;
    if(signOfY  < 1)
        signOfY=-1;
        
    float targetXWorld = movementOnX*signOfX;
    float targetYWorld = movementOnY*signOfY;
    float targetAngleWorld = 2*((float)(rand()%31416)-15708)/10000.0;
    this->go_to_point_with_angle(targetXWorld, targetYWorld, targetAngleWorld);
}

//move of targetXWorld and targetYWorld ending in a targetAngleWorld
void MiniExplorerCoimbra::go_to_point_with_angle(float targetXWorld, float targetYWorld, float targetAngleWorld) {
    bool keepGoing=true;
    float leftMm;
    float frontMm; 
    float rightMm;
    float dt;
    Timer t;
    float distanceToTarget;
    do {
        //Timer stuff
        dt = t.read();
        t.reset();
        t.start();
        
        //Updating X,Y and theta with the odometry values
        this->myOdometria();
       	leftMm = get_distance_left_sensor();
    	frontMm = get_distance_front_sensor();
    	rightMm = get_distance_right_sensor();

        //if in dangerzone 
        if((frontMm < 150 && frontMm > 0)|| (leftMm <150 && leftMm > 0) || (rightMm <150 && rightMm > 0) ){
            leftMotor(1,0);
            rightMotor(1,0);
            this->update_sonar_values(leftMm, frontMm, rightMm);
            //TODO Giorgos maybe you can also test the do_half_flip() function
            this->myOdometria();
            //do a flip TODO
            keepGoing=false;
            this->do_half_flip();   
        }else{
            //if not in danger zone continue as usual
            this->update_sonar_values(leftMm, frontMm, rightMm);

        	//Updating motor velocities
            distanceToTarget=this->update_angular_speed_wheels_go_to_point_with_angle(targetXWorld,targetYWorld,targetAngleWorld,dt);
    
            wait(0.2);
            //Timer stuff
            t.stop();
            pc.printf("\n\r dist to target= %f",distanceToTarget);
        }
    } while(distanceToTarget>1 && (abs(targetAngleWorld-this->thetaWorld)>0.01) && keepGoing);

    //Stop at the end
    leftMotor(1,0);
    rightMotor(1,0);
}

float MiniExplorerCoimbra::update_angular_speed_wheels_go_to_point_with_angle(float targetXWorld, float targetYWorld, float targetAngleWorld, float dt){
    //compute_angles_and_distance
    //atan2 take the deplacement on x and the deplacement on y as parameters
    float angleToPoint = atan2((targetYWorld-this->yWorld),(targetXWorld-this->xWorld))-this->thetaWorld;
    angleToPoint = atan(sin(angleToPoint)/cos(angleToPoint));
    //rho is the distance to the point of arrival
    float rho = dist(targetXWorld,targetYWorld,this->xWorld,this->yWorld);
    float distanceToTarget = rho;
    //TODO check that
    float beta = targetAngleWorld-angleToPoint-this->thetaWorld;        
    
    //Computing angle error and distance towards the target value
    rho += dt*(-this->khro*cos(angleToPoint)*rho);
    float temp = angleToPoint;
    angleToPoint += dt*(this->khro*sin(angleToPoint)-this->ka*angleToPoint-this->kb*beta);
    beta += dt*(-this->khro*sin(temp));

    //Computing linear and angular velocities
    float linear;
    float angular;
    if(angleToPoint>=-1.5708 && angleToPoint<=1.5708){
        linear=this->khro*rho;
        angular=this->ka*angleToPoint+this->kb*beta;
    }
    else{
        linear=-this->khro*rho;
        angular=-this->ka*angleToPoint-this->kb*beta;
    }
    float angular_left=(linear-0.5*this->distanceWheels*angular)/this->radiusWheels;
    float angular_right=(linear+0.5*this->distanceWheels*angular)/this->radiusWheels;
    
    //Normalize speed for motors
    if(angular_left>angular_right) {
        angular_right=this->speed*angular_right/angular_left;
        angular_left=this->speed;
    } else {
        angular_left=this->speed*angular_left/angular_right;
        angular_right=this->speed;
    }

    //compute_linear_angular_velocities 
    leftMotor(1,angular_left);
    rightMotor(1,angular_right);
    
    return distanceToTarget;
}

void MiniExplorerCoimbra::update_sonar_values(float leftMm,float frontMm,float rightMm){
    float xWorldCell;
    float yWorldCell;
    for(int i=0;i<this->map.nbCellWidth;i++){
        for(int j=0;j<this->map.nbCellHeight;j++){
        	xWorldCell=this->map.cell_width_coordinate_to_world(i);
            yWorldCell=this->map.cell_height_coordinate_to_world(j);
        	this->map.update_cell_value(i,j,this->sonarRight.compute_probability_t(rightMm/10,xWorldCell,yWorldCell,this->xWorld,this->yWorld,this->thetaWorld));
        	this->map.update_cell_value(i,j,this->sonarLeft.compute_probability_t(leftMm/10,xWorldCell,yWorldCell,this->xWorld,this->yWorld,this->thetaWorld));
        	this->map.update_cell_value(i,j,this->sonarFront.compute_probability_t(frontMm/10,xWorldCell,yWorldCell,this->xWorld,this->yWorld,this->thetaWorld));

       	}
    }
}

//Distance computation function
float MiniExplorerCoimbra::dist(float x1, float y1, float x2, float y2){
    return sqrt(pow(y2-y1,2) + pow(x2-x1,2));
}

//use virtual force field
void MiniExplorerCoimbra::try_to_reach_target(float targetXWorld,float targetYWorld){
    //atan2 gives the angle beetween PI and -PI
    this->myOdometria();
    /*
    float deplacementOnXWorld=targetXWorld-this->xWorld;
    float deplacementOnYWorld=targetYWorld-this->yWorld;
    */
    float angleToTarget=atan2(targetYWorld-this->yWorld,targetXWorld-this->xWorld);
    turn_to_target(angleToTarget);
    bool reached=false;
    int print=0;
    while (!reached) {
        vff(&reached,targetXWorld,targetYWorld);
        //test_got_to_line(&reached);
        if(print==10){
            leftMotor(1,0);
            rightMotor(1,0);
            this->print_map_with_robot_position_and_target(targetXWorld,targetYWorld);
            print=0;
        }else
            print+=1;
    }
    //Stop at the end
    leftMotor(1,0);
    rightMotor(1,0);
    pc.printf("\r\n target reached");
    wait(3);//
}

void MiniExplorerCoimbra::vff(bool* reached, float targetXWorld, float targetYWorld){
    float line_a;
    float line_b;
    float line_c;
    //Updating X,Y and theta with the odometry values
    float forceXWorld=0;
    float forceYWorld=0;
    //we update the odometrie
    this->myOdometria();
    //we check the sensors
    float leftMm = get_distance_left_sensor();
    float frontMm = get_distance_front_sensor();
    float rightMm = get_distance_right_sensor();
    //update the probabilities values 
    this->update_sonar_values(leftMm, frontMm, rightMm);
    //we compute the force on X and Y
    this->compute_forceX_and_forceY(&forceXWorld, &forceYWorld,targetXWorld,targetYWorld);
    //we compute a new ine
    this->calculate_line(forceXWorld, forceYWorld, &line_a,&line_b,&line_c);
    //Updating motor velocities
    this->go_to_line(line_a,line_b,line_c,targetXWorld,targetYWorld);

    //wait(0.1);
    this->myOdometria();
    if(dist(this->xWorld,this->yWorld,targetXWorld,targetYWorld)<10)
        *reached=true;
}

//compute the force on X and Y
void MiniExplorerCoimbra::compute_forceX_and_forceY(float* forceXWorld, float* forceYWorld, float targetXWorld, float targetYWorld){
     float forceRepulsionComputedX=0;
     float forceRepulsionComputedY=0;
     //for each cell of the map we compute a force of repulsion
     for(int i=0;i<this->map.nbCellWidth;i++){
        for(int j=0;j<this->map.nbCellHeight;j++){
            this->update_force(i,j,&forceRepulsionComputedX,&forceRepulsionComputedY);
        }
    }
    //update with attraction force
    *forceXWorld=+forceRepulsionComputedX;
    *forceYWorld=+forceRepulsionComputedY;
    float distanceTargetRobot=sqrt(pow(targetXWorld-this->xWorld,2)+pow(targetYWorld-this->yWorld,2));
    if(distanceTargetRobot != 0){
        *forceXWorld-=this->attractionConstantForce*(targetXWorld-this->xWorld)/distanceTargetRobot;
        *forceYWorld-=this->attractionConstantForce*(targetYWorld-this->yWorld)/distanceTargetRobot;
    }
    float amplitude=sqrt(pow(*forceXWorld,2)+pow(*forceYWorld,2));
    if(amplitude!=0){//avoid division by 0 if forceX and forceY  == 0
        *forceXWorld=*forceXWorld/amplitude;
        *forceYWorld=*forceYWorld/amplitude;
    }
}

void MiniExplorerCoimbra::update_force(int widthIndice, int heightIndice, float* forceRepulsionComputedX, float* forceRepulsionComputedY ){
    //get the coordonate of the map and the robot in the ortonormal space
    float xWorldCell=this->map.cell_width_coordinate_to_world(widthIndice);
    float yWorldCell=this->map.cell_height_coordinate_to_world(heightIndice);
    //compute the distance beetween the cell and the robot
    float distanceCellToRobot=sqrt(pow(xWorldCell-this->xWorld,2)+pow(yWorldCell-this->yWorld,2));
    //check if the cell is in range
    if(distanceCellToRobot <= this->rangeForce) {
        float probaCell=this->map.get_proba_cell(widthIndice,heightIndice);
        *forceRepulsionComputedX+=this->repulsionConstantForce*probaCell*(xWorldCell-this->xWorld)/pow(distanceCellToRobot,3);
        *forceRepulsionComputedY+=this->repulsionConstantForce*probaCell*(yWorldCell-this->yWorld)/pow(distanceCellToRobot,3);
    }
}

//robotX and robotY are from this->myOdometria(), calculate line_a, line_b and line_c
void MiniExplorerCoimbra::calculate_line(float forceX, float forceY, float *line_a, float *line_b, float *line_c){
    /*
    in the teachers maths it is 
    
    *line_a=forceY;
    *line_b=-forceX;
    
    because a*x+b*y+c=0
    a impact the vertical and b the horizontal
    and he has to put them like this because
    Robot space:      World space:
      ^                 ^
      |x                |y
   <- R                 O ->
    y                     x
    but since our forceX, forceY are already computed in the orthonormal space I m not sure we need to 
    */
    *line_a=forceX;
    *line_b=forceY;
    //because the line computed always pass by the robot center we dont need lince_c
    //*line_c=forceX*this->yWorld+forceY*this->xWorld;    
    *line_c=0;
}

//currently line_c is not used
void MiniExplorerCoimbra::go_to_line(float line_a, float line_b, float line_c,float targetXWorld, float targetYWorld){
    float lineAngle;
    float angleError;
    float linear;
    float angular; 
    
    //atan2 use the deplacement on X and the deplacement on Y
    float angleToTarget=atan2(targetYWorld-this->yWorld,targetXWorld-this->xWorld);
    bool aligned=false;
    
    //this condition is passed if the target is in the same direction as the robot orientation
   	if((angleToTarget>0 && this->thetaWorld > 0) || (angleToTarget<0 && this->thetaWorld <0))
    	aligned=true;
    
    //line angle is beetween pi/2 and -pi/2
    /*
    ax+by+c=0 (here c==0)
    y=x*-a/b
    if a*b > 0, the robot is going down
    if a*b <0, the robot is going up
    */	
     if(line_b!=0){
        if(!aligned)
            lineAngle=atan(-line_a/line_b);
        else
            lineAngle=atan(line_a/-line_b);
    }
    else{
        lineAngle=1.5708;
    }
    
    //Computing angle error
    angleError = lineAngle-this->thetaWorld;//TODO that I m not sure
    angleError = atan(sin(angleError)/cos(angleError));

    //Calculating velocities
    linear=this->kv*(3.1416);
    angular=this->kh*angleError;

    float angularLeft=(linear-0.5*this->distanceWheels*angular)/this->radiusWheels;
    float angularRight=(linear+0.5*this->distanceWheels*angular)/this->radiusWheels;
    
    //Normalize speed for motors
    if(abs(angularLeft)>abs(angularRight)) {  
        angularRight=this->speed*abs(angularRight/angularLeft)*this->sign1(angularRight);
        angularLeft=this->speed*this->sign1(angularLeft);
    }
    else {
        angularLeft=this->speed*abs(angularLeft/angularRight)*this->sign1(angularLeft);
        angularRight=this->speed*this->sign1(angularRight);
    }
    leftMotor(this->sign2(angularLeft),abs(angularLeft));
    rightMotor(this->sign2(angularRight),abs(angularRight));
}

/*angleToTarget is obtained through atan2 so it s:
< 0 if the angle is bettween PI and 2pi on a trigo circle
> 0 if it is between 0 and PI
*/
void MiniExplorerCoimbra::turn_to_target(float angleToTarget){
    this->myOdometria();
    float theta_plus_h_pi=theta+PI/2;//theta is between -PI and PI
    if(theta_plus_h_pi > PI)
        theta_plus_h_pi=-(2*PI-theta_plus_h_pi);
     if(angleToTarget>0){   
        leftMotor(0,1);
        rightMotor(1,1);
    }else{
        leftMotor(1,1);
        rightMotor(0,1);
    }
    while(abs(angleToTarget-theta_plus_h_pi)>0.05){
        this->myOdometria();
        theta_plus_h_pi=theta+PI/2;//theta is between -PI and PI
         if(theta_plus_h_pi > PI)
            theta_plus_h_pi=-(2*PI-theta_plus_h_pi);
        //pc.printf("\n\r diff=%f", abs(angleToTarget-theta_plus_h_pi));
    }
    leftMotor(1,0);
    rightMotor(1,0);    
}

void MiniExplorerCoimbra::do_half_flip(){
    this->myOdometria();
    float theta_plus_h_pi=theta+PI/2;//theta is between -PI and PI
    if(theta_plus_h_pi > PI)
        theta_plus_h_pi=-(2*PI-theta_plus_h_pi);
    leftMotor(0,100);
    rightMotor(1,100);
    while(abs(theta_plus_h_pi-theta)>0.05){
        this->myOdometria();
       // pc.printf("\n\r diff=%f", abs(theta_plus_pi-theta));
    }
    leftMotor(1,0);
    rightMotor(1,0);    
}

void MiniExplorerCoimbra::print_map_with_robot_position() {
    float currProba;
    
    float heightIndiceInOrthonormal;
    float widthIndiceInOrthonormal;
    
    float widthMalus=-(3*this->map.sizeCellWidth/2);
    float widthBonus=this->map.sizeCellWidth/2;
    
    float heightMalus=-(3*this->map.sizeCellHeight/2);
    float heightBonus=this->map.sizeCellHeight/2;

    pc.printf("\n\r");
    for (int y = this->map.nbCellHeight -1; y>-1; y--) {
        for (int x= 0; x<this->map.nbCellWidth; x++) {
            heightIndiceInOrthonormal=this->map.cell_height_coordinate_to_world(y);
            widthIndiceInOrthonormal=this->map.cell_width_coordinate_to_world(x);
            if(this->yWorld >= (heightIndiceInOrthonormal+heightMalus) && this->yWorld <= (heightIndiceInOrthonormal+heightBonus) && this->xWorld >= (widthIndiceInOrthonormal+widthMalus) && this->xWorld <= (widthIndiceInOrthonormal+widthBonus))                    
                pc.printf(" R ");
            else{
                currProba=this->map.log_to_proba(this->map.cellsLogValues[x][y]);
                if ( currProba < 0.5)
                    pc.printf("   ");
                else{
                    if(currProba==0.5)
                        pc.printf(" . ");
                    else
                        pc.printf(" X ");
                } 
            }
        }
        pc.printf("\n\r");
    }
}

void MiniExplorerCoimbra::print_map_with_robot_position_and_target(float targetXWorld, float targetYWorld) {
    float currProba;
    
    float heightIndiceInOrthonormal;
    float widthIndiceInOrthonormal;
    
    float widthMalus=-(3*this->map.sizeCellWidth/2);
    float widthBonus=this->map.sizeCellWidth/2;
    
    float heightMalus=-(3*this->map.sizeCellHeight/2);
    float heightBonus=this->map.sizeCellHeight/2;

    pc.printf("\n\r");
    for (int y = this->map.nbCellHeight -1; y>-1; y--) {
        for (int x= 0; x<this->map.nbCellWidth; x++) {
            heightIndiceInOrthonormal=this->map.cell_height_coordinate_to_world(y);
            widthIndiceInOrthonormal=this->map.cell_width_coordinate_to_world(x);
            if(this->yWorld >= (heightIndiceInOrthonormal+heightMalus) && this->yWorld <= (heightIndiceInOrthonormal+heightBonus) && this->xWorld >= (widthIndiceInOrthonormal+widthMalus) && this->xWorld <= (widthIndiceInOrthonormal+widthBonus))
                pc.printf(" R ");
            else{
                if(targetYWorld >= (heightIndiceInOrthonormal+heightMalus) && targetYWorld <= (heightIndiceInOrthonormal+heightBonus) && targetXWorld >= (widthIndiceInOrthonormal+widthMalus) && targetXWorld <= (widthIndiceInOrthonormal+widthBonus))                    
                    pc.printf(" T ");
                else{
                    currProba=this->map.log_to_proba(this->map.cellsLogValues[x][y]);
                    if ( currProba < 0.5)
                        pc.printf("   ");
                    else{
                        if(currProba==0.5)
                            pc.printf(" . ");
                        else
                            pc.printf(" X ");
                    } 
                }
            }
        }
        pc.printf("\n\r");
    }
}

void MiniExplorerCoimbra::print_map() {
    float currProba;
    pc.printf("\n\r");
    for (int y = this->map.nbCellHeight -1; y>-1; y--) {
        for (int x= 0; x<this->map.nbCellWidth; x++) {
                currProba=this->map.log_to_proba(this->map.cellsLogValues[x][y]);
            if ( currProba < 0.5) {
                pc.printf("   ");
            } else {
                if(currProba==0.5)
                    pc.printf(" . ");
                else
                    pc.printf(" X ");
            }
        }
        pc.printf("\n\r");
    }
}

//return 1 if positiv, -1 if negativ
float MiniExplorerCoimbra::sign1(float value){
    if(value>=0) 
        return 1;
    else 
        return -1;
}

//return 1 if positiv, 0 if negativ
int MiniExplorerCoimbra::sign2(float value){
    if(value>=0) 
        return 1;
    else 
        return 0;
}

/*UNUSED
float MiniExplorerCoimbra::get_converted_robot_X_into_world(){
	//x world coordinate
	return this->map.nbCellWidth*this->map.sizeCellWidth-Y;
}

float MiniExplorerCoimbra::get_converted_robot_Y_into_world(){
	//y worldcoordinate
	return X;
}


//x and y passed are TargetNotMap
float get_error_angles(float x, float y,float theta){
    float angleBeetweenRobotAndTarget=atan2(y,x);
    if(y > 0){
        if(angleBeetweenRobotAndTarget < PI/2)//up right
            angleBeetweenRobotAndTarget=-PI/2+angleBeetweenRobotAndTarget;
        else//up right
            angleBeetweenRobotAndTarget=angleBeetweenRobotAndTarget-PI/2;
    }else{
        if(angleBeetweenRobotAndTarget > -PI/2)//lower right
            angleBeetweenRobotAndTarget=angleBeetweenRobotAndTarget-PI/2;
        else//lower left
            angleBeetweenRobotAndTarget=2*PI+angleBeetweenRobotAndTarget-PI/2;
    }
    return angleBeetweenRobotAndTarget-theta;
}
*/

   
/*
Lab4

make the robot do the same square over and over, see that at one point the errors have accumulated and it is not where the odometria think it is
solution:
(here our sensors are bad but our odometrie s okay
before each new movement we compute an estimation of where we are, compare it to what the robot think (maybe choose our estimate?)
*/

void MiniExplorerCoimbra::test_procedure_lab_4(){
    this->map.fill_map_with_initial();
    float xEstimatedK=this->xWorld;
    float yEstimatedK=this->yWorld;
    float thetaWorldEstimatedK=this->thetaWorld;
    float distanceMoved;
    float angleMoved;
    float PreviousCovarianceOdometricPositionEstimate[3][3];
    PreviousCovarianceOdometricPositionEstimate[0][0]=0;
    PreviousCovarianceOdometricPositionEstimate[0][1]=0;
    PreviousCovarianceOdometricPositionEstimate[0][2]=0;
    PreviousCovarianceOdometricPositionEstimate[1][0]=0;
    PreviousCovarianceOdometricPositionEstimate[1][1]=0;
    PreviousCovarianceOdometricPositionEstimate[1][2]=0;
    PreviousCovarianceOdometricPositionEstimate[2][0]=0;
    PreviousCovarianceOdometricPositionEstimate[2][1]=0;
    PreviousCovarianceOdometricPositionEstimate[2][2]=0;
    while(1){
        distanceMoved=50;
        angleMoved=0;
        this->go_straight_line(50);
        wait(1);
        procedure_lab_4(xEstimatedK,yEstimatedK,thetaEstimatedK,distanceMoved,angleMoved,PreviousCovarianceOdometricPositionEstimate);
        pc.printf("\n\r x:%f,y:%f,theta:%f",this->xWorld,this->yWorld,this->thetaWorld);
        pc.printf("\n\r xEstim:%f,yEstim:%f,thetaEstim:%f",xEstimatedK,yEstimatedK,thetaEstimatedK);
        this->turn_to_target(PI/2);
        distanceMoved=0;
        angleMoved=PI/2;
        procedure_lab_4(xEstimatedK,yEstimatedK,thetaEstimatedK,distanceMoved,angleMoved,PreviousCovarianceOdometricPositionEstimate);
        pc.printf("\n\r x:%f,y:%f,theta:%f",this->xWorld,this->yWorld,this->thetaWorld);
        pc.printf("\n\r xEstim:%f,yEstim:%f,thetaEstim:%f",xEstimatedK,yEstimatedK,thetaEstimatedK);
    }
}


//compute predicted localisation (assume movements are either straight lines or pure rotation), update PreviousCovarianceOdometricPositionEstimate
//TODO tweak constant rewritte it good
void MiniExplorerCoimbra::procedure_lab_4(float xEstimatedK,float yEstimatedK, float thetaWorldEstimatedK, float distanceMoved, float angleMoved, float PreviousCovarianceOdometricPositionEstimate[3][3]){

    float distanceMovedByLeftWheel=distanceMoved/2;
    float distanceMovedByRightWheel=distanceMoved/2;
    if(angleMoved !=0){
        //TODO check that not sure
        distanceMovedByLeftWheel=-angleMoved/(2*this->distanceWheels);
        distanceMovedByRightWheel=angleMoved/(2*this->distanceWheels);
    }else{
        distanceMovedByLeftWheel=distanceMoved/2;
        distanceMovedByRightWheel=distanceMoved/2;
    }
        
    float xEstimatedKNext=xEstimatedK+distanceMoved*cos(angleMoved);
    float yEstimatedKNext=yEstimatedK+distanceMoved*sin(angleMoved);
    float thetaEstimatedKNext=thetaWorldEstimatedK+angleMoved;
    
    float KRight=0.1;//error constant 
    float KLEft=0.1;//error constant 
    float motionIncrementCovarianceMatrix[2][2];
    motionIncrementCovarianceMatrix[0][0]=kRight*abs(distanceMovedRight);
    motionIncrementCovarianceMatrix[0][1]=0;
    motionIncrementCovarianceMatrix[1][0]=0;
    motionIncrementCovarianceMatrix[1][1]=kLeft*abs(distanceMovedLeft);
    
    float jacobianFp[3][3];
    jacobianFp[0][0]=1;
    jacobianFp[0][1]=0;
    jacobianFp[0][2]=-distanceMoved*sin(thetaWorldEstimatedK+angleMoved/2);
    jacobianFp[1][0]=0;
    jacobianFp[1][1]=1;
    jacobianFp[1][2]=distanceMoved*cos(thetaWorldEstimatedK+angleMoved/2);;
    jacobianFp[2][0]=0;
    jacobianFp[2][1]=0;
    jacobianFp[2][2]=1;
    
    float jacobianFpTranspose[3][3];
    jacobianFpTranspose[0][0]=1;
    jacobianFpTranspose[0][1]=0;
    jacobianFpTranspose[0][2]=0;
    jacobianFpTranspose[1][0]=0;
    jacobianFpTranspose[1][1]=1;
    jacobianFpTranspose[1][2]=0;
    jacobianFpTranspose[2][0]=-distanceMoved*sin(thetaWorldEstimatedK+angleMoved/2);
    jacobianFpTranspose[2][1]=distanceMoved*cos(thetaWorldEstimatedK+angleMoved/2);
    jacobianFpTranspose[2][2]=1;
    
    float jacobianFDeltarl[3][2];
    jacobianFDeltarl[0][0]=(1/2)*cos(thetaWorldEstimatedK+angleMoved/2)-(distancedMoved/(2*this->distanceWheels))*sin(thetaWorldEstimatedK+angleMoved/2);
    jacobianFDeltarl[0][1]=(1/2)*cos(thetaWorldEstimatedK+angleMoved/2)+(distancedMoved/(2*this->distanceWheels))*sin(thetaWorldEstimatedK+angleMoved/2);
    jacobianFDeltarl[1][0]=(1/2)*sin(thetaWorldEstimatedK+angleMoved/2)+(distancedMoved/(2*this->distanceWheels))*cos(thetaWorldEstimatedK+angleMoved/2);
    jacobianFDeltarl[1][1]=(1/2)*sin(thetaWorldEstimatedK+angleMoved/2)-(distancedMoved/(2*this->distanceWheels))*cos(thetaWorldEstimatedK+angleMoved/2);
    jacobianFDeltarl[2][0]=1/this->distanceWheels;
    jacobianFDeltarl[2][1]=-1/this->distanceWheels;
    
    float jacobianFDeltarlTranspose[2][3];//1,3,5;2,4,6
    jacobianFDeltarlTranspose[0][0]=(1/2)*cos(thetaWorldEstimatedK+angleMoved/2)-(distancedMoved/(2*this->distanceWheels))*sin(thetaWorldEstimatedK+angleMoved/2);
    jacobianFDeltarlTranspose[0][1]=(1/2)*sin(thetaWorldEstimatedK+angleMoved/2)+(distancedMoved/(2*this->distanceWheels))*cos(thetaWorldEstimatedK+angleMoved/2);
    jacobianFDeltarlTranspose[0][2]=1/this->distanceWheels;
    jacobianFDeltarlTranspose[1][0]=(1/2)*cos(thetaWorldEstimatedK+angleMoved/2)+(distancedMoved/(2*this->distanceWheels))*sin(thetaWorldEstimatedK+angleMoved/2);
    jacobianFDeltarlTranspose[1][1]=(1/2)*sin(thetaWorldEstimatedK+angleMoved/2)-(distancedMoved/(2*this->distanceWheels))*cos(thetaWorldEstimatedK+angleMoved/2);
    jacobianFDeltarlTranspose[1][2]=-1/this->distanceWheels;
    
    float tempMatrix3_3[3][3];
    for(i = 0; i < 3; ++i){//mult 3*3, 3*3
        for(j = 0; j < 3; ++j){
            for(k = 0; k < 3; ++k){
                tempMatrix3_3[i][j] += jacobianFp[i][k] * PreviousCovarianceOdometricPositionEstimate[k][j];
            }
        }
    }
    float sndTempMatrix3_3[3][3];
    for(i = 0; i < 3; ++i){//mult 3*3, 3*3
        for(j = 0; j < 3; ++j){
            for(k = 0; k < 3; ++k){
                sndTempMatrix3_3[i][j] += tempMatrix3_3[i][k] * jacobianFpTranspose[k][j];
            }
        }
    }
    float tempMatrix3_2[3][2];
    for(i = 0; i < 3; ++i){//mult 3*2 , 2,2
        for(j = 0; j < 2; ++j){
            for(k = 0; k < 2; ++k){
                tempMatrix3_2[i][j] += jacobianFDeltarl[i][k] * motionIncrementCovarianceMatrix[k][j];
            }
        }
    }
    float thrdTempMatrix3_3[3][3];
    for(i = 0; i < 3; ++i){//mult 3*2 , 2,3
        for(j = 0; j < 3; ++j){
            for(k = 0; k < 2; ++k){
                thrdTempMatrix3_3[i][j] += tempMatrix3_2[i][k] * jacobianFDeltarlTranspose[k][j];
            }
        }
    }
    float newCovarianceOdometricPositionEstimate[3][3];
    for(i = 0; i < 3; ++i){//add 3*3 , 3*3
        for(j = 0; j < 3; ++j){
            newCovarianceOdometricPositionEstimate[i][j]=sndTempMatrix3_3[i][j]+thrdTempMatrix3_3[i][j];
        }
    }
    
    //check measurements from sonars, see if they passed the validation gate
    float leftCm = get_distance_left_sensor()/10;
    float frontCm = get_distance_front_sensor()/10;
    float rightCm = get_distance_right_sensor()/10;
    //if superior to sonar range, put the value to sonar max range + 1
    if(leftCm > this->sonarLeft.maxRange)
        leftCm=this->sonarLeft.maxRange;
    if(frontCm > this->sonarFront.maxRange)
        frontCm=this->sonarFront.maxRange;
    if(rightCm > this->sonarRight.maxRange)
        rightCm=this->sonarRight.maxRange;
    //get the estimated measure we should have according to our knowledge of the map and the previously computed localisation
    float leftCmEstimated=this->sonarLeft.maxRange;
    float frontCmEstimated=this->sonarFront.maxRange;
    float rightCmEstimated=this->sonarRight.maxRange;
    float xWorldCell;
    float yWorldCell;
    float currDistance;
    float xClosetCellLeft;
    float yClosetCellLeft;
    float xClosetCellFront;
    float yClosetCellFront;
    float xClosetCellRight;
    float yClosetCellRight;
    for(int i=0;i<this->map.nbCellWidth;i++){
        for(int j=0;j<this->map.nbCellHeight;j++){
            //check if occupied, if not discard
            if(this->map.get_proba_cell(i,j)==1){
                //check if in sonar range
                xWorldCell=this->map.cell_width_coordinate_to_world(i);
                yWorldCell=this->map.cell_height_coordinate_to_world(j);
                //check left
                currDistance=this->sonarLeft.isInRange(xWorldCell,yWorldCell,xEstimatedKNext,yEstimatedKNext,thetaEstimatedKNext);           
                if((currDistance < this->sonarLeft.maxRange) && currDistance!=-1)
                    //check if distance is lower than previous, update lowest if so
                    if(currDistance < leftCmEstimated){
                        leftCmEstimated= currDistance;
                        xClosetCellLeft=xWorldCell;
                        yClosetCellLeft=yWorldCell;
                    }
                }
                //check front
                currDistance=this->sonarFront.isInRange(xWorldCell,yWorldCell,xEstimatedKNext,yEstimatedKNext,thetaEstimatedKNext);           
                if((currDistance < this->sonarFront.maxRange) && currDistance!=-1)
                    //check if distance is lower than previous, update lowest if so
                    if(currDistance < frontCmEstimated){
                        frontCmEstimated= currDistance;
                        xClosetCellFront=xWorldCell;
                        yClosetCellFront=yWorldCell;
                    }
                }
                //check right
                currDistance=this->sonarRight.isInRange(xWorldCell,yWorldCell,xEstimatedKNext,yEstimatedKNext,thetaEstimatedKNext);              
                if((currDistance < this->sonarRight.maxRange) && currDistance!=-1)
                    //check if distance is lower than previous, update lowest if so
                    if(currDistance < rightCmEstimated){
                        rightCmEstimated= currDistance;
                        xClosetCellRight=xWorldCell;
                        yClosetCellRight=yWorldCell;
                    }
                }
            }
        }
    }
    //get the innoncation: the value of the difference between the actual measure and what we anticipated
    float innovationLeft=leftCm-leftCmEstimated;
    float innovationFront=frontCm-frontCmEstimated;
    float innovationRight=-rightCm-rightCmEstimated;
    //compute jacobian of observation
    float jacobianOfObservationLeft[3];
    float jacobianOfObservationRight[3];
    float jacobianOfObservationFront[3];
    float xSonarLeft=xEstimatedKNext+this->sonarLeft.distanceX;
    float ySonarLeft=yEstimatedKNext+this->sonarLeft.distanceY;
    //left
    jacobianOfObservationLeft[0]=(xSonarLeft-xClosestCellLeft)/leftCmEstimated;
    jacobianOfObservationLeft[1]=(ySonarLeft-yClosestCellLeft)/leftCmEstimated;
    jacobianOfObservationLeft[2]=(xClosestCellLeft-xSonarLeft)*(xSonarLeft*sin(thetaEstimatedKNext)+ySonarLeft*cos(thetaEstimatedKNext)+(yClosestCellLeft-ySonarLeft)*(-xSonarLeft*cos(thetaEstimatedKNext)+ySonarLeft*sin(thetaEstimatedKNext));
    //front
    float xSonarFront=xEstimatedKNext+this->sonarFront.distanceX;
    float ySonarFront=yEstimatedKNext+this->sonarFront.distanceY;
    jacobianOfObservationFront[0]=(xSonarFront-xClosestCellFront)/FrontCmEstimated;
    jacobianOfObservationFront[1]=(ySonarFront-yClosestCellFront)/FrontCmEstimated;
    jacobianOfObservationFront[2]=(xClosestCellFront-xSonarFront)*(xSonarFront*sin(thetaEstimatedKNext)+ySonarFront*cos(thetaEstimatedKNext)+(yClosestCellFront-ySonarFront)*(-xSonarFront*cos(thetaEstimatedKNext)+ySonarFront*sin(thetaEstimatedKNext));
    //right
    float xSonarRight=xEstimatedKNext+this->sonarRight.distanceX;
    float ySonarRight=yEstimatedKNext+this->sonarRight.distanceY;
    jacobianOfObservationRight[0]=(xSonarRight-xClosestCellRight)/RightCmEstimated;
    jacobianOfObservationRight[1]=(ySonarRight-yClosestCellRight)/RightCmEstimated;
    jacobianOfObservationRight[2]=(xClosestCellRight-xSonarRight)*(xSonarRight*sin(thetaEstimatedKNext)+ySonarRight*cos(thetaEstimatedKNext)+(yClosestCellRight-ySonarRight)*(-xSonarRight*cos(thetaEstimatedKNext)+ySonarRight*sin(thetaEstimatedKNext));
    
    float innovationCovarianceLeft[3][3];
    float innovationCovarianceFront[3][3];
    float innovationCovarianceRight[3][3];
    //left
    float TempMatrix3_3InnovationLeft[3];
    TempMatrix3_3InnovationLeft[0]=jacobianOfObservationLeft[0]*newCovarianceOdometricPositionEstimate[0][0]+jacobianOfObservationLeft[1]*newCovarianceOdometricPositionEstimate[1][0]+jacobianOfObservationLeft[2]*newCovarianceOdometricPositionEstimate[2][0];
    TempMatrix3_3InnovationLeft[1]=jacobianOfObservationLeft[0]*newCovarianceOdometricPositionEstimate[0][1]+jacobianOfObservationLeft[1]*newCovarianceOdometricPositionEstimate[1][1]+jacobianOfObservationLeft[2]*newCovarianceOdometricPositionEstimate[2][1];
    TempMatrix3_3InnovationLeft[2]jacobianOfObservationLeft[0]*newCovarianceOdometricPositionEstimate[0][2]+jacobianOfObservationLeft[1]*newCovarianceOdometricPositionEstimate[1][2]+jacobianOfObservationLeft[2]*newCovarianceOdometricPositionEstimate[2][2];
    float innovationConvarianceLeft=TempMatrix3_3InnovationLeft[0]*jacobianOfObservationLeft[0]+TempMatrix3_3InnovationLeft[1]*jacobianOfObservationLeft[1]+TempMatrix3_3InnovationLeft[2]*jacobianOfObservationLeft[2];
    //front
    float TempMatrix3_3InnovationFront[3];
    TempMatrix3_3InnovationFront[0]=jacobianOfObservationFront[0]*newCovarianceOdometricPositionEstimate[0][0]+jacobianOfObservationFront[1]*newCovarianceOdometricPositionEstimate[1][0]+jacobianOfObservationFront[2]*newCovarianceOdometricPositionEstimate[2][0];
    TempMatrix3_3InnovationFront[1]=jacobianOfObservationFront[0]*newCovarianceOdometricPositionEstimate[0][1]+jacobianOfObservationFront[1]*newCovarianceOdometricPositionEstimate[1][1]+jacobianOfObservationFront[2]*newCovarianceOdometricPositionEstimate[2][1];
    TempMatrix3_3InnovationFront[2]jacobianOfObservationFront[0]*newCovarianceOdometricPositionEstimate[0][2]+jacobianOfObservationFront[1]*newCovarianceOdometricPositionEstimate[1][2]+jacobianOfObservationFront[2]*newCovarianceOdometricPositionEstimate[2][2];
    float innovationConvarianceFront=TempMatrix3_3InnovationFront[0]*jacobianOfObservationFront[0]+TempMatrix3_3InnovationFront[1]*jacobianOfObservationFront[1]+TempMatrix3_3InnovationFront[2]*jacobianOfObservationFront[2];
    //right
    float TempMatrix3_3InnovationRight[3];
    TempMatrix3_3InnovationRight[0]=jacobianOfObservationRight[0]*newCovarianceOdometricPositionEstimate[0][0]+jacobianOfObservationRight[1]*newCovarianceOdometricPositionEstimate[1][0]+jacobianOfObservationRight[2]*newCovarianceOdometricPositionEstimate[2][0];
    TempMatrix3_3InnovationRight[1]=jacobianOfObservationRight[0]*newCovarianceOdometricPositionEstimate[0][1]+jacobianOfObservationRight[1]*newCovarianceOdometricPositionEstimate[1][1]+jacobianOfObservationRight[2]*newCovarianceOdometricPositionEstimate[2][1];
    TempMatrix3_3InnovationRight[2]jacobianOfObservationRight[0]*newCovarianceOdometricPositionEstimate[0][2]+jacobianOfObservationRight[1]*newCovarianceOdometricPositionEstimate[1][2]+jacobianOfObservationRight[2]*newCovarianceOdometricPositionEstimate[2][2];
    float innovationConvarianceRight=TempMatrix3_3InnovationRight[0]*jacobianOfObservationRight[0]+TempMatrix3_3InnovationRight[1]*jacobianOfObservationRight[1]+TempMatrix3_3InnovationRight[2]*jacobianOfObservationRight[2];
    
    //check if it pass the validation gate 
    float gateScoreLeft=innovationLeft*innovationLeft/innovationConvarianceLeft;
    float gateScoreFront=innovationFront*innovationFront/innovationConvarianceFront;
    float gateScoreRight=innovationRight*innovationRight/innovationConvarianceRight;
    int leftPassed=0;
    int frontPassed=0;
    int rightPassed=0;
    int e=10;//constant 
    if(gateScoreLeft < e)
        leftPassed=1;
    if(gateScoreFront < e)
        frontPassed=10;
    if(gateScoreRight < e)
        rightPassed=100;
    //for those who passed
    //compute composite innovation
    float compositeInnovation[3];
    int nbPassed=leftPassed+frontPassed+rightPassed;
    switch(nbPassed){
        case 0://none
            compositeInnovation[0]=0;
            compositeInnovation[1]=0;
            compositeInnovation[2]=0;
            break;
        case 1://left
            compositeInnovation[0]=innovationLeft;
            compositeInnovation[1]=0;
            compositeInnovation[2]=0;
            break;
        case 10://front
            compositeInnovation[0]=0;
            compositeInnovation[1]=innovationFront;
            compositeInnovation[2]=0;
            break;
        case 11://left and front
            compositeInnovation[0]=innovationLeft;
            compositeInnovation[1]=innovationFront;
            compositeInnovation[2]=0;
            break;
        case 100://right
            compositeInnovation[0]=0;
            compositeInnovation[1]=0;
            compositeInnovation[2]=innovationRight;
            break;
        case 101://right and left
            compositeInnovation[0]=innovationLeft;
            compositeInnovation[1]=0;
            compositeInnovation[2]=innovationRight;
            break;
        case 110://right and front
            compositeInnovation[0]=0;
            compositeInnovation[1]=innovationFront;
            compositeInnovation[2]=innovationRight;
            break
        case 111://right front and left
            compositeInnovation[0]=innovationLeft;
            compositeInnovation[1]=innovationFront;
            compositeInnovation[2]=innovationRight;
            break;
    }
    //compute composite measurement Jacobian
    switch(nbPassed){
        case 0://none
            break;
        case 1://left
            //compositeMeasurementJacobian = jacobianOfObservationLeft
            break;
        case 10://front
            //compositeMeasurementJacobian = jacobianOfObservationFront
            break;
        case 11://left and front
            float compositeMeasurementJacobian[6]
            compositeMeasurementJacobian[0]= jacobianOfObservationLeft[0];
            compositeMeasurementJacobian[1]= jacobianOfObservationLeft[1];
            compositeMeasurementJacobian[2]= jacobianOfObservationLeft[2];
            compositeMeasurementJacobian[3]= jacobianOfObservationFront[0];
            compositeMeasurementJacobian[4]= jacobianOfObservationFront[1];
            compositeMeasurementJacobian[5]= jacobianOfObservationFront[2];
            break;
        case 100://right
            //compositeMeasurementJacobian = jacobianOfObservationRight
            break;
        case 101://right and left
            float compositeMeasurementJacobian[6]
            compositeMeasurementJacobian[0]= jacobianOfObservationLeft[0];
            compositeMeasurementJacobian[1]= jacobianOfObservationLeft[1];
            compositeMeasurementJacobian[2]= jacobianOfObservationLeft[2];
            compositeMeasurementJacobian[3]= jacobianOfObservationRight[0];
            compositeMeasurementJacobian[4]= jacobianOfObservationRight[1];
            compositeMeasurementJacobian[5]= jacobianOfObservationRight[2];
    
            break;
        case 110://right and front
            float compositeMeasurementJacobian[6]
            compositeMeasurementJacobian[0]= jacobianOfObservationFront[0];
            compositeMeasurementJacobian[1]= jacobianOfObservationFront[1];
            compositeMeasurementJacobian[2]= jacobianOfObservationFront[2];
            compositeMeasurementJacobian[3]= jacobianOfObservationRight[0];
            compositeMeasurementJacobian[4]= jacobianOfObservationRight[1];
            compositeMeasurementJacobian[5]= jacobianOfObservationRight[2];
    
            break
        case 111://right front and left
            float compositeMeasurementJacobian[9]
            compositeMeasurementJacobian[0]= jacobianOfObservationLeft[0];
            compositeMeasurementJacobian[1]= jacobianOfObservationLeft[1];
            compositeMeasurementJacobian[2]= jacobianOfObservationLeft[2];
            compositeMeasurementJacobian[3]= jacobianOfObservationFront[0];
            compositeMeasurementJacobian[4]= jacobianOfObservationFront[1];
            compositeMeasurementJacobian[5]= jacobianOfObservationFront[2];
            compositeMeasurementJacobian[6]= jacobianOfObservationRight[0];
            compositeMeasurementJacobian[7]= jacobianOfObservationRight[1];
            compositeMeasurementJacobian[8]= jacobianOfObservationRight[2];
            break;
    }
    //compute composite innovation covariance TODO dont have time, I assume you can just multiply them, can also simply all this easily
    float compositeInnovationCovariance
    switch(nbPassed){
        case 0://none
            compositeInnovationCovariance=1;
            break;
        case 1://left
            compositeInnovationCovariance = innovationConvarianceLeft;
            
            break;
        case 10://front
            compositeInnovationCovariance = innovationConvarianceFront;
            break;
        case 11://left and front
            compositeInnovationCovariance=innovationConvarianceLeft*innovationConvarianceFront;
    
            break;
        case 100://right
            compositeInnovationCovariance = innovationConvarianceRight;
            break;
        case 101://right and left
            compositeInnovationCovariance=innovationConvarianceLeft*innovationConvarianceRight;
    
            break;
        case 110://right and front
            compositeInnovationCovariance=innovationConvarianceFront*innovationConvarianceRight;
    
    
            break
        case 111://right front and left
            compositeInnovationCovariance=innovationConvarianceLeft*innovationConvarianceFront*innovationConvarianceRight;
      
            break;
    }
    
    //compute kalman gain
    switch(nbPassed){
        //mult nbSonarPassed*3 , 3*3
        case 0://none
            break;
        case 1://left
            float kalmanGain[3][3];
            for(i = 0; i < 3; ++i){//mult 3*3, 3*1
                for(j = 0; j < 1; ++j){
                    for(k = 0; k < 3; ++k){
                        kalmanGain[i][j] += newCovarianceOdometricPositionEstimate[i][k] * compositeInnovationCovariance[k];
                    }
                }
            }
            
            break;
        case 10://front
            float kalmanGain[3][3];
            for(i = 0; i < 3; ++i){//mult 3*3, 3*1
                for(j = 0; j < 1; ++j){
                    for(k = 0; k < 3; ++k){
                        kalmanGain[i][j] += newCovarianceOdometricPositionEstimate[i][k] * compositeInnovationCovariance[k];
                    }
                }
            }
            break;
        case 11://left and front
            float kalmanGain[3][3];
            for(i = 0; i < 3; ++i){//mult 3*3, 3*2
                for(j = 0; j < 2; ++j){
                    for(k = 0; k < 3; ++k){
                        kalmanGain[i][j] += newCovarianceOdometricPositionEstimate[i][k] * compositeInnovationCovariance[k+j*3];
                    }
                }
            }
    
            break;
        case 100://right
            float kalmanGain[3][3];
            for(i = 0; i < 3; ++i){//mult 3*3, 3*1
                for(j = 0; j < 1; ++j){
                    for(k = 0; k < 3; ++k){
                        kalmanGain[i][j] += newCovarianceOdometricPositionEstimate[i][k] * compositeInnovationCovariance[k];
                    }
                }
            }
            break;
        case 101://right and left
            float kalmanGain[3][3];
            for(i = 0; i < 3; ++i){//mult 3*3, 3*2
                for(j = 0; j < 2; ++j){
                    for(k = 0; k < 3; ++k){
                        kalmanGain[i][j] += newCovarianceOdometricPositionEstimate[i][k] * compositeInnovationCovariance[k+j*3];
                    }
                }
            }
    
            break;
        case 110://right and front
            float kalmanGain[3][3];
            for(i = 0; i < 3; ++i){//mult 3*3, 3*2
                for(j = 0; j < 2; ++j){
                    for(k = 0; k < 3; ++k){
                        kalmanGain[i][j] += newCovarianceOdometricPositionEstimate[i][k] * compositeInnovationCovariance[k+j*3];
                    }
                }
            }
    
    
            break
        case 111://right front and left
            float kalmanGain[3][3];
            for(i = 0; i < 3; ++i){//mult 3*3, 3*3
                for(j = 0; j < 3; ++j){
                    for(k = 0; k < 3; ++k){
                        kalmanGain[i][j] += newCovarianceOdometricPositionEstimate[i][k] * compositeInnovationCovariance[k+j*3];
                    }
                }
            }
      
            break;
    }
    for(i = 0; i < 3; ++i){//mult 3*3, 3*3
        for(j = 0; j < 3; ++j){
            kalmanGain[i][j] = kalmanGain[i][j][i][k]/compositeInnovationCovariance;
        }
    }
    //compute kalman gain * composite innovation
    //mult 3*3 , 3*1
    float result3_1[3][1];
    for(i = 0; i < 3; ++i){//mult 3*3, 3*1
        for(j = 0; j < 1; ++j){
            for(k = 0; k < 3; ++k){
                result3_1[i][j] += kalmanGain[i][k] * compositeInnovation[k];
            }
        }
    }
    //compute updated robot position estimate
    float xEstimatedKLast=xEstimatedKNext+result3_1[0];
    float yEstimatedKLast=yEstimatedKNext+result3_1[1];
    float thetaEstimatedKLast=thetaEstimatedKNext+result3_1[2];
    //store the resultant covariance for next estimation
    /*
    a b c
    d e f
    g h i
    
    a d g
    b e h
    c f i
    */
    float kalmanGainTranspose[3][3];
    kalmanGainTranspose[0][0]=kalmanGain[0][0];
    kalmanGainTranspose[0][1]=kalmanGain[1][0];
    kalmanGainTranspose[0][2]=kalmanGain[2][0];
    kalmanGainTranspose[1][0]=kalmanGain[0][1];
    kalmanGainTranspose[1][1]=kalmanGain[1][1];
    kalmanGainTranspose[1][2]=kalmanGain[2][1];
    kalmanGainTranspose[2][0]=kalmanGain[0][2];
    kalmanGainTranspose[2][1]=kalmanGain[1][2];
    kalmanGainTranspose[2][2]=kalmanGain[2][2];
    
    //mult 3*3 , 3*3
    float fithTempMatrix3_3[3][3];
    for(i = 0; i < 3; ++i){//mult 3*3 , 3*3
        for(j = 0; j < 3; ++j){
            for(k = 0; k < 3; ++k){
                fithTempMatrix3_3[i][j] += kalmanGain[i][k] * kalmanGainTranspose[k][j];
            }
        }
    }
    for(i = 0; i < 3; ++i){//add 3*3 , 3*3
        for(j = 0; j < 3; ++j){
            fithTempMatrix3_3[i][j]=fithTempMatrix3_3[i][j]*compositeInnovationCovariance;
        }
    }
    float covariancePositionEsimatedLast[3][3];
    for(i = 0; i < 3; ++i){//add 3*3 , 3*3
        for(j = 0; j < 3; ++j){
            covariancePositionEsimatedLast[i][j]=fithTempMatrix3_3[i][j]+newCovarianceOdometricPositionEstimate[i][j];
        }
    }
    //update PreviousCovarianceOdometricPositionEstimate
    for(i = 0; i < 3; ++i){
        for(j = 0; j < 3; ++j){
            PreviousCovarianceOdometricPositionEstimate[i][j]=covariancePositionEsimatedLast[i][j];
        }
    }
    
    xEstimatedK=xEstimatedKLast;
    yEstimatedK=yEstimatedKLast;
    thetaEstimatedK=thetaEstimatedKLast;
}