2012-04-17 15:54:05 +02:00
/**
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* @ addtogroup OpenPilotModules OpenPilot Modules
* @ {
* @ addtogroup AirspeedModule Airspeed Module
2012-07-20 13:08:54 +02:00
* @ brief Calculate airspeed from diverse sources and update @ ref Airspeed " Airspeed UAV Object "
2012-04-17 15:54:05 +02:00
* @ {
*
* @ file airspeed . c
2012-07-20 13:08:54 +02:00
* @ author The OpenPilot Team , http : //www.openpilot.org Copyright (C) 2012.
* @ brief Airspeed module
2012-04-17 15:54:05 +02:00
*
* @ see The GNU Public License ( GPL ) Version 3
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
/**
* Output object : BaroAirspeed
*
* This module will periodically update the value of the BaroAirspeed object .
*
*/
# include "openpilot.h"
2012-04-17 16:38:53 +02:00
# include "hwsettings.h"
2012-04-17 15:54:05 +02:00
# include "airspeed.h"
2012-07-20 13:08:54 +02:00
# include "gpsvelocity.h"
# include "airspeedsettings.h"
# include "gps_airspeed.h"
# include "baroaltitude.h"
# include "baroairspeed.h" // object that will be updated by the module
# include "attitudeactual.h"
# include "CoordinateConversions.h"
2012-04-17 15:54:05 +02:00
// Private constants
2012-07-20 13:08:54 +02:00
# if defined (PIOS_INCLUDE_GPS)
# define GPS_AIRSPEED_PRESENT
# endif
# if defined (PIOS_INCLUDE_MPXV5004) || defined (PIOS_INCLUDE_MPXV7002) || defined (PIOS_INCLUDE_ETASV3)
# define BARO_AIRSPEED_PRESENT
# endif
# if defined (GPS_AIRSPEED_PRESENT) && defined (BARO_AIRSPEED_PRESENT)
# define STACK_SIZE_BYTES 700
# elif defined (GPS_AIRSPEED_PRESENT)
# define STACK_SIZE_BYTES 600
# elif defined (BARO_AIRSPEED_PRESENT)
# define STACK_SIZE_BYTES 550
2012-07-23 14:06:44 +02:00
# else
# define STACK_SIZE_BYTES 0
# define NO_AIRSPEED_SENSOR_PRESENT
2012-07-20 13:08:54 +02:00
# endif
2012-04-17 15:54:05 +02:00
# define TASK_PRIORITY (tskIDLE_PRIORITY+1)
2012-07-20 13:08:54 +02:00
# define SAMPLING_DELAY_MS_FALLTHROUGH 50 //Fallthrough update at 20Hz. The fallthrough runs faster than the GPS to ensure that we don't miss GPS updates because we're slightly out of sync
2012-07-24 15:16:52 +02:00
# define GPS_AIRSPEED_BIAS_KP 0.01f //Needs to be settable in a UAVO
# define GPS_AIRSPEED_BIAS_KI 0.01f //Needs to be settable in a UAVO
# define GPS_AIRSPEED_TIME_CONSTANT_MS 500.0f //Needs to be settable in a UAVO
# define BARO_TRUEAIRSPEED_TIME_CONSTANT_S 1.0f //Needs to be settable in a UAVO
2012-07-20 13:08:54 +02:00
# define F_PI 3.141526535897932f
# define DEG2RAD (F_PI / 180.0f)
2012-07-23 16:09:36 +02:00
# define T0 288.15f
# define BARO_TEMPERATURE_OFFSET 5
2012-04-17 15:54:05 +02:00
// Private types
// Private variables
static xTaskHandle taskHandle ;
2012-07-20 13:08:54 +02:00
static bool airspeedEnabled = false ;
volatile bool gpsNew = false ;
static uint8_t airspeedSensorType ;
static uint16_t gpsSamplePeriod_ms ;
# ifdef BARO_AIRSPEED_PRESENT
static int8_t airspeedADCPin = - 1 ;
# endif
2012-04-17 15:54:05 +02:00
// Private functions
static void airspeedTask ( void * parameters ) ;
2012-07-20 13:08:54 +02:00
void baro_airspeedGet ( BaroAirspeedData * baroAirspeedData , portTickType * lastSysTime , uint8_t airspeedSensorType , int8_t airspeedADCPin ) ;
static void AirspeedSettingsUpdatedCb ( UAVObjEvent * ev ) ;
2012-04-17 15:54:05 +02:00
2012-07-20 13:08:54 +02:00
# ifdef GPS_AIRSPEED_PRESENT
static void GPSVelocityUpdatedCb ( UAVObjEvent * ev ) ;
# endif
2012-04-17 15:54:05 +02:00
/**
* Initialise the module , called on startup
* \ returns 0 on success or - 1 if initialisation failed
*/
int32_t AirspeedStart ( )
{
2012-07-23 14:06:44 +02:00
# if defined (NO_AIRSPEED_SENSOR_PRESENT)
return - 1 ;
# endif
2012-07-20 13:08:54 +02:00
//Check if module is enabled or not
2012-04-17 15:54:05 +02:00
if ( airspeedEnabled = = false ) {
return - 1 ;
}
2012-07-20 13:08:54 +02:00
2012-04-17 15:54:05 +02:00
// Start main task
xTaskCreate ( airspeedTask , ( signed char * ) " Airspeed " , STACK_SIZE_BYTES / 4 , NULL , TASK_PRIORITY , & taskHandle ) ;
2012-04-17 18:59:46 +02:00
TaskMonitorAdd ( TASKINFO_RUNNING_AIRSPEED , taskHandle ) ;
2012-04-17 15:54:05 +02:00
return 0 ;
}
/**
* Initialise the module , called on startup
* \ returns 0 on success or - 1 if initialisation failed
*/
int32_t AirspeedInitialize ( )
{
2012-07-20 13:08:54 +02:00
# ifdef MODULE_AIRSPEED_BUILTIN
airspeedEnabled = true ;
# else
HwSettingsInitialize ( ) ;
uint8_t optionalModules [ HWSETTINGS_OPTIONALMODULES_NUMELEM ] ;
HwSettingsOptionalModulesGet ( optionalModules ) ;
if ( optionalModules [ HWSETTINGS_OPTIONALMODULES_AIRSPEED ] = = HWSETTINGS_OPTIONALMODULES_ENABLED ) {
2012-04-17 15:54:05 +02:00
airspeedEnabled = true ;
2012-07-20 13:08:54 +02:00
} else {
airspeedEnabled = false ;
return - 1 ;
}
# endif
# ifdef BARO_AIRSPEED_PRESENT
uint8_t adcRouting [ HWSETTINGS_ADCROUTING_NUMELEM ] ;
HwSettingsADCRoutingGet ( adcRouting ) ;
//Determine if the barometric airspeed sensor is routed to an ADC pin
for ( int i = 0 ; i < HWSETTINGS_ADCROUTING_NUMELEM ; i + + ) {
if ( adcRouting [ i ] = = HWSETTINGS_ADCROUTING_ANALOGAIRSPEED ) {
airspeedADCPin = i ;
2012-04-17 15:54:05 +02:00
}
2012-07-20 13:08:54 +02:00
}
# endif
2012-04-17 15:54:05 +02:00
BaroAirspeedInitialize ( ) ;
2012-07-20 13:08:54 +02:00
AirspeedSettingsInitialize ( ) ;
AirspeedSettingsConnectCallback ( AirspeedSettingsUpdatedCb ) ;
2012-04-17 15:54:05 +02:00
return 0 ;
}
MODULE_INITCALL ( AirspeedInitialize , AirspeedStart )
2012-07-20 13:08:54 +02:00
2012-04-17 15:54:05 +02:00
/**
* Module thread , should not return .
*/
static void airspeedTask ( void * parameters )
{
2012-07-20 13:08:54 +02:00
AirspeedSettingsUpdatedCb ( AirspeedSettingsHandle ( ) ) ;
2012-04-17 15:54:05 +02:00
2012-07-20 13:08:54 +02:00
BaroAirspeedData airspeedData ;
BaroAirspeedGet ( & airspeedData ) ;
airspeedData . BaroConnected = BAROAIRSPEED_BAROCONNECTED_FALSE ;
# ifdef BARO_AIRSPEED_PRESENT
2012-07-24 15:16:52 +02:00
portTickType lastGPSTime = xTaskGetTickCount ( ) ; //Time since last GPS-derived airspeed calculation
portTickType lastLoopTime = xTaskGetTickCount ( ) ; //Time since last loop
2012-07-20 13:08:54 +02:00
float airspeedErrInt = 0 ;
# endif
//GPS airspeed calculation variables
# ifdef GPS_AIRSPEED_PRESENT
GPSVelocityConnectCallback ( GPSVelocityUpdatedCb ) ;
gps_airspeedInitialize ( ) ;
# endif
2012-04-17 15:54:05 +02:00
// Main task loop
2012-07-20 13:08:54 +02:00
portTickType lastSysTime = xTaskGetTickCount ( ) ;
2012-04-17 15:54:05 +02:00
while ( 1 )
{
2012-07-20 13:08:54 +02:00
// Update the airspeed object
BaroAirspeedGet ( & airspeedData ) ;
# ifdef BARO_AIRSPEED_PRESENT
2012-07-23 14:06:44 +02:00
float airspeed_tas_baro = 0 ;
2012-07-20 13:08:54 +02:00
if ( airspeedSensorType ! = AIRSPEEDSETTINGS_AIRSPEEDSENSORTYPE_GPSONLY ) {
//Fetch calibrated airspeed from sensors
baro_airspeedGet ( & airspeedData , & lastSysTime , airspeedSensorType , airspeedADCPin ) ;
//Calculate true airspeed, not taking into account compressibility effects
float baroTemperature ;
BaroAltitudeTemperatureGet ( & baroTemperature ) ;
2012-07-23 16:09:36 +02:00
baroTemperature - = BARO_TEMPERATURE_OFFSET ; //Do this just because we suspect that the board heats up relative to its surroundings. THIS IS BAD(tm)
# ifdef GPS_AIRSPEED_PRESENT
//GPS present, so use baro sensor to filter TAS
airspeed_tas_baro = airspeedData . CalibratedAirspeed * sqrtf ( ( baroTemperature + 273.15 ) / T0 ) + airspeedErrInt * GPS_AIRSPEED_BIAS_KI ;
# else
//No GPS, so TAS comes only from baro sensor
airspeedData . TrueAirspeed = airspeedData . CalibratedAirspeed * sqrtf ( ( baroTemperature + 273.15 ) / T0 ) + airspeedErrInt * GPS_AIRSPEED_BIAS_KI ;
# endif
2012-07-20 13:08:54 +02:00
}
else
# endif
{ //Have to catch the fallthrough, or else this loop will monopolize the processor!
airspeedData . BaroConnected = BAROAIRSPEED_BAROCONNECTED_FALSE ;
airspeedData . SensorValue = 12345 ;
//Likely, we have a GPS, so let's configure the fallthrough at close to GPS refresh rates
vTaskDelayUntil ( & lastSysTime , SAMPLING_DELAY_MS_FALLTHROUGH / portTICK_RATE_MS ) ;
2012-04-18 10:48:31 +02:00
}
2012-07-20 13:08:54 +02:00
# ifdef GPS_AIRSPEED_PRESENT
float v_air_GPS = - 1.0f ;
//Check if sufficient time has passed. This will depend on whether we have a pitot tube
//sensor or not. In the case we do, shoot for about once per second. Otherwise, consume GPS
//as quickly as possible.
# ifdef BARO_AIRSPEED_PRESENT
2012-07-24 15:16:52 +02:00
float delT = ( lastSysTime - lastLoopTime ) / ( portTICK_RATE_MS * 1000.0f ) ;
lastLoopTime = lastSysTime ;
if ( ( ( lastSysTime - lastGPSTime ) > 1000 * portTICK_RATE_MS | | airspeedSensorType = = AIRSPEEDSETTINGS_AIRSPEEDSENSORTYPE_GPSONLY )
2012-07-20 13:08:54 +02:00
& & gpsNew ) {
lastGPSTime = lastSysTime ;
# else
if ( gpsNew ) {
# endif
gpsNew = false ; //Do this first
2012-04-18 10:48:31 +02:00
2012-07-20 13:08:54 +02:00
//Calculate airspeed as a function of GPS groundspeed and vehicle attitude. From "IMU Wind Estimation (Theory)", by William Premerlani
gps_airspeedGet ( & v_air_GPS ) ;
}
//Use the GPS error to correct the airspeed estimate.
if ( v_air_GPS > 0 ) //We have valid GPS estimate...
{
airspeedData . GPSAirspeed = v_air_GPS ;
# ifdef BARO_AIRSPEED_PRESENT
if ( airspeedData . BaroConnected = = BAROAIRSPEED_BAROCONNECTED_TRUE ) { //Check if there is an airspeed sensors present...
//Calculate error and error integral
float airspeedErr = v_air_GPS - airspeed_tas_baro ;
airspeedErrInt + = airspeedErr * delT ;
//Saturate integral component at 5 m/s
airspeedErrInt = airspeedErrInt > ( 5.0f / GPS_AIRSPEED_BIAS_KI ) ? ( 5.0f / GPS_AIRSPEED_BIAS_KI ) : airspeedErrInt ;
airspeedErrInt = airspeedErrInt < - ( 5.0f / GPS_AIRSPEED_BIAS_KI ) ? - ( 5.0f / GPS_AIRSPEED_BIAS_KI ) : airspeedErrInt ;
//There's already an airspeed sensor, so instead correct it for bias with P correction. The I correction happened earlier in the function.
airspeedData . TrueAirspeed = airspeed_tas_baro + airspeedErr * GPS_AIRSPEED_BIAS_KP ;
2012-07-23 16:09:36 +02:00
/* Note:
This would be a good place to change the airspeed calibration , so that it matches the GPS computed values . However ,
this might be a bad idea , as their are two degrees of freedom here : temperature and sensor calibration . I don ' t
know how to control for temperature bias .
*/
2012-04-18 10:48:31 +02:00
}
2012-07-20 13:08:54 +02:00
else
# endif
{
//...there's no airspeed sensor, so everything comes from GPS. In this
//case, filter the airspeed for smoother output
float alpha = gpsSamplePeriod_ms / ( gpsSamplePeriod_ms + GPS_AIRSPEED_TIME_CONSTANT_MS ) ; //Low pass filter.
airspeedData . TrueAirspeed = v_air_GPS * ( alpha ) + airspeedData . TrueAirspeed * ( 1.0f - alpha ) ;
2012-07-23 16:09:36 +02:00
//Calculate calibrated airspeed from GPS, since we're not getting it from a discrete airspeed sensor
float baroTemperature ;
BaroAltitudeTemperatureGet ( & baroTemperature ) ;
baroTemperature - = BARO_TEMPERATURE_OFFSET ; //Do this just because we suspect that the board heats up relative to its surroundings. THIS IS BAD(tm)
airspeedData . CalibratedAirspeed = airspeedData . TrueAirspeed / sqrtf ( ( baroTemperature + 273.15 ) / T0 ) ;
2012-07-20 13:08:54 +02:00
}
}
2012-07-23 16:09:36 +02:00
# ifdef BARO_AIRSPEED_PRESENT
else if ( airspeedData . BaroConnected = = BAROAIRSPEED_BAROCONNECTED_TRUE ) {
2012-07-24 15:16:52 +02:00
//No GPS velocity estimate this loop, so filter true airspeed data with baro airspeed
float alpha = delT / ( delT + BARO_TRUEAIRSPEED_TIME_CONSTANT_S ) ; //Low pass filter.
airspeedData . TrueAirspeed = airspeed_tas_baro * ( alpha ) + airspeedData . TrueAirspeed * ( 1.0f - alpha ) ;
2012-07-20 13:08:54 +02:00
}
2012-07-23 16:09:36 +02:00
# endif
# endif
2012-07-20 13:08:54 +02:00
//Set the UAVO
BaroAirspeedSet ( & airspeedData ) ;
}
}
# ifdef GPS_AIRSPEED_PRESENT
static void GPSVelocityUpdatedCb ( UAVObjEvent * ev )
{
gpsNew = true ;
}
# endif
2012-04-18 10:48:31 +02:00
2012-07-20 13:08:54 +02:00
static void AirspeedSettingsUpdatedCb ( UAVObjEvent * ev )
{
AirspeedSettingsData airspeedSettingsData ;
AirspeedSettingsGet ( & airspeedSettingsData ) ;
airspeedSensorType = airspeedSettingsData . AirspeedSensorType ;
gpsSamplePeriod_ms = airspeedSettingsData . GPSSamplePeriod_ms ;
2012-04-17 15:54:05 +02:00
2012-07-20 13:08:54 +02:00
# if defined(PIOS_INCLUDE_MPXV7002)
if ( airspeedSensorType = = AIRSPEEDSETTINGS_AIRSPEEDSENSORTYPE_DIYDRONESMPXV7002 ) {
PIOS_MPXV7002_UpdateCalibration ( airspeedSettingsData . ZeroPoint ) ; //This makes sense for the user if the initial calibration was not good and the user does not wish to reboot.
}
# endif
# if defined(PIOS_INCLUDE_MPXV5004)
if ( airspeedSensorType = = AIRSPEEDSETTINGS_AIRSPEEDSENSORTYPE_DIYDRONESMPXV5004 ) {
PIOS_MPXV5004_UpdateCalibration ( airspeedSettingsData . ZeroPoint ) ; //This makes sense for the user if the initial calibration was not good and the user does not wish to reboot.
2012-04-17 15:54:05 +02:00
}
2012-07-20 13:08:54 +02:00
# endif
2012-04-17 15:54:05 +02:00
}
2012-07-20 13:08:54 +02:00
2012-04-17 15:54:05 +02:00
/**
* @ }
* @ }
*/