/**
 * \brief       Vector field for attractor object
 *
 * \details     Computes a 3D velocity vector attracting the MAV towards a 3D location
 *
 * \param       pos_mav         Current position of the MAV (input)
 * \param       pos_obj         Position of the attractor (input)
 * \param       attractiveness  Weight given to this object (input)
 * \param       vector          Velocity command vector (output)
 */
static void vector_field_attractor(const float pos_mav[3], const float pos_obj[3], const float attractiveness, float vector[3])
{
    float direction[3] = {0.0f, 0.0f, 0.0f};    // desired direction (unit vector)
    float speed = 0.0f;                         // desired speed

    /**
     *  Student code Here
     */

    // Compute vector from MAV to goal
    float mav_to_obj[3] = { pos_obj[X] - pos_mav[X],
                            pos_obj[Y] - pos_mav[Y],
                            pos_obj[Z] - pos_mav[Z]
                          };

    // Compute norm of this vector
    float dist_to_object = vectors_norm(mav_to_obj);

    // Get desired direction
    direction[X] = mav_to_obj[X] / dist_to_object;
    direction[Y] = mav_to_obj[Y] / dist_to_object;
    direction[Z] = mav_to_obj[Z] / dist_to_object;

    // Compute desired speed
    speed = attractiveness * dist_to_object;

    /**
     *  End of Student code
     */

    vector[X] = direction[X] * speed;
    vector[Y] = direction[Y] * speed;
    vector[Z] = direction[Z] * speed;
}
/**
 * \brief       Vector field for repulsive object
 *
 * \details     Computes a 3D velocity vector pushing the MAV away from a 3D location
 *
 * \param       pos_mav         Current position of the MAV (input)
 * \param       pos_obj         Position of the repulsor (input)
 * \param       repulsiveness   Weight given to this object (input)
 * \param       max_range       Range of action (in m)
 * \param       safety_radius   Minimum distance the object can be approached (in m)
 * \param       vector          Velocity command vector (output)
 */
static void vector_field_repulsor_sphere(const float pos_mav[3], const float pos_obj[3], const float repulsiveness, const float max_range, const float safety_radius, float vector[3])
{
    float direction[3] = {0.0f, 0.0f, 0.0f};    // desired direction (unit vector)
    float speed = 0.0f;                         // desired speed

    /**
     *  Student code Here
     */

    // Compute vector from MAV to goal
    float mav_to_obj[3] = { pos_obj[X] - pos_mav[X],
                            pos_obj[Y] - pos_mav[Y],
                            pos_obj[Z] - pos_mav[Z]
                          };

    // Compute norm of this vector
    float dist_to_object = vectors_norm(mav_to_obj);

    // Get desired direction
    direction[X] = mav_to_obj[X] / dist_to_object;
    direction[Y] = mav_to_obj[Y] / dist_to_object;
    direction[Z] = mav_to_obj[Z] / dist_to_object;

    // Compute desired speed
    if (dist_to_object <= safety_radius)
    {
        speed = 0.0f;
    }
    else if (dist_to_object > max_range)
    {
        speed = 0.0f;
    }
    else
    {
        speed = - repulsiveness  / (dist_to_object - safety_radius) * (max_range - dist_to_object);
    }

    /**
     *  End of Student code
     */

    vector[X] = direction[X] * speed;
    vector[Y] = direction[Y] * speed;
    vector[Z] = direction[Z] * speed;
}
bool vector_field_waypoint_update(vector_field_waypoint_t* vector_field)
{
    float tmp_vector[3];
    float pos_obj[3];

    // Re-init velocity command
    vector_field->velocity_command->mode    = VELOCITY_COMMAND_MODE_LOCAL;
    vector_field->velocity_command->xyz[X]  = 0.0f;
    vector_field->velocity_command->xyz[Y]  = 0.0f;
    vector_field->velocity_command->xyz[Z]  = 0.0f;

    // Compute vector field for floor avoidance
    vector_field_floor(vector_field->pos_est->local_position.pos,
                       20,
                       tmp_vector);

    // Add floor vector field to the velocity command
    vector_field->velocity_command->xyz[X] += tmp_vector[X];
    vector_field->velocity_command->xyz[Y] += tmp_vector[Y];
    vector_field->velocity_command->xyz[Z] += tmp_vector[Z];

    // Go through waypoint list
    for (uint16_t i = 0; i < vector_field->waypoint_handler->waypoint_count(); ++i)
    {
        // Get waypoint in NED coordinates
        Mavlink_waypoint_handler::waypoint_struct_t waypoint = convert_waypoint_to_local_ned(&vector_field->waypoint_handler->waypoint_list[i],
                                     &vector_field->pos_est->local_position.origin);

        // Get object position
        pos_obj[X] = waypoint.x;
        pos_obj[Y] = waypoint.y;
        pos_obj[Z] = waypoint.z;

        switch (waypoint.command)
        {
            // Attractive object
            case 22:
                vector_field_attractor(vector_field->pos_est->local_position.pos,
                                       pos_obj,
                                       waypoint.param1,     // attractiveness
                                       tmp_vector);
                break;

            // Cylindrical repulsive object
            case 17:
                vector_field_repulsor_cylinder(vector_field->pos_est->local_position.pos,
                                               pos_obj,
                                               waypoint.param1,     // repulsiveness
                                               waypoint.param2,     // max_range
                                               waypoint.param3,     // safety_radius
                                               tmp_vector);
                break;

            // Spherical repulsive object
            case 18:
                vector_field_repulsor_sphere(vector_field->pos_est->local_position.pos,
                                             pos_obj,
                                             waypoint.param1,   // repulsiveness
                                             waypoint.param2,   // max_range
                                             waypoint.param3,   // safety_radius
                                             tmp_vector);
                break;

            // Circular waypoint
            case 16:
                vector_field_circular_waypoint(vector_field->pos_est->local_position.pos,
                                               pos_obj,
                                               waypoint.param1,     // attractiveness
                                               waypoint.param2,     // cruise_speed
                                               waypoint.param3, // radius
                                               tmp_vector);
                break;

            // Unknown object
            default:
                tmp_vector[X] = 0.0f;
                tmp_vector[Y] = 0.0f;
                tmp_vector[Z] = 0.0f;
                break;
        }

        // Add vector field to the velocity command
        vector_field->velocity_command->xyz[X] += tmp_vector[X];
        vector_field->velocity_command->xyz[Y] += tmp_vector[Y];
        vector_field->velocity_command->xyz[Z] += tmp_vector[Z];
    }

    // Limit velocity
    float vel_norm = vectors_norm(vector_field->velocity_command->xyz);
    if (vel_norm > 5.0f)
    {
        vector_field->velocity_command->xyz[X] = 5.0f * vector_field->velocity_command->xyz[X] / vel_norm;
        vector_field->velocity_command->xyz[Y] = 5.0f * vector_field->velocity_command->xyz[Y] / vel_norm;
        vector_field->velocity_command->xyz[Z] = 5.0f * vector_field->velocity_command->xyz[Z] / vel_norm;
    }

    return true;
}
bool vector_field_waypoint_update(vector_field_waypoint_t* vector_field)
{
    float tmp_vector[3];
    float pos_obj[3];

    // Re-init velocity command
    vector_field->velocity_command->xyz[X]  = 0.0f;
    vector_field->velocity_command->xyz[Y]  = 0.0f;
    vector_field->velocity_command->xyz[Z]  = 0.0f;

    // Compute vector field for floor avoidance
    vector_field_floor(vector_field->ins->position_lf().data(),
                       20,
                       tmp_vector);

    // Add floor vector field to the velocity command
    vector_field->velocity_command->xyz[X] += tmp_vector[X];
    vector_field->velocity_command->xyz[Y] += tmp_vector[Y];
    vector_field->velocity_command->xyz[Z] += tmp_vector[Z];

    // Go through waypoint list
    for (uint16_t i = 0; i < vector_field->waypoint_handler->waypoint_count(); ++i)
    {
        const Waypoint& waypoint = vector_field->waypoint_handler->waypoint_from_index(i);
        local_position_t local_wpt = waypoint.local_pos();

        // Get object position
        pos_obj[X] = local_wpt[X];
        pos_obj[Y] = local_wpt[Y];
        pos_obj[Z] = local_wpt[Z];

        switch (waypoint.command())
        {
            // Attractive object
            case 22:
                vector_field_attractor(vector_field->ins->position_lf().data(),
                                       pos_obj,
                                       waypoint.param1(),     // attractiveness
                                       tmp_vector);
                break;

            // Cylindrical repulsive object
            case 17:
                vector_field_repulsor_cylinder(vector_field->ins->position_lf().data(),
                                               pos_obj,
                                               waypoint.param1(),     // repulsiveness
                                               waypoint.param2(),     // max_range
                                               waypoint.param3(),     // safety_radius
                                               tmp_vector);
                break;

            // Spherical repulsive object
            case 18:
                vector_field_repulsor_sphere(vector_field->ins->position_lf().data(),
                                             pos_obj,
                                             waypoint.param1(),   // repulsiveness
                                             waypoint.param2(),   // max_range
                                             waypoint.param3(),   // safety_radius
                                             tmp_vector);
                break;

            // Circular waypoint
          case 16:
                vector_field_circular_waypoint(vector_field->ins->position_lf().data(),
                                               pos_obj,
                                               waypoint.param1(),     // attractiveness
                                               waypoint.param2(),     // cruise_speed
                                               waypoint.param3(), // radius
                                               tmp_vector);
                break;

            // Unknown object
            default:
                tmp_vector[X] = 0.0f;
                tmp_vector[Y] = 0.0f;
                tmp_vector[Z] = 0.0f;
                break;
        }

        // Add vector field to the velocity command
        vector_field->velocity_command->xyz[X] += tmp_vector[X];
        vector_field->velocity_command->xyz[Y] += tmp_vector[Y];
        vector_field->velocity_command->xyz[Z] += tmp_vector[Z];
    }

    // Limit velocity
    float vel_norm = vectors_norm(vector_field->velocity_command->xyz.data());
    if (vel_norm > 5.0f)
    {
        vector_field->velocity_command->xyz[X] = 5.0f * vector_field->velocity_command->xyz[X] / vel_norm;
        vector_field->velocity_command->xyz[Y] = 5.0f * vector_field->velocity_command->xyz[Y] / vel_norm;
        vector_field->velocity_command->xyz[Z] = 5.0f * vector_field->velocity_command->xyz[Z] / vel_norm;
    }

    return true;
}
void velocity_controller_copter_update(velocity_controller_copter_t* controller)
{
	float velocity_command_global[3];
	float errors[3];
	float thrust_vector[3];
	float thrust_norm;
	float thrust_dir[3];

	// Get the command velocity in global frame
	switch( controller->velocity_command->mode )
	{
		case VELOCITY_COMMAND_MODE_LOCAL:
			get_velocity_command_from_local_to_global( 	controller,
														velocity_command_global );
		break;

		case VELOCITY_COMMAND_MODE_SEMI_LOCAL:
			get_velocity_command_from_semilocal_to_global( 	controller,
															velocity_command_global );
		break;

		case VELOCITY_COMMAND_MODE_GLOBAL:
			velocity_command_global[X] = controller->velocity_command->xyz[X];
			velocity_command_global[Y] = controller->velocity_command->xyz[Y];
			velocity_command_global[Z] = controller->velocity_command->xyz[Z];
		break;
		
		default:
			velocity_command_global[X] = 0.0f;
			velocity_command_global[Y] = 0.0f;
			velocity_command_global[Z] = 0.0f;
		break;
	}

	// Compute errors
	errors[X] = velocity_command_global[X] - controller->pos_est->vel[X];
	errors[Y] = velocity_command_global[Y] - controller->pos_est->vel[Y];
	errors[Z] = velocity_command_global[Z] - controller->pos_est->vel[Z];		// WARNING: it was multiplied by (-1) in stabilisation_copter.c

	// Update PID
	thrust_vector[X] = pid_controller_update_dt( &controller->pid[X], errors[X], controller->ahrs->dt );				// should be multiplied by mass
	thrust_vector[Y] = pid_controller_update_dt( &controller->pid[Y], errors[Y], controller->ahrs->dt );				// should be multiplied by mass
	// thrust_vector[Z] = - GRAVITY + pid_controller_update_dt( &controller->pid[Z], errors[Z], controller->ahrs->dt );	// should be multiplied by mass
	thrust_vector[Z] = pid_controller_update_dt( &controller->pid[Z], errors[Z], controller->ahrs->dt );	// should be multiplied by mass

	// Compute the norm of the thrust that should be applied
	thrust_norm = vectors_norm(thrust_vector);

	// Compute the direction in which thrust should be apply
	thrust_dir[X] = thrust_vector[X] / thrust_norm;
	thrust_dir[Y] = thrust_vector[Y] / thrust_norm;
	thrust_dir[Z] = thrust_vector[Z] / thrust_norm;

	// Map thrust dir to attitude
	controller->attitude_command->mode 		 = ATTITUDE_COMMAND_MODE_RPY;
	controller->attitude_command->rpy[ROLL]  = maths_clip(thrust_vector[Y], 1);
	controller->attitude_command->rpy[PITCH] = - maths_clip(thrust_vector[X], 1);
	controller->attitude_command->rpy[YAW]   = 0.0f;

	// Map PID output to thrust
	// float max_thrust = 30.0f;			// 10 Newton max thrust
	// controller->thrust_command->thrust 	= maths_clip(thrust_norm/max_thrust, 1.0f) * 2.0f - 1; 
	// controller->thrust_command->thrust 	= maths_clip(thrust_norm, 1.0f) * 2.0f - 1; 
	// controller->thrust_command->thrust 	= - 1.0 + 2 * thrust_norm/max_thrust; 

	// // Map PID output to attitude
	// controller->attitude_command->mode 		 = ATTITUDE_COMMAND_MODE_RPY;
	// controller->attitude_command->rpy[ROLL]  = maths_clip(thrust_vector[Y], 1);
	// controller->attitude_command->rpy[PITCH] = - maths_clip(thrust_vector[X], 1);
	// controller->attitude_command->rpy[YAW]   = 0.0f;

	// Map PID output to thrust
	controller->thrust_command->thrust 	= controller->thrust_hover_point - thrust_vector[Z];
}
void stabilisation_copter_cascade_stabilise(stabilisation_copter_t* stabilisation_copter)
{
	float rpyt_errors[4];
	control_command_t input;
	int32_t i;
	quat_t qtmp, q_rot;
	aero_attitude_t attitude_yaw_inverse;
	
	// set the controller input
	input= *stabilisation_copter->controls;
	switch (stabilisation_copter->controls->control_mode) 
	{
	case VELOCITY_COMMAND_MODE:
		
		attitude_yaw_inverse = coord_conventions_quat_to_aero(stabilisation_copter->ahrs->qe);
		attitude_yaw_inverse.rpy[0] = 0.0f;
		attitude_yaw_inverse.rpy[1] = 0.0f;
		attitude_yaw_inverse.rpy[2] = attitude_yaw_inverse.rpy[2];
		
		//qtmp=quaternions_create_from_vector(input.tvel);
		//quat_t input_global = quaternions_local_to_global(stabilisation_copter->ahrs->qe, qtmp);
		
		q_rot = coord_conventions_quaternion_from_aero(attitude_yaw_inverse);
		
		quat_t input_global;
		quaternions_rotate_vector(q_rot, input.tvel, input_global.v);
		
		input.tvel[X] = input_global.v[X];
		input.tvel[Y] = input_global.v[Y];
		input.tvel[Z] = input_global.v[Z];
		
		rpyt_errors[X] = input.tvel[X] - stabilisation_copter->pos_est->vel[X];
		rpyt_errors[Y] = input.tvel[Y] - stabilisation_copter->pos_est->vel[Y];
		rpyt_errors[3] = -(input.tvel[Z] - stabilisation_copter->pos_est->vel[Z]);
		
		if (stabilisation_copter->controls->yaw_mode == YAW_COORDINATED) 
		{
			float rel_heading_coordinated;
			if ((maths_f_abs(stabilisation_copter->pos_est->vel_bf[X])<0.001f)&&(maths_f_abs(stabilisation_copter->pos_est->vel_bf[Y])<0.001f))
			{
				rel_heading_coordinated = 0.0f;
			}
			else
			{
				rel_heading_coordinated = atan2(stabilisation_copter->pos_est->vel_bf[Y], stabilisation_copter->pos_est->vel_bf[X]);
			}
			
			float w = 0.5f * (maths_sigmoid(vectors_norm(stabilisation_copter->pos_est->vel_bf)-stabilisation_copter->stabiliser_stack.yaw_coordination_velocity) + 1.0f);
			input.rpy[YAW] = (1.0f - w) * input.rpy[YAW] + w * rel_heading_coordinated;
		}

		rpyt_errors[YAW]= input.rpy[YAW];
		
		// run PID update on all velocity controllers
		stabilisation_run(&stabilisation_copter->stabiliser_stack.velocity_stabiliser, stabilisation_copter->imu->dt, rpyt_errors);
		
		//velocity_stabiliser.output.thrust = maths_f_min(velocity_stabiliser.output.thrust,stabilisation_param.controls->thrust);
		stabilisation_copter->stabiliser_stack.velocity_stabiliser.output.thrust += stabilisation_copter->thrust_hover_point;
		stabilisation_copter->stabiliser_stack.velocity_stabiliser.output.theading = input.theading;
		input = stabilisation_copter->stabiliser_stack.velocity_stabiliser.output;
		
		qtmp=quaternions_create_from_vector(stabilisation_copter->stabiliser_stack.velocity_stabiliser.output.rpy);
		//quat_t rpy_local = quaternions_global_to_local(stabilisation_copter->ahrs->qe, qtmp);
		
		quat_t rpy_local;
		quaternions_rotate_vector(quaternions_inverse(q_rot), qtmp.v, rpy_local.v);
		
		input.rpy[ROLL] = rpy_local.v[Y];
		input.rpy[PITCH] = -rpy_local.v[X];
		//input.thrust = stabilisation_copter->controls->tvel[Z];
		
	// -- no break here  - we want to run the lower level modes as well! -- 
	
	case ATTITUDE_COMMAND_MODE:
		// run absolute attitude_filter controller
		rpyt_errors[0]= input.rpy[0] - ( - stabilisation_copter->ahrs->up_vec.v[1] ); 
		rpyt_errors[1]= input.rpy[1] - stabilisation_copter->ahrs->up_vec.v[0];
		
		if ((stabilisation_copter->controls->yaw_mode == YAW_ABSOLUTE) ) 
		{
			rpyt_errors[2] =maths_calc_smaller_angle(input.theading- stabilisation_copter->pos_est->local_position.heading);
		}
		else
		{ // relative yaw
			rpyt_errors[2]= input.rpy[2];
		}
		
		rpyt_errors[3]= input.thrust;       // no feedback for thrust at this level
		
		// run PID update on all attitude_filter controllers
		stabilisation_run(&stabilisation_copter->stabiliser_stack.attitude_stabiliser, stabilisation_copter->imu->dt, rpyt_errors);
		
		// use output of attitude_filter controller to set rate setpoints for rate controller 
		input = stabilisation_copter->stabiliser_stack.attitude_stabiliser.output;
	
	// -- no break here  - we want to run the lower level modes as well! -- 
	
	case RATE_COMMAND_MODE: // this level is always run
		// get rate measurements from IMU (filtered angular rates)
		for (i=0; i<3; i++)
		{
			rpyt_errors[i]= input.rpy[i]- stabilisation_copter->ahrs->angular_speed[i];
		}
		rpyt_errors[3] = input.thrust ;  // no feedback for thrust at this level
		
		// run PID update on all rate controllers
		stabilisation_run(&stabilisation_copter->stabiliser_stack.rate_stabiliser, stabilisation_copter->imu->dt, rpyt_errors);
	}
	
	// mix to servo outputs depending on configuration
	if( stabilisation_copter->motor_layout == QUADCOPTER_MOTOR_LAYOUT_DIAG )
	{
		stabilisation_copter_mix_to_servos_diag_quad(&stabilisation_copter->stabiliser_stack.rate_stabiliser.output, stabilisation_copter->servos);
	}
	else if( stabilisation_copter->motor_layout == QUADCOPTER_MOTOR_LAYOUT_CROSS )
	{
		stabilisation_copter_mix_to_servos_cross_quad(&stabilisation_copter->stabiliser_stack.rate_stabiliser.output, stabilisation_copter->servos);

	}
}