ardupilot/libraries/SITL/SIM_Aircraft.cpp
Nicholas Engle 1103e92884 SITL: Add SIM_WIND_DIR_Z parameter for SITL
This controls the vertical pitch of the 3d wind vector, allowing futher control of the wind
using systems like dronekit. This change directly effects the calcuation of the wind vector
2018-02-05 16:38:53 -08:00

812 lines
26 KiB
C++

/*
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, see <http://www.gnu.org/licenses/>.
*/
/*
parent class for aircraft simulators
*/
#include "SIM_Aircraft.h"
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#ifdef __CYGWIN__
#include <windows.h>
#include <time.h>
#include <mmsystem.h>
#endif
#include <DataFlash/DataFlash.h>
#include <AP_Param/AP_Param.h>
using namespace SITL;
/*
parent class for all simulator types
*/
Aircraft::Aircraft(const char *home_str, const char *frame_str) :
ground_level(0.0f),
frame_height(0.0f),
dcm(),
gyro(),
gyro_prev(),
ang_accel(),
velocity_ef(),
mass(0.0f),
accel_body(0.0f, 0.0f, -GRAVITY_MSS),
time_now_us(0),
gyro_noise(radians(0.1f)),
accel_noise(0.3f),
rate_hz(1200.0f),
autotest_dir(nullptr),
frame(frame_str),
#ifdef __CYGWIN__
min_sleep_time(20000)
#else
min_sleep_time(5000)
#endif
{
// make the SIM_* variables available to simulator backends
sitl = (SITL *)AP_Param::find_object("SIM_");
parse_home(home_str, home, home_yaw);
location = home;
ground_level = home.alt * 0.01f;
dcm.from_euler(0.0f, 0.0f, radians(home_yaw));
set_speedup(1.0f);
last_wall_time_us = get_wall_time_us();
frame_counter = 0;
// allow for orientation settings, such as with tailsitters
enum ap_var_type ptype;
ahrs_orientation = (AP_Int8 *)AP_Param::find("AHRS_ORIENTATION", &ptype);
terrain = (AP_Terrain *)AP_Param::find_object("TERRAIN_");
}
/*
parse a home string into a location and yaw
*/
bool Aircraft::parse_home(const char *home_str, Location &loc, float &yaw_degrees)
{
char *saveptr = nullptr;
char *s = strdup(home_str);
if (!s) {
free(s);
return false;
}
char *lat_s = strtok_r(s, ",", &saveptr);
if (!lat_s) {
free(s);
return false;
}
char *lon_s = strtok_r(nullptr, ",", &saveptr);
if (!lon_s) {
free(s);
return false;
}
char *alt_s = strtok_r(nullptr, ",", &saveptr);
if (!alt_s) {
free(s);
return false;
}
char *yaw_s = strtok_r(nullptr, ",", &saveptr);
if (!yaw_s) {
free(s);
return false;
}
memset(&loc, 0, sizeof(loc));
loc.lat = static_cast<int32_t>(strtof(lat_s, nullptr) * 1.0e7f);
loc.lng = static_cast<int32_t>(strtof(lon_s, nullptr) * 1.0e7f);
loc.alt = static_cast<int32_t>(strtof(alt_s, nullptr) * 1.0e2f);
if (loc.lat == 0 && loc.lng == 0) {
// default to CMAC instead of middle of the ocean. This makes
// SITL in MissionPlanner a bit more useful
loc.lat = -35.363261*1e7;
loc.lng = 149.165230*1e7;
loc.alt = 584*100;
}
yaw_degrees = strtof(yaw_s, nullptr);
free(s);
return true;
}
/*
return difference in altitude between home position and current loc
*/
float Aircraft::ground_height_difference() const
{
float h1, h2;
if (sitl->terrain_enable && terrain &&
terrain->height_amsl(home, h1, false) &&
terrain->height_amsl(location, h2, false)) {
return h2 - h1;
}
return 0.0f;
}
/*
return current height above ground level (metres)
*/
float Aircraft::hagl() const
{
return (-position.z) + home.alt * 0.01f - ground_level - frame_height - ground_height_difference();
}
/*
return true if we are on the ground
*/
bool Aircraft::on_ground() const
{
return hagl() <= 0;
}
/*
update location from position
*/
void Aircraft::update_position(void)
{
location = home;
location_offset(location, position.x, position.y);
location.alt = static_cast<int32_t>(home.alt - position.z * 100.0f);
#if 0
// logging of raw sitl data
Vector3f accel_ef = dcm * accel_body;
DataFlash_Class::instance()->Log_Write("SITL", "TimeUS,VN,VE,VD,AN,AE,AD,PN,PE,PD", "Qfffffffff",
AP_HAL::micros64(),
velocity_ef.x, velocity_ef.y, velocity_ef.z,
accel_ef.x, accel_ef.y, accel_ef.z,
position.x, position.y, position.z);
#endif
}
/*
update body magnetic field from position and rotation
*/
void Aircraft::update_mag_field_bf()
{
// get the magnetic field intensity and orientation
float intensity;
float declination;
float inclination;
get_mag_field_ef(location.lat * 1e-7f, location.lng * 1e-7f, intensity, declination, inclination);
// create a field vector and rotate to the required orientation
Vector3f mag_ef(1e3f * intensity, 0.0f, 0.0f);
Matrix3f R;
R.from_euler(0.0f, -ToRad(inclination), ToRad(declination));
mag_ef = R * mag_ef;
// calculate frame height above ground
const float frame_height_agl = fmaxf((-position.z) + home.alt * 0.01f - ground_level, 0.0f);
// calculate scaling factor that varies from 1 at ground level to 1/8 at sitl->mag_anomaly_hgt
// Assume magnetic anomaly strength scales with 1/R**3
float anomaly_scaler = (sitl->mag_anomaly_hgt / (frame_height_agl + sitl->mag_anomaly_hgt));
anomaly_scaler = anomaly_scaler * anomaly_scaler * anomaly_scaler;
// add scaled anomaly to earth field
mag_ef += sitl->mag_anomaly_ned.get() * anomaly_scaler;
// Rotate into body frame
mag_bf = dcm.transposed() * mag_ef;
// add motor interference
mag_bf += sitl->mag_mot.get() * battery_current;
}
/* advance time by deltat in seconds */
void Aircraft::time_advance()
{
// we only advance time if it hasn't been advanced already by the
// backend
if (last_time_us == time_now_us) {
time_now_us += frame_time_us;
}
last_time_us = time_now_us;
if (use_time_sync) {
sync_frame_time();
}
}
/* setup the frame step time */
void Aircraft::setup_frame_time(float new_rate, float new_speedup)
{
rate_hz = new_rate;
target_speedup = new_speedup;
frame_time_us = static_cast<uint64_t>(1.0e6f/rate_hz);
scaled_frame_time_us = frame_time_us/target_speedup;
last_wall_time_us = get_wall_time_us();
achieved_rate_hz = rate_hz;
}
/* adjust frame_time calculation */
void Aircraft::adjust_frame_time(float new_rate)
{
if (rate_hz != new_rate) {
rate_hz = new_rate;
frame_time_us = static_cast<uint64_t>(1.0e6f/rate_hz);
scaled_frame_time_us = frame_time_us/target_speedup;
}
}
/*
try to synchronise simulation time with wall clock time, taking
into account desired speedup
This tries to take account of possible granularity of
get_wall_time_us() so it works reasonably well on windows
*/
void Aircraft::sync_frame_time(void)
{
frame_counter++;
uint64_t now = get_wall_time_us();
if (frame_counter >= 40 &&
now > last_wall_time_us) {
const float rate = frame_counter * 1.0e6f/(now - last_wall_time_us);
achieved_rate_hz = (0.99f*achieved_rate_hz) + (0.01f * rate);
if (achieved_rate_hz < rate_hz * target_speedup) {
scaled_frame_time_us *= 0.999f;
} else {
scaled_frame_time_us /= 0.999f;
}
#if 0
::printf("achieved_rate_hz=%.3f rate=%.2f rate_hz=%.3f sft=%.1f\n",
static_cast<double>(achieved_rate_hz),
static_cast<double>(rate),
static_cast<double>(rate_hz),
static_cast<double>(scaled_frame_time_us));
#endif
const uint32_t sleep_time = static_cast<uint32_t>(scaled_frame_time_us * frame_counter);
if (sleep_time > min_sleep_time) {
usleep(sleep_time);
}
last_wall_time_us = now;
frame_counter = 0;
}
}
/* add noise based on throttle level (from 0..1) */
void Aircraft::add_noise(float throttle)
{
gyro += Vector3f(rand_normal(0, 1),
rand_normal(0, 1),
rand_normal(0, 1)) * gyro_noise * fabsf(throttle);
accel_body += Vector3f(rand_normal(0, 1),
rand_normal(0, 1),
rand_normal(0, 1)) * accel_noise * fabsf(throttle);
}
/*
normal distribution random numbers
See
http://en.literateprograms.org/index.php?title=Special:DownloadCode/Box-Muller_transform_%28C%29&oldid=7011
*/
double Aircraft::rand_normal(double mean, double stddev)
{
static double n2 = 0.0;
static int n2_cached = 0;
if (!n2_cached) {
double x, y, r;
do
{
x = 2.0 * rand()/RAND_MAX - 1;
y = 2.0 * rand()/RAND_MAX - 1;
r = x*x + y*y;
} while (is_zero(r) || r > 1.0);
const double d = sqrt(-2.0 * log(r)/r);
const double n1 = x * d;
n2 = y * d;
const double result = n1 * stddev + mean;
n2_cached = 1;
return result;
} else {
n2_cached = 0;
return n2 * stddev + mean;
}
}
/*
fill a sitl_fdm structure from the simulator state
*/
void Aircraft::fill_fdm(struct sitl_fdm &fdm)
{
if (use_smoothing) {
smooth_sensors();
}
fdm.timestamp_us = time_now_us;
if (fdm.home.lat == 0 && fdm.home.lng == 0) {
// initialise home
fdm.home = home;
}
fdm.latitude = location.lat * 1.0e-7;
fdm.longitude = location.lng * 1.0e-7;
fdm.altitude = location.alt * 1.0e-2;
fdm.heading = degrees(atan2f(velocity_ef.y, velocity_ef.x));
fdm.speedN = velocity_ef.x;
fdm.speedE = velocity_ef.y;
fdm.speedD = velocity_ef.z;
fdm.xAccel = accel_body.x;
fdm.yAccel = accel_body.y;
fdm.zAccel = accel_body.z;
fdm.rollRate = degrees(gyro.x);
fdm.pitchRate = degrees(gyro.y);
fdm.yawRate = degrees(gyro.z);
fdm.angAccel.x = degrees(ang_accel.x);
fdm.angAccel.y = degrees(ang_accel.y);
fdm.angAccel.z = degrees(ang_accel.z);
float r, p, y;
dcm.to_euler(&r, &p, &y);
fdm.rollDeg = degrees(r);
fdm.pitchDeg = degrees(p);
fdm.yawDeg = degrees(y);
fdm.quaternion.from_rotation_matrix(dcm);
fdm.airspeed = airspeed_pitot;
fdm.battery_voltage = battery_voltage;
fdm.battery_current = battery_current;
fdm.rpm1 = rpm1;
fdm.rpm2 = rpm2;
fdm.rcin_chan_count = rcin_chan_count;
fdm.range = range;
memcpy(fdm.rcin, rcin, rcin_chan_count * sizeof(float));
fdm.bodyMagField = mag_bf;
if (smoothing.enabled) {
fdm.xAccel = smoothing.accel_body.x;
fdm.yAccel = smoothing.accel_body.y;
fdm.zAccel = smoothing.accel_body.z;
fdm.rollRate = degrees(smoothing.gyro.x);
fdm.pitchRate = degrees(smoothing.gyro.y);
fdm.yawRate = degrees(smoothing.gyro.z);
fdm.speedN = smoothing.velocity_ef.x;
fdm.speedE = smoothing.velocity_ef.y;
fdm.speedD = smoothing.velocity_ef.z;
fdm.latitude = smoothing.location.lat * 1.0e-7;
fdm.longitude = smoothing.location.lng * 1.0e-7;
fdm.altitude = smoothing.location.alt * 1.0e-2;
}
if (ahrs_orientation != nullptr) {
enum Rotation imu_rotation = (enum Rotation)ahrs_orientation->get();
if (imu_rotation != ROTATION_NONE) {
Matrix3f m = dcm;
Matrix3f rot;
rot.from_rotation(imu_rotation);
m = m * rot.transposed();
m.to_euler(&r, &p, &y);
fdm.rollDeg = degrees(r);
fdm.pitchDeg = degrees(p);
fdm.yawDeg = degrees(y);
fdm.quaternion.from_rotation_matrix(m);
}
}
if (last_speedup != sitl->speedup && sitl->speedup > 0) {
set_speedup(sitl->speedup);
last_speedup = sitl->speedup;
}
}
uint64_t Aircraft::get_wall_time_us() const
{
#ifdef __CYGWIN__
static DWORD tPrev;
static uint64_t last_ret_us;
if (tPrev == 0) {
tPrev = timeGetTime();
return 0;
}
DWORD now = timeGetTime();
last_ret_us += (uint64_t)((now - tPrev)*1000UL);
tPrev = now;
return last_ret_us;
#else
struct timeval tp;
gettimeofday(&tp, nullptr);
return static_cast<uint64_t>(tp.tv_sec * 1.0e6 + tp.tv_usec);
#endif
}
/*
set simulation speedup
*/
void Aircraft::set_speedup(float speedup)
{
setup_frame_time(rate_hz, speedup);
}
/*
update the simulation attitude and relative position
*/
void Aircraft::update_dynamics(const Vector3f &rot_accel)
{
const float delta_time = frame_time_us * 1.0e-6f;
// update rotational rates in body frame
gyro += rot_accel * delta_time;
gyro.x = constrain_float(gyro.x, -radians(2000.0f), radians(2000.0f));
gyro.y = constrain_float(gyro.y, -radians(2000.0f), radians(2000.0f));
gyro.z = constrain_float(gyro.z, -radians(2000.0f), radians(2000.0f));
// estimate angular acceleration using a first order difference calculation
// TODO the simulator interface should provide the angular acceleration
ang_accel = (gyro - gyro_prev) / delta_time;
gyro_prev = gyro;
// update attitude
dcm.rotate(gyro * delta_time);
dcm.normalize();
Vector3f accel_earth = dcm * accel_body;
accel_earth += Vector3f(0.0f, 0.0f, GRAVITY_MSS);
// if we're on the ground, then our vertical acceleration is limited
// to zero. This effectively adds the force of the ground on the aircraft
if (on_ground() && accel_earth.z > 0) {
accel_earth.z = 0;
}
// work out acceleration as seen by the accelerometers. It sees the kinematic
// acceleration (ie. real movement), plus gravity
accel_body = dcm.transposed() * (accel_earth + Vector3f(0.0f, 0.0f, -GRAVITY_MSS));
// new velocity vector
velocity_ef += accel_earth * delta_time;
const bool was_on_ground = on_ground();
// new position vector
position += velocity_ef * delta_time;
// velocity relative to air mass, in earth frame
velocity_air_ef = velocity_ef + wind_ef;
// velocity relative to airmass in body frame
velocity_air_bf = dcm.transposed() * velocity_air_ef;
// airspeed
airspeed = velocity_air_ef.length();
// airspeed as seen by a fwd pitot tube (limited to 120m/s)
airspeed_pitot = constrain_float(velocity_air_bf * Vector3f(1.0f, 0.0f, 0.0f), 0.0f, 120.0f);
// constrain height to the ground
if (on_ground()) {
if (!was_on_ground && AP_HAL::millis() - last_ground_contact_ms > 1000) {
printf("Hit ground at %f m/s\n", velocity_ef.z);
last_ground_contact_ms = AP_HAL::millis();
}
position.z = -(ground_level + frame_height - home.alt * 0.01f + ground_height_difference());
switch (ground_behavior) {
case GROUND_BEHAVIOR_NONE:
break;
case GROUND_BEHAVIOR_NO_MOVEMENT: {
// zero roll/pitch, but keep yaw
float r, p, y;
dcm.to_euler(&r, &p, &y);
dcm.from_euler(0.0f, 0.0f, y);
// no X or Y movement
velocity_ef.x = 0.0f;
velocity_ef.y = 0.0f;
if (velocity_ef.z > 0.0f) {
velocity_ef.z = 0.0f;
}
gyro.zero();
use_smoothing = true;
break;
}
case GROUND_BEHAVIOR_FWD_ONLY: {
// zero roll/pitch, but keep yaw
float r, p, y;
dcm.to_euler(&r, &p, &y);
dcm.from_euler(0.0f, 0.0f, y);
// only fwd movement
Vector3f v_bf = dcm.transposed() * velocity_ef;
v_bf.y = 0.0f;
if (v_bf.x < 0.0f) {
v_bf.x = 0.0f;
}
velocity_ef = dcm * v_bf;
if (velocity_ef.z > 0.0f) {
velocity_ef.z = 0.0f;
}
gyro.zero();
use_smoothing = true;
break;
}
case GROUND_BEHAVIOR_TAILSITTER: {
// point straight up
float r, p, y;
dcm.to_euler(&r, &p, &y);
dcm.from_euler(0.0f, radians(90), y);
// no movement
if (accel_earth.z > -1.1*GRAVITY_MSS) {
velocity_ef.zero();
}
gyro.zero();
use_smoothing = true;
break;
}
}
}
}
/*
update wind vector
*/
void Aircraft::update_wind(const struct sitl_input &input)
{
// wind vector in earth frame
wind_ef = Vector3f(cosf(radians(input.wind.direction))*cosf(radians(input.wind.dir_z)),
sinf(radians(input.wind.direction))*cosf(radians(input.wind.dir_z)),
sinf(radians(input.wind.dir_z))) * input.wind.speed;
const float wind_turb = input.wind.turbulence * 10.0f; // scale input.wind.turbulence to match standard deviation when using iir_coef=0.98
const float iir_coef = 0.98f; // filtering high frequencies from turbulence
if (wind_turb > 0 && !on_ground()) {
turbulence_azimuth = turbulence_azimuth + (2 * rand());
turbulence_horizontal_speed =
static_cast<float>(turbulence_horizontal_speed * iir_coef+wind_turb * rand_normal(0, 1) * (1 - iir_coef));
turbulence_vertical_speed = static_cast<float>((turbulence_vertical_speed * iir_coef) + (wind_turb * rand_normal(0, 1) * (1 - iir_coef)));
wind_ef += Vector3f(
cosf(radians(turbulence_azimuth)) * turbulence_horizontal_speed,
sinf(radians(turbulence_azimuth)) * turbulence_horizontal_speed,
turbulence_vertical_speed);
}
}
/*
calculate magnetic field intensity and orientation
*/
bool Aircraft::get_mag_field_ef(float latitude_deg, float longitude_deg, float &intensity_gauss, float &declination_deg, float &inclination_deg)
{
bool valid_input_data = true;
/* round down to nearest sampling resolution */
int32_t min_lat = static_cast<int32_t>(static_cast<int32_t>(latitude_deg / SAMPLING_RES) * SAMPLING_RES);
int32_t min_lon = static_cast<int32_t>(static_cast<int32_t>(longitude_deg / SAMPLING_RES) * SAMPLING_RES);
/* for the rare case of hitting the bounds exactly
* the rounding logic wouldn't fit, so enforce it.
*/
/* limit to table bounds - required for maxima even when table spans full globe range */
if (latitude_deg <= SAMPLING_MIN_LAT) {
min_lat = static_cast<int32_t>(SAMPLING_MIN_LAT);
valid_input_data = false;
}
if (latitude_deg >= SAMPLING_MAX_LAT) {
min_lat = static_cast<int32_t>(static_cast<int32_t>(latitude_deg / SAMPLING_RES) * SAMPLING_RES - SAMPLING_RES);
valid_input_data = false;
}
if (longitude_deg <= SAMPLING_MIN_LON) {
min_lon = static_cast<int32_t>(SAMPLING_MIN_LON);
valid_input_data = false;
}
if (longitude_deg >= SAMPLING_MAX_LON) {
min_lon = static_cast<int32_t>(static_cast<int32_t>(longitude_deg / SAMPLING_RES) * SAMPLING_RES - SAMPLING_RES);
valid_input_data = false;
}
/* find index of nearest low sampling point */
uint32_t min_lat_index = static_cast<uint32_t>((-(SAMPLING_MIN_LAT) + min_lat) / SAMPLING_RES);
uint32_t min_lon_index = static_cast<uint32_t>((-(SAMPLING_MIN_LON) + min_lon) / SAMPLING_RES);
/* calculate intensity */
float data_sw = intensity_table[min_lat_index][min_lon_index];
float data_se = intensity_table[min_lat_index][min_lon_index + 1];;
float data_ne = intensity_table[min_lat_index + 1][min_lon_index + 1];
float data_nw = intensity_table[min_lat_index + 1][min_lon_index];
/* perform bilinear interpolation on the four grid corners */
float data_min = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_se - data_sw) + data_sw;
float data_max = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_ne - data_nw) + data_nw;
intensity_gauss = ((latitude_deg - min_lat) / SAMPLING_RES) * (data_max - data_min) + data_min;
/* calculate declination */
data_sw = declination_table[min_lat_index][min_lon_index];
data_se = declination_table[min_lat_index][min_lon_index + 1];;
data_ne = declination_table[min_lat_index + 1][min_lon_index + 1];
data_nw = declination_table[min_lat_index + 1][min_lon_index];
/* perform bilinear interpolation on the four grid corners */
data_min = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_se - data_sw) + data_sw;
data_max = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_ne - data_nw) + data_nw;
declination_deg = ((latitude_deg - min_lat) / SAMPLING_RES) * (data_max - data_min) + data_min;
/* calculate inclination */
data_sw = inclination_table[min_lat_index][min_lon_index];
data_se = inclination_table[min_lat_index][min_lon_index + 1];;
data_ne = inclination_table[min_lat_index + 1][min_lon_index + 1];
data_nw = inclination_table[min_lat_index + 1][min_lon_index];
/* perform bilinear interpolation on the four grid corners */
data_min = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_se - data_sw) + data_sw;
data_max = ((longitude_deg - min_lon) / SAMPLING_RES) * (data_ne - data_nw) + data_nw;
inclination_deg = ((latitude_deg - min_lat) / SAMPLING_RES) * (data_max - data_min) + data_min;
return valid_input_data;
}
/*
smooth sensors for kinematic consistancy when we interact with the ground
*/
void Aircraft::smooth_sensors(void)
{
uint64_t now = time_now_us;
Vector3f delta_pos = position - smoothing.position;
if (smoothing.last_update_us == 0 || delta_pos.length() > 10) {
smoothing.position = position;
smoothing.rotation_b2e = dcm;
smoothing.accel_body = accel_body;
smoothing.velocity_ef = velocity_ef;
smoothing.gyro = gyro;
smoothing.last_update_us = now;
smoothing.location = location;
printf("Smoothing reset at %.3f\n", now * 1.0e-6f);
return;
}
const float delta_time = (now - smoothing.last_update_us) * 1.0e-6f;
if (delta_time < 0 || delta_time > 0.1) {
return;
}
// calculate required accel to get us to desired position and velocity in the time_constant
const float time_constant = 0.1f;
Vector3f dvel = (velocity_ef - smoothing.velocity_ef) + (delta_pos / time_constant);
Vector3f accel_e = dvel / time_constant + (dcm * accel_body + Vector3f(0.0f, 0.0f, GRAVITY_MSS));
const float accel_limit = 14 * GRAVITY_MSS;
accel_e.x = constrain_float(accel_e.x, -accel_limit, accel_limit);
accel_e.y = constrain_float(accel_e.y, -accel_limit, accel_limit);
accel_e.z = constrain_float(accel_e.z, -accel_limit, accel_limit);
smoothing.accel_body = smoothing.rotation_b2e.transposed() * (accel_e + Vector3f(0.0f, 0.0f, -GRAVITY_MSS));
// calculate rotational rate to get us to desired attitude in time constant
Quaternion desired_q, current_q, error_q;
desired_q.from_rotation_matrix(dcm);
desired_q.normalize();
current_q.from_rotation_matrix(smoothing.rotation_b2e);
current_q.normalize();
error_q = desired_q / current_q;
error_q.normalize();
Vector3f angle_differential;
error_q.to_axis_angle(angle_differential);
smoothing.gyro = gyro + angle_differential / time_constant;
float R, P, Y;
smoothing.rotation_b2e.to_euler(&R, &P, &Y);
float R2, P2, Y2;
dcm.to_euler(&R2, &P2, &Y2);
#if 0
DataFlash_Class::instance()->Log_Write("SMOO", "TimeUS,AEx,AEy,AEz,DPx,DPy,DPz,R,P,Y,R2,P2,Y2",
"Qffffffffffff",
AP_HAL::micros64(),
degrees(angle_differential.x),
degrees(angle_differential.y),
degrees(angle_differential.z),
delta_pos.x, delta_pos.y, delta_pos.z,
degrees(R), degrees(P), degrees(Y),
degrees(R2), degrees(P2), degrees(Y2));
#endif
// integrate to get new attitude
smoothing.rotation_b2e.rotate(smoothing.gyro * delta_time);
smoothing.rotation_b2e.normalize();
// integrate to get new position
smoothing.velocity_ef += accel_e * delta_time;
smoothing.position += smoothing.velocity_ef * delta_time;
smoothing.location = home;
location_offset(smoothing.location, smoothing.position.x, smoothing.position.y);
smoothing.location.alt = static_cast<int32_t>(home.alt - smoothing.position.z * 100.0f);
smoothing.last_update_us = now;
smoothing.enabled = true;
}
/*
return a filtered servo input as a value from -1 to 1
servo is assumed to be 1000 to 2000, trim at 1500
*/
float Aircraft::filtered_idx(float v, uint8_t idx)
{
if (sitl->servo_speed <= 0) {
return v;
}
const float cutoff = 1.0f / (2 * M_PI * sitl->servo_speed);
servo_filter[idx].set_cutoff_frequency(cutoff);
return servo_filter[idx].apply(v, frame_time_us * 1.0e-6f);
}
/*
return a filtered servo input as a value from -1 to 1
servo is assumed to be 1000 to 2000, trim at 1500
*/
float Aircraft::filtered_servo_angle(const struct sitl_input &input, uint8_t idx)
{
const float v = (input.servos[idx] - 1500)/500.0f;
return filtered_idx(v, idx);
}
/*
return a filtered servo input as a value from 0 to 1
servo is assumed to be 1000 to 2000
*/
float Aircraft::filtered_servo_range(const struct sitl_input &input, uint8_t idx)
{
const float v = (input.servos[idx] - 1000)/1000.0f;
return filtered_idx(v, idx);
}
// extrapolate sensors by a given delta time in seconds
void Aircraft::extrapolate_sensors(float delta_time)
{
Vector3f accel_earth = dcm * accel_body;
accel_earth.z += GRAVITY_MSS;
dcm.rotate(gyro * delta_time);
dcm.normalize();
// work out acceleration as seen by the accelerometers. It sees the kinematic
// acceleration (ie. real movement), plus gravity
accel_body = dcm.transposed() * (accel_earth + Vector3f(0,0,-GRAVITY_MSS));
// new velocity and position vectors
velocity_ef += accel_earth * delta_time;
position += velocity_ef * delta_time;
velocity_air_ef = velocity_ef + wind_ef;
velocity_air_bf = dcm.transposed() * velocity_air_ef;
}