Exemple #1
0
void hoverPitchCntrl(void)
{
	union longww pitchAccum;

	if (flags._.pitch_feedback)
	{
		pitchAccum.WW = (__builtin_mulss(-rmat[7] , omegagyro[0])
		               - __builtin_mulss(rmat[6] , omegagyro[1])) << 1;
		pitchrate = pitchAccum._.W1;

		int16_t elevInput = (udb_flags._.radio_on == 1) ?
		    REVERSE_IF_NEEDED(ELEVATOR_CHANNEL_REVERSED, udb_pwIn[ELEVATOR_INPUT_CHANNEL] - udb_pwTrim[ELEVATOR_INPUT_CHANNEL]) : 0;
		int16_t manualPitchOffset = elevInput * (int16_t)(RMAX/600);

		int32_t pitchToWP;

		if (flags._.GPS_steering)
		{
			pitchToWP = (tofinish_line > HOVER_NAV_MAX_PITCH_RADIUS) ?
			    HOVERPTOWP : (HOVERPTOWP / HOVER_NAV_MAX_PITCH_RADIUS * tofinish_line);
		}
		else
		{
			pitchToWP = 0;
		}

		pitchAccum.WW = __builtin_mulsu(rmat[8] + HOVERPOFFSET - pitchToWP + manualPitchOffset , hoverpitchgain)
		              + __builtin_mulus(hoverpitchkd , pitchrate);
	}
	else
	{
		pitchAccum.WW = 0;
	}
	pitch_control = (int32_t)pitchAccum._.W1;
}
Exemple #2
0
// Called at 40 Hz, before sending servo pulses
void dcm_servo_callback_prepare_outputs(void)
{
	if (!dcm_flags._.calib_finished)
	{
		udb_pwOut[ROLL_OUTPUT_CHANNEL] = 3000;
		udb_pwOut[PITCH_OUTPUT_CHANNEL] = 3000;
		udb_pwOut[YAW_OUTPUT_CHANNEL] = 3000;
	}
	else
	{
		union longww accum;
		
		accum.WW = __builtin_mulss(rmat[6], 4000);
		udb_pwOut[ROLL_OUTPUT_CHANNEL] = udb_servo_pulsesat(3000 + accum._.W1);
		
		accum.WW = __builtin_mulss(rmat[7], 4000);
		udb_pwOut[PITCH_OUTPUT_CHANNEL] = udb_servo_pulsesat(3000 + accum._.W1);
		
		accum.WW = __builtin_mulss(rmat[1], 4000);
		udb_pwOut[YAW_OUTPUT_CHANNEL] = udb_servo_pulsesat(3000 + accum._.W1);
	}
	
	// Serial output at 10Hz
	if ((udb_heartbeat_counter % (HEARTBEAT_HZ/10)) == 0)
	{
		if (dcm_flags._.calib_finished)
		{
			send_debug_line();
		}
	}
}
Exemple #3
0
// Calculate the groundspeed in cm/s
int16_t calc_groundspeed(void) // computes (1/2gravity)*( actual_speed^2 - desired_speed^2 )
{
	int32_t gndspd2;
	gndspd2 = __builtin_mulss ( IMUvelocityx._.W1 , IMUvelocityx._.W1 ) ;
	gndspd2 += __builtin_mulss ( IMUvelocityy._.W1 , IMUvelocityy._.W1 ) ;
	gndspd2 += __builtin_mulss ( IMUvelocityz._.W1 , IMUvelocityz._.W1 ) ;

	return sqrt_long(gndspd2);
}
Exemple #4
0
void normalRollCntrl(void)
{
	union longww rollAccum = { 0 } ;
	union longww gyroRollFeedback ;
	union longww gyroYawFeedback ;
	
	fractional rmat6 ;
	fractional omegaAccum2 ;
	
	if ( !canStabilizeInverted() || !desired_behavior._.inverted )
	{
		rmat6 = rmat[6] ;
		omegaAccum2 = omegaAccum[2] ;
	}
	else
	{
		rmat6 = -rmat[6] ;
		omegaAccum2 = -omegaAccum[2] ;
	}
	
#ifdef TestGains
	flags._.GPS_steering = 0 ; // turn off navigation
#endif
	if ( AILERON_NAVIGATION && flags._.GPS_steering )
	{
		rollAccum._.W1 = determine_navigation_deflection( 'a' ) ;
	}
	
#ifdef TestGains
	flags._.pitch_feedback = 1 ;
#endif
	
	if ( ROLL_STABILIZATION_AILERONS && flags._.pitch_feedback )
	{
		gyroRollFeedback.WW = __builtin_mulss( rollkd , omegaAccum[1] ) ;
		rollAccum.WW += __builtin_mulss( rmat6 , rollkp ) ;
	}
	else
	{
		gyroRollFeedback.WW = 0 ;
	}
	
	if ( YAW_STABILIZATION_AILERON && flags._.pitch_feedback )
	{
		gyroYawFeedback.WW = __builtin_mulss( yawkdail, omegaAccum2 ) ;
	}
	else
	{
		gyroYawFeedback.WW = 0 ;
	}
	
	roll_control = (long)rollAccum._.W1 - (long)gyroRollFeedback._.W1 - (long)gyroYawFeedback._.W1 ;
	// Servo reversing is handled in servoMix.c
	
	return ;
}
Exemple #5
0
void hoverRollCntrl(void)
{
	int rollNavDeflection ;
	union longww gyroRollFeedback ;
	
	if ( flags._.pitch_feedback )
	{
		if ( AILERON_NAVIGATION && flags._.GPS_steering )
		{
			rollNavDeflection = (tofinish_line > HOVER_NAV_MAX_PITCH_RADIUS/2) ? determine_navigation_deflection( 'h' ) : 0 ;
		}
		else
		{
			rollNavDeflection = 0 ;
		}
		
		gyroRollFeedback.WW = __builtin_mulss( hoverrollkd , omegaAccum[1] ) ;
	}
	else
	{
		rollNavDeflection = 0 ;
		gyroRollFeedback.WW = 0 ;
	}
	
	roll_control = rollNavDeflection -(long)gyroRollFeedback._.W1 ;
	
	return ;
}
static int16_t wingLift(int16_t X, int16_t Z, int16_t XoverZ)
{
	// compute (1/16*(Z + X *(X/Z)))*2^16
	union longww lift;
	union longww accum;

	// compute the first term. X is already scaled by 1/4, so apply another 1/4:

	accum._.W1 = Z;
	accum._.W0 = 0;
	accum.WW = accum.WW >> 2; // divide by 4
	lift.WW = accum.WW; // Z/16

	// compute the second term in result, X *(X/Z)
	// X has been divided by 4
	// X/Z has been divided by 8
	// so we need to multiply by 2
	accum.WW = __builtin_mulss(X, XoverZ);
	accum.WW += accum.WW; // multiply by 2

	// add the two terms
	lift.WW += accum.WW;

	return lift._.W1;
}
Exemple #7
0
// Calculate the airspeed.
// Note that this airspeed is a magnitude regardless of direction.
// It is not a calculation of forward airspeed.
int16_t calc_airspeed(void)
{
	int16_t speed_component ;
	int32_t fwdaspd2;

	speed_component = IMUvelocityx._.W1 - estimatedWind[0] ;
	fwdaspd2 = __builtin_mulss ( speed_component , speed_component ) ;

	speed_component = IMUvelocityy._.W1 - estimatedWind[1] ;
	fwdaspd2 += __builtin_mulss ( speed_component , speed_component ) ;

	speed_component = IMUvelocityz._.W1 - estimatedWind[2] ;
	fwdaspd2 += __builtin_mulss ( speed_component , speed_component ) ;

	airspeed  = sqrt_long(fwdaspd2);

	return airspeed;
}
Exemple #8
0
// Calculate the expected climb rate depending on a throttle setting and airspeed
// glide_ratio.  rate and airspeed in cm/s.  Descent rate is positive falling.
// Returned value is positive rising in cm/s
int16_t feedforward_climb_rate(fractional throttle, int16_t glide_descent_rate, int16_t airspeed)
{
	// TODO - Correct the assumption that scale changes with descent rate.
	int16_t rateScale = (MAX_THROTTLE_CLIMB_RATE * 100) + glide_descent_rate;

	union longww temp;
	temp.WW = __builtin_mulss( throttle, rateScale);
	temp.WW <<= 2;
	temp._.W1 -= glide_descent_rate;
	return temp._.W1;
}
Exemple #9
0
void AggiornaDatiVelocita(void)
{
    long tmp;

    /*
     *  RPMruota = RPMmotore * ( RapportoRiduzioneMotore:RapportoRiduzioneAsse)
     *
     *  MotoreX.I_MotorAxelSpeed    :	RPM asse motore, un motore "standard" ruota nel range 0-10000RPM, il dato è un intero.
     *  MotoreX.I_GearAxelSpeed     :	RPM uscita riduttore, considerando un rapporto riduzione minimo di 1:100 per motori da
     * 					10000RPM e di 1:10 per motori sotto i 3000RPM il dato varierà al max nel range 0-300
     *					**** Il dato GearAxelSpeed è un intero moltiplicato per un fattore 100		******
     *					**** Range 0-30000 con due cifre decimali in visualizzazione			******
     * I calcoli vengono effettuati passando per un LONG per evitare problemi di overflow nelle moltiplicazioni tra interi
     */
    tmp = __builtin_mulss(Motore1.I_MotorAxelSpeed, ParametriEEPROM[EEPROM_MODBUS_ROBOT_GEARBOX_RATIO_AXE_LEFT]);
    tmp *= 100; // Per ottenere I_GearAxelSpeed riscalato di un fattore 100
    tmp = __builtin_divsd(tmp, ParametriEEPROM[EEPROM_MODBUS_ROBOT_GEARBOX_RATIO_MOTOR_LEFT]);
    Motore1.I_GearAxelSpeed = (int) tmp;

    tmp = __builtin_mulss(Motore2.I_MotorAxelSpeed, ParametriEEPROM[EEPROM_MODBUS_ROBOT_GEARBOX_RATIO_AXE_RIGHT]);
    tmp *= 100; // Per ottenere I_GearAxelSpeed riscalato di un fattore 100
    tmp = __builtin_divsd(tmp, ParametriEEPROM[EEPROM_MODBUS_ROBOT_GEARBOX_RATIO_MOTOR_RIGHT]);
    Motore2.I_GearAxelSpeed = (int) tmp;

    /* Calcolo la velocità di rotazione delle ruote espressa in centesimi di radianti al secondo
     * MotoreX.L_WheelSpeed deriva da I_GearAxelSpeed ed è ritornato alla GUI moltiplicato per 100
     *
     * EEPROM_MODBUS_ROBOT_WHEEL_RADIUS_xxxx : Raggio espresso in decimi di millimetro 360 = 3,6Cm
     *  */
    tmp = __builtin_mulss(ParametriEEPROM[EEPROM_MODBUS_ROBOT_WHEEL_RADIUS_LEFT], Motore1.I_GearAxelSpeed);
    tmp *= TWOPI;
    tmp = __builtin_divsd(tmp, 6000);
    Motore1.L_WheelSpeed = tmp;

    tmp = __builtin_mulss(ParametriEEPROM[EEPROM_MODBUS_ROBOT_WHEEL_RADIUS_RIGHT], Motore2.I_GearAxelSpeed);
    tmp *= TWOPI;
    tmp = __builtin_divsd(tmp, 6000);
    Motore2.L_WheelSpeed = tmp;
}
Exemple #10
0
struct relative3D dcm_absolute_to_relative(struct waypoint3D absolute)
{
	struct relative3D rel ;
	union longww accum_nav ;
	
	rel.z = absolute.z ;
	
	rel.y = (absolute.y - lat_origin.WW)/90 ; // in meters
	
	accum_nav.WW = ((absolute.x - long_origin.WW)/90) ; // in meters
	accum_nav.WW = ((__builtin_mulss ( cos_lat , accum_nav._.W0 )<<2)) ;
	rel.x = accum_nav._.W1 ;
	
	return rel ;
}
Exemple #11
0
void dcm_set_origin_location(int32_t o_lon, int32_t o_lat, int32_t o_alt)
{
	union longbbbb accum_nav;
	unsigned char lat_cir;

	lat_origin.WW = o_lat;
	lon_origin.WW = o_lon;
	alt_origin.WW = o_alt;

	// scale the low 16 bits of latitude from GPS units to gentleNAV units
	accum_nav.WW = __builtin_mulss(LONGDEG_2_BYTECIR, lat_origin._.W1);

	lat_cir = accum_nav.__.B2;  // effectively divides by 256
	// estimate the cosine of the latitude, which is used later computing desired course
	cos_lat = cosine(lat_cir);
}
Exemple #12
0
void dcm_set_origin_location(long o_long, long o_lat, long o_alt)
{
	union longbbbb accum_nav ;
	
	lat_origin.WW = o_lat ;
	long_origin.WW = o_long ;
	alt_origin.WW = o_alt;
	
	//	scale the latitude from GPS units to gentleNAV units
	accum_nav.WW = __builtin_mulss( LONGDEG_2_BYTECIR , lat_origin._.W1 ) ;
	lat_cir = accum_nav.__.B2 ;
	//	estimate the cosine of the latitude, which is used later computing desired course
	cos_lat = cosine ( lat_cir ) ;
	
	return ;
}
Exemple #13
0
// Called at HEARTBEAT_HZ, before sending servo pulses
void udb_heartbeat_callback(void)
{
	if (calib_countdown) {
		udb_pwOut[ROLL_OUTPUT_CHANNEL]    = 3000;
		udb_pwOut[PITCH_OUTPUT_CHANNEL]   = 3000;
		udb_pwOut[YAW_OUTPUT_CHANNEL]     = 3000;
		udb_pwOut[X_ACCEL_OUTPUT_CHANNEL] = 3000;
		udb_pwOut[Y_ACCEL_OUTPUT_CHANNEL] = 3000;
		udb_pwOut[Z_ACCEL_OUTPUT_CHANNEL] = 3000;
	} else if (eepromSuccess == 0 && eepromFailureFlashCount) {
		// eeprom failure!
		DPRINT("eeprom failure!\r\n");
		if (udb_heartbeat_counter % 6 == 0) {
			udb_led_toggle(LED_RED);
			udb_led_toggle(LED_GREEN);
			udb_led_toggle(LED_BLUE);
			udb_led_toggle(LED_ORANGE);
			eepromFailureFlashCount--;
		}
	} else {
		union longww accum;

		accum.WW = __builtin_mulss(y_rate, 4000);
		udb_pwOut[ROLL_OUTPUT_CHANNEL] = udb_servo_pulsesat(3000 + accum._.W1);

		accum.WW = __builtin_mulss(x_rate, 4000);
		udb_pwOut[PITCH_OUTPUT_CHANNEL] = udb_servo_pulsesat(3000 + accum._.W1);

		accum.WW = __builtin_mulss(z_rate, 4000);
		udb_pwOut[YAW_OUTPUT_CHANNEL] = udb_servo_pulsesat(3000 + accum._.W1);

		accum.WW = __builtin_mulss(x_accel, 4000);
		udb_pwOut[X_ACCEL_OUTPUT_CHANNEL] = udb_servo_pulsesat(3000 + accum._.W1);

		accum.WW = __builtin_mulss(y_accel, 4000);
		udb_pwOut[Y_ACCEL_OUTPUT_CHANNEL] = udb_servo_pulsesat(3000 + accum._.W1);

		accum.WW = __builtin_mulss(z_accel, 4000);
		udb_pwOut[Z_ACCEL_OUTPUT_CHANNEL] = udb_servo_pulsesat(3000 + accum._.W1);

		if ((udb_heartbeat_counter / 600) % 2 == 0) {
			led_on(LED_RED);
			((abs(udb_pwOut[ROLL_OUTPUT_CHANNEL]  - 3000) > RATE_THRESHOLD_LED) ? led_on(LED_ORANGE) : led_off(LED_ORANGE));
			((abs(udb_pwOut[PITCH_OUTPUT_CHANNEL] - 3000) > RATE_THRESHOLD_LED) ? led_on(LED_BLUE) : led_off(LED_BLUE));
			((abs(udb_pwOut[YAW_OUTPUT_CHANNEL]   - 3000) > RATE_THRESHOLD_LED) ? led_on(LED_GREEN) : led_off(LED_GREEN));
		} else {
			led_off(LED_RED);
			((abs(udb_pwOut[X_ACCEL_OUTPUT_CHANNEL] - 3000) > ACCEL_THRESHOLD_LED) ? led_on(LED_ORANGE) : led_off(LED_ORANGE));
			((abs(udb_pwOut[Y_ACCEL_OUTPUT_CHANNEL] - 3000) > ACCEL_THRESHOLD_LED) ? led_on(LED_BLUE) : led_off(LED_BLUE));
			((abs(udb_pwOut[Z_ACCEL_OUTPUT_CHANNEL] - 3000) > ACCEL_THRESHOLD_LED) ? led_on(LED_GREEN) : led_off(LED_GREEN));
		}
	}
}
Exemple #14
0
int main(void)
{
    //int i = sin90[1];
    int z;
    int a = 10, b = 5, c;
    //_Fract fr;
    //fr = 0.6;
    unsigned int i = 0xFFFF;
    float y = 0.5;
    unsigned int x;
    x = (unsigned int)(y * i);
    x++;
    z = MPY(10000, 5000);
    x++;
    c = __builtin_mulss(a, b);
    c = a * b;
    return z;

}
Exemple #15
0
void dead_reckon(void)
{
	int16_t air_speed_x, air_speed_y, air_speed_z;
	union longww accum;
	union longww energy;

	if (dcm_flags._.dead_reckon_enable == 1)  // wait for startup of GPS
	{
		// integrate the accelerometers to update IMU velocity
		IMUintegralAccelerationx.WW += __builtin_mulss(((int16_t)(ACCEL2DELTAV)), accelEarth[0]);
		IMUintegralAccelerationy.WW += __builtin_mulss(((int16_t)(ACCEL2DELTAV)), accelEarth[1]);
		IMUintegralAccelerationz.WW += __builtin_mulss(((int16_t)(ACCEL2DELTAV)), accelEarth[2]);

		// integrate IMU velocity to update the IMU location	
		IMUlocationx.WW += (__builtin_mulss(((int16_t)(VELOCITY2LOCATION)), IMUintegralAccelerationx._.W1)>>4);
		IMUlocationy.WW += (__builtin_mulss(((int16_t)(VELOCITY2LOCATION)), IMUintegralAccelerationy._.W1)>>4);
		IMUlocationz.WW += (__builtin_mulss(((int16_t)(VELOCITY2LOCATION)), IMUintegralAccelerationz._.W1)>>4);

		if (dead_reckon_clock > 0)
		// apply drift adjustments only while valid GPS data is in force.
		// This is done with a countdown clock that gets reset each time new data comes in.
		{
			dead_reckon_clock --;

			IMUintegralAccelerationx.WW += __builtin_mulss(DR_FILTER_GAIN, velocityErrorEarth[0]);
			IMUintegralAccelerationy.WW += __builtin_mulss(DR_FILTER_GAIN, velocityErrorEarth[1]);
			IMUintegralAccelerationz.WW += __builtin_mulss(DR_FILTER_GAIN, velocityErrorEarth[2]);

			IMUlocationx.WW += __builtin_mulss(DR_FILTER_GAIN, locationErrorEarth[0]);
			IMUlocationy.WW += __builtin_mulss(DR_FILTER_GAIN, locationErrorEarth[1]);
			IMUlocationz.WW += __builtin_mulss(DR_FILTER_GAIN, locationErrorEarth[2]);

			IMUvelocityx.WW = IMUintegralAccelerationx.WW +
			                  __builtin_mulus(ONE_OVER_TAU, 100*locationErrorEarth[0]);
			IMUvelocityy.WW = IMUintegralAccelerationy.WW +
			                  __builtin_mulus(ONE_OVER_TAU, 100*locationErrorEarth[1]);
			IMUvelocityz.WW = IMUintegralAccelerationz.WW +
			                  __builtin_mulus(ONE_OVER_TAU, 100*locationErrorEarth[2]);

		}
		else  // GPS has gotten disconnected
		{
			yaw_drift_reset();
			dcm_flags._.gps_history_valid = 0; // restart GPS history variables
			IMUvelocityx.WW = IMUintegralAccelerationx.WW;
			IMUvelocityy.WW = IMUintegralAccelerationy.WW;
			IMUvelocityz.WW = IMUintegralAccelerationz.WW;
		}

		if (gps_nav_valid() && (dcm_flags._.reckon_req == 1))
		{
			// compute error indications and restart the dead reckoning clock to apply them
			dcm_flags._.reckon_req = 0;
			dead_reckon_clock = DR_PERIOD;

			locationErrorEarth[0] = GPSlocation.x - IMUlocationx._.W1;
			locationErrorEarth[1] = GPSlocation.y - IMUlocationy._.W1;
			locationErrorEarth[2] = GPSlocation.z - IMUlocationz._.W1;

			velocityErrorEarth[0] = GPSvelocity.x - IMUintegralAccelerationx._.W1;
			velocityErrorEarth[1] = GPSvelocity.y - IMUintegralAccelerationy._.W1;
			velocityErrorEarth[2] = GPSvelocity.z - IMUintegralAccelerationz._.W1;
		}
	}
void normalPitchCntrl(void)
{
	union longww pitchAccum;
	int16_t rtlkick;
//	int16_t aspd_adj;
//	fractional aspd_err, aspd_diff;

	fractional rmat6;
	fractional rmat7;
	fractional rmat8;

	if (!canStabilizeInverted() || current_orientation != F_INVERTED)
	{
		rmat6 = rmat[6];
		rmat7 = rmat[7];
		rmat8 = rmat[8];
	}
	else
	{
		rmat6 = -rmat[6];
		rmat7 = -rmat[7];
		rmat8 = -rmat[8];
		pitchAltitudeAdjust = -pitchAltitudeAdjust - INVNPITCH;
	}

	navElevMix = 0;
	if (flags._.pitch_feedback)
	{
		if (RUDDER_OUTPUT_CHANNEL != CHANNEL_UNUSED && RUDDER_INPUT_CHANNEL != CHANNEL_UNUSED) {
			pitchAccum.WW = __builtin_mulsu(rmat6 , rudderElevMixGain) << 1;
			pitchAccum.WW = __builtin_mulss(pitchAccum._.W1 ,
			    REVERSE_IF_NEEDED(RUDDER_CHANNEL_REVERSED, udb_pwTrim[RUDDER_INPUT_CHANNEL] - udb_pwOut[RUDDER_OUTPUT_CHANNEL])) << 3;
			navElevMix += pitchAccum._.W1;
		}

		pitchAccum.WW = __builtin_mulsu(rmat6 , rollElevMixGain) << 1;
		pitchAccum.WW = __builtin_mulss(pitchAccum._.W1 , rmat[6]) >> 3;
		navElevMix += pitchAccum._.W1;
	}

	pitchAccum.WW = (__builtin_mulss(rmat8 , omegagyro[0])
	               - __builtin_mulss(rmat6 , omegagyro[2])) << 1;
	pitchrate = pitchAccum._.W1;
	
	if (!udb_flags._.radio_on && flags._.GPS_steering)
	{
		rtlkick = RTLKICK;
	}
	else
	{
		rtlkick = 0;
	}

#if(GLIDE_AIRSPEED_CONTROL == 1)
	fractional aspd_pitch_adj = gliding_airspeed_pitch_adjust();
#endif

	if (PITCH_STABILIZATION && flags._.pitch_feedback)
	{
#if(GLIDE_AIRSPEED_CONTROL == 1)
		pitchAccum.WW = __builtin_mulsu(rmat7 - rtlkick + aspd_pitch_adj + pitchAltitudeAdjust, pitchgain) 
		              + __builtin_mulus(pitchkd , pitchrate);
#else
		pitchAccum.WW = __builtin_mulsu(rmat7 - rtlkick + pitchAltitudeAdjust, pitchgain) 
		              + __builtin_mulus(pitchkd , pitchrate);
#endif
	}
	else
	{
		pitchAccum.WW = 0;
	}

	pitch_control = (int32_t)pitchAccum._.W1 + navElevMix;
}
Exemple #17
0
void estLocation(void)
{
	static int8_t cog_previous = 64;
	static int16_t sog_previous = 0;
	static int16_t climb_rate_previous = 0;
	static uint16_t velocity_previous = 0;
	static int16_t location_previous[] = { 0, 0, 0 };

	union longbbbb accum;
	union longww accum_velocity;
	int8_t cog_circular;
	int8_t cog_delta;
	int16_t sog_delta;
	int16_t climb_rate_delta;
#ifdef USE_EXTENDED_NAV
	int32_t location[3];
#else
	int16_t location[3];
#endif // USE_EXTENDED_NAV
	int16_t location_deltaZ;
	struct relative2D location_deltaXY;
	struct relative2D velocity_thru_air;
	int16_t velocity_thru_airz;

	location_plane(&location[0]);

		// convert GPS course of 360 degrees to a binary model with 256
		accum.WW = __builtin_muluu(COURSEDEG_2_BYTECIR, cog_gps.BB) + 0x00008000;
		// re-orientate from compass (clockwise) to maths (anti-clockwise) with 0 degrees in East
		cog_circular = -accum.__.B2 + 64;

		// compensate for GPS reporting latency.
		// The dynamic model of the EM406 and uBlox is not well known.
		// However, it seems likely much of it is simply reporting latency.
		// This section of the code compensates for reporting latency.
		// markw: what is the latency? It doesn't appear numerically or as a comment
		// in the following code. Since this method is called at the GPS reporting rate
		// it must be assumed to be one reporting interval?

		if (dcm_flags._.gps_history_valid)
		{
			cog_delta = cog_circular - cog_previous;
			sog_delta = sog_gps.BB - sog_previous;
			climb_rate_delta = climb_gps.BB - climb_rate_previous;

			location_deltaXY.x = location[0] - location_previous[0];
			location_deltaXY.y = location[1] - location_previous[1];
			location_deltaZ    = location[2] - location_previous[2];
		}
		else
		{
			cog_delta = 0;
			sog_delta = 0;
			climb_rate_delta = 0;
			location_deltaXY.x = location_deltaXY.y = location_deltaZ = 0;
		}
		dcm_flags._.gps_history_valid = 1;
		actual_dir = cog_circular + cog_delta;
		cog_previous = cog_circular;

		// Note that all these velocities are in centimeters / second

		ground_velocity_magnitudeXY = sog_gps.BB + sog_delta;
		sog_previous = sog_gps.BB;

		GPSvelocity.z = climb_gps.BB + climb_rate_delta;
		climb_rate_previous = climb_gps.BB;

		accum_velocity.WW = (__builtin_mulss(cosine(actual_dir), ground_velocity_magnitudeXY) << 2) + 0x00008000;
		GPSvelocity.x = accum_velocity._.W1;

		accum_velocity.WW = (__builtin_mulss(sine(actual_dir), ground_velocity_magnitudeXY) << 2) + 0x00008000;
		GPSvelocity.y = accum_velocity._.W1;

		rotate_2D(&location_deltaXY, cog_delta); // this is a key step to account for rotation effects!!

		GPSlocation.x = location[0] + location_deltaXY.x;
		GPSlocation.y = location[1] + location_deltaXY.y;
		GPSlocation.z = location[2] + location_deltaZ;

		location_previous[0] = location[0];
		location_previous[1] = location[1];
		location_previous[2] = location[2];

		velocity_thru_air.y = GPSvelocity.y - estimatedWind[1];
		velocity_thru_air.x = GPSvelocity.x - estimatedWind[0];
		velocity_thru_airz  = GPSvelocity.z - estimatedWind[2];

#if (HILSIM == 1)
		air_speed_3DGPS = hilsim_airspeed.BB; // use Xplane as a pitot
#else
		air_speed_3DGPS = vector3_mag(velocity_thru_air.x, velocity_thru_air.y, velocity_thru_airz);
#endif

		calculated_heading  = rect_to_polar(&velocity_thru_air);
		// veclocity_thru_air.x becomes XY air speed as a by product of CORDIC routine in rect_to_polar()
		air_speed_magnitudeXY = velocity_thru_air.x; // in cm / sec

#if (GPS_RATE == 4)
		forward_acceleration = (air_speed_3DGPS - velocity_previous) << 2; // Ublox enters code 4 times per second
#elif (GPS_RATE == 2)
		forward_acceleration = (air_speed_3DGPS - velocity_previous) << 1; // Ublox enters code 2 times per second
#else
		forward_acceleration = (air_speed_3DGPS - velocity_previous);      // EM406 standard GPS enters code once per second
#endif

		velocity_previous = air_speed_3DGPS;
}
void motor_get_vind(int * u) {
	u[0] =__builtin_divsd(__builtin_mulss(vind_filtered[0],64), settings.mot256[0]);
	u[1] =__builtin_divsd(__builtin_mulss(vind_filtered[1],64), settings.mot256[1]);
}
Exemple #19
0
void osd_update_values( void )
{
	switch (osd_phase)
	{
		case 0:
		{
#if (OSD_LOC_ALTITUDE != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_ALTITUDE+1) ;
			osd_spi_write_number(IMUlocationz._.W1, 0, 0, NUM_FLAG_SIGNED, 0, 0) ;		// Altitude
#endif
			
#if (OSD_LOC_CPU_LOAD != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_CPU_LOAD+1) ;
			osd_spi_write_number(udb_cpu_load(), 3, 0, 0, 0, 0) ;					// CPU
#endif
			
#if (OSD_LOC_VARIO_NUM != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_VARIO_NUM) ;
			osd_spi_write_number(IMUvelocityz._.W1, 0, 0, NUM_FLAG_SIGNED, 0, 0) ;		// Variometer
#endif
			
#if (OSD_LOC_VARIO_ARROW != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_VARIO_ARROW) ;
			if (IMUvelocityz._.W1 <= -VARIOMETER_HIGH)
				osd_spi_write(0x7, 0xD4) ;						// Variometer down fast
			else if (IMUvelocityz._.W1 <= -VARIOMETER_LOW)
				osd_spi_write(0x7, 0xD2) ;						// Variometer down slowly
			else if (IMUvelocityz._.W1 < VARIOMETER_LOW)
				osd_spi_write(0x7, 0x00) ;						// Variometer flat (was 0xD0)
			else if (IMUvelocityz._.W1 < VARIOMETER_HIGH)
				osd_spi_write(0x7, 0xD1) ;						// Variometer up slowly
			else if (IMUvelocityz._.W1 >= VARIOMETER_HIGH)
				osd_spi_write(0x7, 0xD3) ;						// Variometer up fast
#endif
			
#if (OSD_LOC_AP_MODE != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_AP_MODE) ;
			if (!flags._.pitch_feedback)
				osd_spi_write(0x7, 0x97) ;						// M : Manual Mode
			else if (!flags._.GPS_steering)
				osd_spi_write(0x7, 0x9D) ;						// S : Stabilized Mode
			else if (udb_flags._.radio_on && !flags._.rtl_hold)
				osd_spi_write(0x7, 0xA1) ;						// W : Waypoint Mode
			else if (flags._.rtl_hold && udb_flags._.radio_on)
				osd_spi_write(0x7, 0x92) ;						// H : RTL Hold, has signal
			else
				osd_spi_write(0x7, 0x9C) ;						// R : RTL Mode, lost signal
#endif
			break ;
		}
		case 1:
		{
			signed char dir_to_goal ;
			int dist_to_goal ;
			
			struct relative2D curHeading ;
			curHeading.x = -rmat[1] ;
			curHeading.y = rmat[4] ;
			signed char earth_yaw = rect_to_polar(&curHeading) ;// 0-255 (0=East,  ccw)
			
			if (flags._.GPS_steering)
			{
				dir_to_goal = desired_dir - earth_yaw ;
				dist_to_goal = abs(tofinish_line) ;
			}
			else 
			{
				struct relative2D toGoal ;
				toGoal.x = 0 - IMUlocationx._.W1 ;
				toGoal.y = 0 - IMUlocationy._.W1 ;
				dir_to_goal = rect_to_polar ( &toGoal ) - earth_yaw ;
				dist_to_goal = toGoal.x ;
			}
	
#if (OSD_LOC_DIST_TO_GOAL != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_DIST_TO_GOAL+1) ;
			osd_spi_write_number(dist_to_goal, 0, 0, 0, 0, 0) ;	// Distance to wp/home
#endif
			
#if (OSD_LOC_ARROW_TO_GOAL != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_ARROW_TO_GOAL) ;
			osd_write_arrow(dir_to_goal) ;
#endif
			
#if (OSD_LOC_HEADING_NUM != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_HEADING_NUM+1) ;
			// earth_yaw										// 0-255 (0=East,  ccw)
			int angle = (earth_yaw * 180 + 64) >> 7 ;			// 0-359 (0=East,  ccw)
			angle = -angle + 90;								// 0-359 (0=North, clockwise)
			if (angle < 0) angle += 360 ;						// 0-359 (0=North, clockwise)
			osd_spi_write_number(angle, 3, 0, NUM_FLAG_ZERO_PADDED, 0, 0) ;	// heading
#endif
			
#if (OSD_LOC_HEADING_CARDINAL != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_HEADING_CARDINAL) ;
			osd_spi_write_string(heading_strings[((unsigned char)(earth_yaw+8))>>4]) ;	// heading
#endif
			
#if (OSD_LOC_VERTICAL_ANGLE_HOME != OSD_LOC_DISABLED)
			// Vertical angle from origin to plane
			int verticalAngle = 0 ;
			if (dist_to_goal != 0)
			{
				struct relative2D componentsToPlane ;
				componentsToPlane.x = dist_to_goal ;
				componentsToPlane.y = IMUlocationz._.W1 ;
				verticalAngle = rect_to_polar(&componentsToPlane) ;		// binary angle (0 - 256 = 360 degrees)
				verticalAngle = (verticalAngle * BYTECIR_TO_DEGREE) >> 16 ;	// switch polarity, convert to -180 - 180 degrees
			}
			osd_spi_write_location(OSD_LOC_VERTICAL_ANGLE_HOME) ;
			osd_spi_write_number(verticalAngle, 0, 0, NUM_FLAG_SIGNED, 0, 0x4D); // Footer: Degree symbol
#endif
			
			
#if (OSD_LOC_ROLL_RATE != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_ROLL_RATE) ;
			osd_spi_write_number(abs(omegagyro[1])/DEGPERSEC, 3, 0, 0, 0, 0) ;	// roll rate in degrees/sec/sec
#endif
			
#if (OSD_LOC_PITCH_RATE != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_PITCH_RATE) ;
			osd_spi_write_number(abs(omegagyro[0])/DEGPERSEC, 3, 0, 0, 0, 0) ;	// pitch rate in degrees/sec/sec
#endif
			
#if (OSD_LOC_YAW_RATE != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_YAW_RATE) ;
			osd_spi_write_number(abs(omegagyro[2])/DEGPERSEC, 3, 0, 0, 0, 0) ;	// yaw rate in degrees/sec/sec
#endif
			
			
#if (ANALOG_CURRENT_INPUT_CHANNEL != CHANNEL_UNUSED)
			
#if (OSD_LOC_BATT_CURRENT != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_BATT_CURRENT) ;
			osd_spi_write_number(battery_current._.W1, 3, 1, 0, 0, 0xB4) ;	// tenths of Amps being used right now
#endif
			
#if (OSD_LOC_BATT_USED != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_BATT_USED) ;
			osd_spi_write_number(battery_mAh_used._.W1, 4, 0, 0, 0, 0xB7) ;	// mAh used so far
#endif

#endif


#if (ANALOG_VOLTAGE_INPUT_CHANNEL != CHANNEL_UNUSED)
			
#if (OSD_LOC_BATT_VOLTAGE != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_BATT_VOLTAGE) ;
			osd_spi_write_number(battery_voltage._.W1, 3, 1, 0, 0, 0xA0) ;	// tenths of Volts
#endif
			
#endif

			
#if (ANALOG_RSSI_INPUT_CHANNEL != CHANNEL_UNUSED)

#if (OSD_LOC_RSSI != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_RSSI) ;
			osd_spi_write_number(rc_signal_strength, 3, 0, 0, 0, 0xB3) ;	// RC Receiver signal strength as 0-100%
#endif

#endif
			break ;
		}
		case 2:
		{
#if (OSD_SHOW_HORIZON == 1)
			osd_update_horizon() ;
#endif
			break ;
		}
		case 3:
		{
#if (OSD_LOC_AIR_SPEED_M_S != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_AIR_SPEED_M_S) ;
			osd_spi_write_number(air_speed_3DIMU/100, 3, 0, 0, 0, 0) ;	// speed in m/s
#endif
			
#if (OSD_LOC_AIR_SPEED_MI_HR != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_AIR_SPEED_MI_HR) ;
			osd_spi_write_number(air_speed_3DIMU/45, 3, 0, 0, 0, 0) ;	// speed in mi/hr
#endif
			
#if (OSD_LOC_AIR_SPEED_KM_HR != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_AIR_SPEED_KM_HR) ;
			osd_spi_write_number(air_speed_3DIMU/28, 3, 0, 0, 0, 0) ;	// speed in km/hr
#endif
			
			
#if (OSD_LOC_GROUND_SPEED_M_S != OSD_LOC_DISABLED || OSD_LOC_GROUND_SPEED_MI_HR != OSD_LOC_DISABLED || OSD_LOC_GROUND_SPEED_KM_HR != OSD_LOC_DISABLED)
			unsigned int ground_speed_3DIMU = 
				vector3_mag ( 	IMUvelocityx._.W1 ,
								IMUvelocityy._.W1 ,
								IMUvelocityz._.W1   ) ;
#endif
			
#if (OSD_LOC_GROUND_SPEED_M_S != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_GROUND_SPEED_M_S) ;
			osd_spi_write_number(ground_speed_3DIMU/100, 3, 0, 0, 0, 0) ;	// speed in m/s
#endif
			
#if (OSD_LOC_GROUND_SPEED_MI_HR != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_GROUND_SPEED_MI_HR) ;
			osd_spi_write_number(ground_speed_3DIMU/45, 3, 0, 0, 0, 0) ;	// speed in mi/hr
#endif
			
#if (OSD_LOC_GROUND_SPEED_KM_HR != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_GROUND_SPEED_KM_HR) ;
			osd_spi_write_number(ground_speed_3DIMU/28, 3, 0, 0, 0, 0) ;	// speed in km/hr
#endif
			
			
#if (OSD_LOC_VERTICAL_ACCEL != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_VERTICAL_ACCEL) ;
			union longww gravity_z ;
			gravity_z.WW = __builtin_mulss(GRAVITY, rmat[8]) << 2;
			osd_spi_write_number((ZACCEL_VALUE - gravity_z._.W1)/(100*ACCELSCALE), 3, 0, NUM_FLAG_SIGNED, 0, 0) ;	// vertical acceleration rate in units of m/sec/sec
#endif
			
			
#if (OSD_LOC_VERTICAL_WIND_SPEED != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_VERTICAL_WIND_SPEED) ;
			osd_spi_write_number(estimatedWind[2]/10, 4, 1, NUM_FLAG_SIGNED, 0, 0) ;	// vertical wind speed in m/s
#endif
			
			
#if (OSD_LOC_TOTAL_ENERGY != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_TOTAL_ENERGY) ;
			osd_spi_write_number(total_energy, 4, 0, NUM_FLAG_SIGNED, 0, 0) ;	// total energy in meters above the origin
#endif
			
			
#if (OSD_AUTO_HIDE_GPS == 1)
			boolean showGPS = ( IMUlocationz._.W1 < 20 || ground_velocity_magnitudeXY < 150) ;
#else
			boolean showGPS = 1 ;
#endif
			
#if (OSD_LOC_NUM_SATS != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_NUM_SATS) ;
			if (showGPS)
			{
				osd_spi_write_number(svs, 0, 0, 0, 0xEB, 0) ;			// Num satelites locked, with SatDish icon header
			}
			else
			{
				osd_spi_erase_chars(3) ;
			}
#endif
			
#if (OSD_LOC_GPS_LAT != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_GPS_LAT) ;
			if (showGPS)
			{
				osd_spi_write_number(labs(lat_gps.WW/10), 8, 6, 0, 0, (lat_gps.WW >= 0) ? 0x98 : 0x9D) ; // Footer: N/S
			}
			else
			{
				osd_spi_erase_chars(9) ;
			}
#endif
			
#if (OSD_LOC_GPS_LONG != OSD_LOC_DISABLED)
			osd_spi_write_location(OSD_LOC_GPS_LONG) ;
			if (showGPS)
			{
				osd_spi_write_number(labs(long_gps.WW/10), 9, 6, 0, 0, (long_gps.WW >= 0) ? 0x8F : 0xA1) ; // Footer: E/W
			}
			else
			{
				osd_spi_erase_chars(10) ;
			}
#endif
			break ;
		}
	}
	return ;
}
static boolean process_one_instruction(struct logoInstructionDef instr)
{
	if (instr.use_param)
	{
		// Use the subroutine's parameter instead of the instruction's arg value
		int16_t ind = get_current_stack_parameter_frame_index();
		instr.arg = logoStack[ind].arg;
	}

	switch (instr.cmd)
	{
		case 1: // Repeat
			switch (instr.subcmd)
			{
				case 0: // Repeat N times (or forever if N == -1)
					if (logoStackIndex < LOGO_STACK_DEPTH-1)
					{
						logoStackIndex++;
						logoStack[logoStackIndex].frameType = LOGO_FRAME_TYPE_REPEAT;
						logoStack[logoStackIndex].arg = instr.arg;
						logoStack[logoStackIndex].returnInstructionIndex = instructionIndex;
					}
					break;
				case 1: // End
					if (logoStackIndex > 0)
					{
						if (logoStack[logoStackIndex].frameType == LOGO_FRAME_TYPE_REPEAT)
						{
							// END REPEAT
							if (logoStack[logoStackIndex].arg > 1 || logoStack[logoStackIndex].arg == -1)
							{
								if (logoStack[logoStackIndex].arg != -1)
								{
									logoStack[logoStackIndex].arg--;
								}
								instructionIndex = logoStack[logoStackIndex].returnInstructionIndex;
							}
							else
							{
								logoStackIndex--;
							}
						}
						else if (logoStack[logoStackIndex].frameType == LOGO_FRAME_TYPE_SUBROUTINE)
						{
							// END SUBROUTINE
							instructionIndex = logoStack[logoStackIndex].returnInstructionIndex;
							logoStackIndex--;
							if (logoStackIndex < interruptStackBase)
							{
								interruptStackBase = 0;
								instructionsProcessed = MAX_INSTRUCTIONS_PER_CYCLE; // stop processing instructions after finishing interrupt
							}
						}
						else if (logoStack[logoStackIndex].frameType == LOGO_FRAME_TYPE_IF)
						{
							// Do nothing at the end of an IF block
							logoStackIndex--;
						}
					}
					else
					{
						// Extra, unmatched END goes back to the start of the program
						instructionIndex = logoStack[0].returnInstructionIndex;
						logoStackIndex = 0;
						interruptStackBase = 0;
					}
					break;
				
				case 3: // Else
					if (logoStack[logoStackIndex].frameType == LOGO_FRAME_TYPE_IF)
					{
						instructionIndex = find_end_of_current_if_block();
						logoStackIndex--;
					}
					break;
				
				case 2: // To (define a function)
				{
					// Shouldn't ever run these lines.
					// If we do get here, restart from the top of the logo program.
					instructionIndex = logoStack[0].returnInstructionIndex;
					logoStackIndex = 0;
					interruptStackBase = 0;
				}
				break;
			}
			break;

		case 10: // Exec (reset the stack and then call a subroutine)
			instructionIndex = find_start_of_subroutine(instr.subcmd);
			logoStack[0].returnInstructionIndex = instructionIndex;
			logoStackIndex = 0;
			interruptStackBase = 0;
			break;

		case 2: // Do (call a subroutine)
			if (logoStackIndex < LOGO_STACK_DEPTH-1)
			{
				logoStackIndex++;
				logoStack[logoStackIndex].frameType = LOGO_FRAME_TYPE_SUBROUTINE;
				logoStack[logoStackIndex].arg = instr.arg;
				logoStack[logoStackIndex].returnInstructionIndex = instructionIndex;
			}
			instructionIndex = find_start_of_subroutine(instr.subcmd);
			break;

		case 3: // Forward/Back
			switch (instr.subcmd)
			{
				case 0: // Forward
				{
					int16_t cangle = turtleAngles[currentTurtle];   // 0-359 (clockwise, 0=North)
					int8_t b_angle = (cangle * 182 + 128) >> 8;     // 0-255 (clockwise, 0=North)
					b_angle = -b_angle - 64;                        // 0-255 (ccw, 0=East)
					
					turtleLocations[currentTurtle].x.WW += (__builtin_mulss(-cosine(b_angle), instr.arg) << 2);
					turtleLocations[currentTurtle].y.WW += (__builtin_mulss(-sine(b_angle), instr.arg) << 2);
				}
				break;
			}
			break;

		case 4: // Rotate
			switch (instr.subcmd)
			{
				case 0: // Right
				{
					int16_t angle = turtleAngles[currentTurtle] + instr.arg;
					while (angle < 0) angle += 360;
					angle = angle % 360;
					turtleAngles[currentTurtle] = angle;
					break;
				}
				case 1: // Set Angle
					turtleAngles[currentTurtle] = instr.arg;
					break;
				case 2: // Use current angle
				{
					turtleAngles[currentTurtle] = get_current_angle();
					break;
				}
				case 3: // Use angle to goal
				{
					turtleAngles[currentTurtle] = get_angle_to_point(IMUlocationx._.W1, IMUlocationy._.W1);
					break;
				}
			}
			break;

		case 5: // MV/SET location - X, Y, and Z
			switch (instr.subcmd)
			{
				case 0: // Move X
					turtleLocations[currentTurtle].x._.W1 += instr.arg;
					break;
				case 1: // Set X location
					turtleLocations[currentTurtle].x._.W0 = 0;
					turtleLocations[currentTurtle].x._.W1 = instr.arg;
					break;
				case 2: // Move Y
					turtleLocations[currentTurtle].y._.W1 += instr.arg;
					break;
				case 3: // Set Y location
					turtleLocations[currentTurtle].y._.W0 = 0;
					turtleLocations[currentTurtle].y._.W1 = instr.arg;
					break;
				case 4: // Move Z
					turtleLocations[currentTurtle].z += instr.arg;
					break;
				case 5: // Set Z location
					turtleLocations[currentTurtle].z = instr.arg;
					break;
				case 6: // Use current position (for x and y)
					turtleLocations[currentTurtle].x._.W0 = 0;
					turtleLocations[currentTurtle].x._.W1 = IMUlocationx._.W1;
					turtleLocations[currentTurtle].y._.W0 = 0;
					turtleLocations[currentTurtle].y._.W1 = IMUlocationy._.W1;
					break;
				case 7: // HOME
					turtleAngles[currentTurtle] = 0;
					turtleLocations[currentTurtle].x.WW = 0;
					turtleLocations[currentTurtle].y.WW = 0;
					break;
				case 8: // Absolute set high value
					absoluteHighWord = instr.arg;
					break;
				case 9: // Absolute set low X value
				{
					absoluteXLong._.W1 = absoluteHighWord;
					absoluteXLong._.W0 = instr.arg;
					break;
				}
				case 10: // Absolute set low Y value
				{
					struct waypoint3D wp;
					struct relative3D rel;
					union longww absoluteYLong;

					absoluteYLong._.W1 = absoluteHighWord;
					absoluteYLong._.W0 = instr.arg;

					wp.x = absoluteXLong.WW;
					wp.y = absoluteYLong.WW;
					wp.z = 0;
					rel = dcm_absolute_to_relative(wp);
					turtleLocations[currentTurtle].x._.W0 = 0;
					turtleLocations[currentTurtle].x._.W1 = rel.x;
					turtleLocations[currentTurtle].y._.W0 = 0;
					turtleLocations[currentTurtle].y._.W1 = rel.y;
					break;
				}
			}
			break;

		case 6: // Flags
			switch (instr.subcmd)
			{
				case 0: // Flag On
					setBehavior(desired_behavior.W | instr.arg);
					break;
				case 1: // Flag Off
					setBehavior(desired_behavior.W & ~instr.arg);
					break;
				case 2: // Flag Toggle
					setBehavior(desired_behavior.W ^ instr.arg);
					break;
			}
			break;

		case 7: // Pen Up/Down
			switch (instr.subcmd)
			{
				case 0: // Pen Up
					penState++;
					break;
				case 1: // Pen Down
					if (penState > 0)
						penState--;
					break;
				case 2: // Pen Toggle
					penState = (penState == 0);
					if (penState == 0) instr.do_fly = 1; // Set the Fly Flag
					break;
			}
			break;

		case 8: // Set Turtle (choose plane or camera target)
			currentTurtle = (instr.arg == CAMERA) ? CAMERA : PLANE;
			break;

		case 9: // Modify PARAM
			switch (instr.subcmd)
			{
				case 0: // Set param
				{
					int16_t ind = get_current_stack_parameter_frame_index();
					logoStack[ind].arg = instr.arg;
					break;
				}
				case 1: // Add to param
				{
					int16_t ind = get_current_stack_parameter_frame_index();
					logoStack[ind].arg += instr.arg;
					break;
				}
				case 2: // Multiply param
				{
					int16_t ind = get_current_stack_parameter_frame_index();
					logoStack[ind].arg *= instr.arg;
					break;
				}
				case 3: // Divide param
				{
					int16_t ind = get_current_stack_parameter_frame_index();
					if (instr.arg != 0) // Avoid divide by 0!
					{
						logoStack[ind].arg /= instr.arg;
					}
					break;
				}
			}
			break;

		case 11: // Speed
#if (SPEED_CONTROL == 1)
			switch (instr.subcmd)
			{
				case 0: // Increase Speed
					desiredSpeed += instr.arg * 10;
					break;
				case 1: // Set Speed
					desiredSpeed = instr.arg * 10;
					break;
			}
			if (desiredSpeed < 0) desiredSpeed = 0;
#endif
			break;

		case 12: // Interrupts
			switch (instr.subcmd) {
				case 1: // Set
					interruptIndex = find_start_of_subroutine(instr.arg);
					break;
				case 0: // Clear
					interruptIndex = 0;
					break;
			}
			break;

		case 13: // Load to PARAM
		{
			int16_t ind = get_current_stack_parameter_frame_index();
			logoStack[ind].arg = logo_value_for_identifier(instr.subcmd);
			break;
		}

		case 14: // IF commands
		case 15:
		case 16:
		case 17:
		case 18:
		case 19:
		{
			int16_t val = logo_value_for_identifier(instr.subcmd);
			boolean condTrue = false;

			if (instr.cmd == 14 && val == instr.arg) condTrue = true;       // IF_EQ
			else if (instr.cmd == 15 && val != instr.arg) condTrue = true;  // IF_NE
			else if (instr.cmd == 16 && val > instr.arg) condTrue = true;   // IF_GT
			else if (instr.cmd == 17 && val < instr.arg) condTrue = true;   // IF_LT
			else if (instr.cmd == 18 && val >= instr.arg) condTrue = true;  // IF_GE
			else if (instr.cmd == 19 && val <= instr.arg) condTrue = true;  // IF_LE

			if (condTrue)
			{
				if (logoStackIndex < LOGO_STACK_DEPTH-1)
				{
					logoStackIndex++;
					logoStack[logoStackIndex].frameType = LOGO_FRAME_TYPE_IF;
				}
			}
			else
			{
				// jump to the matching END or ELSE
				instructionIndex = find_end_of_current_if_block();
				if (currentInstructionSet[instructionIndex].subcmd == 3) // is entering an ELSE block
				{
					if (logoStackIndex < LOGO_STACK_DEPTH-1)
					{
						logoStackIndex++;
						logoStack[logoStackIndex].frameType = LOGO_FRAME_TYPE_IF;
					}
				}
			}
			break;
		}
	}
	return instr.do_fly;
}
void helicalTurnCntrl(void)
{
	union longww accum;
	int16_t pitchAdjustAngleOfAttack;
	int16_t rollErrorVector[3];
	int16_t rtlkick;
	int16_t desiredPitch;
	int16_t steeringInput;
	int16_t desiredTiltVector[3];
	int16_t desiredRotationRateGyro[3];
	uint16_t airSpeed;
	union longww desiredTilt;
	int16_t desiredPitchVector[2];
	int16_t desiredPerpendicularPitchVector[2];
	int16_t actualPitchVector[2];
	int16_t pitchDot;
	int16_t pitchCross;
	int16_t pitchError;
	int16_t pitchEarthBodyProjection[2];
	int16_t angleOfAttack;
#ifdef TestGains
	state_flags._.GPS_steering = 0;   // turn off navigation
	state_flags._.pitch_feedback = 1; // turn on stabilization
	airSpeed = 981; // for testing purposes, an airspeed is needed
#else
	airSpeed = air_speed_3DIMU;
	if (airSpeed < TURN_CALC_MINIMUM_AIRSPEED) airSpeed = TURN_CALC_MINIMUM_AIRSPEED;
#endif

	// determine the desired turn rate as the sum of navigation and fly by wire.
	// this allows the pilot to override navigation if needed.
	steeringInput = 0 ; // just in case no airframe type is specified or radio is off
	if (udb_flags._.radio_on == 1)
	{
#if ( (AIRFRAME_TYPE == AIRFRAME_STANDARD) || (AIRFRAME_TYPE == AIRFRAME_GLIDER) )
		if (AILERON_INPUT_CHANNEL != CHANNEL_UNUSED)  // compiler is smart about this
		{
			steeringInput = udb_pwIn[ AILERON_INPUT_CHANNEL ] - udb_pwTrim[ AILERON_INPUT_CHANNEL ];
			steeringInput = REVERSE_IF_NEEDED(AILERON_CHANNEL_REVERSED, steeringInput);
		}
		else if (RUDDER_INPUT_CHANNEL != CHANNEL_UNUSED)
		{
			steeringInput = udb_pwIn[ RUDDER_INPUT_CHANNEL ] - udb_pwTrim[ RUDDER_INPUT_CHANNEL ];
			steeringInput = REVERSE_IF_NEEDED(RUDDER_CHANNEL_REVERSED, steeringInput);
		}
		else
		{
			steeringInput = 0;
		}
#endif // AIRFRAME_STANDARD

#if (AIRFRAME_TYPE == AIRFRAME_VTAIL)
		// use aileron channel if it is available, otherwise use rudder
		if (AILERON_INPUT_CHANNEL != CHANNEL_UNUSED)  // compiler is smart about this
		{
			steeringInput = udb_pwIn[AILERON_INPUT_CHANNEL] - udb_pwTrim[AILERON_INPUT_CHANNEL];
			steeringInput = REVERSE_IF_NEEDED(AILERON_CHANNEL_REVERSED, steeringInput);
		}
		else if (RUDDER_INPUT_CHANNEL != CHANNEL_UNUSED)
		{
			// unmix the Vtail
			int16_t rudderInput  = REVERSE_IF_NEEDED(RUDDER_CHANNEL_REVERSED, (udb_pwIn[ RUDDER_INPUT_CHANNEL] - udb_pwTrim[RUDDER_INPUT_CHANNEL]));
			int16_t elevatorInput = REVERSE_IF_NEEDED(ELEVATOR_CHANNEL_REVERSED, (udb_pwIn[ ELEVATOR_INPUT_CHANNEL] - udb_pwTrim[ELEVATOR_INPUT_CHANNEL]));
			steeringInput = (-rudderInput + elevatorInput);
		}
		else
		{
			steeringInput = 0;
		}
#endif // AIRFRAME_VTAIL

#if (AIRFRAME_TYPE == AIRFRAME_DELTA)
		// delta wing must have an aileron input, so use that
		// unmix the elevons
		int16_t aileronInput  = REVERSE_IF_NEEDED(AILERON_CHANNEL_REVERSED, (udb_pwIn[AILERON_INPUT_CHANNEL] - udb_pwTrim[AILERON_INPUT_CHANNEL]));
		int16_t elevatorInput = REVERSE_IF_NEEDED(ELEVATOR_CHANNEL_REVERSED, (udb_pwIn[ELEVATOR_INPUT_CHANNEL] - udb_pwTrim[ELEVATOR_INPUT_CHANNEL]));
		steeringInput = REVERSE_IF_NEEDED(ELEVON_VTAIL_SURFACES_REVERSED, ((elevatorInput - aileronInput)));
#endif // AIRFRAME_DELTA
	}

	if (steeringInput > MAX_INPUT) steeringInput = MAX_INPUT;
	if (steeringInput < - MAX_INPUT) steeringInput = - MAX_INPUT;

	// note that total steering is the sum of pilot input and waypoint navigation,
	// so that the pilot always has some say in the matter

	accum.WW = __builtin_mulsu(steeringInput, turngainfbw) /(2*MAX_INPUT);

	if ((settings._.AileronNavigation || settings._.RudderNavigation) && state_flags._.GPS_steering)
	{
		accum.WW +=(int32_t) navigate_determine_deflection('t');
	}

	if (accum.WW >(int32_t) 2*(int32_t) RMAX - 1) accum.WW =(int32_t) 2*(int32_t) RMAX - 1;
	if (accum.WW <  -(int32_t) 2*(int32_t) RMAX + 1) accum.WW = -(int32_t) 2*(int32_t) RMAX + 1;

	desiredTurnRateRadians = accum._.W0;

	// compute the desired tilt from desired turn rate and air speed
	// range for acceleration is plus minus 4 times gravity
	// range for turning rate is plus minus 4 radians per second

	// desiredTilt is the ratio(-rmat[6]/rmat[8]), times RMAX/2 required for the turn
	// desiredTilt = desiredTurnRate * airSpeed / gravity
	// desiredTilt = RMAX/2*"real desired tilt"
	// desiredTurnRate = RMAX/2*"real desired turn rate", desired turn rate in radians per second
	// airSpeed is air speed centimeters per second
	// gravity is 981 centimeters per second per second 

	desiredTilt.WW = - __builtin_mulsu(desiredTurnRateRadians, airSpeed);
	desiredTilt.WW /= GRAVITYCMSECSEC;

	// limit the lateral acceleration to +- 4 times gravity, total wing loading approximately 4.12 times gravity

	if (desiredTilt.WW > (int32_t)2 * (int32_t)RMAX - 1)
	{
		desiredTilt.WW = (int32_t)2 * (int32_t)RMAX - 1;
		accum.WW = __builtin_mulsu(-desiredTilt._.W0, GRAVITYCMSECSEC);
		accum.WW /= airSpeed;
		desiredTurnRateRadians = accum._.W0;
	}
	else if (desiredTilt.WW < -(int32_t)2 * (int32_t)RMAX + 1)
	{
		desiredTilt.WW = -(int32_t)2 * (int32_t)RMAX + 1;
		accum.WW = __builtin_mulsu(-desiredTilt._.W0, GRAVITYCMSECSEC);
		accum.WW /= airSpeed;
		desiredTurnRateRadians = accum._.W0;
	}

	// Compute the amount of lift needed to perform the desired turn
	// Tests show that the best estimate of lift is obtained using
	// actual values of rmat[6] and rmat[8], and the commanded value of their ratio
	estimatedLift = wingLift(rmat[6], rmat[8], desiredTilt._.W0);

	// compute angle of attack and elevator trim based on relative wing loading.
	// relative wing loading is the ratio of wing loading divided by the stall wing loading, as a function of air speed
	// both angle of attack and trim are computed by a linear approximation as a function of relative loading:
	// y = (2m)*(x/2) + b, y is either angle of attack or elevator trim.
	// x is relative wing loading. (x/2 is computed instead of x)
	// 2m and b are determined from values of angle of attack and trim at stall speed, normal and inverted.
	// b =  (y_normal + y_inverted) / 2.
	// 2m = (y_normal - y_inverted).

	// If airspeed is greater than stall speed, compute angle of attack and elevator trim,
	// otherwise set AoA and trim to zero.

	if (air_speed_3DIMU > STALL_SPEED_CM_SEC)
	{
		// compute "x/2", the relative wing loading
		relativeLoading = relativeWingLoading(estimatedLift, air_speed_3DIMU);

		// multiply x/2 by 2m for angle of attack
		accum.WW = __builtin_mulss(AOA_SLOPE, relativeLoading);
		// add mx to b
		angleOfAttack = AOA_OFFSET + accum._.W1;

		// project angle of attack into the earth frame
		accum.WW =(__builtin_mulss(angleOfAttack, rmat[8])) << 2;
		pitchAdjustAngleOfAttack = accum._.W1;

		// similarly, compute elevator trim
		accum.WW = __builtin_mulss(ELEVATOR_TRIM_SLOPE, relativeLoading);
		elevatorLoadingTrim = ELEVATOR_TRIM_OFFSET + accum._.W1;
	}
	else
	{
		angleOfAttack = 0;
		pitchAdjustAngleOfAttack = 0;
		elevatorLoadingTrim = 0;
	}
//	SetAofA(angleOfAttack); // removed by helicalTurns

	// convert desired turn rate from radians/second to gyro units

	accum.WW = (((int32_t)desiredTurnRateRadians) << 4);  // desired turn rate in radians times 16 to provide resolution for the divide to follow
	accum.WW = accum.WW / RADSTOGYRO; // at this point accum._.W0 has 2 times the required gyro signal for the turn.

	// compute desired rotation rate vector in body frame, scaling is same as gyro signal

	VectorScale(3, desiredRotationRateGyro, &rmat[6], accum._.W0); // this operation has side effect of dividing by 2

	// compute desired rotation rate vector in body frame, scaling is in RMAX/2*radians/sec

	VectorScale(3, desiredRotationRateRadians, &rmat[6], desiredTurnRateRadians); // this produces half of what we want
	VectorAdd(3, desiredRotationRateRadians, desiredRotationRateRadians, desiredRotationRateRadians); // double

	// incorporate roll into desired tilt vector

	desiredTiltVector[0] = desiredTilt._.W0;
	desiredTiltVector[1] =  0;
	desiredTiltVector[2] = RMAX/2; // the divide by 2 is to account for the RMAX/2 scaling in both tilt and rotation rate
	vector3_normalize(desiredTiltVector, desiredTiltVector); // make sure tilt vector has magnitude RMAX

	// incorporate pitch into desired tilt vector
	// compute return to launch pitch down kick for unpowered RTL
	if (!udb_flags._.radio_on && state_flags._.GPS_steering)
	{
		rtlkick = RTLKICK;
	}
	else
	{
		rtlkick = 0;
	}

	// Compute Matt's glider pitch adjustment
#if (GLIDE_AIRSPEED_CONTROL == 1)
	fractional aspd_pitch_adj = gliding_airspeed_pitch_adjust();
#endif

	// Compute total desired pitch
#if (GLIDE_AIRSPEED_CONTROL == 1)
	desiredPitch = - rtlkick + aspd_pitch_adj + pitchAltitudeAdjust;
#else
	desiredPitch = - rtlkick + pitchAltitudeAdjust;
#endif

	// Adjustment for inverted flight
	if (!canStabilizeInverted() || !desired_behavior._.inverted)
	{
		// normal flight
		desiredTiltVector[1] =  - desiredPitch - pitchAdjustAngleOfAttack;
	}
	else
	{
		// inverted flight
		desiredTiltVector[0] = - desiredTiltVector[0];
		desiredTiltVector[1] = - desiredPitch - pitchAdjustAngleOfAttack - INVNPITCH; // only one of the adjustments is not zero
		desiredTiltVector[2] = - desiredTiltVector[2];
	}

	vector3_normalize(desiredTiltVector, desiredTiltVector); // make sure tilt vector has magnitude RMAX

	// compute roll error

	VectorCross(rollErrorVector, &rmat[6], desiredTiltVector); // compute tilt orientation error
	if (VectorDotProduct(3, &rmat[6], desiredTiltVector) < 0) // more than 90 degree error
	{
		vector3_normalize(rollErrorVector, rollErrorVector); // for more than 90 degrees, make the tilt error vector parallel to desired axis, with magnitude RMAX
	}
	
	tiltError[1] = rollErrorVector[1];

	// compute pitch error

	// start by computing the projection of earth frame pitch error to body frame

	pitchEarthBodyProjection[0] = rmat[6];
	pitchEarthBodyProjection[1] = rmat[8];

	// normalize the projection vector and compute the cosine of the actual pitch as a side effect 

	actualPitchVector[1] =(int16_t) vector2_normalize(pitchEarthBodyProjection, pitchEarthBodyProjection);

	// complete the actual pitch vector

	actualPitchVector[0] = rmat[7];

	// compute the desired pitch vector

	desiredPitchVector[0] = - desiredPitch;
	desiredPitchVector[1] = RMAX;
	vector2_normalize(desiredPitchVector, desiredPitchVector);

	// rotate desired pitch vector by 90 degrees to be able to compute cross product using VectorDot

	desiredPerpendicularPitchVector[0] = desiredPitchVector[1];
	desiredPerpendicularPitchVector[1] = - desiredPitchVector[0];

	// compute pitchDot, the dot product of actual and desired pitch vector
	// (the 2* that appears in several of the following expressions is a result of the Q2.14 format)

	pitchDot = 2*VectorDotProduct(2, actualPitchVector, desiredPitchVector);

	// compute pitchCross, the cross product of the actual and desired pitch vector

	pitchCross = 2*VectorDotProduct(2, actualPitchVector, desiredPerpendicularPitchVector);

	if (pitchDot > 0)
	{
		pitchError = pitchCross;
	}
	else
	{
		if (pitchCross > 0)
		{
			pitchError = RMAX;
		}
		else
		{
			pitchError = - RMAX;
		}
	}

	// multiply the normalized rmat[6], rmat[8] vector by the pitch error
	VectorScale(2, pitchEarthBodyProjection, pitchEarthBodyProjection, pitchError);
	tiltError[0] =   2*pitchEarthBodyProjection[1];
	tiltError[2] = - 2*pitchEarthBodyProjection[0];

	// compute the rotation rate error vector
	VectorSubtract(3, rotationRateError, omegaAccum, desiredRotationRateGyro);
}