2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
5 * @addtogroup ManualControl
6 * @brief Interpretes the control input in ManualControlCommand
10 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016.
11 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
13 * @see The GNU Public License (GPL) Version 3
15 ******************************************************************************/
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include "inc/manualcontrol.h"
33 #include <sanitycheck.h>
34 #include <manualcontrolcommand.h>
35 #include <accessorydesired.h>
36 #include <flightstatus.h>
37 #include <flightmodesettings.h>
38 #include <stabilizationdesired.h>
39 #ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
40 #include <statusvtolland.h>
44 #define ARMED_THRESHOLD_SWITCH 0.20f
45 #define ARMED_THRESHOLD_STICK 0.80f
46 #define GROUND_LOW_THROTTLE 0.01f
51 ARM_STATE_ARMING_MANUAL
,
53 ARM_STATE_DISARMING_MANUAL
,
54 ARM_STATE_DISARMING_TIMEOUT
60 static void setArmedIfChanged(uint8_t val
);
61 static uint32_t timeDifferenceMs(portTickType start_time
, portTickType end_time
);
62 static bool okToArm(void);
63 static bool forcedDisArm(void);
67 * @brief Handler to interprete Command inputs in regards to arming/disarming
68 * @input: ManualControlCommand, AccessoryDesired
69 * @output: FlightStatus.Arming
71 void armHandler(bool newinit
, FrameType_t frameType
)
73 static ArmState_t armState
;
76 AccessoryDesiredInitialize();
77 setArmedIfChanged(FLIGHTSTATUS_ARMED_DISARMED
);
78 armState
= ARM_STATE_DISARMED
;
81 portTickType sysTime
= xTaskGetTickCount();
83 FlightModeSettingsData settings
;
84 FlightModeSettingsGet(&settings
);
85 ManualControlCommandData cmd
;
86 ManualControlCommandGet(&cmd
);
87 AccessoryDesiredData acc
;
89 bool lowThrottle
= cmd
.Throttle
< 0;
91 if (frameType
== FRAME_TYPE_GROUND
) {
92 // Deadbanding applied in receiver.c typically at 2% but we don't assume its enabled.
93 lowThrottle
= fabsf(cmd
.Throttle
) < GROUND_LOW_THROTTLE
;
96 bool armSwitch
= false;
98 switch (settings
.Arming
) {
99 case FLIGHTMODESETTINGS_ARMING_ACCESSORY0
:
100 AccessoryDesiredInstGet(0, &acc
);
103 case FLIGHTMODESETTINGS_ARMING_ACCESSORY1
:
104 AccessoryDesiredInstGet(1, &acc
);
107 case FLIGHTMODESETTINGS_ARMING_ACCESSORY2
:
108 AccessoryDesiredInstGet(2, &acc
);
111 case FLIGHTMODESETTINGS_ARMING_ACCESSORY3
:
112 AccessoryDesiredInstGet(3, &acc
);
119 // immediate disarm on switch
120 if (armSwitch
&& acc
.AccessoryVal
<= -ARMED_THRESHOLD_SWITCH
) {
124 if (forcedDisArm()) {
125 // PathPlanner forces explicit disarming due to error condition (crash, impact, fire, ...)
126 armState
= ARM_STATE_DISARMED
;
127 setArmedIfChanged(FLIGHTSTATUS_ARMED_DISARMED
);
131 if (settings
.Arming
== FLIGHTMODESETTINGS_ARMING_ALWAYSDISARMED
) {
132 // In this configuration we always disarm
133 setArmedIfChanged(FLIGHTSTATUS_ARMED_DISARMED
);
138 // The throttle is not low, in case we where arming or disarming, abort
141 case ARM_STATE_DISARMING_MANUAL
:
142 case ARM_STATE_DISARMING_TIMEOUT
:
143 armState
= ARM_STATE_ARMED
;
145 case ARM_STATE_ARMING_MANUAL
:
146 armState
= ARM_STATE_DISARMED
;
149 // Nothing needs to be done in the other states
155 // The rest of these cases throttle is low
156 if (settings
.Arming
== FLIGHTMODESETTINGS_ARMING_ALWAYSARMED
) {
157 // In this configuration, we go into armed state as soon as the throttle is low, never disarm
158 setArmedIfChanged(FLIGHTSTATUS_ARMED_ARMED
);
162 // When the configuration is not "Always armed" and no "Always disarmed",
163 // the state will not be changed when the throttle is not low
164 static portTickType armedDisarmStart
;
165 float armingInputLevel
= 0;
167 // Calc channel see assumptions7
168 switch (settings
.Arming
) {
169 case FLIGHTMODESETTINGS_ARMING_ROLLLEFT
:
170 armingInputLevel
= 1.0f
* cmd
.Roll
;
172 case FLIGHTMODESETTINGS_ARMING_ROLLRIGHT
:
173 armingInputLevel
= -1.0f
* cmd
.Roll
;
175 case FLIGHTMODESETTINGS_ARMING_PITCHFORWARD
:
176 armingInputLevel
= 1.0f
* cmd
.Pitch
;
178 case FLIGHTMODESETTINGS_ARMING_PITCHAFT
:
179 armingInputLevel
= -1.0f
* cmd
.Pitch
;
181 case FLIGHTMODESETTINGS_ARMING_YAWLEFT
:
182 armingInputLevel
= 1.0f
* cmd
.Yaw
;
184 case FLIGHTMODESETTINGS_ARMING_YAWRIGHT
:
185 armingInputLevel
= -1.0f
* cmd
.Yaw
;
187 case FLIGHTMODESETTINGS_ARMING_ACCESSORY0
:
188 case FLIGHTMODESETTINGS_ARMING_ACCESSORY1
:
189 case FLIGHTMODESETTINGS_ARMING_ACCESSORY2
:
190 case FLIGHTMODESETTINGS_ARMING_ACCESSORY3
:
191 armingInputLevel
= -1.0f
* acc
.AccessoryVal
;
197 bool manualArm
= false;
198 bool manualDisarm
= false;
200 static FlightModeSettingsArmingOptions previousArmingSettings
= -1;
201 static float previousArmingInputLevel
= 0.0f
;
203 if (previousArmingSettings
!= settings
.Arming
) {
204 previousArmingSettings
= settings
.Arming
;
205 previousArmingInputLevel
= 0.0f
;
208 // ignore previous arming input level if not transitioning from fully ARMED/DISARMED states.
209 if ((armState
!= ARM_STATE_DISARMED
) && (armState
!= ARM_STATE_ARMED
)) {
210 previousArmingInputLevel
= 0.0f
;
213 float armedThreshold
= armSwitch
? ARMED_THRESHOLD_SWITCH
: ARMED_THRESHOLD_STICK
;
215 if ((armingInputLevel
<= -armedThreshold
) && (previousArmingInputLevel
> -armedThreshold
)) {
217 } else if ((armingInputLevel
>= +armedThreshold
) && (previousArmingInputLevel
< +armedThreshold
)) {
221 previousArmingInputLevel
= armingInputLevel
;
224 case ARM_STATE_DISARMED
:
225 setArmedIfChanged(FLIGHTSTATUS_ARMED_DISARMED
);
227 // only allow arming if it's OK too
228 if (manualArm
&& okToArm()) {
229 armedDisarmStart
= sysTime
;
230 armState
= ARM_STATE_ARMING_MANUAL
;
234 case ARM_STATE_ARMING_MANUAL
:
235 setArmedIfChanged(FLIGHTSTATUS_ARMED_ARMING
);
237 if (manualArm
&& (timeDifferenceMs(armedDisarmStart
, sysTime
) > settings
.ArmingSequenceTime
)) {
238 armState
= ARM_STATE_ARMED
;
239 } else if (!manualArm
) {
240 armState
= ARM_STATE_DISARMED
;
244 case ARM_STATE_ARMED
:
245 // When we get here, the throttle is low,
246 // we go immediately to disarming due to timeout, also when the disarming mechanism is not enabled
247 armedDisarmStart
= sysTime
;
248 armState
= ARM_STATE_DISARMING_TIMEOUT
;
249 setArmedIfChanged(FLIGHTSTATUS_ARMED_ARMED
);
252 case ARM_STATE_DISARMING_TIMEOUT
:
254 // we should never reach the disarming timeout if the pathfollower is engaged - reset timeout
255 FlightStatusControlChainData cc
;
256 FlightStatusControlChainGet(&cc
);
257 if (cc
.PathFollower
== FLIGHTSTATUS_CONTROLCHAIN_TRUE
) {
258 armedDisarmStart
= sysTime
;
262 // We get here when armed while throttle low, even when the arming timeout is not enabled
263 if ((settings
.ArmedTimeout
!= 0) && (timeDifferenceMs(armedDisarmStart
, sysTime
) > settings
.ArmedTimeout
)) {
264 armState
= ARM_STATE_DISARMED
;
267 // Switch to disarming due to manual control when needed
269 armedDisarmStart
= sysTime
;
270 armState
= ARM_STATE_DISARMING_MANUAL
;
274 case ARM_STATE_DISARMING_MANUAL
:
275 // arming switch disarms immediately,
276 if (manualDisarm
&& (timeDifferenceMs(armedDisarmStart
, sysTime
) > settings
.DisarmingSequenceTime
)) {
277 armState
= ARM_STATE_DISARMED
;
278 } else if (!manualDisarm
) {
279 armState
= ARM_STATE_ARMED
;
285 static uint32_t timeDifferenceMs(portTickType start_time
, portTickType end_time
)
287 return (end_time
- start_time
) * portTICK_RATE_MS
;
291 * @brief Determine if the aircraft is safe to arm
292 * @returns True if safe to arm, false otherwise
294 static bool okToArm(void)
297 configuration_check();
300 SystemAlarmsData alarms
;
302 SystemAlarmsGet(&alarms
);
305 for (int i
= 0; i
< SYSTEMALARMS_ALARM_NUMELEM
; i
++) {
306 if (SystemAlarmsAlarmToArray(alarms
.Alarm
)[i
] >= SYSTEMALARMS_ALARM_CRITICAL
) { // found an alarm thats set
307 if (i
== SYSTEMALARMS_ALARM_GPS
|| i
== SYSTEMALARMS_ALARM_TELEMETRY
) {
315 StabilizationDesiredStabilizationModeData stabDesired
;
317 FlightStatusData flightStatus
;
318 FlightStatusGet(&flightStatus
);
319 switch (flightStatus
.FlightMode
) {
320 case FLIGHTSTATUS_FLIGHTMODE_MANUAL
:
321 case FLIGHTSTATUS_FLIGHTMODE_STABILIZED1
:
322 case FLIGHTSTATUS_FLIGHTMODE_STABILIZED2
:
323 case FLIGHTSTATUS_FLIGHTMODE_STABILIZED3
:
324 case FLIGHTSTATUS_FLIGHTMODE_STABILIZED4
:
325 case FLIGHTSTATUS_FLIGHTMODE_STABILIZED5
:
326 case FLIGHTSTATUS_FLIGHTMODE_STABILIZED6
:
327 // Prevent arming if unsafe due to the current Thrust Mode
328 StabilizationDesiredStabilizationModeGet(&stabDesired
);
329 if (stabDesired
.Thrust
== STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEHOLD
||
330 stabDesired
.Thrust
== STABILIZATIONDESIRED_STABILIZATIONMODE_ALTITUDEVARIO
) {
334 // avoid assisted control with auto throttle. As it sits waiting to launch,
335 // it will move to hold, and auto thrust will auto launch otherwise!
336 if (flightStatus
.FlightModeAssist
== FLIGHTSTATUS_FLIGHTMODEASSIST_GPSASSIST
) {
340 // Avoid arming while AlwaysStabilizeWhenArmed is active
341 if (flightStatus
.AlwaysStabilizeWhenArmed
== FLIGHTSTATUS_ALWAYSSTABILIZEWHENARMED_TRUE
) {
348 case FLIGHTSTATUS_FLIGHTMODE_LAND
:
349 #if !defined(PIOS_EXCLUDE_ADVANCED_FEATURES)
350 case FLIGHTSTATUS_FLIGHTMODE_AUTOTUNE
:
351 #endif /* !defined(PIOS_EXCLUDE_ADVANCED_FEATURES) */
354 case FLIGHTSTATUS_FLIGHTMODE_AUTOTAKEOFF
:
355 case FLIGHTSTATUS_FLIGHTMODE_PATHPLANNER
:
356 // Avoid arming while AlwaysStabilizeWhenArmed is active
357 if (flightStatus
.AlwaysStabilizeWhenArmed
== FLIGHTSTATUS_ALWAYSSTABILIZEWHENARMED_TRUE
) {
369 * @brief Determine if the aircraft is forced to disarm by an explicit alarm
370 * @returns True if safe to arm, false otherwise
372 static bool forcedDisArm(void)
375 SystemAlarmsAlarmData alarms
;
377 SystemAlarmsAlarmGet(&alarms
);
379 if (alarms
.Guidance
== SYSTEMALARMS_ALARM_CRITICAL
) {
382 if (alarms
.Receiver
== SYSTEMALARMS_ALARM_CRITICAL
) {
390 * @brief Update the flightStatus object only if value changed. Reduces callbacks
391 * @param[in] val The new value
393 static void setArmedIfChanged(uint8_t val
)
395 FlightStatusData flightStatus
;
397 FlightStatusGet(&flightStatus
);
399 if (flightStatus
.Armed
!= val
) {
400 flightStatus
.Armed
= val
;
401 FlightStatusSet(&flightStatus
);