diff --git a/conf/airframes/tudelft/rot_wing_v3c_oneloop.xml b/conf/airframes/tudelft/rot_wing_v3c_oneloop.xml
index ca2e691818..5641c316fc 100644
--- a/conf/airframes/tudelft/rot_wing_v3c_oneloop.xml
+++ b/conf/airframes/tudelft/rot_wing_v3c_oneloop.xml
@@ -139,7 +139,7 @@
-
+
diff --git a/conf/airframes/tudelft/rot_wing_v3c_oneloop_optitrack.xml b/conf/airframes/tudelft/rot_wing_v3c_oneloop_optitrack.xml
index ad4294202a..d72c7d910b 100644
--- a/conf/airframes/tudelft/rot_wing_v3c_oneloop_optitrack.xml
+++ b/conf/airframes/tudelft/rot_wing_v3c_oneloop_optitrack.xml
@@ -140,7 +140,7 @@
-
+
diff --git a/conf/airframes/tudelft/rot_wing_v3c_oneloop_optitrack_ext_pose.xml b/conf/airframes/tudelft/rot_wing_v3c_oneloop_optitrack_ext_pose.xml
index 48c7a223c5..d11d7fb55f 100644
--- a/conf/airframes/tudelft/rot_wing_v3c_oneloop_optitrack_ext_pose.xml
+++ b/conf/airframes/tudelft/rot_wing_v3c_oneloop_optitrack_ext_pose.xml
@@ -132,7 +132,7 @@
-
+
diff --git a/conf/airframes/tudelft/rot_wing_v3c_oneloop_simulation.xml b/conf/airframes/tudelft/rot_wing_v3c_oneloop_simulation.xml
index 2d884eb6b5..51b703baa7 100644
--- a/conf/airframes/tudelft/rot_wing_v3c_oneloop_simulation.xml
+++ b/conf/airframes/tudelft/rot_wing_v3c_oneloop_simulation.xml
@@ -103,7 +103,7 @@
-
+
diff --git a/conf/modules/eff_scheduling_rot_wing_V2.xml b/conf/modules/eff_scheduling_rot_wing_V2.xml
new file mode 100644
index 0000000000..03f1accc04
--- /dev/null
+++ b/conf/modules/eff_scheduling_rot_wing_V2.xml
@@ -0,0 +1,78 @@
+
+
+
+ T
+ The control effectiveness scheduler for the rotating wing quadplane drone type
+ - it requires a servo called ROTATION_MECH
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conf/modules/rotwing_state_V2.xml b/conf/modules/rotwing_state_V2.xml
new file mode 100644
index 0000000000..8da136c51d
--- /dev/null
+++ b/conf/modules/rotwing_state_V2.xml
@@ -0,0 +1,29 @@
+
+
+
+ This module keeps track of the current state of a rotating wing drone and desired state set by the RC or flightplan. Paramters are being scheduled in each change of a current state and desired state. Functions are defined in this module to call the actual state and desired state and set a desired state.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conf/modules/wing_rotation_adc_sensor.xml b/conf/modules/wing_rotation_adc_sensor.xml
index 70161cd08c..71f7438c8d 100644
--- a/conf/modules/wing_rotation_adc_sensor.xml
+++ b/conf/modules/wing_rotation_adc_sensor.xml
@@ -13,7 +13,7 @@
-
+
diff --git a/sw/airborne/modules/ctrl/eff_scheduling_rot_wing_V2.c b/sw/airborne/modules/ctrl/eff_scheduling_rot_wing_V2.c
new file mode 100644
index 0000000000..6e85fe7bbd
--- /dev/null
+++ b/sw/airborne/modules/ctrl/eff_scheduling_rot_wing_V2.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2023 Tomaso De Ponti
+ *
+ * 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/ctrl/eff_scheduling_rot_wing_V2.c"
+ * @author Tomaso De Ponti
+ * The control effectiveness scheduler for the rotating wing drone type
+ */
+
+#include "modules/ctrl/eff_scheduling_rot_wing_V2.h"
+#include "generated/airframe.h"
+#include "state.h"
+#include "modules/actuators/actuators.h"
+#include "modules/core/abi.h"
+#include "filters/low_pass_filter.h"
+
+#define FORCE_ONELOOP
+#ifdef FORCE_ONELOOP
+#include "firmwares/rotorcraft/oneloop/oneloop_andi.h"
+float actuator_state_filt_vect[EFF_MAT_COLS_NB] = {0};
+#else
+#include "firmwares/rotorcraft/stabilization/stabilization_indi.h"
+#endif
+
+#ifndef ROT_WING_EFF_SCHED_IXX_BODY
+#error "NO ROT_WING_EFF_SCHED_IXX_BODY defined"
+#endif
+
+#ifndef ROT_WING_EFF_SCHED_IYY_BODY
+#error "NO ROT_WING_EFF_SCHED_IYY_BODY defined"
+#endif
+
+#ifndef ROT_WING_EFF_SCHED_IZZ
+#error "NO ROT_WING_EFF_SCHED_IZZ defined"
+#endif
+
+#ifndef ROT_WING_EFF_SCHED_IXX_WING
+#error "NO ROT_WING_EFF_SCHED_IXX_WING defined"
+#endif
+
+#ifndef ROT_WING_EFF_SCHED_IYY_WING
+#error "NO ROT_WING_EFF_SCHED_IYY_WING defined"
+#endif
+
+#ifndef ROT_WING_EFF_SCHED_M
+#error "NO ROT_WING_EFF_SCHED_M defined"
+#endif
+
+/* Effectiveness Matrix definition */
+float G2_RW[EFF_MAT_COLS_NB] = {0};//ROT_WING_EFF_SCHED_G2; //scaled by RW_G_SCALE
+float G1_RW[EFF_MAT_ROWS_NB][EFF_MAT_COLS_NB] = {0};//{ROT_WING_EFF_SCHED_G1_ZERO, ROT_WING_EFF_SCHED_G1_ZERO, ROT_WING_EFF_SCHED_G1_THRUST, ROT_WING_EFF_SCHED_G1_ROLL, ROT_WING_EFF_SCHED_G1_PITCH, ROT_WING_EFF_SCHED_G1_YAW}; //scaled by RW_G_SCALE
+float EFF_MAT_RW[EFF_MAT_ROWS_NB][EFF_MAT_COLS_NB] = {0};
+static float flt_cut = 1.0e-4;
+
+struct FloatEulers eulers_zxy_RW_EFF;
+static Butterworth2LowPass skew_filt;
+/* Temp variables*/
+bool airspeed_fake_on = false;
+float airspeed_fake = 0.0;
+float ele_eff = 22.0;
+float ele_min = 0.0;
+/* Define Forces and Moments tructs for each actuator*/
+struct RW_Model RW;
+
+inline void eff_scheduling_rot_wing_update_wing_angle(void);
+inline void eff_scheduling_rot_wing_update_airspeed(void);
+void ele_pref_sched(void);
+void update_attitude(void);
+void sum_EFF_MAT_RW(void);
+void init_RW_Model(void);
+void calc_G1_G2_RW(void);
+
+/** ABI binding wing position data.
+ */
+#ifndef WING_ROTATION_CAN_ROT_WING_ID
+#define WING_ROTATION_CAN_ROT_WING_ID ABI_BROADCAST
+#endif
+PRINT_CONFIG_VAR(WING_ROTATION_CAN_ROT_WING_ID)
+static abi_event wing_position_ev;
+
+float skew_meas = 0.0;
+static void wing_position_cb(uint8_t sender_id UNUSED, struct act_feedback_t *pos_msg, uint8_t num_act)
+{
+ for (int i=0; i ELE_MIN_AS){
+ RW.ele_pref = (ZERO_ELE_PPRZ - ele_min) / (ELE_MAX_AS - ELE_MIN_AS) * (RW.as - ELE_MIN_AS) + ele_min;
+ Bound(RW.ele_pref,ele_min,ZERO_ELE_PPRZ);
+ } else {
+ RW.ele_pref = ele_min;
+ }
+}
diff --git a/sw/airborne/modules/ctrl/eff_scheduling_rot_wing_V2.h b/sw/airborne/modules/ctrl/eff_scheduling_rot_wing_V2.h
new file mode 100644
index 0000000000..c47c48817a
--- /dev/null
+++ b/sw/airborne/modules/ctrl/eff_scheduling_rot_wing_V2.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2023 Tomaso De Ponti
+ *
+ * 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/ctrl/eff_scheduling_rot_wing_V2.h"
+ * @author Tomaso De Ponti
+ * The control effectiveness scheduler for the rotating wing drone type
+ */
+
+#ifndef CTRL_EFF_SCHED_ROT_WING_H
+#define CTRL_EFF_SCHED_ROT_WING_H
+
+#include "std.h"
+#include "generated/airframe.h"
+
+#ifndef EFF_MAT_ROWS_NB
+#define EFF_MAT_ROWS_NB 6
+#endif
+
+#define RW_aX 0 // X body axis (linear acceleration)
+#define RW_aY 1 // Y body axis (linear acceleration)
+#define RW_aZ 2 // Z body axis (linear acceleration)
+#define RW_aN 0 // North axis (linear acceleration)
+#define RW_aE 1 // East axis (linear acceleration)
+#define RW_aD 2 // Down axis (linear acceleration)
+#define RW_ap 3 // X body axis (angular acceleration)
+#define RW_aq 4 // Y body axis (angular acceleration)
+#define RW_ar 5 // Z body axis (angular acceleration)
+
+#ifndef EFF_MAT_COLS_NB
+#define EFF_MAT_COLS_NB (COMMANDS_NB_REAL + COMMANDS_NB_VIRTUAL)
+#endif
+
+#define RW_G_SCALE 1000.0f
+
+// Elevator ctrl definitions
+#ifndef ZERO_ELE_PPRZ
+#define ZERO_ELE_PPRZ 8000
+#endif
+
+#ifndef ELE_MIN_AS
+#define ELE_MIN_AS 8.8
+#endif
+
+#ifndef ELE_MAX_AS // should match the max airspeed
+#define ELE_MAX_AS 16.0
+#endif
+
+extern float EFF_MAT_RW[EFF_MAT_ROWS_NB][EFF_MAT_COLS_NB];
+extern float G2_RW[EFF_MAT_COLS_NB] ;
+extern float G1_RW[EFF_MAT_ROWS_NB][EFF_MAT_COLS_NB] ;
+struct rot_wing_eff_sched_param_t {
+ float Ixx_body; // body MMOI around roll axis [kgm²]
+ float Iyy_body; // body MMOI around pitch axis [kgm²]
+ float Izz; // total MMOI around yaw axis [kgm²]
+ float Ixx_wing; // wing MMOI around the chordwise direction of the wing [kgm²]
+ float Iyy_wing; // wing MMOI around the spanwise direction of the wing [kgm²]
+ float m; // mass [kg]
+ float DMdpprz_hover_roll[2]; // Moment coeficients for roll motors (Scaled by 10000)
+ float hover_roll_pitch_coef[2]; // Model coefficients to correct pitch effective for roll motors
+ float hover_roll_roll_coef[2]; // Model coefficients to correct roll effectiveness for roll motors
+ float k_elevator[3];
+ float k_rudder[3];
+ float k_aileron;
+ float k_flaperon;
+ float k_pusher[2];
+ float k_elevator_deflection[2];
+ float d_rudder_d_pprz;
+ float k_rpm_pprz_pusher[3];
+ float k_lift_wing[2];
+ float k_lift_fuselage;
+ float k_lift_tail;
+};
+
+struct rot_wing_eff_sched_var_t {
+ float Ixx; // Total MMOI around roll axis [kgm²]
+ float Iyy; // Total MMOI around pitch axis [kgm²]
+ float wing_rotation_rad; // Wing rotation angle in radians: from ABI message
+ float wing_rotation_deg; // Wing rotation angle in degrees: (clone in degrees)
+ float cosr; // cosine of wing rotation angle
+ float sinr; // sine of wing rotation angle
+ float cosr2; // cosine² of wing rotation angle
+ float sinr2; // sine² of wing rotation angle
+ float cosr3; // cosine³ of wing rotation angle
+ float sinr3; // sine³ of wing rotation angle
+
+ // Set during initialization
+ float pitch_motor_dMdpprz; // derivative of delta moment with respect to a delta paparazzi command for the pitch motors [Nm/pprz]
+ float roll_motor_dMdpprz; // derivative of delta moment with respect to a delta paparazzi command for the roll motors [Nm/pprz]
+
+ // commands
+ float cmd_elevator;
+ float cmd_pusher;
+ float cmd_pusher_scaled;
+ float cmd_T_mean_scaled;
+
+ // airspeed
+ float airspeed;
+ float airspeed2;
+};
+
+struct I{
+ float xx;
+ float yy;
+ float zz;
+ float w_xx;
+ float w_yy;
+ float b_xx;
+ float b_yy;
+};
+struct F_M_Body{
+ float dFdu; // derivative of the force with respect to the control input (e.g. linear coefficient)
+ float dMdu; // derivative of the reaction trque with respect to the control input (e.g. linear coefficient)
+ float dMdud; // derivative of the reaction torque with respect to the control input time derivative
+ float l; // arm length
+};
+
+struct wing_model{
+ float k0; // Linear coefficient (theta u²)
+ float k1; // Linear coeeficient (theta u² s(Lambda)²)
+ float k2; // linear coefficient with (u² s(Lambda)²)
+ float dLdtheta; // derivative of lift with respect to the pitch angle
+ float L; // Lift
+};
+
+struct RW_attitude{
+ float phi;
+ float theta;
+ float psi;
+ float sphi;
+ float cphi;
+ float stheta;
+ float ctheta;
+ float spsi;
+ float cpsi;
+};
+
+struct RW_skew{
+ float rad; // Wing rotation angle in radians: from ABI message
+ float deg; // Wing rotation angle in degrees: (clone in degrees)
+ float cosr; // cosine of wing rotation angle
+ float sinr; // sine of wing rotation angle
+ float cosr2; // cosine² of wing rotation angle
+ float sinr2; // sine² of wing rotation angle
+ float cosr3; // cosine³ of wing rotation angle
+ float sinr3; // sine³ of wing rotation angle
+};
+struct RW_Model{
+ struct I I; // Inertia matrix
+ float m; // mass [kg]
+ float T; // Thrust [N]
+ float P; // Pusher thrust [N]
+ struct RW_skew skew;
+ struct RW_attitude att;
+ struct wing_model wing;
+ struct F_M_Body mF;
+ struct F_M_Body mR;
+ struct F_M_Body mB;
+ struct F_M_Body mL;
+ struct F_M_Body mP;
+ struct F_M_Body ele;
+ struct F_M_Body rud;
+ struct F_M_Body ail;
+ struct F_M_Body flp;
+ float as; // airspeed [m/s]
+ float as2; // airspeed squared [m/s²]
+ float ele_pref;
+
+};
+
+extern bool airspeed_fake_on;
+extern float airspeed_fake;
+extern float ele_eff;
+extern float ele_min;
+
+extern float rotation_angle_setpoint_deg;
+extern int16_t rotation_cmd;
+
+extern float eff_sched_pusher_time;
+
+extern void eff_scheduling_rot_wing_init(void);
+extern void eff_scheduling_rot_wing_periodic(void);
+
+extern struct RW_Model RW;
+#endif // CTRL_EFF_SCHED_ROT_WING_H
+
diff --git a/sw/airborne/modules/rot_wing_drone/rot_wing_automation.h b/sw/airborne/modules/rot_wing_drone/rot_wing_automation.h
index 2ca119d0b4..d9d2e303b7 100644
--- a/sw/airborne/modules/rot_wing_drone/rot_wing_automation.h
+++ b/sw/airborne/modules/rot_wing_drone/rot_wing_automation.h
@@ -28,7 +28,12 @@
#include "std.h"
#include "math/pprz_algebra_float.h"
+
+#ifdef RW_USE_MODULES_V2
+#include "modules/rot_wing_drone/rotwing_state_V2.h"
+#else
#include "modules/rot_wing_drone/rotwing_state.h"
+#endif
extern void init_rot_wing_automation(void);
extern void periodic_rot_wing_automation(void);
diff --git a/sw/airborne/modules/rot_wing_drone/rotwing_state_V2.c b/sw/airborne/modules/rot_wing_drone/rotwing_state_V2.c
new file mode 100644
index 0000000000..46b18c4040
--- /dev/null
+++ b/sw/airborne/modules/rot_wing_drone/rotwing_state_V2.c
@@ -0,0 +1,773 @@
+/*
+ * Copyright (C) 2023 Tomaso De Ponti
+ *
+ * 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/rotwing/rotwing_state_V2.c"
+ * @author Tomaso De Ponti
+ * This module keeps track of the current state of a rotating wing drone and desired state set by the RC or flightplan. Paramters are being scheduled in each change of a current state and desired state. Functions are defined in this module to call the actual state and desired state and set a desired state.
+ */
+
+#include "modules/rot_wing_drone/rotwing_state_V2.h"
+#include "firmwares/rotorcraft/autopilot_firmware.h"
+#include "modules/core/commands.h"
+#include "modules/actuators/actuators.h"
+#include "modules/core/abi.h"
+
+// Quad state identification
+#ifndef ROTWING_MIN_SKEW_ANGLE_DEG_QUAD
+#define ROTWING_MIN_SKEW_ANGLE_DEG_QUAD 10.0
+#endif
+
+#ifndef ROTWING_MIN_SKEW_ANGLE_COUNTER
+#define ROTWING_MIN_SKEW_ANGLE_COUNTER 10 // Minimum number of loops the skew angle is below ROTWING_MIN_SKEW_ANGLE_COUNTER
+#endif
+
+// Skewing state identification
+#ifndef ROTWING_SKEWING_COUNTER
+#define ROTWING_SKEWING_COUNTER 10 // Minimum number of loops the skew angle is in between QUAD and FW
+#endif
+
+// maximum quad airspeed to force quad state
+#ifndef ROTWING_MAX_QUAD_AIRSPEED
+#define ROTWING_MAX_QUAD_AIRSPEED 20.0
+#endif
+
+// Half skew state identification
+#ifndef ROTWING_HALF_SKEW_ANGLE_DEG
+#define ROTWING_HALF_SKEW_ANGLE_DEG 55.0
+#endif
+
+#ifndef ROTWING_HALF_SKEW_ANGLE_RANG
+#define ROTWING_HALF_SKEW_ANGLE_HALF_RANGE 10.0
+#endif
+
+#ifndef ROTWING_HALF_SKEW_COUNTER
+#define ROTWING_HALF_SKEW_COUNTER 10 // Minimum number of loops the skew angle is at HALF_SKEW_ANGLE_DEG +/- ROTWING_HALF_SKEW_ANGLE_HALF_RANGE to trigger ROTWING_HALF_SKEW_ANGLE state
+#endif
+
+// FW state identification
+#ifndef ROTWING_MIN_FW_SKEW_ANGLE_DEG
+#define ROTWING_MIN_FW_SKEW_ANGLE_DEG 80.0 // Minimum wing angle to fly in fixed wing state
+#endif
+
+#ifndef ROTWING_MIN_FW_COUNTER
+#define ROTWING_MIN_FW_COUNTER 10 // Minimum number of loops the skew angle is above the MIN_FW_SKEW_ANGLE
+#endif
+
+// FW idle state identification
+#ifndef ROTWING_MIN_THRUST_IDLE
+#define ROTWING_MIN_THRUST_IDLE 100
+#endif
+
+#ifndef ROTWING_MIN_THRUST_IDLE_COUNTER
+#define ROTWING_MIN_THRUST_IDLE_COUNTER 10
+#endif
+
+// FW hov mot off state identification
+#ifndef ROTWING_HOV_MOT_RUN_RPM_TH
+#define ROTWING_HOV_MOT_RUN_RPM_TH 800
+#endif
+#ifndef ROTWING_HOV_MOT_OFF_RPM_TH
+#define ROTWING_HOV_MOT_OFF_RPM_TH 50
+#endif
+
+#ifndef ROTWING_HOV_MOT_OFF_COUNTER
+#define ROTWING_HOV_MOT_OFF_COUNTER 10
+#endif
+
+#ifndef ROTWING_STATE_USE_ROTATION_REF_MODEL
+#define ROTWING_STATE_USE_ROTATION_REF_MODEL FALSE
+#endif
+
+// Hover preferred pitch (deg)
+#ifndef ROTWING_STATE_HOVER_PREF_PITCH
+#define ROTWING_STATE_HOVER_PREF_PITCH 0.0
+#endif
+
+// Transition preferred pitch (deg)
+#ifndef ROTWING_STATE_TRANSITION_PREF_PITCH
+#define ROTWING_STATE_TRANSITION_PREF_PITCH 3.0
+#endif
+
+// Forward preferred pitch (deg)
+#ifndef ROTWING_STATE_FW_PREF_PITCH
+#define ROTWING_STATE_FW_PREF_PITCH 8.0
+#endif
+
+// Make sure the rotmech dynamics are provided if the virtual rotmech is used
+#ifndef USE_ROTMECH_VIRTUAL
+#define USE_ROTMECH_VIRTUAL FALSE
+#endif
+
+#if USE_ROTMECH_VIRTUAL
+#ifndef ROTMECH_DYN
+#error "Rotmech dynamics are not provided. Please provide them in your airframe file."
+#endif
+#endif
+
+// stream ADC data if ADC rotation sensor
+#ifndef ADC_WING_ROTATION
+#define ADC_WING_ROTATION FALSE
+#endif
+#if ADC_WING_ROTATION
+#include "wing_rotation_adc_sensor.h"
+#endif
+/** ABI binding feedback data.
+ */
+#ifndef ROTWING_STATE_ACT_FEEDBACK_ID
+#define ROTWING_STATE_ACT_FEEDBACK_ID ABI_BROADCAST
+#endif
+abi_event rotwing_state_feedback_ev;
+static void rotwing_state_feedback_cb(uint8_t sender_id, struct act_feedback_t *feedback_msg, uint8_t num_act);
+#define ROTWING_STATE_NUM_HOVER_RPM 4
+int32_t rotwing_state_hover_rpm[ROTWING_STATE_NUM_HOVER_RPM] = {0, 0, 0, 0};
+
+struct RotwingState rotwing_state;
+struct RotWingStateSettings rotwing_state_settings;
+struct RotWingStateSkewing rotwing_state_skewing;
+
+uint8_t rotwing_state_hover_counter = 0;
+uint8_t rotwing_state_skewing_counter = 0;
+uint8_t rotwing_state_fw_counter = 0;
+uint8_t rotwing_state_fw_idle_counter = 0;
+uint8_t rotwing_state_fw_m_off_counter = 0;
+
+float rotwing_state_max_hover_speed = 7;
+#ifdef NAV_HYBRID_MAX_AIRSPEED
+float rotwing_state_max_fw_speed = NAV_HYBRID_MAX_AIRSPEED;
+#else
+float rotwing_state_max_fw_speed = 20;
+#endif
+
+bool hover_motors_active = true;
+bool bool_disable_hover_motors = false;
+
+inline void rotwing_check_set_current_state(void);
+inline void rotwing_switch_state(void);
+
+inline void rotwing_state_set_hover_settings(void);
+inline void rotwing_state_set_skewing_settings(void);
+inline void rotwing_state_set_fw_settings(void);
+inline void rotwing_state_set_fw_hov_mot_idle_settings(void);
+inline void rotwing_state_set_fw_hov_mot_off_settings(void);
+
+inline void rotwing_state_set_state_settings(void);
+inline void rotwing_state_skewer(void);
+inline void rotwing_state_free_processor(void);
+
+#if PERIODIC_TELEMETRY
+#include "modules/datalink/telemetry.h"
+static void send_rotating_wing_state(struct transport_tx *trans, struct link_device *dev)
+{
+ uint16_t adc_dummy = 0;
+
+ pprz_msg_send_ROTATING_WING_STATE(trans, dev, AC_ID,
+ &rotwing_state.current_state,
+ &rotwing_state.desired_state,
+ &rotwing_state_skewing.wing_angle_deg,
+ &rotwing_state_skewing.wing_angle_deg_sp,
+ &adc_dummy,
+ &rotwing_state_skewing.servo_pprz_cmd);
+}
+#endif // PERIODIC_TELEMETRY
+
+void rotwing_state_force_skew_off(void)
+{
+ rotwing_state_skewing.force_rotation_angle = false;
+}
+
+void init_rotwing_state(void)
+{
+ // Bind ABI messages
+ AbiBindMsgACT_FEEDBACK(ROTWING_STATE_ACT_FEEDBACK_ID, &rotwing_state_feedback_ev, rotwing_state_feedback_cb);
+
+ // Start the drone in a desired hover state
+ rotwing_state.current_state = ROTWING_STATE_HOVER;
+ rotwing_state.desired_state = ROTWING_STATE_HOVER;
+ rotwing_state.requested_config = ROTWING_CONFIGURATION_HOVER;
+
+ rotwing_state_settings.preferred_pitch_value = 0;
+
+ rotwing_state_skewing.wing_angle_deg_sp = 0;
+ rotwing_state_skewing.wing_angle_deg = 0;
+ rotwing_state_skewing.servo_pprz_cmd = -MAX_PPRZ;
+ rotwing_state_skewing.airspeed_scheduling = false;
+ rotwing_state_skewing.force_rotation_angle = false;
+
+#if PERIODIC_TELEMETRY
+ register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_ROTATING_WING_STATE, send_rotating_wing_state);
+#endif
+}
+
+void periodic_rotwing_state(void)
+{
+ // Check and set the current state
+ rotwing_check_set_current_state();
+
+ // Check and update desired state
+ if (guidance_h.mode == GUIDANCE_H_MODE_NAV) {
+ // Run the free state requester if free configuration requestes
+ if(rotwing_state.requested_config == ROTWING_CONFIGURATION_FREE) {
+ rotwing_state_free_processor();
+ }
+ rotwing_switch_state();
+ } else if (guidance_h.mode == GUIDANCE_H_MODE_NONE) {
+ if (stabilization.mode == STABILIZATION_MODE_ATTITUDE) {
+ if (stabilization.att_submode == STABILIZATION_ATT_SUBMODE_FORWARD) {
+ rotwing_state_set_fw_settings();
+ } else {
+ rotwing_state_set_hover_settings();
+ }
+ }
+ }
+ // } else {
+ // rotwing_switch_state();
+ // }
+
+ // Run the wing skewer
+ rotwing_state_skewer();
+
+ //TODO: incorparate motor active / disbaling depending on called flight state
+ // Switch on motors if flight mode is attitude
+ if (guidance_h.mode == GUIDANCE_H_MODE_NONE) {
+ bool_disable_hover_motors = false;
+ }
+ struct FloatEulers eulers_zxy;
+ float_eulers_of_quat_zxy(&eulers_zxy, stateGetNedToBodyQuat_f());
+
+ // Evaluate motors_on boolean
+ if (!hover_motors_active) {
+ if (stateGetAirspeed_f() < 15.) {
+ hover_motors_active = true;
+ bool_disable_hover_motors = false;
+ } else if (eulers_zxy.theta > RadOfDeg(15.0)) {
+ hover_motors_active = true;
+ bool_disable_hover_motors = false;
+ }
+ } else {
+ bool_disable_hover_motors = false;
+ }
+ float pitch_progression = (rotwing_state_skewing.wing_angle_deg - ROTWING_HALF_SKEW_ANGLE_DEG) / (90.0-ROTWING_HALF_SKEW_ANGLE_DEG);
+ // Calculate scheduled pitch angle
+ switch (rotwing_state_settings.preferred_pitch_setting){
+ case ROTWING_STATE_PITCH_QUAD_SETTING:
+ rotwing_state_settings.preferred_pitch_value = RadOfDeg(ROTWING_STATE_HOVER_PREF_PITCH);
+ break;
+ case ROTWING_STATE_PITCH_TRANSITION_SETTING:
+ rotwing_state_settings.preferred_pitch_value = RadOfDeg(ROTWING_STATE_TRANSITION_PREF_PITCH * pitch_progression);
+ break;
+ case ROTWING_STATE_PITCH_FW_SETTING:
+ rotwing_state_settings.preferred_pitch_value = RadOfDeg(ROTWING_STATE_FW_PREF_PITCH);
+ break;
+ }
+}
+
+// Function to request a state
+void request_rotwing_state(uint8_t state)
+{
+ if (state <= ROTWING_STATE_FW_HOV_MOT_OFF) {
+ rotwing_state.desired_state = state;
+ }
+}
+
+// Function to prefer configuration
+void rotwing_request_configuration(uint8_t configuration)
+{
+ switch (configuration) {
+ case ROTWING_CONFIGURATION_HOVER:
+ request_rotwing_state(ROTWING_STATE_HOVER);
+ rotwing_state.requested_config = ROTWING_CONFIGURATION_HOVER;
+ break;
+ case ROTWING_CONFIGURATION_HYBRID:
+ request_rotwing_state(ROTWING_STATE_SKEWING);
+ rotwing_state.requested_config = ROTWING_CONFIGURATION_HYBRID;
+ break;
+ case ROTWING_CONFIGURATION_EFFICIENT:
+ request_rotwing_state(ROTWING_STATE_FW_HOV_MOT_OFF);
+ rotwing_state.requested_config = ROTWING_CONFIGURATION_EFFICIENT;
+ break;
+ case ROTWING_CONFIGURATION_FREE:
+ rotwing_state.requested_config = ROTWING_CONFIGURATION_FREE;
+ break;
+ }
+}
+
+void rotwing_check_set_current_state(void)
+{
+ float current_thrust = (commands[COMMAND_MOTOR_FRONT] + commands[COMMAND_MOTOR_RIGHT] + commands[COMMAND_MOTOR_BACK] + commands[COMMAND_MOTOR_LEFT])/4.0;
+ // if !in_flight, set state to hover
+ if (!autopilot.in_flight) {
+ rotwing_state_hover_counter = 0;
+ rotwing_state_skewing_counter = 0;
+ rotwing_state_fw_counter = 0;
+ rotwing_state_fw_idle_counter = 0;
+ rotwing_state_fw_m_off_counter = 0;
+ rotwing_state.current_state = ROTWING_STATE_HOVER;
+ return;
+ }
+
+ // States can be checked according to wing angle sensor, setpoints .....
+ uint8_t prev_state = rotwing_state.current_state;
+ switch (prev_state) {
+ case ROTWING_STATE_HOVER:
+ // Check if state needs to be set to skewing
+ if (rotwing_state_skewing.wing_angle_deg > ROTWING_MIN_SKEW_ANGLE_DEG_QUAD) {
+ rotwing_state_skewing_counter++;
+ } else {
+ rotwing_state_skewing_counter = 0;
+ }
+
+ // Switch state if necessary
+ if (rotwing_state_skewing_counter > ROTWING_MIN_SKEW_ANGLE_COUNTER) {
+ rotwing_state.current_state = ROTWING_STATE_SKEWING;
+ rotwing_state_skewing_counter = 0;
+ }
+ break;
+
+ case ROTWING_STATE_SKEWING:
+ // Check if state needs to be set to hover
+ if (rotwing_state_skewing.wing_angle_deg < ROTWING_MIN_SKEW_ANGLE_DEG_QUAD) {
+ rotwing_state_hover_counter++;
+ } else {
+ rotwing_state_hover_counter = 0;
+ }
+
+ // Check if state needs to be set to fixed wing
+ if (rotwing_state_skewing.wing_angle_deg > ROTWING_MIN_FW_SKEW_ANGLE_DEG) {
+ rotwing_state_fw_counter++;
+ } else {
+ rotwing_state_fw_counter = 0;
+ }
+
+ // Switch state if necessary
+ if (rotwing_state_hover_counter > ROTWING_MIN_SKEW_ANGLE_COUNTER) {
+ rotwing_state.current_state = ROTWING_STATE_HOVER;
+ rotwing_state_hover_counter = 0;
+ }
+
+ if (rotwing_state_fw_counter > ROTWING_MIN_FW_COUNTER) {
+ rotwing_state.current_state = ROTWING_STATE_FW;
+ rotwing_state_fw_counter = 0;
+ }
+ break;
+
+ case ROTWING_STATE_FW:
+ // Check if state needs to be set to skewing
+ if (rotwing_state_skewing.wing_angle_deg < ROTWING_MIN_FW_SKEW_ANGLE_DEG) {
+ rotwing_state_skewing_counter++;
+ } else {
+ rotwing_state_skewing_counter = 0;
+ }
+
+ // Check if state needs to be set to fixed wing with hover motors idle (If hover thrust below threshold)
+ if (current_thrust < ROTWING_MIN_THRUST_IDLE && rotwing_state.desired_state > ROTWING_STATE_FW) {
+ rotwing_state_fw_idle_counter++;
+ } else {
+ rotwing_state_fw_idle_counter = 0;
+ }
+
+ // Switch state if necessary
+ if (rotwing_state_skewing_counter > ROTWING_MIN_FW_COUNTER) {
+ rotwing_state.current_state = ROTWING_STATE_SKEWING;
+ rotwing_state_skewing_counter = 0;
+ rotwing_state_fw_idle_counter = 0;
+ } else if (rotwing_state_fw_idle_counter > ROTWING_MIN_THRUST_IDLE_COUNTER
+ && rotwing_state_skewing_counter == 0) {
+ rotwing_state.current_state = ROTWING_STATE_FW_HOV_MOT_IDLE;
+ rotwing_state_skewing_counter = 0;
+ rotwing_state_fw_idle_counter = 0;
+ }
+ break;
+
+ case ROTWING_STATE_FW_HOV_MOT_IDLE:
+ // Check if state needs to be set to fixed wing with hover motors activated
+ if (current_thrust > ROTWING_MIN_THRUST_IDLE
+ || rotwing_state.desired_state < ROTWING_STATE_FW_HOV_MOT_IDLE) {
+ rotwing_state_fw_counter++;
+ } else {
+ rotwing_state_fw_counter = 0;
+ }
+ // Check if state needs to be set to fixed wing with hover motors off (if zero rpm on hover motors)
+ if (rotwing_state_hover_rpm[0] == 0
+ && rotwing_state_hover_rpm[1] == 0
+ && rotwing_state_hover_rpm[2] == 0
+ && rotwing_state_hover_rpm[3] == 0) {
+#if !USE_NPS
+ rotwing_state_fw_m_off_counter++;
+#endif
+ } else {
+ rotwing_state_fw_m_off_counter = 0;
+ }
+
+ // Switch state if necessary
+ if (rotwing_state_fw_counter > ROTWING_MIN_THRUST_IDLE_COUNTER) {
+ rotwing_state.current_state = ROTWING_STATE_FW;
+ rotwing_state_fw_counter = 0;
+ rotwing_state_fw_m_off_counter = 0;
+ } else if (rotwing_state_fw_m_off_counter > ROTWING_HOV_MOT_OFF_COUNTER
+ && rotwing_state_fw_counter == 0) {
+ rotwing_state.current_state = ROTWING_STATE_FW_HOV_MOT_OFF;
+ rotwing_state_fw_counter = 0;
+ rotwing_state_fw_m_off_counter = 0;
+ }
+ break;
+
+ case ROTWING_STATE_FW_HOV_MOT_OFF:
+ // Check if state needs to be set to fixed wing with hover motors idle (if rpm on hover motors)
+ if (rotwing_state_hover_rpm[0] > ROTWING_HOV_MOT_OFF_RPM_TH
+ && rotwing_state_hover_rpm[1] > ROTWING_HOV_MOT_OFF_RPM_TH
+ && rotwing_state_hover_rpm[2] > ROTWING_HOV_MOT_OFF_RPM_TH
+ && rotwing_state_hover_rpm[3] > ROTWING_HOV_MOT_OFF_RPM_TH) {
+ rotwing_state_fw_idle_counter++;
+ } else {
+ rotwing_state_fw_idle_counter = 0;
+ }
+
+ // Switch state if necessary
+ if (rotwing_state_fw_idle_counter > ROTWING_MIN_THRUST_IDLE_COUNTER) {
+ rotwing_state.current_state = ROTWING_STATE_FW_HOV_MOT_IDLE;
+ rotwing_state_fw_idle_counter = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+// Function that handles settings for switching state(s)
+void rotwing_switch_state(void)
+{
+ switch (rotwing_state.current_state) {
+ case ROTWING_STATE_HOVER:
+ if (rotwing_state.desired_state > ROTWING_STATE_HOVER) {
+ rotwing_state_set_skewing_settings();
+ } else {
+ rotwing_state_set_hover_settings();
+ }
+ break;
+
+ case ROTWING_STATE_SKEWING:
+ if (rotwing_state.desired_state < ROTWING_STATE_SKEWING && stateGetAirspeed_f() < ROTWING_MAX_QUAD_AIRSPEED) {
+ rotwing_state_set_hover_settings();
+ } else {
+ rotwing_state_set_skewing_settings();
+ }
+ break;
+
+ case ROTWING_STATE_FW:
+ if (rotwing_state.desired_state > ROTWING_STATE_FW) {
+ rotwing_state_set_fw_hov_mot_idle_settings();
+ } else if (rotwing_state.desired_state < ROTWING_STATE_FW) {
+ rotwing_state_set_skewing_settings();
+ } else {
+ rotwing_state_set_fw_settings();
+ }
+ break;
+
+ case ROTWING_STATE_FW_HOV_MOT_IDLE:
+ if (rotwing_state.desired_state > ROTWING_STATE_FW_HOV_MOT_IDLE) {
+ rotwing_state_set_fw_hov_mot_off_settings();
+ } else if (rotwing_state.desired_state < ROTWING_STATE_FW_HOV_MOT_IDLE) {
+ rotwing_state_set_fw_settings();
+ } else {
+ rotwing_state_set_fw_hov_mot_idle_settings();
+ }
+ break;
+
+ case ROTWING_STATE_FW_HOV_MOT_OFF:
+ if (rotwing_state.desired_state < ROTWING_STATE_FW_HOV_MOT_OFF) {
+ rotwing_state_set_fw_hov_mot_idle_settings();
+ } else {
+ rotwing_state_set_fw_hov_mot_off_settings();
+ }
+ break;
+ }
+}
+
+void rotwing_state_set_hover_settings(void)
+{
+ rotwing_state_settings.wing_scheduler = ROTWING_STATE_WING_QUAD_SETTING;
+ rotwing_state_settings.hover_motors_active = true;
+ rotwing_state_settings.hover_motors_disable = false;
+ rotwing_state_settings.force_forward = false;
+ rotwing_state_settings.preferred_pitch_setting = ROTWING_STATE_PITCH_QUAD_SETTING;
+ rotwing_state_settings.stall_protection = false;
+ rotwing_state_settings.max_v_climb = 2.0;
+ rotwing_state_settings.max_v_descend = 1.0;
+ rotwing_state_settings.nav_max_speed = rotwing_state_max_hover_speed; // Using setting
+ rotwing_state_set_state_settings();
+}
+
+void rotwing_state_set_skewing_settings(void)
+{
+ // Wing may be skewed to quad when desired state is hover and skewing settings set by state machine
+ if (rotwing_state.desired_state == ROTWING_STATE_HOVER) {
+ rotwing_state_settings.wing_scheduler = ROTWING_STATE_WING_QUAD_SETTING;
+ } else {
+ rotwing_state_settings.wing_scheduler = ROTWING_STATE_WING_SCHEDULING_SETTING;
+ }
+ rotwing_state_settings.hover_motors_active = true;
+ rotwing_state_settings.hover_motors_disable = false;
+ rotwing_state_settings.force_forward = false;
+ rotwing_state_settings.preferred_pitch_setting = ROTWING_STATE_PITCH_TRANSITION_SETTING;
+ rotwing_state_settings.stall_protection = false;
+ rotwing_state_settings.max_v_climb = 2.0;
+ rotwing_state_settings.max_v_descend = 1.0;
+ rotwing_state_settings.nav_max_speed = rotwing_state_max_fw_speed;// Big as we use airspeed guidance
+ rotwing_state_set_state_settings();
+}
+
+void rotwing_state_set_fw_settings(void)
+{
+ rotwing_state_settings.wing_scheduler = ROTWING_STATE_WING_FW_SETTING;
+ rotwing_state_settings.hover_motors_active = true;
+ rotwing_state_settings.hover_motors_disable = false;
+ rotwing_state_settings.force_forward = true;
+ rotwing_state_settings.preferred_pitch_setting = ROTWING_STATE_PITCH_FW_SETTING;
+ rotwing_state_settings.stall_protection = true;
+ rotwing_state_settings.max_v_climb = 4.0;
+ rotwing_state_settings.max_v_descend = 4.0;
+ rotwing_state_settings.nav_max_speed = rotwing_state_max_fw_speed; // Big as we use airspeed guidance
+ //rotwing_state_settings.nav_max_speed = 100; // Big as we use airspeed guidance
+ rotwing_state_set_state_settings();
+}
+
+void rotwing_state_set_fw_hov_mot_idle_settings(void)
+{
+ rotwing_state_settings.wing_scheduler = ROTWING_STATE_WING_FW_SETTING;
+ rotwing_state_settings.hover_motors_active = false;
+ rotwing_state_settings.hover_motors_disable = false;
+ rotwing_state_settings.force_forward = true;
+ rotwing_state_settings.preferred_pitch_setting = ROTWING_STATE_PITCH_FW_SETTING;
+ rotwing_state_settings.stall_protection = true;
+ rotwing_state_settings.max_v_climb = 4.0;
+ rotwing_state_settings.max_v_descend = 4.0;
+ rotwing_state_settings.nav_max_speed = rotwing_state_max_fw_speed; // Big as we use airspeed guidance
+ rotwing_state_set_state_settings();
+}
+
+void rotwing_state_set_fw_hov_mot_off_settings(void)
+{
+ rotwing_state_settings.wing_scheduler = ROTWING_STATE_WING_FW_SETTING;
+ rotwing_state_settings.hover_motors_active = false;
+ rotwing_state_settings.hover_motors_disable = true;
+ rotwing_state_settings.force_forward = true;
+ rotwing_state_settings.preferred_pitch_setting = ROTWING_STATE_PITCH_FW_SETTING;
+ rotwing_state_settings.stall_protection = true;
+ rotwing_state_settings.max_v_climb = 4.0;
+ rotwing_state_settings.max_v_descend = 4.0;
+ rotwing_state_settings.nav_max_speed = rotwing_state_max_fw_speed; // Big as we use airspeed guidance
+ rotwing_state_set_state_settings();
+}
+
+void rotwing_state_set_state_settings(void)
+{
+ if (!rotwing_state_skewing.force_rotation_angle) {
+ switch (rotwing_state_settings.wing_scheduler) {
+ case ROTWING_STATE_WING_QUAD_SETTING:
+ rotwing_state_skewing.airspeed_scheduling = false;
+ rotwing_state_skewing.wing_angle_deg_sp = 0;
+ break;
+ case ROTWING_STATE_WING_SCHEDULING_SETTING:
+ rotwing_state_skewing.airspeed_scheduling = true;
+ break;
+ case ROTWING_STATE_WING_FW_SETTING:
+ rotwing_state_skewing.airspeed_scheduling = true;
+ break;
+ }
+ } else {
+ rotwing_state_skewing.airspeed_scheduling = false;
+ }
+
+ hover_motors_active = rotwing_state_settings.hover_motors_active;
+
+ bool_disable_hover_motors = rotwing_state_settings.hover_motors_disable;
+
+ force_forward = rotwing_state_settings.force_forward;
+
+ nav_max_speed = rotwing_state_settings.nav_max_speed;
+ nav_goto_max_speed = rotwing_state_settings.nav_max_speed;
+
+ // TO DO: pitch angle now hard coded scheduled by wing angle
+
+ // Stall protection handled by hover_motors_active boolean
+
+ // TO DO: Climb and descend speeds now handled by guidance airspeed
+}
+
+void rotwing_state_skewer(void)
+{
+ if (rotwing_state_skewing.airspeed_scheduling) {
+ float wing_angle_scheduled_sp_deg = 0;
+ float airspeed = stateGetAirspeed_f();
+ if (airspeed < 8) {
+ wing_angle_scheduled_sp_deg = 0;
+ } else if (airspeed < 10 && (rotwing_state.desired_state > ROTWING_STATE_HOVER)) {
+ wing_angle_scheduled_sp_deg = ROTWING_HALF_SKEW_ANGLE_DEG;
+ } else if (airspeed > 10) {
+ wing_angle_scheduled_sp_deg = ((airspeed - 10.)) / 4. * (90.-ROTWING_HALF_SKEW_ANGLE_DEG) + ROTWING_HALF_SKEW_ANGLE_DEG;
+ } else {
+ wing_angle_scheduled_sp_deg = 0;
+ }
+
+ Bound(wing_angle_scheduled_sp_deg, 0., 90.)
+ rotwing_state_skewing.wing_angle_deg_sp = wing_angle_scheduled_sp_deg;
+ }
+}
+
+void rotwing_state_free_processor(void)
+{
+ // Get current speed vector
+ struct EnuCoor_f *groundspeed = stateGetSpeedEnu_f();
+ float current_groundspeed = FLOAT_VECT2_NORM(*groundspeed);
+
+ // Get current airspeed
+ float airspeed = stateGetAirspeed_f();
+
+ // Get windspeed vector
+ struct FloatEulers eulers_zxy;
+ float_eulers_of_quat_zxy(&eulers_zxy, stateGetNedToBodyQuat_f());
+
+ float psi = eulers_zxy.psi;
+ float cpsi = cosf(psi);
+ float spsi = sinf(psi);
+ struct FloatVect2 airspeed_v = { spsi * airspeed, cpsi * airspeed };
+ struct FloatVect2 windspeed_v;
+ VECT2_DIFF(windspeed_v, *groundspeed, airspeed_v);
+
+ // Get goto target information
+ struct FloatVect2 pos_error;
+ struct EnuCoor_f *pos = stateGetPositionEnu_f();
+ VECT2_DIFF(pos_error, nav_rotorcraft_base.goto_wp.to, *pos);
+
+ /*
+ Calculations
+ */
+ // speed over pos_error projection
+ struct FloatVect2 pos_error_norm;
+ VECT2_COPY(pos_error_norm, pos_error);
+ float_vect2_normalize(&pos_error_norm);
+ float dist_to_target = sqrtf(nav_rotorcraft_base.goto_wp.dist2_to_wp);
+ float max_speed_decel2 = fabsf(2.f * dist_to_target * nav_max_deceleration_sp); // dist_to_wp can only be positive, but just in case
+ float max_speed_decel = sqrtf(max_speed_decel2);
+
+ // Check if speed setpoint above set airspeed
+ struct FloatVect2 desired_airspeed_v;
+ struct FloatVect2 groundspeed_sp;
+ groundspeed_sp.x = pos_error.x * nav_hybrid_pos_gain;
+ groundspeed_sp.y = pos_error.y * nav_hybrid_pos_gain;
+ VECT2_COPY(desired_airspeed_v, groundspeed_sp);
+ VECT2_ADD(desired_airspeed_v, windspeed_v);
+
+ float desired_airspeed = FLOAT_VECT2_NORM(desired_airspeed_v);
+ float airspeed_error = rotwing_state_max_fw_speed - airspeed;
+
+ // Request hybrid if we have to decelerate and approaching target
+ if (max_speed_decel < current_groundspeed) {
+ request_rotwing_state(ROTWING_STATE_SKEWING);
+ } else if ((desired_airspeed > 15) && ((current_groundspeed + airspeed_error) < max_speed_decel)) {
+ request_rotwing_state(ROTWING_STATE_FW_HOV_MOT_OFF);
+ }
+}
+
+void rotwing_state_skew_actuator_periodic(void)
+{
+
+ // calc rotation percentage of setpoint (0 deg = -1, 45 deg = 0, 90 deg = 1)
+ float wing_rotation_percentage = (rotwing_state_skewing.wing_angle_deg_sp - 45.) / 45.;
+ Bound(wing_rotation_percentage, -1., 1.);
+
+ float servo_pprz_cmd = MAX_PPRZ * wing_rotation_percentage;
+ // Calulcate rotation_cmd
+ Bound(servo_pprz_cmd, -MAX_PPRZ, MAX_PPRZ);
+
+#if ROTWING_STATE_USE_ROTATION_REF_MODEL
+ // Rotate with second order filter
+ static float rotwing_state_skew_p_cmd = -MAX_PPRZ;
+ static float rotwing_state_skew_d_cmd = 0;
+ float speed_sp = 0.001 * (servo_pprz_cmd - rotwing_state_skew_p_cmd);
+ rotwing_state_skew_d_cmd += 0.003 * (speed_sp - rotwing_state_skew_d_cmd);
+ rotwing_state_skew_p_cmd += rotwing_state_skew_d_cmd;
+ Bound(rotwing_state_skew_p_cmd, -MAX_PPRZ, MAX_PPRZ);
+ rotwing_state_skewing.servo_pprz_cmd = rotwing_state_skew_p_cmd;
+#else
+ // Directly controlling the wing rotation
+ rotwing_state_skewing.servo_pprz_cmd = servo_pprz_cmd;
+#endif
+
+#if USE_NPS
+ // Export to the index of the SKEW in the NPS_ACTUATOR_NAMES array
+ commands[COMMAND_ROT_MECH] = (rotwing_state_skewing.servo_pprz_cmd + MAX_PPRZ) / 2.; // Scale to simulation command
+
+#if USE_ROTMECH_VIRTUAL
+ // Calculate discrete first order dynamics of rot mech
+ float dyn_dis = 1.0-exp(-ROTMECH_DYN / PERIODIC_FREQUENCY);
+ // Simulate wing angle from command
+ float prev_rotmech_state = rotwing_state_skewing.wing_angle_deg;
+ rotwing_state_skewing.wing_angle_deg = prev_rotmech_state + dyn_dis * (rotwing_state_skewing.wing_angle_deg_sp - prev_rotmech_state);
+#else
+ // Simulate wing angle from command
+ rotwing_state_skewing.wing_angle_deg = (float) rotwing_state_skewing.servo_pprz_cmd / MAX_PPRZ * 45. + 45.;
+#endif // USE_ROTMECH_VIRTUAL
+ // SEND ABI Message to ctr_eff_sched and other modules that want Actuator position feedback
+ struct act_feedback_t feedback;
+ feedback.idx = COMMAND_ROT_MECH;
+ feedback.position = 0.5 * M_PI - RadOfDeg(rotwing_state_skewing.wing_angle_deg);
+ feedback.set.position = true;
+ // Send ABI message
+ AbiSendMsgACT_FEEDBACK(ACT_FEEDBACK_UAVCAN_ID, &feedback, 1);
+#else
+ commands[COMMAND_ROT_MECH] = rotwing_state_skewing.servo_pprz_cmd;
+#endif // USE_NPS
+}
+
+static void rotwing_state_feedback_cb(uint8_t __attribute__((unused)) sender_id,
+ struct act_feedback_t UNUSED *feedback_msg, uint8_t UNUSED num_act_message)
+{
+ for (int i = 0; i < num_act_message; i++) {
+
+ for (int i = 0; i < num_act_message; i++) {
+ // Check for wing rotation feedback
+ if ((feedback_msg[i].set.position) && (feedback_msg[i].idx == COMMAND_ROT_MECH)) {
+ // Get wing rotation angle from sensor
+ float wing_angle_rad = 0.5 * M_PI - feedback_msg[i].position;
+ rotwing_state_skewing.wing_angle_deg = DegOfRad(wing_angle_rad);
+
+ // Bound wing rotation angle
+ Bound(rotwing_state_skewing.wing_angle_deg, 0, 90.);
+ }
+ }
+
+ // Sanity check that index is valid
+ int idx = feedback_msg[i].idx;
+ if (feedback_msg[i].set.rpm) {
+ if ((idx == SERVO_MOTOR_FRONT_IDX) || (idx == SERVO_BMOTOR_FRONT_IDX)) {
+ rotwing_state_hover_rpm[0] = feedback_msg->rpm;
+ } else if ((idx == SERVO_MOTOR_RIGHT_IDX) || (idx == SERVO_BMOTOR_RIGHT_IDX)) {
+ rotwing_state_hover_rpm[1] = feedback_msg->rpm;
+ } else if ((idx == SERVO_MOTOR_BACK_IDX) || (idx == SERVO_BMOTOR_BACK_IDX)) {
+ rotwing_state_hover_rpm[2] = feedback_msg->rpm;
+ } else if ((idx == SERVO_MOTOR_LEFT_IDX) || (idx == SERVO_BMOTOR_LEFT_IDX)) {
+ rotwing_state_hover_rpm[3] = feedback_msg->rpm;
+ }
+ }
+ }
+}
+
diff --git a/sw/airborne/modules/rot_wing_drone/rotwing_state_V2.h b/sw/airborne/modules/rot_wing_drone/rotwing_state_V2.h
new file mode 100644
index 0000000000..d3b4068be0
--- /dev/null
+++ b/sw/airborne/modules/rot_wing_drone/rotwing_state_V2.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2023 Tomaso De Ponti
+ *
+ * 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/rotwing/rotwing_state_V2.h"
+ * @author Tomaso De Ponti
+ * This module keeps track of the current state of a rotating wing drone and desired state set by the RC or flightplan. Paramters are being scheduled in each change of a current state and desired state. Functions are defined in this module to call the actual state and desired state and set a desired state.
+ */
+
+#ifndef ROTWING_STATE_H
+#define ROTWING_STATE_H
+
+#include "std.h"
+
+/** Rotwing States
+ */
+#define ROTWING_STATE_HOVER 0 // Wing is skewed in 0 degrees (quad)
+#define ROTWING_STATE_SKEWING 1 // WIng is skewing
+#define ROTWING_STATE_FW 2 // Wing is skewed at 90 degrees (fixed wing), hover motors have full authority
+#define ROTWING_STATE_FW_HOV_MOT_IDLE 3 // Wing is skewed at 90 degrees (fixed wing), hover motors are forced to idle
+#define ROTWING_STATE_FW_HOV_MOT_OFF 4 // Wing is skewed at 90 degrees (fixed wubg), hover motors are switched off
+#define ROTWING_STATE_FREE 5 // This is a desired state for which the controller has to decide the desired state itself
+
+/** Rotwing Configurations
+ */
+#define ROTWING_CONFIGURATION_HOVER 0 // UAV is in hover
+#define ROTWING_CONFIGURATION_HYBRID 1 // UAV can fly forward and hover if airspeed low, hover motors on
+#define ROTWING_CONFIGURATION_EFFICIENT 2 // UAV flies efficiently forward (FW)
+#define ROTWING_CONFIGURATION_FREE 3 // UAV switched between efficient and hybrid dependent on deceleration
+
+struct RotwingState {
+ uint8_t current_state;
+ uint8_t desired_state;
+ uint8_t requested_config;
+};
+
+#define ROTWING_STATE_WING_QUAD_SETTING 0 // Wing skew at 0 degrees
+#define ROTWING_STATE_WING_SCHEDULING_SETTING 1 // Wing skew handled by airspeed scheduler
+#define ROTWING_STATE_WING_FW_SETTING 2 // Wing skew at 90 degrees
+
+#define ROTWING_STATE_PITCH_QUAD_SETTING 0 // Pitch at prefered hover
+#define ROTWING_STATE_PITCH_TRANSITION_SETTING 1 // Pitch scheduled
+#define ROTWING_STATE_PITCH_FW_SETTING 2 // Pitch at prefered forward
+
+struct RotWingStateSettings {
+ uint8_t wing_scheduler;
+ bool hover_motors_active;
+ bool hover_motors_disable;
+ bool force_forward;
+ uint8_t preferred_pitch_setting;
+ float preferred_pitch_value;
+ bool stall_protection;
+ float max_v_climb;
+ float max_v_descend;
+ float nav_max_speed;
+};
+
+struct RotWingStateSkewing {
+ float wing_angle_deg_sp; // Wing angle setpoint in deg
+ float wing_angle_deg; // Wing angle from sensor in deg
+ int32_t servo_pprz_cmd; // Wing rotation servo pprz cmd
+ bool airspeed_scheduling; // Airspeed scheduling on or off
+ bool force_rotation_angle; // Setting to force wing_angle_deg_sp
+};
+
+extern struct RotwingState rotwing_state;
+extern struct RotWingStateSettings rotwing_state_settings;
+extern struct RotWingStateSkewing rotwing_state_skewing;
+
+extern float rotwing_state_max_hover_speed;
+
+extern bool hover_motors_active;
+extern bool bool_disable_hover_motors;
+
+extern void init_rotwing_state(void);
+extern void periodic_rotwing_state(void);
+extern void request_rotwing_state(uint8_t state);
+extern void rotwing_request_configuration(uint8_t configuration);
+extern void rotwing_state_skew_actuator_periodic(void);
+extern void rotwing_state_force_skew_off(void);
+
+#endif // ROTWING_STATE_H
diff --git a/sw/airborne/modules/rot_wing_drone/wing_rotation_adc_sensor.c b/sw/airborne/modules/rot_wing_drone/wing_rotation_adc_sensor.c
index d525110a6b..b79e3d09dc 100644
--- a/sw/airborne/modules/rot_wing_drone/wing_rotation_adc_sensor.c
+++ b/sw/airborne/modules/rot_wing_drone/wing_rotation_adc_sensor.c
@@ -71,7 +71,7 @@ void wing_rotation_adc_to_deg(void)
// SEND ABI Message to ctr_eff_sched and other modules that want Actuator position feedback
struct act_feedback_t feedback = {0};
- feedback.idx = SERVO_ROTATION_MECH_IDX;
+ feedback.idx = COMMAND_ROT_MECH;
feedback.position = 0.5 * M_PI - RadOfDeg(wing_angle_deg);
feedback.set.position = true;
diff --git a/sw/airborne/modules/rot_wing_drone/wing_rotation_adc_sensor.h b/sw/airborne/modules/rot_wing_drone/wing_rotation_adc_sensor.h
index 736474d748..32dac4452b 100644
--- a/sw/airborne/modules/rot_wing_drone/wing_rotation_adc_sensor.h
+++ b/sw/airborne/modules/rot_wing_drone/wing_rotation_adc_sensor.h
@@ -26,6 +26,7 @@
#ifndef WING_ROTATION_ADC_SENSOR_H
#define WING_ROTATION_ADC_SENSOR_H
+extern float adc_wing_rotation_extern;
extern void wing_rotation_adc_init(void);
extern void wing_rotation_adc_to_deg(void);