diff --git a/conf/firmwares/subsystems/shared/nps.makefile b/conf/firmwares/subsystems/shared/nps.makefile index 6a3179b22f..7c2d005282 100644 --- a/conf/firmwares/subsystems/shared/nps.makefile +++ b/conf/firmwares/subsystems/shared/nps.makefile @@ -49,6 +49,8 @@ nps.srcs += \ $(NPSDIR)/nps_sensor_gps.c \ $(NPSDIR)/nps_sensor_airspeed.c \ $(NPSDIR)/nps_sensor_temperature.c \ + $(NPSDIR)/nps_sensor_aoa.c \ + $(NPSDIR)/nps_sensor_sideslip.c \ $(NPSDIR)/nps_electrical.c \ $(NPSDIR)/nps_atmosphere.c \ $(NPSDIR)/nps_ivy.c \ diff --git a/conf/modules/AOA_pwm.xml b/conf/modules/AOA_pwm.xml index 63797e9011..88cc2d8004 100644 --- a/conf/modules/AOA_pwm.xml +++ b/conf/modules/AOA_pwm.xml @@ -2,7 +2,15 @@ - Angle of Attack sensor using PWM input + + Angle of Attack sensor using PWM input + + Driver for a PWM based angle of attack sensor + A second sensor can be defined for the sideslip angle + It is assumed that both sensors are the same, only sensitivity, offset and direction can differ. + + Sensor example: US DIGITAL MA3-P12-125-B + @@ -12,12 +20,19 @@ + + + + + + + @@ -35,5 +50,9 @@ + + + + diff --git a/conf/modules/nps.xml b/conf/modules/nps.xml index c445e31bd2..e7b6ca0dff 100644 --- a/conf/modules/nps.xml +++ b/conf/modules/nps.xml @@ -55,6 +55,8 @@ + + diff --git a/conf/modules/wind_estimator.xml b/conf/modules/wind_estimator.xml new file mode 100644 index 0000000000..738e1e16a4 --- /dev/null +++ b/conf/modules/wind_estimator.xml @@ -0,0 +1,41 @@ + + + + + + Wind Estimator. + Using an UKF filter generated by MATLAB running in a CHibiOS thread + Requires: + - IMU for inertial data (rates and accel) + - GPS for ground speed vector + - magnetometer for true heading + - pitot for airspeed norm + - angle of attack probe (better and faster estimate of vertical component + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ diff --git a/conf/simulator/nps/nps_sensors_params_wind_estimator.h b/conf/simulator/nps/nps_sensors_params_wind_estimator.h new file mode 100644 index 0000000000..9638a758df --- /dev/null +++ b/conf/simulator/nps/nps_sensors_params_wind_estimator.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2016 Gautier Hattenberger + * + * This file is part of paparazzi. + * + * paparazzi 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 2, or (at your option) + * any later version. + * + * paparazzi 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 paparazzi; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef NPS_SENSORS_PARAMS_H +#define NPS_SENSORS_PARAMS_H + +#include "generated/airframe.h" +#include "subsystems/imu.h" + + +#define NPS_BODY_TO_IMU_PHI IMU_BODY_TO_IMU_PHI +#define NPS_BODY_TO_IMU_THETA IMU_BODY_TO_IMU_THETA +#define NPS_BODY_TO_IMU_PSI IMU_BODY_TO_IMU_PSI + +/* + * Accelerometer + */ +/* MPU60x0 has 16bit resolution */ +#define NPS_ACCEL_MIN -32767 +#define NPS_ACCEL_MAX 32767 +/* ms-2 */ +/* aka 2^10/ACCEL_X_SENS */ +#define NPS_ACCEL_SENSITIVITY_XX (IMU_ACCEL_X_SIGN * ACCEL_BFP_OF_REAL(1./IMU_ACCEL_X_SENS)) +#define NPS_ACCEL_SENSITIVITY_YY (IMU_ACCEL_Y_SIGN * ACCEL_BFP_OF_REAL(1./IMU_ACCEL_Y_SENS)) +#define NPS_ACCEL_SENSITIVITY_ZZ (IMU_ACCEL_Z_SIGN * ACCEL_BFP_OF_REAL(1./IMU_ACCEL_Z_SENS)) + +#define NPS_ACCEL_NEUTRAL_X IMU_ACCEL_X_NEUTRAL +#define NPS_ACCEL_NEUTRAL_Y IMU_ACCEL_Y_NEUTRAL +#define NPS_ACCEL_NEUTRAL_Z IMU_ACCEL_Z_NEUTRAL +/* m2s-4 */ +#define NPS_ACCEL_NOISE_STD_DEV_X 5.e-2 +#define NPS_ACCEL_NOISE_STD_DEV_Y 5.e-2 +#define NPS_ACCEL_NOISE_STD_DEV_Z 5.e-2 +/* ms-2 */ +#define NPS_ACCEL_BIAS_X 0 +#define NPS_ACCEL_BIAS_Y 0 +#define NPS_ACCEL_BIAS_Z 0 +/* s */ +#ifndef NPS_ACCEL_DT +#define NPS_ACCEL_DT (1./125.) +#endif + + + +/* + * Gyrometer + */ +/* MPU60x0 has 16 bit resolution */ +#define NPS_GYRO_MIN -32767 +#define NPS_GYRO_MAX 32767 + +/* 2^12/GYRO_X_SENS */ +#define NPS_GYRO_SENSITIVITY_PP (IMU_GYRO_P_SIGN * RATE_BFP_OF_REAL(1./IMU_GYRO_P_SENS)) +#define NPS_GYRO_SENSITIVITY_QQ (IMU_GYRO_Q_SIGN * RATE_BFP_OF_REAL(1./IMU_GYRO_Q_SENS)) +#define NPS_GYRO_SENSITIVITY_RR (IMU_GYRO_R_SIGN * RATE_BFP_OF_REAL(1./IMU_GYRO_R_SENS)) + +#define NPS_GYRO_NEUTRAL_P IMU_GYRO_P_NEUTRAL +#define NPS_GYRO_NEUTRAL_Q IMU_GYRO_Q_NEUTRAL +#define NPS_GYRO_NEUTRAL_R IMU_GYRO_R_NEUTRAL + +#define NPS_GYRO_NOISE_STD_DEV_P RadOfDeg(0.) +#define NPS_GYRO_NOISE_STD_DEV_Q RadOfDeg(0.) +#define NPS_GYRO_NOISE_STD_DEV_R RadOfDeg(0.) + +#define NPS_GYRO_BIAS_INITIAL_P RadOfDeg( 0.0) +#define NPS_GYRO_BIAS_INITIAL_Q RadOfDeg( 0.0) +#define NPS_GYRO_BIAS_INITIAL_R RadOfDeg( 0.0) + +#define NPS_GYRO_BIAS_RANDOM_WALK_STD_DEV_P RadOfDeg(0.5) +#define NPS_GYRO_BIAS_RANDOM_WALK_STD_DEV_Q RadOfDeg(0.5) +#define NPS_GYRO_BIAS_RANDOM_WALK_STD_DEV_R RadOfDeg(0.5) +/* s */ +#ifndef NPS_GYRO_DT +#define NPS_GYRO_DT (1./125.) +#endif + + + +/* + * Magnetometer + */ + /* HMC5843 has 12 bit resolution */ +#define NPS_MAG_MIN -2047 +#define NPS_MAG_MAX 2047 + +#define NPS_MAG_IMU_TO_SENSOR_PHI 0. +#define NPS_MAG_IMU_TO_SENSOR_THETA 0. +#define NPS_MAG_IMU_TO_SENSOR_PSI 0. + +#define NPS_MAG_SENSITIVITY_XX (IMU_MAG_X_SIGN * MAG_BFP_OF_REAL(1./IMU_MAG_X_SENS)) +#define NPS_MAG_SENSITIVITY_YY (IMU_MAG_Y_SIGN * MAG_BFP_OF_REAL(1./IMU_MAG_Y_SENS)) +#define NPS_MAG_SENSITIVITY_ZZ (IMU_MAG_Z_SIGN * MAG_BFP_OF_REAL(1./IMU_MAG_Z_SENS)) + +#define NPS_MAG_NEUTRAL_X IMU_MAG_X_NEUTRAL +#define NPS_MAG_NEUTRAL_Y IMU_MAG_Y_NEUTRAL +#define NPS_MAG_NEUTRAL_Z IMU_MAG_Z_NEUTRAL + +#define NPS_MAG_NOISE_STD_DEV_X 2e-3 +#define NPS_MAG_NOISE_STD_DEV_Y 2e-3 +#define NPS_MAG_NOISE_STD_DEV_Z 2e-3 + +#define NPS_MAG_DT (1./60.) + + +/* + * Barometer + */ +/* m */ +/* aka 2^8/INS_BARO_SENS */ +#define NPS_BARO_QNH 1013.25 +#define NPS_BARO_SENSITIVITY 4.0 +#define NPS_BARO_DT (1./5.) +#define NPS_BARO_NOISE_STD_DEV 5.e-2 + +/* + * GPS + */ + +#ifndef GPS_PERFECT +#define GPS_PERFECT 1 +#endif + +#if GPS_PERFECT + +#define NPS_GPS_SPEED_NOISE_STD_DEV 0. +#define NPS_GPS_SPEED_LATENCY 0. +#define NPS_GPS_POS_NOISE_STD_DEV 0.001 +#define NPS_GPS_POS_BIAS_INITIAL_X 0. +#define NPS_GPS_POS_BIAS_INITIAL_Y 0. +#define NPS_GPS_POS_BIAS_INITIAL_Z 0. +#define NPS_GPS_POS_BIAS_RANDOM_WALK_STD_DEV_X 0. +#define NPS_GPS_POS_BIAS_RANDOM_WALK_STD_DEV_Y 0. +#define NPS_GPS_POS_BIAS_RANDOM_WALK_STD_DEV_Z 0. +#define NPS_GPS_POS_LATENCY 0. + +#else + +#define NPS_GPS_SPEED_NOISE_STD_DEV 0.5 +#define NPS_GPS_SPEED_LATENCY 0.2 +#define NPS_GPS_POS_NOISE_STD_DEV 2 +#define NPS_GPS_POS_BIAS_INITIAL_X 0e-1 +#define NPS_GPS_POS_BIAS_INITIAL_Y -0e-1 +#define NPS_GPS_POS_BIAS_INITIAL_Z -0e-1 +#define NPS_GPS_POS_BIAS_RANDOM_WALK_STD_DEV_X 1e-3 +#define NPS_GPS_POS_BIAS_RANDOM_WALK_STD_DEV_Y 1e-3 +#define NPS_GPS_POS_BIAS_RANDOM_WALK_STD_DEV_Z 1e-3 +#define NPS_GPS_POS_LATENCY 0.2 + +#endif /* GPS_PERFECT */ + +#define NPS_GPS_DT (1./5.) + +#endif /* NPS_SENSORS_PARAMS_H */ diff --git a/conf/telemetry/fixedwing_flight_recorder.xml b/conf/telemetry/fixedwing_flight_recorder.xml index 0f6de6cc72..cbd7da7005 100644 --- a/conf/telemetry/fixedwing_flight_recorder.xml +++ b/conf/telemetry/fixedwing_flight_recorder.xml @@ -137,6 +137,7 @@ + diff --git a/sw/airborne/boards/apogee/chibios/v1.0/board.h b/sw/airborne/boards/apogee/chibios/v1.0/board.h index 9585626f53..5814edd991 100644 --- a/sw/airborne/boards/apogee/chibios/v1.0/board.h +++ b/sw/airborne/boards/apogee/chibios/v1.0/board.h @@ -932,7 +932,7 @@ #if (USE_PWM1 && USE_PWM_INPUT2) #error "PW1 and PWM_INPUT2 are not compatible" #endif -#define PWM_INPUT2_ICU ICUD2 +#define PWM_INPUT2_ICU ICUD9 #define PWM_INPUT2_CHANNEL ICU_CHANNEL_1 #define PWM_INPUT2_GPIO_PORT GPIOA #define PWM_INPUT2_GPIO_PIN GPIO2 diff --git a/sw/airborne/boards/apogee/chibios/v1.0/mcuconf.h b/sw/airborne/boards/apogee/chibios/v1.0/mcuconf.h index d32c85afc8..a3b7ad8acb 100644 --- a/sw/airborne/boards/apogee/chibios/v1.0/mcuconf.h +++ b/sw/airborne/boards/apogee/chibios/v1.0/mcuconf.h @@ -194,11 +194,7 @@ * ICU driver system settings. */ #define STM32_ICU_USE_TIM1 TRUE -#ifdef USE_PWM_INPUT2 -#define STM32_ICU_USE_TIM2 TRUE -#else #define STM32_ICU_USE_TIM2 FALSE -#endif #define STM32_ICU_USE_TIM3 FALSE #define STM32_ICU_USE_TIM4 FALSE #define STM32_ICU_USE_TIM5 FALSE diff --git a/sw/airborne/modules/meteo/lib_ukf_wind_estimator/UKF_Wind_Estimator.c b/sw/airborne/modules/meteo/lib_ukf_wind_estimator/UKF_Wind_Estimator.c new file mode 100644 index 0000000000..ddaf173f24 --- /dev/null +++ b/sw/airborne/modules/meteo/lib_ukf_wind_estimator/UKF_Wind_Estimator.c @@ -0,0 +1,2107 @@ +/* + * File: UKF_Wind_Estimator.c + * + * Code generated for Simulink model 'UKF_Wind_Estimator'. + * + * Model version : 1.120 + * Simulink Coder version : 8.10 (R2016a) 10-Feb-2016 + * C/C++ source code generated on : Wed Nov 2 23:49:42 2016 + * + * Target selection: ert.tlc + * Embedded hardware selection: Custom Processor->Custom + * Code generation objectives: + * 1. Execution efficiency + * 2. RAM efficiency + * Validation result: Not run + */ + +#include "UKF_Wind_Estimator.h" + +/* Exported data definition */ + +/* Definition for custom storage class: Struct */ +ukf_init_type ukf_init; +ukf_params_type ukf_params; + +/* Block signals and states (auto storage) */ +DW ukf_DW; + +/* External inputs (root inport signals with auto storage) */ +ExtU ukf_U; + +/* External outputs (root outports fed by signals with auto storage) */ +ExtY ukf_Y; + +/* Forward declaration for local functions */ +static real32_T xnrm2_f(int32_T n, const real32_T x[147], int32_T ix0); +static void xgeqrf_f(real32_T A[147], real32_T tau[7]); +static void qr_e(const real32_T A[147], real32_T Q[147], real32_T R[49]); +static real32_T xnrm2(int32_T n, const real32_T x[120], int32_T ix0); +static void xgeqrf(real32_T A[120], real32_T tau[6]); +static void qr(const real32_T A[120], real32_T Q[120], real32_T R[36]); +static void mrdivide(real32_T A[42], const real32_T B_0[36]); +static void h(const real32_T x[7], const real32_T q[4], real32_T y[6]); +static void RungeKutta(const real32_T x[7], real32_T dt, const real32_T u[6], + real32_T xi[7]); + +/* Function for MATLAB Function: '/UKF_prediction' */ +static real32_T xnrm2_f(int32_T n, const real32_T x[147], int32_T ix0) +{ + real32_T y; + real32_T scale; + int32_T kend; + real32_T absxk; + real32_T t; + int32_T k; + y = 0.0F; + if (!(n < 1)) { + if (n == 1) { + y = fabsf(x[ix0 - 1]); + } else { + scale = 1.17549435E-38F; + kend = (ix0 + n) - 1; + for (k = ix0; k <= kend; k++) { + absxk = fabsf(x[k - 1]); + if (absxk > scale) { + t = scale / absxk; + y = y * t * t + 1.0F; + scale = absxk; + } else { + t = absxk / scale; + y += t * t; + } + } + + y = scale * sqrtf(y); + } + } + + return y; +} + +/* Function for MATLAB Function: '/UKF_prediction' */ +static void xgeqrf_f(real32_T A[147], real32_T tau[7]) +{ + real32_T work[7]; + int32_T i_i; + real32_T b_atmp; + real32_T xnorm; + int32_T knt; + int32_T lastv; + int32_T lastc; + int32_T ix; + real32_T b_c; + int32_T iy; + int32_T f; + int32_T g; + int32_T b_ia; + int32_T b_ix; + boolean_T exitg2; + int32_T i; + for (i = 0; i < 7; i++) { + work[i] = 0.0F; + } + + for (i = 0; i < 7; i++) { + i_i = i * 21 + i; + b_atmp = A[i_i]; + b_c = 0.0F; + xnorm = xnrm2_f(20 - i, A, i_i + 2); + if (xnorm != 0.0F) { + xnorm = hypotf(A[i_i], xnorm); + if (A[i_i] >= 0.0F) { + xnorm = -xnorm; + } + + if (fabsf(xnorm) < 9.86076132E-32F) { + knt = 0; + do { + knt++; + lastv = (i_i - i) + 21; + for (lastc = i_i + 1; lastc + 1 <= lastv; lastc++) { + A[lastc] *= 1.01412048E+31F; + } + + xnorm *= 1.01412048E+31F; + b_atmp *= 1.01412048E+31F; + } while (!(fabsf(xnorm) >= 9.86076132E-32F)); + + xnorm = hypotf(b_atmp, xnrm2_f(20 - i, A, i_i + 2)); + if (b_atmp >= 0.0F) { + xnorm = -xnorm; + } + + b_c = (xnorm - b_atmp) / xnorm; + b_atmp = 1.0F / (b_atmp - xnorm); + lastv = (i_i - i) + 21; + for (lastc = i_i + 1; lastc + 1 <= lastv; lastc++) { + A[lastc] *= b_atmp; + } + + for (lastv = 1; lastv <= knt; lastv++) { + xnorm *= 9.86076132E-32F; + } + + b_atmp = xnorm; + } else { + b_c = (xnorm - A[i_i]) / xnorm; + b_atmp = 1.0F / (A[i_i] - xnorm); + knt = (i_i - i) + 21; + for (lastv = i_i + 1; lastv + 1 <= knt; lastv++) { + A[lastv] *= b_atmp; + } + + b_atmp = xnorm; + } + } + + tau[i] = b_c; + A[i_i] = b_atmp; + if (i + 1 < 7) { + b_atmp = A[i_i]; + A[i_i] = 1.0F; + knt = (i + 1) * 21 + i; + if (tau[i] != 0.0F) { + lastv = 21 - i; + lastc = (i_i - i) + 20; + while ((lastv > 0) && (A[lastc] == 0.0F)) { + lastv--; + lastc--; + } + + lastc = 6 - i; + exitg2 = false; + while ((!exitg2) && (lastc > 0)) { + iy = ((lastc - 1) * 21 + knt) + 1; + f = iy; + do { + b_ix = 0; + if (f <= (iy + lastv) - 1) { + if (A[f - 1] != 0.0F) { + b_ix = 1; + } else { + f++; + } + } else { + lastc--; + b_ix = 2; + } + } while (b_ix == 0); + + if (b_ix == 1) { + exitg2 = true; + } + } + } else { + lastv = 0; + lastc = 0; + } + + if (lastv > 0) { + if (lastc != 0) { + for (iy = 1; iy <= lastc; iy++) { + work[iy - 1] = 0.0F; + } + + iy = 0; + f = ((lastc - 1) * 21 + knt) + 1; + for (b_ix = knt + 1; b_ix <= f; b_ix += 21) { + ix = i_i; + b_c = 0.0F; + g = (b_ix + lastv) - 1; + for (b_ia = b_ix; b_ia <= g; b_ia++) { + b_c += A[b_ia - 1] * A[ix]; + ix++; + } + + work[iy] += b_c; + iy++; + } + } + + if (!(-tau[i] == 0.0F)) { + iy = 0; + for (f = 1; f <= lastc; f++) { + if (work[iy] != 0.0F) { + b_c = work[iy] * -tau[i]; + b_ix = i_i; + ix = lastv + knt; + for (g = knt; g + 1 <= ix; g++) { + A[g] += A[b_ix] * b_c; + b_ix++; + } + } + + iy++; + knt += 21; + } + } + } + + A[i_i] = b_atmp; + } + } +} + +/* Function for MATLAB Function: '/UKF_prediction' */ +static void qr_e(const real32_T A[147], real32_T Q[147], real32_T R[49]) +{ + real32_T tau[7]; + int32_T c_i; + int32_T itau; + real32_T work[7]; + int32_T iaii; + int32_T lastv; + int32_T lastc; + int32_T ix; + real32_T c; + int32_T iy; + int32_T iac; + int32_T d; + int32_T b_ia; + int32_T jy; + boolean_T exitg2; + memcpy(&Q[0], &A[0], 147U * sizeof(real32_T)); + xgeqrf_f(Q, tau); + for (itau = 0; itau < 7; itau++) { + for (c_i = 0; c_i + 1 <= itau + 1; c_i++) { + R[c_i + 7 * itau] = Q[21 * itau + c_i]; + } + + for (c_i = itau + 1; c_i + 1 < 8; c_i++) { + R[c_i + 7 * itau] = 0.0F; + } + + work[itau] = 0.0F; + } + + itau = 6; + for (c_i = 6; c_i >= 0; c_i += -1) { + iaii = (c_i * 21 + c_i) + 1; + if (c_i + 1 < 7) { + Q[iaii - 1] = 1.0F; + if (tau[itau] != 0.0F) { + lastv = 21 - c_i; + lastc = (iaii - c_i) + 19; + while ((lastv > 0) && (Q[lastc] == 0.0F)) { + lastv--; + lastc--; + } + + lastc = 6 - c_i; + exitg2 = false; + while ((!exitg2) && (lastc > 0)) { + iy = (lastc - 1) * 21 + iaii; + jy = iy; + do { + iac = 0; + if (jy + 21 <= (iy + lastv) + 20) { + if (Q[jy + 20] != 0.0F) { + iac = 1; + } else { + jy++; + } + } else { + lastc--; + iac = 2; + } + } while (iac == 0); + + if (iac == 1) { + exitg2 = true; + } + } + } else { + lastv = 0; + lastc = 0; + } + + if (lastv > 0) { + if (lastc != 0) { + for (iy = 1; iy <= lastc; iy++) { + work[iy - 1] = 0.0F; + } + + iy = 0; + jy = ((lastc - 1) * 21 + iaii) + 21; + for (iac = iaii + 21; iac <= jy; iac += 21) { + ix = iaii; + c = 0.0F; + d = (iac + lastv) - 1; + for (b_ia = iac; b_ia <= d; b_ia++) { + c += Q[b_ia - 1] * Q[ix - 1]; + ix++; + } + + work[iy] += c; + iy++; + } + } + + if (!(-tau[itau] == 0.0F)) { + iy = iaii + 20; + jy = 0; + for (iac = 1; iac <= lastc; iac++) { + if (work[jy] != 0.0F) { + c = work[jy] * -tau[itau]; + ix = iaii; + d = lastv + iy; + for (b_ia = iy; b_ia + 1 <= d; b_ia++) { + Q[b_ia] += Q[ix - 1] * c; + ix++; + } + } + + jy++; + iy += 21; + } + } + } + } + + lastv = (iaii - c_i) + 20; + for (lastc = iaii; lastc + 1 <= lastv; lastc++) { + Q[lastc] *= -tau[itau]; + } + + Q[iaii - 1] = 1.0F - tau[itau]; + for (lastv = 1; lastv <= c_i; lastv++) { + Q[(iaii - lastv) - 1] = 0.0F; + } + + itau--; + } +} + +/* Function for MATLAB Function: '/UKF_correction' */ +static real32_T xnrm2(int32_T n, const real32_T x[120], int32_T ix0) +{ + real32_T y; + real32_T scale; + int32_T kend; + real32_T absxk; + real32_T t; + int32_T k; + y = 0.0F; + if (!(n < 1)) { + if (n == 1) { + y = fabsf(x[ix0 - 1]); + } else { + scale = 1.17549435E-38F; + kend = (ix0 + n) - 1; + for (k = ix0; k <= kend; k++) { + absxk = fabsf(x[k - 1]); + if (absxk > scale) { + t = scale / absxk; + y = y * t * t + 1.0F; + scale = absxk; + } else { + t = absxk / scale; + y += t * t; + } + } + + y = scale * sqrtf(y); + } + } + + return y; +} + +/* Function for MATLAB Function: '/UKF_correction' */ +static void xgeqrf(real32_T A[120], real32_T tau[6]) +{ + real32_T work[6]; + int32_T i_i; + real32_T b_atmp; + real32_T xnorm; + int32_T knt; + int32_T lastv; + int32_T lastc; + int32_T ix; + real32_T b_c; + int32_T iy; + int32_T f; + int32_T g; + int32_T b_ia; + int32_T b_ix; + boolean_T exitg2; + int32_T i; + for (i = 0; i < 6; i++) { + work[i] = 0.0F; + } + + for (i = 0; i < 6; i++) { + i_i = i * 20 + i; + b_atmp = A[i_i]; + b_c = 0.0F; + xnorm = xnrm2(19 - i, A, i_i + 2); + if (xnorm != 0.0F) { + xnorm = hypotf(A[i_i], xnorm); + if (A[i_i] >= 0.0F) { + xnorm = -xnorm; + } + + if (fabsf(xnorm) < 9.86076132E-32F) { + knt = 0; + do { + knt++; + lastv = (i_i - i) + 20; + for (lastc = i_i + 1; lastc + 1 <= lastv; lastc++) { + A[lastc] *= 1.01412048E+31F; + } + + xnorm *= 1.01412048E+31F; + b_atmp *= 1.01412048E+31F; + } while (!(fabsf(xnorm) >= 9.86076132E-32F)); + + xnorm = hypotf(b_atmp, xnrm2(19 - i, A, i_i + 2)); + if (b_atmp >= 0.0F) { + xnorm = -xnorm; + } + + b_c = (xnorm - b_atmp) / xnorm; + b_atmp = 1.0F / (b_atmp - xnorm); + lastv = (i_i - i) + 20; + for (lastc = i_i + 1; lastc + 1 <= lastv; lastc++) { + A[lastc] *= b_atmp; + } + + for (lastv = 1; lastv <= knt; lastv++) { + xnorm *= 9.86076132E-32F; + } + + b_atmp = xnorm; + } else { + b_c = (xnorm - A[i_i]) / xnorm; + b_atmp = 1.0F / (A[i_i] - xnorm); + knt = (i_i - i) + 20; + for (lastv = i_i + 1; lastv + 1 <= knt; lastv++) { + A[lastv] *= b_atmp; + } + + b_atmp = xnorm; + } + } + + tau[i] = b_c; + A[i_i] = b_atmp; + if (i + 1 < 6) { + b_atmp = A[i_i]; + A[i_i] = 1.0F; + knt = (i + 1) * 20 + i; + if (tau[i] != 0.0F) { + lastv = 20 - i; + lastc = (i_i - i) + 19; + while ((lastv > 0) && (A[lastc] == 0.0F)) { + lastv--; + lastc--; + } + + lastc = 5 - i; + exitg2 = false; + while ((!exitg2) && (lastc > 0)) { + iy = ((lastc - 1) * 20 + knt) + 1; + f = iy; + do { + b_ix = 0; + if (f <= (iy + lastv) - 1) { + if (A[f - 1] != 0.0F) { + b_ix = 1; + } else { + f++; + } + } else { + lastc--; + b_ix = 2; + } + } while (b_ix == 0); + + if (b_ix == 1) { + exitg2 = true; + } + } + } else { + lastv = 0; + lastc = 0; + } + + if (lastv > 0) { + if (lastc != 0) { + for (iy = 1; iy <= lastc; iy++) { + work[iy - 1] = 0.0F; + } + + iy = 0; + f = ((lastc - 1) * 20 + knt) + 1; + for (b_ix = knt + 1; b_ix <= f; b_ix += 20) { + ix = i_i; + b_c = 0.0F; + g = (b_ix + lastv) - 1; + for (b_ia = b_ix; b_ia <= g; b_ia++) { + b_c += A[b_ia - 1] * A[ix]; + ix++; + } + + work[iy] += b_c; + iy++; + } + } + + if (!(-tau[i] == 0.0F)) { + iy = 0; + for (f = 1; f <= lastc; f++) { + if (work[iy] != 0.0F) { + b_c = work[iy] * -tau[i]; + b_ix = i_i; + ix = lastv + knt; + for (g = knt; g + 1 <= ix; g++) { + A[g] += A[b_ix] * b_c; + b_ix++; + } + } + + iy++; + knt += 20; + } + } + } + + A[i_i] = b_atmp; + } + } +} + +/* Function for MATLAB Function: '/UKF_correction' */ +static void qr(const real32_T A[120], real32_T Q[120], real32_T R[36]) +{ + real32_T tau[6]; + int32_T c_i; + int32_T itau; + real32_T work[6]; + int32_T iaii; + int32_T lastv; + int32_T lastc; + int32_T ix; + real32_T c; + int32_T iy; + int32_T iac; + int32_T d; + int32_T b_ia; + int32_T jy; + boolean_T exitg2; + memcpy(&Q[0], &A[0], 120U * sizeof(real32_T)); + xgeqrf(Q, tau); + for (itau = 0; itau < 6; itau++) { + for (c_i = 0; c_i + 1 <= itau + 1; c_i++) { + R[c_i + 6 * itau] = Q[20 * itau + c_i]; + } + + for (c_i = itau + 1; c_i + 1 < 7; c_i++) { + R[c_i + 6 * itau] = 0.0F; + } + + work[itau] = 0.0F; + } + + itau = 5; + for (c_i = 5; c_i >= 0; c_i += -1) { + iaii = (c_i * 20 + c_i) + 1; + if (c_i + 1 < 6) { + Q[iaii - 1] = 1.0F; + if (tau[itau] != 0.0F) { + lastv = 20 - c_i; + lastc = (iaii - c_i) + 18; + while ((lastv > 0) && (Q[lastc] == 0.0F)) { + lastv--; + lastc--; + } + + lastc = 5 - c_i; + exitg2 = false; + while ((!exitg2) && (lastc > 0)) { + iy = (lastc - 1) * 20 + iaii; + jy = iy; + do { + iac = 0; + if (jy + 20 <= (iy + lastv) + 19) { + if (Q[jy + 19] != 0.0F) { + iac = 1; + } else { + jy++; + } + } else { + lastc--; + iac = 2; + } + } while (iac == 0); + + if (iac == 1) { + exitg2 = true; + } + } + } else { + lastv = 0; + lastc = 0; + } + + if (lastv > 0) { + if (lastc != 0) { + for (iy = 1; iy <= lastc; iy++) { + work[iy - 1] = 0.0F; + } + + iy = 0; + jy = ((lastc - 1) * 20 + iaii) + 20; + for (iac = iaii + 20; iac <= jy; iac += 20) { + ix = iaii; + c = 0.0F; + d = (iac + lastv) - 1; + for (b_ia = iac; b_ia <= d; b_ia++) { + c += Q[b_ia - 1] * Q[ix - 1]; + ix++; + } + + work[iy] += c; + iy++; + } + } + + if (!(-tau[itau] == 0.0F)) { + iy = iaii + 19; + jy = 0; + for (iac = 1; iac <= lastc; iac++) { + if (work[jy] != 0.0F) { + c = work[jy] * -tau[itau]; + ix = iaii; + d = lastv + iy; + for (b_ia = iy; b_ia + 1 <= d; b_ia++) { + Q[b_ia] += Q[ix - 1] * c; + ix++; + } + } + + jy++; + iy += 20; + } + } + } + } + + lastv = (iaii - c_i) + 19; + for (lastc = iaii; lastc + 1 <= lastv; lastc++) { + Q[lastc] *= -tau[itau]; + } + + Q[iaii - 1] = 1.0F - tau[itau]; + for (lastv = 1; lastv <= c_i; lastv++) { + Q[(iaii - lastv) - 1] = 0.0F; + } + + itau--; + } +} + +/* Function for MATLAB Function: '/main' */ +static void mrdivide(real32_T A[42], const real32_T B_0[36]) +{ + int32_T jp; + real32_T temp; + real32_T b_A[36]; + int8_T ipiv[6]; + int32_T j; + int32_T ix; + real32_T s; + int32_T c_ix; + int32_T d; + int32_T ijA; + int32_T b_jAcol; + int32_T b_kBcol; + memcpy(&b_A[0], &B_0[0], 36U * sizeof(real32_T)); + for (j = 0; j < 6; j++) { + ipiv[j] = (int8_T)(1 + j); + } + + for (j = 0; j < 5; j++) { + jp = j * 7; + b_jAcol = 0; + ix = jp; + temp = fabsf(b_A[jp]); + for (b_kBcol = 2; b_kBcol <= 6 - j; b_kBcol++) { + ix++; + s = fabsf(b_A[ix]); + if (s > temp) { + b_jAcol = b_kBcol - 1; + temp = s; + } + } + + if (b_A[jp + b_jAcol] != 0.0F) { + if (b_jAcol != 0) { + ipiv[j] = (int8_T)((j + b_jAcol) + 1); + ix = j; + b_jAcol += j; + for (b_kBcol = 0; b_kBcol < 6; b_kBcol++) { + temp = b_A[ix]; + b_A[ix] = b_A[b_jAcol]; + b_A[b_jAcol] = temp; + ix += 6; + b_jAcol += 6; + } + } + + b_jAcol = (jp - j) + 6; + for (ix = jp + 1; ix + 1 <= b_jAcol; ix++) { + b_A[ix] /= b_A[jp]; + } + } + + b_jAcol = jp; + ix = jp + 6; + for (b_kBcol = 1; b_kBcol <= 5 - j; b_kBcol++) { + temp = b_A[ix]; + if (b_A[ix] != 0.0F) { + c_ix = jp + 1; + d = (b_jAcol - j) + 12; + for (ijA = 7 + b_jAcol; ijA + 1 <= d; ijA++) { + b_A[ijA] += b_A[c_ix] * -temp; + c_ix++; + } + } + + ix += 6; + b_jAcol += 6; + } + } + + for (j = 0; j < 6; j++) { + jp = 7 * j; + b_jAcol = 6 * j; + for (ix = 1; ix <= j; ix++) { + b_kBcol = (ix - 1) * 7; + if (b_A[(ix + b_jAcol) - 1] != 0.0F) { + for (c_ix = 0; c_ix < 7; c_ix++) { + A[c_ix + jp] -= b_A[(ix + b_jAcol) - 1] * A[c_ix + b_kBcol]; + } + } + } + + temp = 1.0F / b_A[j + b_jAcol]; + for (b_jAcol = 0; b_jAcol < 7; b_jAcol++) { + A[b_jAcol + jp] *= temp; + } + } + + for (j = 5; j >= 0; j += -1) { + jp = 7 * j; + b_jAcol = 6 * j - 1; + for (ix = j + 2; ix < 7; ix++) { + b_kBcol = (ix - 1) * 7; + if (b_A[ix + b_jAcol] != 0.0F) { + for (c_ix = 0; c_ix < 7; c_ix++) { + A[c_ix + jp] -= b_A[ix + b_jAcol] * A[c_ix + b_kBcol]; + } + } + } + } + + for (j = 4; j >= 0; j += -1) { + if (j + 1 != ipiv[j]) { + jp = ipiv[j] - 1; + for (b_jAcol = 0; b_jAcol < 7; b_jAcol++) { + temp = A[7 * j + b_jAcol]; + A[b_jAcol + 7 * j] = A[7 * jp + b_jAcol]; + A[b_jAcol + 7 * jp] = temp; + } + } + } +} + +/* Function for MATLAB Function: '/UKF_correction' */ +static void h(const real32_T x[7], const real32_T q[4], real32_T y[6]) +{ + real32_T Vair; + real32_T beta; + real32_T scale; + real32_T t; + real32_T qinter_idx_0; + real32_T qinv_idx_1; + real32_T qinv_idx_2; + real32_T qinv_idx_3; + real32_T qinter_idx_2; + real32_T qinter_idx_3; + real32_T qinter_idx_1; + + /* -----------------Observation_model------------------- */ + /* description : */ + /* */ + /* Dim of the measurement vector dim(zk) = 10; */ + /* y=[y1 y2 y3 y4 y5 y6 y7 y8 y9 y10] */ + /* y=[V1 V2 V3 P1 P2 P3 B1 B2 B3 YH ] */ + /* */ + /* Inputs : */ + /* -States: x [15 x 1] */ + /* -Measures : zk [10 x 1] */ + /* Outputs : */ + /* -outputs model : y [10 x 1] */ + /* ------------------------------------------------------ */ + scale = 1.17549435E-38F; + beta = fabsf(x[0]); + if (beta > 1.17549435E-38F) { + Vair = 1.0F; + scale = beta; + } else { + t = beta / 1.17549435E-38F; + Vair = t * t; + } + + beta = fabsf(x[1]); + if (beta > scale) { + t = scale / beta; + Vair = Vair * t * t + 1.0F; + scale = beta; + } else { + t = beta / scale; + Vair += t * t; + } + + beta = fabsf(x[2]); + if (beta > scale) { + t = scale / beta; + Vair = Vair * t * t + 1.0F; + scale = beta; + } else { + t = beta / scale; + Vair += t * t; + } + + Vair = scale * sqrtf(Vair); + + /* ==> Ground speed|R0 = [u,v,w]|R0 + wind|R0 */ + /* description : */ + /* q*D*q-1 */ + scale = ((q[0] * q[0] + q[1] * q[1]) + q[2] * q[2]) + q[3] * q[3]; + t = q[0] / scale; + qinv_idx_1 = -q[1] / scale; + qinv_idx_2 = -q[2] / scale; + qinv_idx_3 = -q[3] / scale; + qinter_idx_0 = -((q[1] * x[0] + q[2] * x[1]) + q[3] * x[2]); + qinter_idx_1 = (q[2] * x[2] - q[3] * x[1]) + x[0] * q[0]; + qinter_idx_2 = (q[3] * x[0] - q[1] * x[2]) + x[1] * q[0]; + qinter_idx_3 = (q[1] * x[1] - q[2] * x[0]) + x[2] * q[0]; + + /* ==> Va = scale factor * norm([u,v,w]) */ + if (Vair > 0.0001) { + /* ==> AOA = atan(w/u) */ + scale = atan2f(x[2], x[0]); + + /* ==> Sideslip = asin(v/||Vair||) */ + beta = asinf(x[1] / Vair); + } else { + scale = 0.0F; + beta = 0.0F; + } + + y[0] = (((qinter_idx_2 * qinv_idx_3 - qinter_idx_3 * qinv_idx_2) + + qinter_idx_1 * t) + qinv_idx_1 * qinter_idx_0) + x[3]; + y[1] = (((qinter_idx_3 * qinv_idx_1 - qinter_idx_1 * qinv_idx_3) + + qinter_idx_2 * t) + qinv_idx_2 * qinter_idx_0) + x[4]; + y[2] = (((qinter_idx_1 * qinv_idx_2 - qinter_idx_2 * qinv_idx_1) + + qinter_idx_3 * t) + qinv_idx_3 * qinter_idx_0) + x[5]; + y[3] = x[6] * Vair; + y[4] = scale; + y[5] = beta; +} + +/* Function for MATLAB Function: '/UKF_prediction' */ +static void RungeKutta(const real32_T x[7], real32_T dt, const real32_T u[6], + real32_T xi[7]) +{ + real32_T k2[7]; + real32_T k3[7]; + real32_T k4[7]; + real32_T b_x[7]; + real32_T y; + int32_T i; + + /* ----------------Runge Kutta 4----------------- */ + /* description : */ + /* dt is an integration step (tk+1-tk) */ + /* h_2 is define as step/2 */ + /* */ + /* Non linear function f(x,u), results : X(:,k)=[Xk+1(0) ... Xk+1(2n+1)] */ + /* =RungeKutta(X(:,k),dt,omega,a); */ + /* -------------------Process_model--------------------- */ + /* description : */ + /* */ + /* Dim of the state vector : dim(x) = 6; */ + /* x=[x1 x2 x3 x4 x5 x6] */ + /* x=[uk vk wk uw0 vw0 ww0] */ + /* */ + /* dx=f(x,u) */ + /* dx1=f(x1,u) */ + /* dx2=f(x2,u) */ + /* ... */ + /* dx=[dx1; dx2; ...] */ + /* Inputs : */ + /* -States: x */ + /* -Command inputs : omega,a */ + /* Outputs : */ + /* -state dot : dx */ + /* ------------------------------------------------------ */ + /* dot_uvw, dot_wind_bias, dot_airspeed_scale */ + for (i = 0; i < 7; i++) { + xi[i] = 0.0F; + } + + /* a = single(rep(q, u(4:6))); */ + /* ==> dot_UVW */ + xi[0] = (u[2] * x[1] + u[3]) - u[1] * x[2]; + xi[1] = (u[0] * x[2] + u[4]) - u[2] * x[0]; + xi[2] = (u[1] * x[0] + u[5]) - u[0] * x[1]; + + /* phi = single(atan2(2*(q(1)*q(2)+q(3)*q(4)), 1-2*(q(2)^2+q(3)^2))); */ + /* theta = single(asin(2*(q(1)*q(3)-q(2)*q(4)))); */ + /* dx(1)=a(1)-9.81*sin(theta)+ omega(3)* x(2)-omega(2)*x(3); */ + /* dx(2)=a(2)+9.81*cos(theta)*sin(phi) + omega(1)* x(3)-omega(3)*x(1); */ + /* dx(3)=a(3)+9.81*cos(theta)*cos(phi) + omega(2)* x(1)-omega(1)*x(2); */ + /* ==> dot_biais */ + /* already at zero */ + /* -------------------Process_model--------------------- */ + /* description : */ + /* */ + /* Dim of the state vector : dim(x) = 6; */ + /* x=[x1 x2 x3 x4 x5 x6] */ + /* x=[uk vk wk uw0 vw0 ww0] */ + /* */ + /* dx=f(x,u) */ + /* dx1=f(x1,u) */ + /* dx2=f(x2,u) */ + /* ... */ + /* dx=[dx1; dx2; ...] */ + /* Inputs : */ + /* -States: x */ + /* -Command inputs : omega,a */ + /* Outputs : */ + /* -state dot : dx */ + /* ------------------------------------------------------ */ + /* dot_uvw, dot_wind_bias, dot_airspeed_scale */ + for (i = 0; i < 7; i++) { + b_x[i] = xi[i] / 2.0F * dt + x[i]; + k2[i] = 0.0F; + } + + /* a = single(rep(q, u(4:6))); */ + /* ==> dot_UVW */ + k2[0] = (u[2] * b_x[1] + u[3]) - u[1] * b_x[2]; + k2[1] = (u[0] * b_x[2] + u[4]) - u[2] * b_x[0]; + k2[2] = (u[1] * b_x[0] + u[5]) - u[0] * b_x[1]; + + /* phi = single(atan2(2*(q(1)*q(2)+q(3)*q(4)), 1-2*(q(2)^2+q(3)^2))); */ + /* theta = single(asin(2*(q(1)*q(3)-q(2)*q(4)))); */ + /* dx(1)=a(1)-9.81*sin(theta)+ omega(3)* x(2)-omega(2)*x(3); */ + /* dx(2)=a(2)+9.81*cos(theta)*sin(phi) + omega(1)* x(3)-omega(3)*x(1); */ + /* dx(3)=a(3)+9.81*cos(theta)*cos(phi) + omega(2)* x(1)-omega(1)*x(2); */ + /* ==> dot_biais */ + /* already at zero */ + /* -------------------Process_model--------------------- */ + /* description : */ + /* */ + /* Dim of the state vector : dim(x) = 6; */ + /* x=[x1 x2 x3 x4 x5 x6] */ + /* x=[uk vk wk uw0 vw0 ww0] */ + /* */ + /* dx=f(x,u) */ + /* dx1=f(x1,u) */ + /* dx2=f(x2,u) */ + /* ... */ + /* dx=[dx1; dx2; ...] */ + /* Inputs : */ + /* -States: x */ + /* -Command inputs : omega,a */ + /* Outputs : */ + /* -state dot : dx */ + /* ------------------------------------------------------ */ + /* dot_uvw, dot_wind_bias, dot_airspeed_scale */ + for (i = 0; i < 7; i++) { + b_x[i] = k2[i] / 2.0F * dt + x[i]; + k3[i] = 0.0F; + } + + /* a = single(rep(q, u(4:6))); */ + /* ==> dot_UVW */ + k3[0] = (u[2] * b_x[1] + u[3]) - u[1] * b_x[2]; + k3[1] = (u[0] * b_x[2] + u[4]) - u[2] * b_x[0]; + k3[2] = (u[1] * b_x[0] + u[5]) - u[0] * b_x[1]; + + /* phi = single(atan2(2*(q(1)*q(2)+q(3)*q(4)), 1-2*(q(2)^2+q(3)^2))); */ + /* theta = single(asin(2*(q(1)*q(3)-q(2)*q(4)))); */ + /* dx(1)=a(1)-9.81*sin(theta)+ omega(3)* x(2)-omega(2)*x(3); */ + /* dx(2)=a(2)+9.81*cos(theta)*sin(phi) + omega(1)* x(3)-omega(3)*x(1); */ + /* dx(3)=a(3)+9.81*cos(theta)*cos(phi) + omega(2)* x(1)-omega(1)*x(2); */ + /* ==> dot_biais */ + /* already at zero */ + /* -------------------Process_model--------------------- */ + /* description : */ + /* */ + /* Dim of the state vector : dim(x) = 6; */ + /* x=[x1 x2 x3 x4 x5 x6] */ + /* x=[uk vk wk uw0 vw0 ww0] */ + /* */ + /* dx=f(x,u) */ + /* dx1=f(x1,u) */ + /* dx2=f(x2,u) */ + /* ... */ + /* dx=[dx1; dx2; ...] */ + /* Inputs : */ + /* -States: x */ + /* -Command inputs : omega,a */ + /* Outputs : */ + /* -state dot : dx */ + /* ------------------------------------------------------ */ + /* dot_uvw, dot_wind_bias, dot_airspeed_scale */ + for (i = 0; i < 7; i++) { + b_x[i] = dt * k3[i] + x[i]; + k4[i] = 0.0F; + } + + /* a = single(rep(q, u(4:6))); */ + /* ==> dot_UVW */ + k4[0] = (u[2] * b_x[1] + u[3]) - u[1] * b_x[2]; + k4[1] = (u[0] * b_x[2] + u[4]) - u[2] * b_x[0]; + k4[2] = (u[1] * b_x[0] + u[5]) - u[0] * b_x[1]; + + /* phi = single(atan2(2*(q(1)*q(2)+q(3)*q(4)), 1-2*(q(2)^2+q(3)^2))); */ + /* theta = single(asin(2*(q(1)*q(3)-q(2)*q(4)))); */ + /* dx(1)=a(1)-9.81*sin(theta)+ omega(3)* x(2)-omega(2)*x(3); */ + /* dx(2)=a(2)+9.81*cos(theta)*sin(phi) + omega(1)* x(3)-omega(3)*x(1); */ + /* dx(3)=a(3)+9.81*cos(theta)*cos(phi) + omega(2)* x(1)-omega(1)*x(2); */ + /* ==> dot_biais */ + /* already at zero */ + y = dt / 6.0F; + for (i = 0; i < 7; i++) { + xi[i] = (((k2[i] + k3[i]) * 2.0F + xi[i]) + k4[i]) * y + x[i]; + } +} + +/* Model step function */ +void UKF_Wind_Estimator_step(void) +{ + real32_T lambda; + int32_T jmax; + int32_T colj; + int32_T b_j; + boolean_T exitg1; + real32_T Y[49]; + real32_T u[6]; + real32_T SQ[49]; + real32_T p[7]; + real32_T unusedU0[147]; + int32_T jj; + real32_T ajj; + int32_T ix; + int32_T iy; + int32_T b_ix; + int32_T e; + int32_T ia; + real32_T SR[36]; + real32_T unusedU0_0[120]; + real32_T K[42]; + real32_T U[42]; + real32_T d[225]; + real32_T rtb_x1[7]; + real32_T rtb_Wc[15]; + real32_T rtb_Wm[15]; + real32_T rtb_P1[49]; + real32_T rtb_X[105]; + real32_T rtb_Y[105]; + real32_T rtb_Z[90]; + real32_T rtb_z[6]; + int32_T i; + real32_T lambda_0[120]; + real32_T lambda_1[147]; + int32_T b_iy; + real32_T lambda_2[49]; + real32_T SR_0[36]; + real32_T lambda_3[36]; + real32_T rtb_Z_0[90]; + real32_T tmp[6]; + + /* MATLAB Function: '/initialization' incorporates: + * Inport: '/P0' + * Inport: '/alpha' + * Inport: '/beta' + * Inport: '/ki' + * Inport: '/x0' + * UnitDelay: '/ Delay' + * UnitDelay: '/ Delay1' + */ + /* MATLAB Function 'initialization': ':1' */ + /* ----------------Initialisation--------------------- */ + /* P=0*eye(11); */ + /* x=(linspace(0,0,11))'; */ + /* persistent Q */ + /* persistent R */ + if (!ukf_DW.P_not_empty) { + /* ':1:11' */ + /* ':1:12' */ + memcpy(&SQ[0], &(ukf_init.P0[0]), 49U * sizeof(real32_T)); + jmax = 0; + colj = 0; + b_j = 1; + exitg1 = false; + while ((!exitg1) && (b_j < 8)) { + jj = (colj + b_j) - 1; + lambda = 0.0F; + if (!(b_j - 1 < 1)) { + ix = colj; + iy = colj; + for (i = 1; i < b_j; i++) { + lambda += SQ[ix] * SQ[iy]; + ix++; + iy++; + } + } + + ajj = SQ[jj] - lambda; + if (ajj > 0.0F) { + ajj = sqrtf(ajj); + SQ[jj] = ajj; + if (b_j < 7) { + if (b_j - 1 != 0) { + b_iy = jj + 7; + ix = ((6 - b_j) * 7 + colj) + 8; + for (iy = colj + 8; iy <= ix; iy += 7) { + b_ix = colj; + lambda = 0.0F; + e = (iy + b_j) - 2; + for (ia = iy; ia <= e; ia++) { + lambda += SQ[ia - 1] * SQ[b_ix]; + b_ix++; + } + + SQ[b_iy] += -lambda; + b_iy += 7; + } + } + + lambda = 1.0F / ajj; + ix = ((6 - b_j) * 7 + jj) + 8; + for (b_iy = jj + 7; b_iy + 1 <= ix; b_iy += 7) { + SQ[b_iy] *= lambda; + } + + colj += 7; + } + + b_j++; + } else { + SQ[jj] = ajj; + jmax = b_j; + exitg1 = true; + } + } + + if (jmax == 0) { + jmax = 7; + } else { + jmax--; + } + + for (b_j = 0; b_j + 1 <= jmax; b_j++) { + for (colj = b_j + 1; colj + 1 <= jmax; colj++) { + SQ[colj + 7 * b_j] = 0.0F; + } + } + + for (jmax = 0; jmax < 7; jmax++) { + for (b_j = 0; b_j < 7; b_j++) { + rtb_P1[b_j + 7 * jmax] = SQ[7 * b_j + jmax]; + } + } + + ukf_DW.P_not_empty = true; + } else { + /* ':1:14' */ + memcpy(&rtb_P1[0], &ukf_DW.Delay_DSTATE[0], 49U * sizeof(real32_T)); + } + + if (!ukf_DW.x_not_empty) { + /* ':1:17' */ + /* ':1:18' */ + for (i = 0; i < 7; i++) { + rtb_x1[i] = ukf_init.x0[i]; + } + + ukf_DW.x_not_empty = true; + } else { + /* ':1:20' */ + for (i = 0; i < 7; i++) { + rtb_x1[i] = ukf_DW.Delay1_DSTATE[i]; + } + } + + /* %%%%%%%%%%%%%%%--PROGRAMME PRINCIPAL--%%%%%%%%%%%%%%%% */ + /* alpha=0.01; %10^-1==0 : garantis que la covariance soit semi-definit positive. */ + /* Default k=0; */ + /* beta=2; %informations sur les moments d'ordre 3 */ + /* Default beta=2 choix optimal quand la distrib est sup. gaussienne. */ + /* dt=0.02; %dt pour le Runge-Kutta */ + /* --------Calcul du lambda----------------- */ + /* ':1:40' */ + /* dimension de l'etat */ + /* ':1:42' */ + lambda = ukf_init.alpha * ukf_init.alpha * (7.0F + ukf_init.ki) - 7.0F; + + /* ':1:43' */ + /* ----------------Calcul des ponderations Wm et Wc-------------------- */ + /* ':1:47' */ + ajj = 0.5F / (7.0F + lambda); + rtb_Wm[0] = lambda / (7.0F + lambda); + for (i = 0; i < 14; i++) { + rtb_Wm[i + 1] = ajj; + } + + /* ':1:48' */ + for (i = 0; i < 15; i++) { + rtb_Wc[i] = rtb_Wm[i]; + } + + /* ':1:49' */ + rtb_Wc[0] = ((1.0F - ukf_init.alpha * ukf_init.alpha) + ukf_init.beta) + + rtb_Wm[0]; + + /* ':1:50' */ + /* ':1:51' */ + /* ':1:52' */ + lambda = sqrtf(7.0F + lambda); + + /* MATLAB Function: '/sigmas' */ + /* MATLAB Function 'sigmas': ':1' */ + /* ----------------Sigma-point--------------------------- */ + /* sigmas points et concatenation : n*(2n+1) */ + /* Inputs : */ + /* -States: x */ + /* -Covariance on state : P */ + /* -Weighting squate-root */ + /* -sigmas-point (Wm,Wc) c */ + /* Outputs : */ + /* -Sigmas-points (state) : X */ + /* ---------------------------------------------------- */ + /* Covariance with ponderation: A = c * P */ + /* ':1:15' */ + for (jmax = 0; jmax < 49; jmax++) { + SQ[jmax] = lambda * rtb_P1[jmax]; + } + + /* dimension modification on state */ + /* ':1:18' */ + /* concatenation : X=[x Y+A Y-A] */ + /* ':1:21' */ + for (jmax = 0; jmax < 7; jmax++) { + for (b_j = 0; b_j < 7; b_j++) { + Y[b_j + 7 * jmax] = rtb_x1[b_j]; + } + + rtb_X[jmax] = rtb_x1[jmax]; + } + + for (jmax = 0; jmax < 49; jmax++) { + rtb_X[jmax + 7] = Y[jmax] + SQ[jmax]; + rtb_X[jmax + 56] = Y[jmax] - SQ[jmax]; + } + + /* End of MATLAB Function: '/sigmas' */ + + /* MATLAB Function: '/UKF_prediction' incorporates: + * Inport: '/Q' + * Inport: '/accel' + * Inport: '/dt' + * Inport: '/rates' + * MATLAB Function: '/initialization' + */ + /* MATLAB Function 'UKF_prediction': ':1' */ + /* ':1:38' */ + /* ':1:31' */ + /* ----------------Prediction Step------------------------- */ + /* Sigma point and model fonction f(x,u) */ + /* Inputs : */ + /* -Sigmas-points (state) : X */ + /* -Weighting sigmas-points : Wm, Wc */ + /* -Command input : omega, a */ + /* -Dimension vector state: n */ + /* -Integration step : dt */ + /* -Dimension 2n+1 : L */ + /* -Weighting state (Wk) : Q */ + /* Outputs : */ + /* -Predicted state: y */ + /* -Sigmas_state : Y */ + /* -Covariance on state : H */ + /* ---------------------------------------------------- */ + /* ----------------Prediction------------------------- */ + /* description : */ + /* Output : SigmaX(k+1) et xmoy(k+1) */ + /* */ + /* input state size */ + /* sigma state size */ + /* ':1:26' */ + for (i = 0; i < 7; i++) { + rtb_x1[i] = 0.0F; + } + + /* ':1:27' */ + memset(&rtb_Y[0], 0, 105U * sizeof(real32_T)); + + /* ':1:29' */ + u[0] = ukf_U.rates[0]; + u[3] = ukf_U.accel[0]; + u[1] = ukf_U.rates[1]; + u[4] = ukf_U.accel[1]; + u[2] = ukf_U.rates[2]; + u[5] = ukf_U.accel[2]; + + /* ':1:30' */ + for (i = 0; i < 15; i++) { + /* ':1:30' */ + /* ':1:31' */ + RungeKutta(&rtb_X[7 * i], ukf_params.dt, u, &rtb_Y[7 * i]); + + /* non linear function */ + /* ':1:32' */ + for (jmax = 0; jmax < 7; jmax++) { + rtb_x1[jmax] += rtb_Y[7 * i + jmax] * rtb_Wm[i]; + } + + /* predicted vector on the state */ + /* y = Y(:,1); */ + /* ':1:30' */ + } + + /* ':1:36' */ + for (b_iy = 0; b_iy < 49; b_iy++) { + SQ[b_iy] = sqrtf(ukf_params.Q[b_iy]); + } + + /* square-root of R */ + /* ':1:38' */ + lambda = sqrtf(rtb_Wm[1]); + + /* ':1:40' */ + /* 33*11=>[11*22 11*11] */ + /* ':1:42' */ + for (jmax = 0; jmax < 7; jmax++) { + for (b_j = 0; b_j < 14; b_j++) { + lambda_1[b_j + 21 * jmax] = (rtb_Y[(1 + b_j) * 7 + jmax] - rtb_x1[jmax]) * + lambda; + } + } + + for (jmax = 0; jmax < 7; jmax++) { + for (b_j = 0; b_j < 7; b_j++) { + lambda_1[(b_j + 21 * jmax) + 14] = SQ[7 * b_j + jmax]; + } + } + + qr_e(lambda_1, unusedU0, SQ); + + /* ':1:44' */ + for (jmax = 0; jmax < 7; jmax++) { + p[jmax] = rtb_Y[jmax] - rtb_x1[jmax]; + } + + if (rtb_Wc[0] < 0.0F) { + /* ':1:46' */ + /* ':1:47' */ + lambda = powf(-rtb_Wc[0], 0.25F); + ajj = powf(-rtb_Wc[0], 0.25F); + for (jmax = 0; jmax < 7; jmax++) { + for (b_j = 0; b_j < 7; b_j++) { + Y[jmax + 7 * b_j] = 0.0F; + for (colj = 0; colj < 7; colj++) { + Y[jmax + 7 * b_j] += SQ[7 * jmax + colj] * SQ[7 * b_j + colj]; + } + + lambda_2[jmax + 7 * b_j] = lambda * p[jmax] * (ajj * p[b_j]); + } + } + + for (jmax = 0; jmax < 7; jmax++) { + for (b_j = 0; b_j < 7; b_j++) { + rtb_P1[b_j + 7 * jmax] = Y[7 * jmax + b_j] - lambda_2[7 * jmax + b_j]; + } + } + + b_j = 0; + i = 0; + jmax = 1; + exitg1 = false; + while ((!exitg1) && (jmax < 8)) { + colj = (i + jmax) - 1; + lambda = 0.0F; + if (!(jmax - 1 < 1)) { + jj = i; + b_iy = i; + for (ix = 1; ix < jmax; ix++) { + lambda += rtb_P1[jj] * rtb_P1[b_iy]; + jj++; + b_iy++; + } + } + + lambda = rtb_P1[colj] - lambda; + if (lambda > 0.0F) { + lambda = sqrtf(lambda); + rtb_P1[colj] = lambda; + if (jmax < 7) { + if (jmax - 1 != 0) { + jj = colj + 7; + b_iy = ((6 - jmax) * 7 + i) + 8; + for (ix = i + 8; ix <= b_iy; ix += 7) { + iy = i; + ajj = 0.0F; + b_ix = (ix + jmax) - 2; + for (e = ix; e <= b_ix; e++) { + ajj += rtb_P1[e - 1] * rtb_P1[iy]; + iy++; + } + + rtb_P1[jj] += -ajj; + jj += 7; + } + } + + lambda = 1.0F / lambda; + jj = ((6 - jmax) * 7 + colj) + 8; + for (colj += 7; colj + 1 <= jj; colj += 7) { + rtb_P1[colj] *= lambda; + } + + i += 7; + } + + jmax++; + } else { + rtb_P1[colj] = lambda; + b_j = jmax; + exitg1 = true; + } + } + + if (b_j == 0) { + i = 7; + } else { + i = b_j - 1; + } + + for (b_j = 0; b_j + 1 <= i; b_j++) { + for (jmax = b_j + 1; jmax + 1 <= i; jmax++) { + rtb_P1[jmax + 7 * b_j] = 0.0F; + } + } + } else { + /* ':1:49' */ + lambda = powf(rtb_Wc[0], 0.25F); + ajj = powf(rtb_Wc[0], 0.25F); + for (jmax = 0; jmax < 7; jmax++) { + for (b_j = 0; b_j < 7; b_j++) { + Y[jmax + 7 * b_j] = 0.0F; + for (colj = 0; colj < 7; colj++) { + Y[jmax + 7 * b_j] += SQ[7 * jmax + colj] * SQ[7 * b_j + colj]; + } + + lambda_2[jmax + 7 * b_j] = lambda * p[jmax] * (ajj * p[b_j]); + } + } + + for (jmax = 0; jmax < 7; jmax++) { + for (b_j = 0; b_j < 7; b_j++) { + rtb_P1[b_j + 7 * jmax] = Y[7 * jmax + b_j] + lambda_2[7 * jmax + b_j]; + } + } + + jmax = 0; + colj = 0; + b_j = 1; + exitg1 = false; + while ((!exitg1) && (b_j < 8)) { + jj = (colj + b_j) - 1; + lambda = 0.0F; + if (!(b_j - 1 < 1)) { + ix = colj; + iy = colj; + for (b_iy = 1; b_iy < b_j; b_iy++) { + lambda += rtb_P1[ix] * rtb_P1[iy]; + ix++; + iy++; + } + } + + ajj = rtb_P1[jj] - lambda; + if (ajj > 0.0F) { + ajj = sqrtf(ajj); + rtb_P1[jj] = ajj; + if (b_j < 7) { + if (b_j - 1 != 0) { + b_iy = jj + 7; + ix = ((6 - b_j) * 7 + colj) + 8; + for (iy = colj + 8; iy <= ix; iy += 7) { + b_ix = colj; + lambda = 0.0F; + e = (iy + b_j) - 2; + for (ia = iy; ia <= e; ia++) { + lambda += rtb_P1[ia - 1] * rtb_P1[b_ix]; + b_ix++; + } + + rtb_P1[b_iy] += -lambda; + b_iy += 7; + } + } + + lambda = 1.0F / ajj; + ix = ((6 - b_j) * 7 + jj) + 8; + for (i = jj + 7; i + 1 <= ix; i += 7) { + rtb_P1[i] *= lambda; + } + + colj += 7; + } + + b_j++; + } else { + rtb_P1[jj] = ajj; + jmax = b_j; + exitg1 = true; + } + } + + if (jmax == 0) { + jmax = 7; + } else { + jmax--; + } + + for (b_j = 0; b_j + 1 <= jmax; b_j++) { + for (colj = b_j + 1; colj + 1 <= jmax; colj++) { + rtb_P1[colj + 7 * b_j] = 0.0F; + } + } + } + + /* End of MATLAB Function: '/UKF_prediction' */ + + /* MATLAB Function: '/UKF_correction' incorporates: + * Inport: '/R' + * Inport: '/q' + * MATLAB Function: '/initialization' + */ + /* MATLAB Function 'UKF_correction': ':1' */ + /* ':1:49' */ + /* ':1:41' */ + /* ':1:38' */ + /* ----------------Correction Step------------------------- */ + /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */ + /* By Condomines Jean_Philippe - 1 may 2015- */ + /* jp.condomines@gmail.com */ + /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */ + /* Sigma point and output fonction h(x_k) */ + /* Inputs : */ + /* -Sigmas-points (state) : X */ + /* -Weighting sigmas-points : Wm, Wc */ + /* -Dimension of the measurement vector: n */ + /* -Dimension 2n+1 : L */ + /* -Weighting measures (Vk+1): R */ + /* -Measures : zk */ + /* Outputs : */ + /* -Sigmas_measures : Z */ + /* -Predicted measurement : z */ + /* -Covariance on measures : J */ + /* ---------------------------------------------------- */ + /* measurement input size */ + /* input state size */ + /* sigma state size */ + /* Zeros vector for predicted measures */ + /* ':1:29' */ + for (i = 0; i < 6; i++) { + rtb_z[i] = 0.0F; + } + + /* Zeros vector for predicted Sigmas_measures */ + /* ':1:32' */ + /* "for" loop for "measures predicted" and "sigmas_measures" predicted */ + /* ':1:35' */ + for (i = 0; i < 15; i++) { + /* ':1:35' */ + /* non linear function h(x), results : Z(:,k)=[zk+1(0) ... zk+1(2n+1)] */ + /* ':1:38' */ + h(&rtb_Y[7 * i], ukf_U.q, u); + for (jmax = 0; jmax < 6; jmax++) { + rtb_Z[jmax + 6 * i] = u[jmax]; + } + + /* Predicted measurement */ + /* ':1:41' */ + for (jmax = 0; jmax < 6; jmax++) { + rtb_z[jmax] += rtb_Z[6 * i + jmax] * rtb_Wm[i]; + } + + /* z = Z(:,1); */ + /* ':1:35' */ + } + + /* Square-root of R */ + /* ':1:46' */ + for (b_iy = 0; b_iy < 36; b_iy++) { + SR[b_iy] = sqrtf(ukf_params.R[b_iy]); + } + + /* Subtracting of "predicted Sigma measures" et "predicted measurement" with ponderation "Wc" */ + /* ':1:49' */ + lambda = sqrtf(rtb_Wm[1]); + + /* Concatenation with the square root of the matrix "R" */ + /* ':1:52' */ + /* factorization qr, result use in "J" and rest in "Temp" */ + /* Sz,k+1 = qr{[sqrt(Wc(1)) * [Zk+1(:,2:2n+1)-zk+1] sqrt(R)] */ + /* ':1:56' */ + for (jmax = 0; jmax < 6; jmax++) { + for (b_j = 0; b_j < 14; b_j++) { + lambda_0[b_j + 20 * jmax] = (rtb_Z[(1 + b_j) * 6 + jmax] - rtb_z[jmax]) * + lambda; + } + } + + for (jmax = 0; jmax < 6; jmax++) { + for (b_j = 0; b_j < 6; b_j++) { + lambda_0[(b_j + 20 * jmax) + 14] = SR[6 * b_j + jmax]; + } + } + + qr(lambda_0, unusedU0_0, SR); + + /* Subtracting the first column "zk+1(0)" with all the measurements zk+1 predicted */ + /* ':1:59' */ + for (jmax = 0; jmax < 6; jmax++) { + u[jmax] = rtb_Z[jmax] - rtb_z[jmax]; + } + + /* Test on Wc(1) for cholesky factorization : Cholupdate{Sz,k+1,p,Wc(0)} */ + if (rtb_Wc[0] < 0.0F) { + /* ':1:62' */ + /* ':1:63' */ + lambda = powf(-rtb_Wc[0], 0.25F); + ajj = powf(-rtb_Wc[0], 0.25F); + for (jmax = 0; jmax < 6; jmax++) { + for (b_j = 0; b_j < 6; b_j++) { + SR_0[jmax + 6 * b_j] = 0.0F; + for (colj = 0; colj < 6; colj++) { + SR_0[jmax + 6 * b_j] += SR[6 * jmax + colj] * SR[6 * b_j + colj]; + } + + lambda_3[jmax + 6 * b_j] = lambda * u[jmax] * (ajj * u[b_j]); + } + } + + for (jmax = 0; jmax < 6; jmax++) { + for (b_j = 0; b_j < 6; b_j++) { + SR[b_j + 6 * jmax] = SR_0[6 * jmax + b_j] - lambda_3[6 * jmax + b_j]; + } + } + + b_j = 0; + i = 0; + jmax = 1; + exitg1 = false; + while ((!exitg1) && (jmax < 7)) { + colj = (i + jmax) - 1; + lambda = 0.0F; + if (!(jmax - 1 < 1)) { + jj = i; + b_iy = i; + for (ix = 1; ix < jmax; ix++) { + lambda += SR[jj] * SR[b_iy]; + jj++; + b_iy++; + } + } + + lambda = SR[colj] - lambda; + if (lambda > 0.0F) { + lambda = sqrtf(lambda); + SR[colj] = lambda; + if (jmax < 6) { + if (jmax - 1 != 0) { + jj = colj + 6; + b_iy = ((5 - jmax) * 6 + i) + 7; + for (ix = i + 7; ix <= b_iy; ix += 6) { + iy = i; + ajj = 0.0F; + b_ix = (ix + jmax) - 2; + for (e = ix; e <= b_ix; e++) { + ajj += SR[e - 1] * SR[iy]; + iy++; + } + + SR[jj] += -ajj; + jj += 6; + } + } + + lambda = 1.0F / lambda; + jj = ((5 - jmax) * 6 + colj) + 7; + for (colj += 6; colj + 1 <= jj; colj += 6) { + SR[colj] *= lambda; + } + + i += 6; + } + + jmax++; + } else { + SR[colj] = lambda; + b_j = jmax; + exitg1 = true; + } + } + + if (b_j == 0) { + i = 6; + } else { + i = b_j - 1; + } + + for (b_j = 0; b_j + 1 <= i; b_j++) { + for (jmax = b_j + 1; jmax + 1 <= i; jmax++) { + SR[jmax + 6 * b_j] = 0.0F; + } + } + } else { + /* ':1:65' */ + lambda = powf(rtb_Wc[0], 0.25F); + ajj = powf(rtb_Wc[0], 0.25F); + for (jmax = 0; jmax < 6; jmax++) { + for (b_j = 0; b_j < 6; b_j++) { + SR_0[jmax + 6 * b_j] = 0.0F; + for (colj = 0; colj < 6; colj++) { + SR_0[jmax + 6 * b_j] += SR[6 * jmax + colj] * SR[6 * b_j + colj]; + } + + lambda_3[jmax + 6 * b_j] = lambda * u[jmax] * (ajj * u[b_j]); + } + } + + for (jmax = 0; jmax < 6; jmax++) { + for (b_j = 0; b_j < 6; b_j++) { + SR[b_j + 6 * jmax] = SR_0[6 * jmax + b_j] + lambda_3[6 * jmax + b_j]; + } + } + + jmax = 0; + colj = 0; + b_j = 1; + exitg1 = false; + while ((!exitg1) && (b_j < 7)) { + jj = (colj + b_j) - 1; + lambda = 0.0F; + if (!(b_j - 1 < 1)) { + ix = colj; + iy = colj; + for (b_iy = 1; b_iy < b_j; b_iy++) { + lambda += SR[ix] * SR[iy]; + ix++; + iy++; + } + } + + ajj = SR[jj] - lambda; + if (ajj > 0.0F) { + ajj = sqrtf(ajj); + SR[jj] = ajj; + if (b_j < 6) { + if (b_j - 1 != 0) { + b_iy = jj + 6; + ix = ((5 - b_j) * 6 + colj) + 7; + for (iy = colj + 7; iy <= ix; iy += 6) { + b_ix = colj; + lambda = 0.0F; + e = (iy + b_j) - 2; + for (ia = iy; ia <= e; ia++) { + lambda += SR[ia - 1] * SR[b_ix]; + b_ix++; + } + + SR[b_iy] += -lambda; + b_iy += 6; + } + } + + lambda = 1.0F / ajj; + ix = ((5 - b_j) * 6 + jj) + 7; + for (i = jj + 6; i + 1 <= ix; i += 6) { + SR[i] *= lambda; + } + + colj += 6; + } + + b_j++; + } else { + SR[jj] = ajj; + jmax = b_j; + exitg1 = true; + } + } + + if (jmax == 0) { + jmax = 6; + } else { + jmax--; + } + + for (b_j = 0; b_j + 1 <= jmax; b_j++) { + for (colj = b_j + 1; colj + 1 <= jmax; colj++) { + SR[colj + 6 * b_j] = 0.0F; + } + } + } + + /* Transposition to have a lower triangular matrix */ + /* ':1:69' */ + for (jmax = 0; jmax < 6; jmax++) { + for (b_j = 0; b_j < 6; b_j++) { + SR_0[b_j + 6 * jmax] = SR[6 * b_j + jmax]; + } + } + + for (jmax = 0; jmax < 6; jmax++) { + for (b_j = 0; b_j < 6; b_j++) { + SR[b_j + 6 * jmax] = SR_0[6 * jmax + b_j]; + } + } + + /* End of MATLAB Function: '/UKF_correction' */ + + /* MATLAB Function: '/main' incorporates: + * Inport: '/aoa' + * Inport: '/sideslip' + * Inport: '/va ' + * Inport: '/vk' + */ + memcpy(&Y[0], &rtb_P1[0], 49U * sizeof(real32_T)); + + /* MATLAB Function 'main': ':1' */ + /* ':1:9' */ + /* measurement input size */ + /* input state size */ + /* sigma state size */ + /* ':1:7' */ + /* ':1:9' */ + memset(&d[0], 0, 225U * sizeof(real32_T)); + + /* Kalman gain : (Pxz/Sz')/Sz */ + /* Caution "/" is least squares root for "Ax = b" */ + /* ':1:13' */ + for (b_j = 0; b_j < 15; b_j++) { + d[b_j + 15 * b_j] = rtb_Wc[b_j]; + for (jmax = 0; jmax < 7; jmax++) { + rtb_X[jmax + 7 * b_j] = rtb_Y[7 * b_j + jmax] - rtb_x1[jmax]; + } + } + + for (jmax = 0; jmax < 7; jmax++) { + for (b_j = 0; b_j < 15; b_j++) { + rtb_Y[jmax + 7 * b_j] = 0.0F; + for (colj = 0; colj < 15; colj++) { + rtb_Y[jmax + 7 * b_j] += rtb_X[7 * colj + jmax] * d[15 * b_j + colj]; + } + } + } + + for (jmax = 0; jmax < 6; jmax++) { + for (b_j = 0; b_j < 15; b_j++) { + rtb_Z_0[b_j + 15 * jmax] = rtb_Z[6 * b_j + jmax] - rtb_z[jmax]; + } + } + + for (jmax = 0; jmax < 7; jmax++) { + for (b_j = 0; b_j < 6; b_j++) { + K[jmax + 7 * b_j] = 0.0F; + for (colj = 0; colj < 15; colj++) { + K[jmax + 7 * b_j] += rtb_Y[7 * colj + jmax] * rtb_Z_0[15 * b_j + colj]; + } + } + } + + for (jmax = 0; jmax < 6; jmax++) { + for (b_j = 0; b_j < 6; b_j++) { + SR_0[b_j + 6 * jmax] = SR[6 * b_j + jmax]; + } + } + + mrdivide(K, SR_0); + mrdivide(K, SR); + + /* Predicted state with Kalman gain "K" : x = xpred + K * (zk - z) */ + /* ':1:16' */ + /* Quaternion normalisation */ + /* x(1:4)=normalQ(x(1:4)'); */ + /* Cholesky factorization : U = K * Sz */ + /* ':1:22' */ + for (jmax = 0; jmax < 7; jmax++) { + for (b_j = 0; b_j < 6; b_j++) { + U[jmax + 7 * b_j] = 0.0F; + for (colj = 0; colj < 6; colj++) { + U[jmax + 7 * b_j] += K[7 * colj + jmax] * SR[6 * b_j + colj]; + } + } + } + + /* "for" loop for cholesky factorization */ + /* ':1:25' */ + /* "Sk" in "P" and Transpose of "P" */ + /* ':1:30' */ + /* SqrtP = sqrt(diag(Sk*Sk')); */ + tmp[0] = ukf_U.vk[0]; + tmp[1] = ukf_U.vk[1]; + tmp[2] = ukf_U.vk[2]; + tmp[3] = ukf_U.va; + tmp[4] = ukf_U.aoa; + tmp[5] = ukf_U.sideslip; + for (i = 0; i < 6; i++) { + /* ':1:25' */ + /* ':1:26' */ + for (jmax = 0; jmax < 7; jmax++) { + for (b_j = 0; b_j < 7; b_j++) { + rtb_P1[jmax + 7 * b_j] = 0.0F; + for (colj = 0; colj < 7; colj++) { + rtb_P1[jmax + 7 * b_j] += Y[7 * jmax + colj] * Y[7 * b_j + colj]; + } + + lambda_2[jmax + 7 * b_j] = U[7 * i + jmax] * U[7 * i + b_j]; + } + } + + for (jmax = 0; jmax < 7; jmax++) { + for (b_j = 0; b_j < 7; b_j++) { + SQ[b_j + 7 * jmax] = rtb_P1[7 * jmax + b_j] - lambda_2[7 * jmax + b_j]; + } + } + + jmax = 0; + colj = 0; + b_j = 1; + exitg1 = false; + while ((!exitg1) && (b_j < 8)) { + jj = (colj + b_j) - 1; + lambda = 0.0F; + if (!(b_j - 1 < 1)) { + ix = colj; + iy = colj; + for (b_iy = 1; b_iy < b_j; b_iy++) { + lambda += SQ[ix] * SQ[iy]; + ix++; + iy++; + } + } + + ajj = SQ[jj] - lambda; + if (ajj > 0.0F) { + ajj = sqrtf(ajj); + SQ[jj] = ajj; + if (b_j < 7) { + if (b_j - 1 != 0) { + b_iy = jj + 7; + ix = ((6 - b_j) * 7 + colj) + 8; + for (iy = colj + 8; iy <= ix; iy += 7) { + b_ix = colj; + lambda = 0.0F; + e = (iy + b_j) - 2; + for (ia = iy; ia <= e; ia++) { + lambda += SQ[ia - 1] * SQ[b_ix]; + b_ix++; + } + + SQ[b_iy] += -lambda; + b_iy += 7; + } + } + + lambda = 1.0F / ajj; + ix = ((6 - b_j) * 7 + jj) + 8; + for (b_iy = jj + 7; b_iy + 1 <= ix; b_iy += 7) { + SQ[b_iy] *= lambda; + } + + colj += 7; + } + + b_j++; + } else { + SQ[jj] = ajj; + jmax = b_j; + exitg1 = true; + } + } + + if (jmax == 0) { + jmax = 7; + } else { + jmax--; + } + + for (b_j = 0; b_j + 1 <= jmax; b_j++) { + for (colj = b_j + 1; colj + 1 <= jmax; colj++) { + SQ[colj + 7 * b_j] = 0.0F; + } + } + + memcpy(&Y[0], &SQ[0], 49U * sizeof(real32_T)); + + /* ':1:25' */ + u[i] = tmp[i] - rtb_z[i]; + } + + for (i = 0; i < 7; i++) { + lambda = 0.0F; + for (jmax = 0; jmax < 6; jmax++) { + lambda += K[7 * jmax + i] * u[jmax]; + } + + p[i] = rtb_x1[i] + lambda; + for (jmax = 0; jmax < 7; jmax++) { + rtb_P1[jmax + 7 * i] = Y[7 * jmax + i]; + } + + /* Outport: '/xout' */ + ukf_Y.xout[i] = p[i]; + } + + /* End of MATLAB Function: '/main' */ + + /* Outport: '/Pout' */ + memcpy(&ukf_Y.Pout[0], &rtb_P1[0], 49U * sizeof(real32_T)); + + /* Update for UnitDelay: '/ Delay1' */ + for (i = 0; i < 7; i++) { + ukf_DW.Delay1_DSTATE[i] = p[i]; + } + + /* End of Update for UnitDelay: '/ Delay1' */ + + /* Update for UnitDelay: '/ Delay' */ + memcpy(&ukf_DW.Delay_DSTATE[0], &rtb_P1[0], 49U * sizeof(real32_T)); +} + +/* Model initialize function */ +void UKF_Wind_Estimator_initialize(void) +{ + /* (no initialization code required) */ +} + +/* + * File trailer for generated code. + * + * [EOF] + */ diff --git a/sw/airborne/modules/meteo/lib_ukf_wind_estimator/UKF_Wind_Estimator.h b/sw/airborne/modules/meteo/lib_ukf_wind_estimator/UKF_Wind_Estimator.h new file mode 100644 index 0000000000..0246a03507 --- /dev/null +++ b/sw/airborne/modules/meteo/lib_ukf_wind_estimator/UKF_Wind_Estimator.h @@ -0,0 +1,115 @@ +/* + * File: UKF_Wind_Estimator.h + * + * Code generated for Simulink model 'UKF_Wind_Estimator'. + * + * Model version : 1.120 + * Simulink Coder version : 8.10 (R2016a) 10-Feb-2016 + * C/C++ source code generated on : Wed Nov 2 23:49:42 2016 + * + * Target selection: ert.tlc + * Embedded hardware selection: Custom Processor->Custom + * Code generation objectives: + * 1. Execution efficiency + * 2. RAM efficiency + * Validation result: Not run + */ + +#ifndef RTW_HEADER_UKF_Wind_Estimator_h_ +#define RTW_HEADER_UKF_Wind_Estimator_h_ +#include +#include +#ifndef UKF_Wind_Estimator_COMMON_INCLUDES_ +# define UKF_Wind_Estimator_COMMON_INCLUDES_ +#include "rtwtypes.h" +#endif /* UKF_Wind_Estimator_COMMON_INCLUDES_ */ + +/* Macros for accessing real-time model data structure */ + +/* Block signals and states (auto storage) for system '' */ +typedef struct { + real32_T Delay1_DSTATE[7]; /* '/ Delay1' */ + real32_T Delay_DSTATE[49]; /* '/ Delay' */ + boolean_T x_not_empty; /* '/initialization' */ + boolean_T P_not_empty; /* '/initialization' */ +} DW; + +/* External inputs (root inport signals with auto storage) */ +typedef struct { + real32_T rates[3]; /* '/rates' */ + real32_T accel[3]; /* '/accel' */ + real32_T q[4]; /* '/q' */ + real32_T vk[3]; /* '/vk' */ + real32_T va; /* '/va ' */ + real32_T aoa; /* '/aoa' */ + real32_T sideslip; /* '/sideslip' */ +} ExtU; + +/* External outputs (root outports fed by signals with auto storage) */ +typedef struct { + real32_T xout[7]; /* '/xout' */ + real32_T Pout[49]; /* '/Pout' */ +} ExtY; + +/* Type definition for custom storage class: Struct */ +typedef struct ukf_init_tag { + real32_T x0[7]; + real32_T P0[49]; + real32_T ki; + real32_T alpha; + real32_T beta; +} ukf_init_type; + +typedef struct ukf_params_tag { + real32_T Q[49]; + real32_T R[36]; + real32_T dt; +} ukf_params_type; + +/* Block signals and states (auto storage) */ +extern DW ukf_DW; + +/* External inputs (root inport signals with auto storage) */ +extern ExtU ukf_U; + +/* External outputs (root outports fed by signals with auto storage) */ +extern ExtY ukf_Y; + +/* Model entry point functions */ +extern void UKF_Wind_Estimator_initialize(void); +extern void UKF_Wind_Estimator_step(void); + +/* Exported data declaration */ + +/* Declaration for custom storage class: Struct */ +extern ukf_init_type ukf_init; +extern ukf_params_type ukf_params; + +/*- + * The generated code includes comments that allow you to trace directly + * back to the appropriate location in the model. The basic format + * is /block_name, where system is the system number (uniquely + * assigned by Simulink) and block_name is the name of the block. + * + * Use the MATLAB hilite_system command to trace the generated code back + * to the model. For example, + * + * hilite_system('') - opens system 3 + * hilite_system('/Kp') - opens and selects block Kp which resides in S3 + * + * Here is the system hierarchy for this model + * + * '' : 'UKF_Wind_Estimator' + * '' : 'UKF_Wind_Estimator/UKF_correction' + * '' : 'UKF_Wind_Estimator/UKF_prediction' + * '' : 'UKF_Wind_Estimator/initialization' + * '' : 'UKF_Wind_Estimator/main' + * '' : 'UKF_Wind_Estimator/sigmas' + */ +#endif /* RTW_HEADER_UKF_Wind_Estimator_h_ */ + +/* + * File trailer for generated code. + * + * [EOF] + */ diff --git a/sw/airborne/modules/meteo/lib_ukf_wind_estimator/rtwtypes.h b/sw/airborne/modules/meteo/lib_ukf_wind_estimator/rtwtypes.h new file mode 100644 index 0000000000..a018b996e8 --- /dev/null +++ b/sw/airborne/modules/meteo/lib_ukf_wind_estimator/rtwtypes.h @@ -0,0 +1,96 @@ +/* + * File: rtwtypes.h + * + * Code generated for Simulink model 'UKF_Wind_Estimator'. + * + * Model version : 1.120 + * Simulink Coder version : 8.10 (R2016a) 10-Feb-2016 + * C/C++ source code generated on : Wed Nov 2 23:49:42 2016 + * + * Target selection: ert.tlc + * Embedded hardware selection: Custom Processor->Custom + * Code generation objectives: + * 1. Execution efficiency + * 2. RAM efficiency + * Validation result: Not run + */ + +#ifndef RTWTYPES_H +#define RTWTYPES_H + +/* Logical type definitions */ +#if (!defined(__cplusplus)) +# ifndef false +# define false (0U) +# endif + +# ifndef true +# define true (1U) +# endif +#endif + +/*=======================================================================* + * Target hardware information + * Device type: Custom Processor->Custom + * Number of bits: char: 8 short: 16 int: 32 + * long: 32 + * native word size: 32 + * Byte ordering: LittleEndian + * Signed integer division rounds to: Zero + * Shift right on a signed integer as arithmetic shift: on + *=======================================================================*/ + +/*=======================================================================* + * Fixed width word size data types: * + * int8_T, int16_T, int32_T - signed 8, 16, or 32 bit integers * + * uint8_T, uint16_T, uint32_T - unsigned 8, 16, or 32 bit integers * + * real32_T, real64_T - 32 and 64 bit floating point numbers * + *=======================================================================*/ +typedef signed char int8_T; +typedef unsigned char uint8_T; +typedef short int16_T; +typedef unsigned short uint16_T; +typedef int int32_T; +typedef unsigned int uint32_T; +typedef float real32_T; +typedef double real64_T; + +/*===========================================================================* + * Generic type definitions: boolean_T, char_T, byte_T, int_T, uint_T, * + * real_T, time_T, ulong_T. * + *===========================================================================*/ +typedef double real_T; +typedef double time_T; +typedef unsigned char boolean_T; +typedef int int_T; +typedef unsigned int uint_T; +typedef unsigned long ulong_T; +typedef char char_T; +typedef unsigned char uchar_T; +typedef char_T byte_T; + +/*=======================================================================* + * Min and Max: * + * int8_T, int16_T, int32_T - signed 8, 16, or 32 bit integers * + * uint8_T, uint16_T, uint32_T - unsigned 8, 16, or 32 bit integers * + *=======================================================================*/ +#define MAX_int8_T ((int8_T)(127)) +#define MIN_int8_T ((int8_T)(-128)) +#define MAX_uint8_T ((uint8_T)(255U)) +#define MAX_int16_T ((int16_T)(32767)) +#define MIN_int16_T ((int16_T)(-32768)) +#define MAX_uint16_T ((uint16_T)(65535U)) +#define MAX_int32_T ((int32_T)(2147483647)) +#define MIN_int32_T ((int32_T)(-2147483647-1)) +#define MAX_uint32_T ((uint32_T)(0xFFFFFFFFU)) + +/* Block D-Work pointer type */ +typedef void * pointer_T; + +#endif /* RTWTYPES_H */ + +/* + * File trailer for generated code. + * + * [EOF] + */ diff --git a/sw/airborne/modules/meteo/wind_estimator.c b/sw/airborne/modules/meteo/wind_estimator.c new file mode 100644 index 0000000000..238a8ad68d --- /dev/null +++ b/sw/airborne/modules/meteo/wind_estimator.c @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2016 Johan Maurin, Gautier Hattenberger + * + * This file is part of paparazzi. + * + * paparazzi 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 2, or (at your option) + * any later version. + * + * paparazzi 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 paparazzi; see the file COPYING. If not, see + * . + */ + +/** + * @file "modules/meteo/wind_estimator.c" + * @author Maurin + * + * Wind Estimator based on generated library from Matlab + */ + +#include "modules/meteo/wind_estimator.h" +#include "modules/meteo/lib_ukf_wind_estimator/UKF_Wind_Estimator.h" +#include "mcu_periph/sys_time.h" +#include "math/pprz_algebra_float.h" +#include "math/pprz_geodetic_float.h" +#include "generated/modules.h" +#include "state.h" +#include +#ifndef SITL // no chibios threads in sim +#if !USE_CHIBIOS_RTOS +#error Only Chibios is supported +#endif +#include +#include +#endif + +/** + * Default parameters + */ +#ifndef WE_UKF_KI +#define WE_UKF_KI 0.f // >= 0 to ensure that covariance is positive semi-definite +#endif +#ifndef WE_UKF_ALPHA +#define WE_UKF_ALPHA 0.5f // sigma point dispersion +#endif +#ifndef WE_UKF_BETA +#define WE_UKF_BETA 2.f // 2 is the best choice when distribution is gaussian +#endif +#ifndef WE_UKF_P0 +#define WE_UKF_P0 0.2f // initial covariance diagonal element +#endif +#ifndef WE_UKF_R_GS +#define WE_UKF_R_GS 0.5f // ground speed measurement confidence +#endif +#ifndef WE_UKF_R_VA +#define WE_UKF_R_VA 0.5f // airspeed measurement confidence +#endif +#ifndef WE_UKF_R_AOA +#define WE_UKF_R_AOA 0.002f // angle of attack measurement confidence +#endif +#ifndef WE_UKF_R_SSA +#define WE_UKF_R_SSA 0.002f // sideslip angle measurement confidence +#endif +#ifndef WE_UKF_Q_VA +#define WE_UKF_Q_VA 0.1f // airspeed model confidence +#endif +#ifndef WE_UKF_Q_VA_SCALE +#define WE_UKF_Q_VA_SCALE 0.0001f // airspeed scale factor model confidence +#endif +#ifndef WE_UKF_Q_WIND +#define WE_UKF_Q_WIND 0.001f // wind model confidence +#endif + +#ifndef SEND_WIND_ESTIMATOR +#define SEND_WIND_ESTIMATOR TRUE +#endif + +#include "subsystems/datalink/downlink.h" + +static void send_wind_estimator(struct transport_tx *trans, struct link_device *dev) +{ + uint8_t flags = 7; // send all data + float upwind = -wind_estimator.wind.z; + float airspeed = float_vect3_norm(&wind_estimator.airspeed); + pprz_msg_send_WIND_INFO_RET(trans, dev, AC_ID, + &flags, + &wind_estimator.wind.y, // east + &wind_estimator.wind.x, // north + &upwind, + &airspeed); +} + +#if PERIODIC_TELEMETRY +#include "subsystems/datalink/telemetry.h" +#endif + +#ifndef LOG_WIND_ESTIMATOR +#define LOG_WIND_ESTIMATOR FALSE +#endif + +#if LOG_WIND_ESTIMATOR +#ifndef SITL +#include "modules/loggers/sdlog_chibios.h" +#define PrintLog sdLogWriteLog +#define LogFileIsOpen() (pprzLogFile != -1) +#else // SITL: print in a file +#include +#define PrintLog fprintf +#define LogFileIsOpen() (pprzLogFile != NULL) +static FILE* pprzLogFile = NULL; +#endif +static bool log_we_started; +#endif + +// matrix element +#define MAT_EL(_m, _l, _c, _n) _m[_l + _c * _n] + +// wind estimator public structure +struct WindEstimator wind_estimator; + +// local variables +static uint32_t time_step_before; // last periodic time + +/* Thread declaration + * MATLAB UKF is using at least 6.6KB of stack + */ +#ifndef SITL +static THD_WORKING_AREA(wa_thd_windestimation, 8 * 1024); +static __attribute__((noreturn)) void thd_windestimate(void *arg); + +static MUTEX_DECL(we_ukf_mtx); // mutex for data acces protection +static SEMAPHORE_DECL(we_ukf_sem, 0); // semaaphore for sending signal +#endif + +/*----------------init_calculator---------------------*/ +/* Init fonction to init different type of variable */ +/* for the calculator before to calculate */ +/*----------------------------------------------------*/ +void init_calculator(void) +{ + UKF_Wind_Estimator_initialize(); + + // FIXME would be better to force Matlab to do this in initialize function + // zero input vector + memset(&ukf_U, 0, sizeof(ExtU)); + // zero output vector + memset(&ukf_Y, 0, sizeof(ExtY)); + // zero internal structure + memset(&ukf_DW, 0, sizeof(DW)); + // zero init structure + memset(&ukf_init, 0, sizeof(ukf_init_type)); + // zero params structure + memset(&ukf_params, 0, sizeof(ukf_params_type)); + + ukf_init.x0[6] = 1.0f; // initial airspeed scale factor + + MAT_EL(ukf_init.P0, 0, 0, 7) = WE_UKF_P0; + MAT_EL(ukf_init.P0, 1, 1, 7) = WE_UKF_P0; + MAT_EL(ukf_init.P0, 2, 2, 7) = WE_UKF_P0; + MAT_EL(ukf_init.P0, 3, 3, 7) = WE_UKF_P0; + MAT_EL(ukf_init.P0, 4, 4, 7) = WE_UKF_P0; + MAT_EL(ukf_init.P0, 5, 5, 7) = WE_UKF_P0; + MAT_EL(ukf_init.P0, 6, 6, 7) = WE_UKF_P0; + + MAT_EL(ukf_params.R, 0, 0, 6) = powf(WE_UKF_R_GS, 2); + MAT_EL(ukf_params.R, 1, 1, 6) = powf(WE_UKF_R_GS, 2); + MAT_EL(ukf_params.R, 2, 2, 6) = powf(WE_UKF_R_GS, 2); + MAT_EL(ukf_params.R, 3, 3, 6) = powf(WE_UKF_R_VA, 2); + MAT_EL(ukf_params.R, 4, 4, 6) = powf(WE_UKF_R_AOA, 2); + MAT_EL(ukf_params.R, 5, 5, 6) = powf(WE_UKF_R_SSA, 2); + + MAT_EL(ukf_params.Q, 0, 0, 7) = powf(WE_UKF_Q_VA, 2); + MAT_EL(ukf_params.Q, 1, 1, 7) = powf(WE_UKF_Q_VA, 2); + MAT_EL(ukf_params.Q, 2, 2, 7) = powf(WE_UKF_Q_VA, 2); + MAT_EL(ukf_params.Q, 3, 3, 7) = powf(WE_UKF_Q_WIND, 2); + MAT_EL(ukf_params.Q, 4, 4, 7) = powf(WE_UKF_Q_WIND, 2); + MAT_EL(ukf_params.Q, 5, 5, 7) = powf(WE_UKF_Q_WIND, 2); + MAT_EL(ukf_params.Q, 6, 6, 7) = powf(WE_UKF_Q_VA_SCALE, 2); + + ukf_init.ki = WE_UKF_KI; + ukf_init.alpha = WE_UKF_ALPHA; + ukf_init.beta = WE_UKF_BETA; + ukf_params.dt = WIND_ESTIMATOR_PERIODIC_PERIOD; // actually measured later + + wind_estimator.data_available = false; + wind_estimator.reset = false; + + wind_estimator.r_gs = WE_UKF_R_GS; + wind_estimator.r_va = WE_UKF_R_VA; + wind_estimator.r_aoa = WE_UKF_R_AOA; + wind_estimator.r_ssa = WE_UKF_R_SSA; + wind_estimator.q_va = WE_UKF_Q_VA; + wind_estimator.q_wind = WE_UKF_Q_WIND; + wind_estimator.q_va_scale = WE_UKF_Q_VA_SCALE; + + time_step_before = 0; +} + +// Run one step and save result +static inline void wind_estimator_step(void) +{ +#if LOG_WIND_ESTIMATOR + if (LogFileIsOpen()) { + if (!log_we_started) { + // print header with initial parameters + int i; + PrintLog(pprzLogFile, "# Wind Estimator\n#\n"); + PrintLog(pprzLogFile, "# Q = diag( "); + for (i = 0; i < 7; i++) + PrintLog(pprzLogFile, "%.8f ", MAT_EL(ukf_params.Q, i, i, 7)); + PrintLog(pprzLogFile, ")\n"); + PrintLog(pprzLogFile, "# R = diag( "); + for (i = 0; i < 5; i++) + PrintLog(pprzLogFile, "%.8f ", MAT_EL(ukf_params.R, i, i, 5)); + PrintLog(pprzLogFile, ")\n"); + PrintLog(pprzLogFile, "# ki = %.5f\n", ukf_init.ki); + PrintLog(pprzLogFile, "# alpha = %.5f\n", ukf_init.alpha); + PrintLog(pprzLogFile, "# beta = %.5f\n", ukf_init.beta); + PrintLog(pprzLogFile, "#\n"); + PrintLog(pprzLogFile, "p q r ax ay az q1 q2 q3 q4 vkx vky vkz va aoa ssa u v w wx wy wz vas t\n"); + log_we_started = true; + } + PrintLog(pprzLogFile, "%.5f %.5f %.5f %.3f %.3f %.3f %.4f %.4f %.4f %.4f %.5f %.5f %.5f %.5f %.5f %.5f ", + ukf_U.rates[0], + ukf_U.rates[1], + ukf_U.rates[2], + ukf_U.accel[0], + ukf_U.accel[1], + ukf_U.accel[2], + ukf_U.q[0], + ukf_U.q[1], + ukf_U.q[2], + ukf_U.q[3], + ukf_U.vk[0], + ukf_U.vk[1], + ukf_U.vk[2], + ukf_U.va, + ukf_U.aoa, + ukf_U.sideslip + ); + } +#endif + + // estimate wind if airspeed is high enough + if (ukf_U.va > 5.0f) { + // run estimation + UKF_Wind_Estimator_step(); + // update output structure + wind_estimator.airspeed.x = ukf_Y.xout[0]; + wind_estimator.airspeed.y = ukf_Y.xout[1]; + wind_estimator.airspeed.z = ukf_Y.xout[2]; + wind_estimator.wind.x = ukf_Y.xout[3]; + wind_estimator.wind.y = ukf_Y.xout[4]; + wind_estimator.wind.z = ukf_Y.xout[5]; + // set ready flag + wind_estimator.data_available = true; + } else { + wind_estimator.airspeed.x = 0.f; + wind_estimator.airspeed.y = 0.f; + wind_estimator.airspeed.z = 0.f; + wind_estimator.wind.x = 0.f; + wind_estimator.wind.y = 0.f; + wind_estimator.wind.z = 0.f; + } + +#if LOG_WIND_ESTIMATOR + if (log_we_started) { + PrintLog(pprzLogFile, "%.3f %.3f %.3f %.3f %.3f %.3f %.3f %d\n", + wind_estimator.airspeed.x, + wind_estimator.airspeed.y, + wind_estimator.airspeed.z, + wind_estimator.wind.x, + wind_estimator.wind.y, + wind_estimator.wind.z, + ukf_Y.xout[6], + time_step_before + ); + } +#endif +} + +#include "subsystems/imu.h" +/*----------------wind_estimator_periodic-------------*/ +/* Put Data from State in struct use by the Thread */ +/*----------------------------------------------------*/ +void wind_estimator_periodic(void) +{ + // try to lock mutex without blocking + // if it fails, it means that previous computation took too long +#ifndef SITL + if (chMtxTryLock(&we_ukf_mtx)) { +#endif + if (wind_estimator.reset) { + init_calculator(); + } + // update input vector from state interface + ukf_U.rates[0] = stateGetBodyRates_f()->p; // rad/s + ukf_U.rates[1] = stateGetBodyRates_f()->q; // rad/s + ukf_U.rates[2] = stateGetBodyRates_f()->r; // rad/s + // transform data in body frame + struct FloatVect3 accel_ned = { + stateGetAccelNed_f()->x, + stateGetAccelNed_f()->y, + stateGetAccelNed_f()->z + }; +// struct FloatRMat *ned_to_body = stateGetNedToBodyRMat_f(); +// struct FloatVect3 accel_body; +// float_rmat_vmult(&accel_body, ned_to_body, &accel_ned); + + ///// IMU test + struct FloatVect3 accel_body = { + .x = ACCEL_FLOAT_OF_BFP(imu.accel.x), + .y = ACCEL_FLOAT_OF_BFP(imu.accel.y), + .z = ACCEL_FLOAT_OF_BFP(imu.accel.z) + }; + struct FloatVect3 tmp = accel_body; + //struct Int32RMat *body_to_imu_rmat = orientationGetRMat_i(&body_to_imu); + //int32_rmat_transp_vmult(&accel_body, body_to_imu_rmat, accel); + // tilt and remove gravity + struct FloatVect3 gravity_ned = { 0.f, 0.f, 9.81f }; + struct FloatVect3 gravity_body = { 0.f, 0.f, 0.f }; +#if 0 + struct FloatRMat *ned_to_body_rmat = stateGetNedToBodyRMat_f(); + float_rmat_vmult(&gravity_body, ned_to_body_rmat, &gravity_ned); + VECT3_ADD(accel_body, gravity_body); +#else + struct FloatEulers *b2i_e = stateGetNedToBodyEulers_f(); + accel_body.x += -9.81*sinf(b2i_e->theta); + accel_body.y += 9.81*cosf(b2i_e->theta)*sinf(b2i_e->phi); + accel_body.z += 9.81*cosf(b2i_e->theta)*cosf(b2i_e->phi); +#endif + ///// End test + + ukf_U.accel[0] = accel_body.x; // m/s^2 + ukf_U.accel[1] = accel_body.y; // m/s^2 + ukf_U.accel[2] = accel_body.z; // m/s^2 + ukf_U.q[0] = stateGetNedToBodyQuat_f()->qi; + ukf_U.q[1] = stateGetNedToBodyQuat_f()->qx; + ukf_U.q[2] = stateGetNedToBodyQuat_f()->qy; + ukf_U.q[3] = stateGetNedToBodyQuat_f()->qz; + ukf_U.vk[0] = stateGetSpeedNed_f()->x; // m/s + ukf_U.vk[1] = stateGetSpeedNed_f()->y; // m/s + ukf_U.vk[2] = stateGetSpeedNed_f()->z; // m/s + ukf_U.va = stateGetAirspeed_f(); // m/s + ukf_U.aoa = stateGetAngleOfAttack_f(); // rad. + ukf_U.sideslip = stateGetSideslip_f(); // rad. + + ///// TEST + // update input vector from state interface + //ukf_U.rates[0] = 0.; + //ukf_U.rates[1] = 0.; + //ukf_U.rates[2] = 0.; + //ukf_U.accel[0] = 0.; + //ukf_U.accel[1] = 0.; + //ukf_U.accel[2] = 0.; + //ukf_U.q[0] = 1.0; + //ukf_U.q[1] = 0.; + //ukf_U.q[2] = 0.; + //ukf_U.q[3] = 0.; + //ukf_U.vk[0] = 15.; + //ukf_U.vk[1] = 0.; + //ukf_U.vk[2] = 0.; + //ukf_U.va = 15.; + //ukf_U.aoa = 0.; + //ukf_U.sideslip = 0.; + + //DOWNLINK_SEND_PAYLOAD_FLOAT(DefaultChannel, DefaultDevice, 16, (float *)(&ukf_U)); + + float msg[] = { + tmp.x, + tmp.y, + tmp.z, + gravity_body.x, + gravity_body.y, + gravity_body.z, + accel_body.x, + accel_body.y, + accel_body.z, + accel_ned.x, + accel_ned.y, + accel_ned.z + }; + DOWNLINK_SEND_PAYLOAD_FLOAT(DefaultChannel, DefaultDevice, 12, msg); + + // compute DT and set input vector + if (time_step_before == 0) { + ukf_params.dt = WIND_ESTIMATOR_PERIODIC_PERIOD; + time_step_before = get_sys_time_msec(); + } else { + ukf_params.dt = (get_sys_time_msec() - time_step_before) / 1000.f; + time_step_before = get_sys_time_msec(); + } +#ifndef SITL + chMtxUnlock(&we_ukf_mtx); + // send signal to computation thread + chSemSignal(&we_ukf_sem); + } +#else + wind_estimator_step(); +#endif +} + +/*----------------- wind_estimator_event -------------*/ +/* Fonction put data in State */ +/*----------------------------------------------------*/ +void wind_estimator_event(void) +{ + if (wind_estimator.data_available) { +#ifndef SITL + if (chMtxTryLock(&we_ukf_mtx)) { +#endif + struct FloatVect2 wind_ne = { + wind_estimator.wind.x, + wind_estimator.wind.y + }; + stateSetHorizontalWindspeed_f(&wind_ne); //NEED CHECK + stateSetVerticalWindspeed_f(wind_estimator.wind.z); //NEED CHECK + + // TODO do something with corrected airspeed norm + +#if SEND_WIND_ESTIMATOR + // send over telemetry + send_wind_estimator(&(DefaultChannel).trans_tx, &(DefaultDevice).device); +#endif + + wind_estimator.data_available = false; +#ifndef SITL + chMtxUnlock(&we_ukf_mtx); + } +#endif + } +} + +/*------------------thd_windestimate------------------*/ +/* Thread fonction */ +/*----------------------------------------------------*/ +#ifndef SITL +static void thd_windestimate(void *arg) +{ + (void) arg; + chRegSetThreadName("wind estimation"); + + while (true) { + // wait for incoming request signal + chSemWait(&we_ukf_sem); + // lock state + chMtxLock(&we_ukf_mtx); + // run estimation step + wind_estimator_step(); + // unlock + chMtxUnlock(&we_ukf_mtx); + } +} +#endif + +/*-----------------wind_estimator_init----------------*/ +/* Init the Thread and the calculator */ +/*----------------------------------------------------*/ +void wind_estimator_init(void) +{ + init_calculator(); + +#if LOG_WIND_ESTIMATOR + log_we_started = false; +#if SITL + // open log file for writing + // path should be specified in airframe file + uint32_t counter = 0; + char filename[512]; + snprintf(filename, 512, "%s/we_ukf_%05d.csv", STRINGIFY(WE_LOG_PATH), counter); + // check availale name + while ((pprzLogFile = fopen(filename, "r"))) { + fclose(pprzLogFile); + snprintf(filename, 512, "%s/we_ukf_%05d.csv", STRINGIFY(WE_LOG_PATH), ++counter); + } + pprzLogFile = fopen(filename, "w"); + if (pprzLogFile == NULL) { + printf("Failed to open WE log file '%s'\n",filename); + } else { + printf("Opening WE log file '%s'\n",filename); + } +#endif +#endif + +#ifndef SITL + // Start wind estimation thread + chThdCreateStatic(wa_thd_windestimation, sizeof(wa_thd_windestimation), + NORMALPRIO + 2, thd_windestimate, NULL); +#endif + +#if PERIODIC_TELEMETRY + // register for periodic telemetry (for logging with flight recorder) + register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_WIND_INFO_RET, send_wind_estimator); +#endif +} + +void wind_estimator_Set_R_GS(float _v) +{ + wind_estimator.r_gs = _v; + MAT_EL(ukf_params.R, 0, 0, 6) = powf(_v, 2); + MAT_EL(ukf_params.R, 1, 1, 6) = powf(_v, 2); + MAT_EL(ukf_params.R, 2, 2, 6) = powf(_v, 2); +} + +void wind_estimator_Set_R_VA(float _v) +{ + wind_estimator.r_va = _v; + MAT_EL(ukf_params.R, 3, 3, 6) = powf(_v, 2); +} + +void wind_estimator_Set_R_AOA(float _v) +{ + wind_estimator.r_aoa = _v; + MAT_EL(ukf_params.R, 4, 4, 6) = powf(_v, 2); +} + +void wind_estimator_Set_R_SSA(float _v) +{ + wind_estimator.r_ssa = _v; + MAT_EL(ukf_params.R, 5, 5, 6) = powf(_v, 2); +} + +void wind_estimator_Set_Q_VA(float _v) +{ + wind_estimator.q_va = _v; + MAT_EL(ukf_params.Q, 0, 0, 7) = powf(_v, 2); + MAT_EL(ukf_params.Q, 1, 1, 7) = powf(_v, 2); + MAT_EL(ukf_params.Q, 2, 2, 7) = powf(_v, 2); +} + +void wind_estimator_Set_Q_WIND(float _v) +{ + wind_estimator.q_wind = _v; + MAT_EL(ukf_params.Q, 3, 3, 7) = powf(_v, 2); + MAT_EL(ukf_params.Q, 4, 4, 7) = powf(_v, 2); + MAT_EL(ukf_params.Q, 5, 5, 7) = powf(_v, 2); +} + +void wind_estimator_Set_Q_VA_SCALE(float _v) +{ + wind_estimator.q_va_scale = _v; + MAT_EL(ukf_params.Q, 6, 6, 7) = powf(_v, 2); +} + diff --git a/sw/airborne/modules/meteo/wind_estimator.h b/sw/airborne/modules/meteo/wind_estimator.h new file mode 100644 index 0000000000..16be21b3c4 --- /dev/null +++ b/sw/airborne/modules/meteo/wind_estimator.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016 Johan Maurin, Gautier Hattenberger + * + * This file is part of paparazzi. + * + * paparazzi 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 2, or (at your option) + * any later version. + * + * paparazzi 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 paparazzi; see the file COPYING. If not, see + * . + */ + +/** + * @file "modules/meteo/wind_estimator.h" + * @author Maurin + * + * Wind Estimator based on generated library from Matlab + */ + +#ifndef WIND_ESTIMATOR_H +#define WIND_ESTIMATOR_H + +#include "std.h" +#include "math/pprz_algebra_float.h" + +struct WindEstimator { + struct FloatVect3 airspeed; ///< airspeed vector in body frame + struct FloatVect3 wind; ///< wind vector in NED frame + bool data_available; ///< new data available + bool reset; ///< reset filter flag + + // filter parameters + float r_gs; ///< noise associated to ground speed measurement + float r_va; ///< noise associated to airspeed norm measurement + float r_aoa; ///< noise associated to angle of attack measurement + float r_ssa; ///< noise associated to sideslip angle measurement + float q_va; ///< noise associated to airspeed vector model + float q_wind; ///< noise associated to wind vector model + float q_va_scale; ///< noise associated to airspeed scale factor model +}; + +extern struct WindEstimator wind_estimator; + +extern void wind_estimator_init(void); +extern void wind_estimator_periodic(void); +extern void wind_estimator_event(void); + +// paramenters settings handler +extern void wind_estimator_Set_R_GS(float _v); +extern void wind_estimator_Set_R_VA(float _v); +extern void wind_estimator_Set_R_AOA(float _v); +extern void wind_estimator_Set_R_SSA(float _v); +extern void wind_estimator_Set_Q_VA(float _v); +extern void wind_estimator_Set_Q_WIND(float _v); +extern void wind_estimator_Set_Q_VA_SCALE(float _v); + +#endif + diff --git a/sw/airborne/modules/sensors/aoa_pwm.c b/sw/airborne/modules/sensors/aoa_pwm.c index da05135060..b6897e9308 100644 --- a/sw/airborne/modules/sensors/aoa_pwm.c +++ b/sw/airborne/modules/sensors/aoa_pwm.c @@ -23,6 +23,11 @@ * @author Jean-François Erdelyi * @brief Angle of Attack sensor on PWM * +* Driver for a PWM based angle of attack sensor +* A second sensor can be defined for the sideslip angle +* It is assumed that both sensors are the same, only +* sensitivity, offset and direction can differ. +* * SENSOR, exemple : US DIGITAL MA3-P12-125-B * @see http://www.usdigital.com/products/encoders/absolute/rotary/shaft/ma3 */ @@ -38,6 +43,8 @@ bool log_started; #endif +/* Config parameters for angle of attack sensor (mandatory) */ + #ifndef AOA_PWM_CHANNEL #error "AOA_PWM_CHANNEL needs to be defined to use AOA_pwm module" #endif @@ -83,16 +90,57 @@ bool log_started; struct Aoa_Pwm aoa_pwm; + +/* Config parameters for sideslip angle sensor (optional) */ + +#if defined USE_SIDESLIP && !(defined SSA_PWM_CHANNEL) +#error "SSA_PWM_CHANNEL needs to be defined to use sideslip sensor" +#endif + +// Default extra offset that can be ajusted from settings +#ifndef SSA_OFFSET +#define SSA_OFFSET 0.0f +#endif +// Default filter value +#ifndef SSA_FILTER +#define SSA_FILTER 0.0f +#endif +// Default sensitivity (2*pi on a PWM of period AOA_PWM_PERIOD) +#ifndef SSA_SENS +#define SSA_SENS ((2.0f*M_PI)/AOA_PWM_PERIOD) +#endif +// Set SSA_REVERSE to TRUE to change rotation direction +#if SSA_REVERSE +#define SSA_SIGN -1 +#else +#define SSA_SIGN 1 +#endif +struct Aoa_Pwm ssa_pwm; + + +/* telemetry */ +enum Aoa_Type aoa_send_type; + #if PERIODIC_TELEMETRY #include "subsystems/datalink/telemetry.h" static void send_aoa(struct transport_tx *trans, struct link_device *dev) { - pprz_msg_send_AOA(trans, dev, AC_ID, &aoa_pwm.raw, &aoa_pwm.angle); + // FIXME use a second message, more fields or add a sensor ID to send sideslip + switch (aoa_send_type) { + case SEND_TYPE_SIDESLIP: + pprz_msg_send_AOA(trans, dev, AC_ID, &ssa_pwm.raw, &ssa_pwm.angle); + break; + case SEND_TYPE_AOA: + default: + pprz_msg_send_AOA(trans, dev, AC_ID, &aoa_pwm.raw, &aoa_pwm.angle); + break; + } } #endif +/* init */ void aoa_pwm_init(void) { aoa_pwm.offset = AOA_OFFSET; @@ -100,22 +148,28 @@ void aoa_pwm_init(void) aoa_pwm.sens = AOA_SENS; aoa_pwm.angle = 0.0f; aoa_pwm.raw = 0.0f; + ssa_pwm.offset = SSA_OFFSET; + ssa_pwm.filter = SSA_FILTER; + ssa_pwm.sens = SSA_SENS; + ssa_pwm.angle = 0.0f; + ssa_pwm.raw = 0.0f; #if LOG_AOA log_started = false; #endif + aoa_send_type = SEND_TYPE_AOA; #if PERIODIC_TELEMETRY register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_AOA, send_aoa); #endif } +/* update, log and send */ void aoa_pwm_update(void) { static float prev_aoa = 0.0f; - // raw duty cycle in usec - uint32_t duty_raw = get_pwm_input_duty_in_usec(AOA_PWM_CHANNEL); + uint32_t aoa_duty_raw = get_pwm_input_duty_in_usec(AOA_PWM_CHANNEL); // remove some offset if needed - aoa_pwm.raw = duty_raw - AOA_PWM_OFFSET; + aoa_pwm.raw = aoa_duty_raw - AOA_PWM_OFFSET; // FIXME for some reason, the last value of the MA3 encoder is not 4096 but 4097 // this case is not handled since we don't care about angles close to +- 180 deg aoa_pwm.angle = AOA_SIGN * (((float)aoa_pwm.raw * aoa_pwm.sens) - aoa_pwm.offset - AOA_ANGLE_OFFSET); @@ -127,18 +181,36 @@ void aoa_pwm_update(void) { stateSetAngleOfAttack_f(aoa_pwm.angle); #endif +#if USE_SIDESLIP + static float prev_ssa = 0.0f; + // raw duty cycle in usec + uint32_t ssa_duty_raw = get_pwm_input_duty_in_usec(SSA_PWM_CHANNEL); + + // remove some offset if needed + ssa_pwm.raw = ssa_duty_raw - AOA_PWM_OFFSET; + // FIXME for some reason, the last value of the MA3 encoder is not 4096 but 4097 + // this case is not handled since we don't care about angles close to +- 180 deg + ssa_pwm.angle = SSA_SIGN * (((float)ssa_pwm.raw * ssa_pwm.sens) - ssa_pwm.offset - AOA_ANGLE_OFFSET); + // filter angle + ssa_pwm.angle = ssa_pwm.filter * prev_ssa + (1.0f - ssa_pwm.filter) * ssa_pwm.angle; + prev_ssa = ssa_pwm.angle; + + stateSetSideslip_f(ssa_pwm.angle); +#endif + #if SEND_SYNC_AOA - RunOnceEvery(10, DOWNLINK_SEND_AOA(DefaultChannel, DefaultDevice, &aoa_pwm.raw, &aoa_pwm.angle)); + RunOnceEvery(10, send_aoa(&(DefaultChannel).trans_tx, &(DefaultDevice).device)); #endif #if LOG_AOA if(pprzLogFile != -1) { if (!log_started) { - sdLogWriteLog(pprzLogFile, "AOA_PWM: ANGLE(deg) RAW(int16)\n"); + sdLogWriteLog(pprzLogFile, "AOA_PWM: AOA_ANGLE(deg) AOA_RAW(int16) SSA_ANGLE(deg) SSA_RAW(int16)\n"); log_started = true; } else { - float angle = DegOfRad(aoa_pwm.angle); - sdLogWriteLog(pprzLogFile, "AOA_PWM: %.3f %d\n", angle, aoa_pwm.raw); + float aoa_angle = DegOfRad(aoa_pwm.angle); + float ssa_angle = DegOfRad(ssa_pwm.angle); + sdLogWriteLog(pprzLogFile, "AOA_PWM: %.3f %d %.3f %d\n", aoa_angle, aoa_pwm.raw, ssa_angle, ssa_pwm.raw); } } #endif diff --git a/sw/airborne/modules/sensors/aoa_pwm.h b/sw/airborne/modules/sensors/aoa_pwm.h index 7834e2e2fc..7aa986b982 100644 --- a/sw/airborne/modules/sensors/aoa_pwm.h +++ b/sw/airborne/modules/sensors/aoa_pwm.h @@ -23,6 +23,11 @@ * @author Jean-François Erdelyi * @brief Angle of Attack sensor on PWM * +* Driver for a PWM based angle of attack sensor +* A second sensor can be defined for the sideslip angle +* It is assumed that both sensors are the same, only +* sensitivity, offset and direction can differ. +* * SENSOR, example : US DIGITAL MA3-P12-125-B */ @@ -44,7 +49,16 @@ struct Aoa_Pwm { float filter; }; -extern struct Aoa_Pwm aoa_pwm; +extern struct Aoa_Pwm aoa_pwm; // angle of attack sensor +extern struct Aoa_Pwm ssa_pwm; // sideslip angle sensor + +/** Selection of sensor type to be send over telemetry + */ +enum Aoa_Type { + SEND_TYPE_AOA, + SEND_TYPE_SIDESLIP +}; +extern enum Aoa_Type aoa_send_type; extern void aoa_pwm_init(void); extern void aoa_pwm_update(void); diff --git a/sw/simulator/mesonh/__init__.py b/sw/simulator/mesonh/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sw/simulator/mesonh/lauch_test b/sw/simulator/mesonh/lauch_test new file mode 100755 index 0000000000..c6abc15cee --- /dev/null +++ b/sw/simulator/mesonh/lauch_test @@ -0,0 +1,2 @@ +python mesonh.py -t 3600 -f ARMHR.1.sc464.001diaKCL.nc ARMHR.1.sc464.002diaKCL.nc ARMHR.1.sc464.003diaKCL.nc ARMHR.1.sc464.004diaKCL.nc ARMHR.1.sc464.005diaKCL.nc ARMHR.1.sc464.006diaKCL.nc ARMHR.1.sc464.007diaKCL.nc ARMHR.1.sc464.008diaKCL.nc -x 2.2 -y 2.2 + diff --git a/sw/simulator/mesonh/mesonh.py b/sw/simulator/mesonh/mesonh.py new file mode 100755 index 0000000000..3f9dd008c9 --- /dev/null +++ b/sw/simulator/mesonh/mesonh.py @@ -0,0 +1,131 @@ +from __future__ import absolute_import, print_function, division +from mesonh_atmosphere import MesoNHAtmosphere +import os +import sys +import signal +import time +import socket +import struct +import cmath +import numpy as np +from os import getenv +# if PAPARAZZI_SRC not set, then assume the tree containing this +# file is a reasonable substitute +PPRZ_SRC = getenv("PAPARAZZI_SRC", os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../../'))) +sys.path.append(PPRZ_SRC + "/sw/ext/pprzlink/lib/v1.0/python") + +from pprzlink.ivy import IvyMessagesInterface +from pprzlink.message import PprzMessage + +M_IN_KM = 1000. + +atm = None +origin = np.array([0, 0, 0, 0]) +scale = np.array([1., 1/M_IN_KM, 1/M_IN_KM, 1/M_IN_KM]) + +start_time = time.time() + + +def get_wind(east, north, up): + t = time.time() - start_time + print("east :",east) + print("north :",north) + print("up :",up) + loc = np.array([t, up, east, north]) + loc = loc*scale + origin + print("loc:",loc) + weast, wnorth, wup = atm.get_wind(loc) + return weast, wnorth, wup + + +def ivy_request_callback(sender, msg, resp, *args, **kwargs): + """ + Ivy Callback for Paparazzi Requests + """ + + if msg.msg_class == "ground" and msg.name == "WORLD_ENV_REQ": + return worldenv_cb(msg, resp) + else: + return None + + +#def worldenv_cb(m, r): +def worldenv_cb(ac_id, msg): + """ + Callback for paparazzi WORLD_ENV requests + """ + # request location (in meters) + east, north, up = float(msg.get_field(3)),\ + float(msg.get_field(4)),\ + float(msg.get_field(5)) + up *= -1 + # convert in km + translation with mesoNH origin + weast, wnorth, wup = get_wind(east, north, up) + print("wind_est:") + print(weast) + print(wnorth) + print(wup) + msg_back=PprzMessage("ground", "WORLD_ENV") + msg_back.set_value_by_name("wind_east",weast) + msg_back.set_value_by_name("wind_north",wnorth) + msg_back.set_value_by_name("wind_up",wup) + msg_back.set_value_by_name("ir_contrast",400) + msg_back.set_value_by_name("time_scale",1) + msg_back.set_value_by_name("gps_availability",1) + ivy.send(msg_back,None) + + +def signal_handler(signal, frame): + print('\nShutting down IVY...') + ivy.shutdown() + print("Done.") + + +def main(): + # parse arguments + import argparse as ap + + argp = ap.ArgumentParser(description="Environment variables provider " + "for Paparazzi simulation from MesoNH data") + + argp.add_argument("-t", "--time-step", required=True, type=int, + help="Duration of a time step between MesoNH Files.") + argp.add_argument("-f", "--files", required=True, nargs='+', + help="MesoNH netCDF files, in temporal ordering") + argp.add_argument("-x", "--origin-x", required=False, type=float, + default=0., + help="Origin translation x.") + argp.add_argument("-y", "--origin-y", required=False, type=float, + default=0., + help="Origin translation y.") + argp.add_argument("-z", "--origin-z", required=False, type=float, + default=0., + help="Origin translation z.") + arguments = argp.parse_args() + + print(arguments) + + # register signal handler for ctrl+c to stop the program + signal.signal(signal.SIGINT, signal_handler) + + # origin for translation from paparazzi to mesoNH frame + global origin + origin = np.array([0, arguments.origin_z, arguments.origin_x, arguments.origin_y]) + + # build atmosphere simulation source + global atm + atm = MesoNHAtmosphere(arguments.files, arguments.time_step, tinit=0) + + # init ivy and register callback for WORLD_ENV_REQ and NPS_SPEED_POS + global ivy + ivy = IvyMessagesInterface("MesoNH"); + ivy.subscribe(worldenv_cb,'(.* WORLD_ENV_REQ .*)') + + # wait for ivy to stop + from ivy.std_api import IvyMainLoop + + signal.pause() + + +if __name__ == '__main__': + main() diff --git a/sw/simulator/mesonh/mesonh_atmosphere.py b/sw/simulator/mesonh/mesonh_atmosphere.py new file mode 100644 index 0000000000..ed07a7a022 --- /dev/null +++ b/sw/simulator/mesonh/mesonh_atmosphere.py @@ -0,0 +1,147 @@ +""" + Reading mesoNH netCDF files and providing an interface to read chunks +of data and interpolate points. + + TODO : test with large netCDF files, add mapping of variables as an argument. +""" + +from __future__ import division, print_function, absolute_import +from netCDF4 import MFDataset, Dataset +import numpy as np +from scipy.interpolate import RegularGridInterpolator + + +def find_lt(a, x): + 'Find rightmost value less than x' + return np.searchsorted(a, x, side='left')-1 + + +def find_le(a, x): + 'Find rightmost value less than or equal to x' + return np.searchsorted(a, x, side='right')-1 + + +def find_gt(a, x): + 'Find leftmost value greater than x' + return np.searchsorted(a, x, side='right') + + +def find_ge(a, x): + 'Find leftmost item greater than or equal to x' + return np.searchsorted(a, x, side='left') + +WX = 'UT' # Wind x,y and z composants +WY = 'VT' +WZ = 'WT' +PTEMP = 'THT' # potential temperature +RVAP = 'RVT' # vapor mixing ratio +LWATER = 'RCT' # liquid water +ZSCALE = 'VLEV' # vertical level (height) scale +XSCALE = 'S_N_direction' # south -> north axis scale +YSCALE = 'W_E_direction' # west -> east axis scale +TIME = 'time' # time dimension +X = 2 +Y = 3 +Z = 1 +T = 0 + + +class MesoNHAtmosphere: + """ + Interpolation in space-time from mesoNH data over a grid. + + The functions valued on the grid are assumed to be periodic in the + x and y axis (south->north and west->east) : + f(x)=f(x+xmax-xmin) where [xmin,xmax] are the bounds on x + of the mesoNH grid. + + Clipping is done on the z (height) and time axis : + f(z)=f(min(max(z,zmin),zmax)) where [zmin,zmax] are the bound on z + of the mesoNH grid. + + Two interpolation methods are currently supported : nearest neighbour + and linear. + + Uses RegularGridInterpolator from scipy package. + + """ + data = [] + + interpolator = None + boundsmax = None + boundsmin = None + + def __init__(self, files, tstep, tinit=0): + if np.isscalar(files): + self.data = Dataset(files) + else: + self.data = MFDataset(files) + pz = self.data.variables[ZSCALE][:, 0, 0] + px = self.data.variables[XSCALE] + py = self.data.variables[YSCALE] + pt = np.array([tinit + i * tstep for i in + range(self.data.variables[TIME].shape[0])]) + self.boundsmax = [pt[-1], pz[-1], px[-1], py[-1]] + self.boundsmin = [pt[0], pz[0], px[0], py[0]] + + self.grid_coordinates = (pt, pz, px, py) + self.grid_shape = (len(pt), len(pz), len(px), len(py)) + + def get_points(self, points, var, method='nearest'): + """ Get value of variable on points + + Arguments: + points: a ndarray containing the point coordinates on the last + dimension + var: the name of the variable in the mesoNH file(s) + method: 'nearest' and 'linear' interpolation are currently supported + """ + points = np.array(points) + p = self._apply_bounds(points) + caxes = tuple(range(p.ndim-1)) + bounds = zip(p.min(axis=caxes), p.max(axis=caxes)) + interpolator = self._get_interpolator(bounds, var, method) + return interpolator(p, method).squeeze() + + def _get_interpolator(self, bounds, var, method="nearest"): + slice_indexes, coordinates = self._slicyfy(bounds) + values = self._get_var_values(var, slice_indexes) + ip = RegularGridInterpolator(coordinates, values, method) + return ip + + def _slicyfy(self, bounds): + slice_indexes = () + coordinates = () + for d, b in enumerate(bounds): + dslice = slice(find_le(self.grid_coordinates[d], b[0]), + find_gt(self.grid_coordinates[d], b[1])+1) + slice_indexes += dslice, + coordinates += self.grid_coordinates[d][dslice], + + return slice_indexes, coordinates + + def _get_var_values(self, var, idx=Ellipsis): + return self.data[var][idx] + + def _apply_bounds(self, point): + x = point[..., X] + y = point[..., Y] + z = point[..., Z] + t = point[..., T] + + p = np.ndarray(point.shape) + p[..., T] = np.clip(t, self.boundsmin[T], self.boundsmax[T]) + p[..., Z] = np.clip(z, self.boundsmin[Z], self.boundsmax[Z]) + p[..., X] = (x-self.boundsmin[X]) % (self.boundsmax[X]-self.boundsmin[X])\ + + self.boundsmin[X] + p[..., Y] = (y-self.boundsmin[Y]) % (self.boundsmax[Y]-self.boundsmin[Y])\ + + self.boundsmin[Y] + + return p + + def get_wind(self, points, method='nearest'): + """Convenience method for getting 3D wind. See get_points.""" + return np.array((self.get_points(points, WX, method), + self.get_points(points, WY, method), + self.get_points(points, WZ, method) + )) diff --git a/sw/simulator/nps/nps_autopilot_fixedwing.c b/sw/simulator/nps/nps_autopilot_fixedwing.c index 8309fe3fe3..f02a2d0175 100644 --- a/sw/simulator/nps/nps_autopilot_fixedwing.c +++ b/sw/simulator/nps/nps_autopilot_fixedwing.c @@ -138,7 +138,7 @@ void nps_autopilot_run_step(double time) AbiSendMsgTEMPERATURE(BARO_SIM_SENDER_ID, (float)sensors.temp.value); } -#if USE_AIRSPEED +#if USE_AIRSPEED || USE_NPS_AIRSPEED if (nps_sensors_airspeed_available()) { stateSetAirspeed_f((float)sensors.airspeed.value); Fbw(event_task); @@ -165,6 +165,22 @@ void nps_autopilot_run_step(double time) } #endif +#if USE_NPS_AOA + if (nps_sensors_aoa_available()) { + stateSetAngleOfAttack_f((float)sensors.aoa.value); + Fbw(event_task); + Ap(event_task); + } +#endif + +#if USE_NPS_SIDESLIP + if (nps_sensors_sideslip_available()) { + stateSetSideslip_f((float)sensors.sideslip.value); + Fbw(event_task); + Ap(event_task); + } +#endif + if (nps_bypass_ahrs) { sim_overwrite_ahrs(); } diff --git a/sw/simulator/nps/nps_fdm.h b/sw/simulator/nps/nps_fdm.h index f76b249450..fbd4e218b6 100644 --- a/sw/simulator/nps/nps_fdm.h +++ b/sw/simulator/nps/nps_fdm.h @@ -112,6 +112,8 @@ struct NpsFdm { double dynamic_pressure; ///< dynamic pressure in Pascal double temperature; ///< current temperature in degrees Celcius double pressure_sl; ///< pressure at sea level in Pascal + double aoa; ///< angle of attack in rad + double sideslip; ///< sideslip angle in rad // Control surface positions (normalized values) float elevator; diff --git a/sw/simulator/nps/nps_fdm_jsbsim.cpp b/sw/simulator/nps/nps_fdm_jsbsim.cpp index 5de4c08905..2760222c64 100644 --- a/sw/simulator/nps/nps_fdm_jsbsim.cpp +++ b/sw/simulator/nps/nps_fdm_jsbsim.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -463,6 +464,12 @@ static void fetch_state(void) fdm.dynamic_pressure = PascalOfPsf(FDMExec->GetAuxiliary()->Getqbar()); fdm.temperature = CelsiusOfRankine(FDMExec->GetAtmosphere()->GetTemperature()); + /* + * angle of attack and SlideSlip. + */ + fdm.aoa= FDMExec->GetPropertyManager()->GetNode("aero/alpha-rad")->getDoubleValue(); + fdm.sideslip = FDMExec->GetPropertyManager()->GetNode("aero/beta-rad")->getDoubleValue(); + /* * Control surface positions * diff --git a/sw/simulator/nps/nps_sensor_aoa.c b/sw/simulator/nps/nps_sensor_aoa.c new file mode 100644 index 0000000000..5524cb69e6 --- /dev/null +++ b/sw/simulator/nps/nps_sensor_aoa.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 Johan Maurin, Gautier Hattenberger + * + * This file is part of paparazzi. + * + * paparazzi 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 2, or (at your option) + * any later version. + * + * paparazzi 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 paparazzi; see the file COPYING. If not, see + * . + */ + +/** + * @file nps_sensor_aoa.c + * + * Simulated Angle of Attack of the Wind for NPS simulator. + * + */ + +#include "nps_sensor_aoa.h" + +#include "generated/airframe.h" + +#include "std.h" +#include "nps_fdm.h" +#include "nps_random.h" +#include NPS_SENSORS_PARAMS + +/// 10Hz default +#ifndef NPS_AOA_DT +#define NPS_AOA_DT 0.01 +#endif + +/// standard deviation in radian (default 0.001 rad) +#ifndef NPS_AOA_NOISE_STD_DEV +#define NPS_AOA_NOISE_STD_DEV 0.001 +#endif + +#ifndef NPS_AOA_OFFSET +#define NPS_AOA_OFFSET 0 +#endif + + +void nps_sensor_aoa_init(struct NpsSensorAngleOfAttack *aoa, double time) +{ + aoa->value = 0.; + aoa->offset = NPS_AOA_OFFSET; + aoa->noise_std_dev = NPS_AOA_NOISE_STD_DEV; + aoa->next_update = time; + aoa->data_available = FALSE; +} + + +void nps_sensor_aoa_run_step(struct NpsSensorAngleOfAttack *aoa, double time) +{ + + if (time < aoa->next_update) { + return; + } + + /* equivalent airspeed + sensor offset */ + aoa->value = fdm.aoa + aoa->offset; + /* add noise with std dev rad */ + aoa->value += get_gaussian_noise() * aoa->noise_std_dev; + + aoa->next_update += NPS_AOA_DT; + aoa->data_available = TRUE; +} + diff --git a/sw/simulator/nps/nps_sensor_aoa.h b/sw/simulator/nps/nps_sensor_aoa.h new file mode 100644 index 0000000000..c6be3f9645 --- /dev/null +++ b/sw/simulator/nps/nps_sensor_aoa.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Johan Maurin, Gautier Hattenberger + * + * This file is part of paparazzi. + * + * paparazzi 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 2, or (at your option) + * any later version. + * + * paparazzi 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 paparazzi; see the file COPYING. If not, see + * . + */ + +/** + * @file nps_sensor_aoa.h + * + * Simulated Angle Of Attack of the Wind for NPS simulator. + * + */ + +#ifndef NPS_SENSOR_AOA_H +#define NPS_SENSOR_AOA_H + +#include "math/pprz_algebra.h" +#include "math/pprz_algebra_double.h" +#include "math/pprz_algebra_float.h" +#include "std.h" + +struct NpsSensorAngleOfAttack { + double value; ///< angle of attack reading in radian + double offset; ///< offset in meters/second + double noise_std_dev; ///< noise standard deviation + double next_update; + bool data_available; +}; + + +extern void nps_sensor_aoa_init(struct NpsSensorAngleOfAttack *aoa, double time); +extern void nps_sensor_aoa_run_step(struct NpsSensorAngleOfAttack *aoa, double time); + +#endif /* NPS_SENSOR_AOA_H */ diff --git a/sw/simulator/nps/nps_sensor_sideslip.c b/sw/simulator/nps/nps_sensor_sideslip.c new file mode 100644 index 0000000000..f2fbab3c43 --- /dev/null +++ b/sw/simulator/nps/nps_sensor_sideslip.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 Johan Maurin, Gautier Hattenberger + * + * This file is part of paparazzi. + * + * paparazzi 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 2, or (at your option) + * any later version. + * + * paparazzi 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 paparazzi; see the file COPYING. If not, see + * . + */ + +/** + * @file nps_sensor_sideslip.c + * + * Simulated Angle of Attack of the Wind for NPS simulator. + * + */ + +#include "nps_sensor_sideslip.h" + +#include "generated/airframe.h" +#include + +#include "std.h" +#include "nps_fdm.h" +#include "nps_random.h" +#include NPS_SENSORS_PARAMS + +/// 10Hz default +#ifndef NPS_SIDESLIP_DT +#define NPS_SIDESLIP_DT 0.01 +#endif + +/// standard deviation in radian (default 0.001 rad) +#ifndef NPS_SIDESLIP_NOISE_STD_DEV +#define NPS_SIDESLIP_NOISE_STD_DEV 0.001 +#endif + +#ifndef NPS_SIDESLIP_OFFSET +#define NPS_SIDESLIP_OFFSET 0 +#endif + + +void nps_sensor_sideslip_init(struct NpsSensorSideSlip *sideslip, double time) +{ + sideslip->value = 0.; + sideslip->offset = NPS_SIDESLIP_OFFSET; + sideslip->noise_std_dev = NPS_SIDESLIP_NOISE_STD_DEV; + sideslip->next_update = time; + sideslip->data_available = FALSE; +} + + +void nps_sensor_sideslip_run_step(struct NpsSensorSideSlip *sideslip, double time) +{ + + if (time < sideslip->next_update) { + return; + } + + /* equivalent airspeed + sensor offset */ + sideslip->value = fdm.sideslip + sideslip->offset; + /* add noise with std dev rad */ + sideslip->value += get_gaussian_noise() * sideslip->noise_std_dev; + + sideslip->next_update += NPS_SIDESLIP_DT; + sideslip->data_available = TRUE; +} + diff --git a/sw/simulator/nps/nps_sensor_sideslip.h b/sw/simulator/nps/nps_sensor_sideslip.h new file mode 100644 index 0000000000..c91f9fef30 --- /dev/null +++ b/sw/simulator/nps/nps_sensor_sideslip.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Johan Maurin, Gautier Hattenberger + * + * This file is part of paparazzi. + * + * paparazzi 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 2, or (at your option) + * any later version. + * + * paparazzi 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 paparazzi; see the file COPYING. If not, see + * . + */ + +/** + * @file nps_sensor_sideslip.h + * + * Simulated Angle Of Attack of the Wind for NPS simulator. + * + */ + +#ifndef NPS_SENSOR_SIDESLIP_H +#define NPS_SENSOR_SIDESLIP_H + +#include "math/pprz_algebra.h" +#include "math/pprz_algebra_double.h" +#include "math/pprz_algebra_float.h" +#include "std.h" + +struct NpsSensorSideSlip{ + double value; ///< sideslip reading in radian + double offset; ///< offset in meters/second + double noise_std_dev; ///< noise standard deviation + double next_update; + bool data_available; +}; + + +extern void nps_sensor_sideslip_init(struct NpsSensorSideSlip *sideslip, double time); +extern void nps_sensor_sideslip_run_step(struct NpsSensorSideSlip *sideslip, double time); + +#endif /* NPS_SENSOR_SIDESLIP_H */ + diff --git a/sw/simulator/nps/nps_sensors.c b/sw/simulator/nps/nps_sensors.c index 0b8744d586..11744c8e7d 100644 --- a/sw/simulator/nps/nps_sensors.c +++ b/sw/simulator/nps/nps_sensors.c @@ -20,7 +20,8 @@ void nps_sensors_init(double time) nps_sensor_sonar_init(&sensors.sonar, time); nps_sensor_airspeed_init(&sensors.airspeed, time); nps_sensor_temperature_init(&sensors.temp, time); - + nps_sensor_aoa_init(&sensors.aoa, time); + nps_sensor_sideslip_init(&sensors.sideslip,time); } @@ -34,6 +35,8 @@ void nps_sensors_run_step(double time) nps_sensor_sonar_run_step(&sensors.sonar, time); nps_sensor_airspeed_run_step(&sensors.airspeed, time); nps_sensor_temperature_run_step(&sensors.temp, time); + nps_sensor_aoa_run_step(&sensors.aoa, time); + nps_sensor_sideslip_run_step(&sensors.sideslip,time); } @@ -99,3 +102,21 @@ bool nps_sensors_temperature_available(void) } return FALSE; } + +bool nps_sensors_aoa_available(void) +{ + if (sensors.aoa.data_available) { + sensors.aoa.data_available = FALSE; + return TRUE; + } + return FALSE; +} + +bool nps_sensors_sideslip_available(void) +{ + if (sensors.sideslip.data_available) { + sensors.sideslip.data_available = FALSE; + return TRUE; + } + return FALSE; +} diff --git a/sw/simulator/nps/nps_sensors.h b/sw/simulator/nps/nps_sensors.h index 4f67a864c6..19f6d1bc18 100644 --- a/sw/simulator/nps/nps_sensors.h +++ b/sw/simulator/nps/nps_sensors.h @@ -10,6 +10,8 @@ #include "nps_sensor_sonar.h" #include "nps_sensor_airspeed.h" #include "nps_sensor_temperature.h" +#include "nps_sensor_aoa.h" +#include "nps_sensor_sideslip.h" struct NpsSensors { struct DoubleRMat body_to_imu_rmat; @@ -21,6 +23,8 @@ struct NpsSensors { struct NpsSensorSonar sonar; struct NpsSensorAirspeed airspeed; struct NpsSensorTemperature temp; + struct NpsSensorAngleOfAttack aoa; + struct NpsSensorSideSlip sideslip; }; extern struct NpsSensors sensors; @@ -35,6 +39,7 @@ extern bool nps_sensors_gps_available(); extern bool nps_sensors_sonar_available(); extern bool nps_sensors_airspeed_available(); extern bool nps_sensors_temperature_available(); - +extern bool nps_sensors_aoa_available(); +extern bool nps_sensors_sideslip_available(); #endif /* NPS_SENSORS_H */