2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
5 * @addtogroup ReceiverModule Manual Control Module
6 * @brief Provide manual control or allow it alter flight mode.
9 * Reads in the ManualControlCommand from receiver then
10 * pass it to ManualControl
13 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015-2017.
14 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014.
15 * @brief Receiver module. Handles safety R/C link and flight mode.
17 * @see The GNU Public License (GPL) Version 3
19 *****************************************************************************/
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 3 of the License, or
24 * (at your option) any later version.
26 * This program is distributed in the hope that it will be useful, but
27 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
28 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
31 * You should have received a copy of the GNU General Public License along
32 * with this program; if not, write to the Free Software Foundation, Inc.,
33 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
36 #include <openpilot.h>
37 #include <accessorydesired.h>
38 #include <manualcontrolsettings.h>
39 #include <manualcontrolcommand.h>
40 #include <receiveractivity.h>
41 #include <receiverstatus.h>
42 #include <flightstatus.h>
43 #include <flighttelemetrystats.h>
44 #ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
45 #include <stabilizationsettings.h>
46 #include <vtolpathfollowersettings.h>
48 #include <flightmodesettings.h>
49 #include <systemsettings.h>
51 #include <sanitycheck.h>
54 #if defined(PIOS_INCLUDE_USB_RCTX)
55 #include "pios_usb_rctx.h"
56 #endif /* PIOS_INCLUDE_USB_RCTX */
59 #if defined(PIOS_RECEIVER_STACK_SIZE)
60 #define STACK_SIZE_BYTES PIOS_RECEIVER_STACK_SIZE
62 #define STACK_SIZE_BYTES 1152
65 #define TASK_PRIORITY (tskIDLE_PRIORITY + 3) // 3 = flight control
66 #define UPDATE_PERIOD_MS 20
68 // safe band to allow a bit of calibration error or trim offset (in microseconds)
69 #define CONNECTION_OFFSET 250
71 #define ASSISTEDCONTROL_DEADBAND_MINIMUM 2 // minimum value for a well behaved Tx, in percent.
76 static xTaskHandle taskHandle
;
77 static portTickType lastSysTime
;
78 static FrameType_t frameType
= FRAME_TYPE_MULTIROTOR
;
81 static portTickType lastSysTimeLPF
;
82 static float inputFiltered
[MANUALCONTROLSETTINGS_RESPONSETIME_NUMELEM
];
86 static void receiverTask(void *parameters
);
87 static float scaleChannel(int16_t value
, int16_t max
, int16_t min
, int16_t neutral
);
88 static uint32_t timeDifferenceMs(portTickType start_time
, portTickType end_time
);
89 static bool validInputRange(int16_t min
, int16_t max
, uint16_t value
);
90 static void applyDeadband(float *value
, uint8_t deadband
);
91 static void SettingsUpdatedCb(UAVObjEvent
*ev
);
93 #ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
94 static uint8_t isAssistedFlightMode(uint8_t position
);
98 static void applyLPF(float *value
, ManualControlSettingsResponseTimeElem channel
, ManualControlSettingsResponseTimeData
*responseTime
, uint8_t deadband
, float dT
);
101 #define RCVR_ACTIVITY_MONITOR_CHANNELS_PER_GROUP 18 // Sbus max channel
102 #define RCVR_ACTIVITY_MONITOR_MIN_RANGE 15
103 struct rcvr_activity_fsm
{
104 ManualControlSettingsChannelGroupsOptions group
;
105 uint16_t prev
[RCVR_ACTIVITY_MONITOR_CHANNELS_PER_GROUP
];
106 uint8_t sample_count
;
109 static struct rcvr_activity_fsm activity_fsm
;
111 static void resetRcvrActivity(struct rcvr_activity_fsm
*fsm
);
112 static bool updateRcvrActivity(struct rcvr_activity_fsm
*fsm
);
113 static void resetRcvrStatus(struct rcvr_activity_fsm
*fsm
);
114 static bool updateRcvrStatus(
115 struct rcvr_activity_fsm
*fsm
,
116 ManualControlSettingsChannelGroupsOptions group
,
119 #define assumptions \
121 ((int)MANUALCONTROLCOMMAND_CHANNEL_NUMELEM == (int)MANUALCONTROLSETTINGS_CHANNELGROUPS_NUMELEM) && \
122 ((int)MANUALCONTROLCOMMAND_CHANNEL_NUMELEM == (int)MANUALCONTROLSETTINGS_CHANNELNUMBER_NUMELEM) && \
123 ((int)MANUALCONTROLCOMMAND_CHANNEL_NUMELEM == (int)MANUALCONTROLSETTINGS_CHANNELMIN_NUMELEM) && \
124 ((int)MANUALCONTROLCOMMAND_CHANNEL_NUMELEM == (int)MANUALCONTROLSETTINGS_CHANNELMAX_NUMELEM) && \
125 ((int)MANUALCONTROLCOMMAND_CHANNEL_NUMELEM == (int)MANUALCONTROLSETTINGS_CHANNELNEUTRAL_NUMELEM))
130 int32_t ReceiverStart()
133 xTaskCreate(receiverTask
, "Receiver", STACK_SIZE_BYTES
/ 4, NULL
, TASK_PRIORITY
, &taskHandle
);
134 PIOS_TASK_MONITOR_RegisterTask(TASKINFO_RUNNING_RECEIVER
, taskHandle
);
135 #ifdef PIOS_INCLUDE_WDG
136 PIOS_WDG_RegisterFlag(PIOS_WDG_MANUAL
);
138 SettingsUpdatedCb(NULL
);
144 * Module initialization
146 int32_t ReceiverInitialize()
148 /* Check the assumptions about uavobject enum's are correct */
149 PIOS_STATIC_ASSERT(assumptions
);
150 AccessoryDesiredInitialize();
151 ManualControlCommandInitialize();
152 ReceiverActivityInitialize();
153 ReceiverStatusInitialize();
154 #ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
155 VtolPathFollowerSettingsConnectCallback(&SettingsUpdatedCb
);
157 SystemSettingsConnectCallback(&SettingsUpdatedCb
);
162 MODULE_INITCALL(ReceiverInitialize
, ReceiverStart
);
164 static void SettingsUpdatedCb(__attribute__((unused
)) UAVObjEvent
*ev
)
166 frameType
= GetCurrentFrameType();
168 #ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
169 uint8_t TreatCustomCraftAs
;
170 VtolPathFollowerSettingsTreatCustomCraftAsGet(&TreatCustomCraftAs
);
173 if (frameType
== FRAME_TYPE_CUSTOM
) {
174 switch (TreatCustomCraftAs
) {
175 case VTOLPATHFOLLOWERSETTINGS_TREATCUSTOMCRAFTAS_FIXEDWING
:
176 frameType
= FRAME_TYPE_FIXED_WING
;
178 case VTOLPATHFOLLOWERSETTINGS_TREATCUSTOMCRAFTAS_VTOL
:
179 frameType
= FRAME_TYPE_MULTIROTOR
;
181 case VTOLPATHFOLLOWERSETTINGS_TREATCUSTOMCRAFTAS_GROUND
:
182 frameType
= FRAME_TYPE_GROUND
;
193 static void receiverTask(__attribute__((unused
)) void *parameters
)
195 ManualControlSettingsData settings
;
196 ManualControlCommandData cmd
;
199 uint8_t disconnected_count
= 0;
200 uint8_t connected_count
= 0;
202 // For now manual instantiate extra instances of Accessory Desired. In future should be done dynamically
203 // this includes not even registering it if not used
204 AccessoryDesiredCreateInstance(); // Accessory1
205 AccessoryDesiredCreateInstance(); // Accessory2
206 AccessoryDesiredCreateInstance(); // Accessory3
209 AccessoryDesiredCreateInstance();
211 ManualControlCommandGet(&cmd
);
213 /* Initialize the RcvrActivty FSM */
214 portTickType lastActivityTime
= xTaskGetTickCount();
215 resetRcvrActivity(&activity_fsm
);
216 resetRcvrStatus(&activity_fsm
);
219 lastSysTime
= xTaskGetTickCount();
221 float scaledChannel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_NUMELEM
] = { 0 };
222 SystemSettingsThrustControlOptions thrustType
;
225 // Wait until next update
226 vTaskDelayUntil(&lastSysTime
, UPDATE_PERIOD_MS
/ portTICK_RATE_MS
);
227 #ifdef PIOS_INCLUDE_WDG
228 PIOS_WDG_UpdateFlag(PIOS_WDG_MANUAL
);
231 int8_t rssiValue
= -1;
234 ManualControlSettingsGet(&settings
);
235 SystemSettingsThrustControlGet(&thrustType
);
237 FlightStatusArmedGet(&armed
);
238 /* Update channel activity monitor */
239 if (armed
== FLIGHTSTATUS_ARMED_DISARMED
) {
240 if (updateRcvrActivity(&activity_fsm
)) {
241 /* Reset the aging timer because activity was detected */
242 lastActivityTime
= lastSysTime
;
246 /* Read signal quality from the group used for the throttle */
247 (void)updateRcvrStatus(&activity_fsm
,
248 settings
.ChannelGroups
.Throttle
,
251 if (timeDifferenceMs(lastActivityTime
, lastSysTime
) > 5000) {
252 resetRcvrActivity(&activity_fsm
);
253 resetRcvrStatus(&activity_fsm
);
254 lastActivityTime
= lastSysTime
;
257 if (ManualControlCommandReadOnly()) {
258 FlightTelemetryStatsData flightTelemStats
;
259 FlightTelemetryStatsGet(&flightTelemStats
);
260 if (flightTelemStats
.Status
!= FLIGHTTELEMETRYSTATS_STATUS_CONNECTED
) {
261 /* trying to fly via GCS and lost connection. fall back to transmitter */
262 UAVObjMetadata metadata
;
263 ManualControlCommandGetMetadata(&metadata
);
264 UAVObjSetAccess(&metadata
, ACCESS_READWRITE
);
265 ManualControlCommandSetMetadata(&metadata
);
267 AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER
, SYSTEMALARMS_ALARM_WARNING
);
271 bool valid_input_detected
= true;
272 bool valid_rssi_input
= false;
274 // Read channel values in us
275 for (uint8_t n
= 0; n
< MANUALCONTROLSETTINGS_CHANNELGROUPS_NUMELEM
&& n
< MANUALCONTROLCOMMAND_CHANNEL_NUMELEM
; ++n
) {
276 extern uint32_t pios_rcvr_group_map
[];
278 if (ManualControlSettingsChannelGroupsToArray(settings
.ChannelGroups
)[n
] >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
279 cmd
.Channel
[n
] = PIOS_RCVR_INVALID
;
281 cmd
.Channel
[n
] = PIOS_RCVR_Read(pios_rcvr_group_map
[
282 ManualControlSettingsChannelGroupsToArray(settings
.ChannelGroups
)[n
]],
283 ManualControlSettingsChannelNumberToArray(settings
.ChannelNumber
)[n
]);
286 // If a channel has timed out this is not valid data and we shouldn't update anything
287 // until we decide to go to failsafe
288 if (cmd
.Channel
[n
] == (uint16_t)PIOS_RCVR_TIMEOUT
) {
289 valid_input_detected
= false;
291 int16_t neutralValue
= ManualControlSettingsChannelNeutralToArray(settings
.ChannelNeutral
)[n
];
293 if (n
== MANUALCONTROLSETTINGS_CHANNELGROUPS_RSSI
) {
294 // Rssi neutral is ignored and set to min, for 0 - 100% output range
295 neutralValue
= ManualControlSettingsChannelMinToArray(settings
.ChannelMin
)[n
];
296 valid_rssi_input
= true;
298 scaledChannel
[n
] = scaleChannel(cmd
.Channel
[n
],
299 ManualControlSettingsChannelMaxToArray(settings
.ChannelMax
)[n
],
300 ManualControlSettingsChannelMinToArray(settings
.ChannelMin
)[n
],
305 if (valid_rssi_input
) {
306 rssiValue
= (int8_t)(scaledChannel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_RSSI
] * 100);
308 /* Read signal quality from the group used for the throttle */
309 (void)updateRcvrStatus(&activity_fsm
,
310 settings
.ChannelGroups
.Throttle
,
313 // Sanity Check Throttle and Yaw
314 if (settings
.ChannelGroups
.Yaw
>= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
315 || settings
.ChannelGroups
.Throttle
>= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
317 // Check all channel mappings are valid
318 cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW
] == (uint16_t)PIOS_RCVR_INVALID
319 || cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE
] == (uint16_t)PIOS_RCVR_INVALID
321 // Check the driver exists
322 cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW
] == (uint16_t)PIOS_RCVR_NODRIVER
323 || cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE
] == (uint16_t)PIOS_RCVR_NODRIVER
325 // Check collective if required
326 (thrustType
== SYSTEMSETTINGS_THRUSTCONTROL_COLLECTIVE
&& (
327 settings
.ChannelGroups
.Collective
>= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
328 || cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE
] == (uint16_t)PIOS_RCVR_INVALID
329 || cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE
] == (uint16_t)PIOS_RCVR_NODRIVER
))
331 // Check the FlightModeNumber is valid
332 settings
.FlightModeNumber
< 1 || settings
.FlightModeNumber
> FLIGHTMODESETTINGS_FLIGHTMODEPOSITION_NUMELEM
334 // Similar checks for FlightMode channel but only if more than one flight mode has been set. Otherwise don't care
335 ((settings
.FlightModeNumber
> 1) && (frameType
!= FRAME_TYPE_GROUND
)
336 && (settings
.ChannelGroups
.FlightMode
>= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
337 || cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_FLIGHTMODE
] == (uint16_t)PIOS_RCVR_INVALID
338 || cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_FLIGHTMODE
] == (uint16_t)PIOS_RCVR_NODRIVER
))) {
339 AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER
, SYSTEMALARMS_ALARM_CRITICAL
);
340 cmd
.Connected
= MANUALCONTROLCOMMAND_CONNECTED_FALSE
;
341 ManualControlCommandSet(&cmd
);
347 if (frameType
!= FRAME_TYPE_GROUND
) {
348 // Sanity Check Pitch and Roll
349 if (settings
.ChannelGroups
.Roll
>= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
350 || settings
.ChannelGroups
.Pitch
>= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
352 // Check all channel mappings are valid
353 cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL
] == (uint16_t)PIOS_RCVR_INVALID
354 || cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH
] == (uint16_t)PIOS_RCVR_INVALID
356 // Check the driver exists
357 cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL
] == (uint16_t)PIOS_RCVR_NODRIVER
358 || cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH
] == (uint16_t)PIOS_RCVR_NODRIVER
) {
359 AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER
, SYSTEMALARMS_ALARM_CRITICAL
);
360 cmd
.Connected
= MANUALCONTROLCOMMAND_CONNECTED_FALSE
;
361 ManualControlCommandSet(&cmd
);
367 // decide if we have valid manual input or not
368 valid_input_detected
&= validInputRange(settings
.ChannelMin
.Throttle
,
369 settings
.ChannelMax
.Throttle
, cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE
])
370 && validInputRange(settings
.ChannelMin
.Yaw
,
371 settings
.ChannelMax
.Yaw
, cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW
]);
373 if (frameType
!= FRAME_TYPE_GROUND
) {
374 valid_input_detected
&= validInputRange(settings
.ChannelMin
.Roll
,
375 settings
.ChannelMax
.Roll
, cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL
])
376 && validInputRange(settings
.ChannelMin
.Pitch
,
377 settings
.ChannelMax
.Pitch
, cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH
]);
380 if (settings
.ChannelGroups
.Collective
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
381 valid_input_detected
&= validInputRange(settings
.ChannelMin
.Collective
,
382 settings
.ChannelMax
.Collective
, cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE
]);
384 if (settings
.ChannelGroups
.Accessory0
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
385 valid_input_detected
&= validInputRange(settings
.ChannelMin
.Accessory0
,
386 settings
.ChannelMax
.Accessory0
, cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY0
]);
388 if (settings
.ChannelGroups
.Accessory1
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
389 valid_input_detected
&= validInputRange(settings
.ChannelMin
.Accessory1
,
390 settings
.ChannelMax
.Accessory1
, cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY1
]);
392 if (settings
.ChannelGroups
.Accessory2
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
393 valid_input_detected
&= validInputRange(settings
.ChannelMin
.Accessory2
,
394 settings
.ChannelMax
.Accessory2
, cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY2
]);
396 if (settings
.ChannelGroups
.Accessory3
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
397 valid_input_detected
&= validInputRange(settings
.ChannelMin
.Accessory3
,
398 settings
.ChannelMax
.Accessory3
, cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY3
]);
400 if (settings
.ChannelGroups
.Rssi
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
401 valid_input_detected
&= validInputRange(settings
.ChannelMin
.Rssi
,
402 settings
.ChannelMax
.Rssi
, cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_RSSI
]);
405 // Implement hysteresis loop on connection status
406 if (valid_input_detected
&& (++connected_count
> 10)) {
407 cmd
.Connected
= MANUALCONTROLCOMMAND_CONNECTED_TRUE
;
409 disconnected_count
= 0;
410 } else if (!valid_input_detected
&& (++disconnected_count
> 10)) {
411 cmd
.Connected
= MANUALCONTROLCOMMAND_CONNECTED_FALSE
;
413 disconnected_count
= 0;
416 if (cmd
.Connected
== MANUALCONTROLCOMMAND_CONNECTED_FALSE
) {
417 if (frameType
!= FRAME_TYPE_GROUND
) {
418 cmd
.Throttle
= settings
.FailsafeChannel
.Throttle
;
422 cmd
.Roll
= settings
.FailsafeChannel
.Roll
;
423 cmd
.Pitch
= settings
.FailsafeChannel
.Pitch
;
424 cmd
.Yaw
= settings
.FailsafeChannel
.Yaw
;
425 cmd
.Collective
= settings
.FailsafeChannel
.Collective
;
426 switch (thrustType
) {
427 case SYSTEMSETTINGS_THRUSTCONTROL_THROTTLE
:
428 cmd
.Thrust
= cmd
.Throttle
;
430 case SYSTEMSETTINGS_THRUSTCONTROL_COLLECTIVE
:
431 cmd
.Thrust
= cmd
.Collective
;
436 if (settings
.FailsafeFlightModeSwitchPosition
>= 0 && settings
.FailsafeFlightModeSwitchPosition
< settings
.FlightModeNumber
) {
437 cmd
.FlightModeSwitchPosition
= (uint8_t)settings
.FailsafeFlightModeSwitchPosition
;
439 AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER
, SYSTEMALARMS_ALARM_WARNING
);
441 AccessoryDesiredData accessory
;
443 if (settings
.ChannelGroups
.Accessory0
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
444 accessory
.AccessoryVal
= settings
.FailsafeChannel
.Accessory0
;
445 if (AccessoryDesiredInstSet(0, &accessory
) != 0) {
446 AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER
, SYSTEMALARMS_ALARM_WARNING
);
450 if (settings
.ChannelGroups
.Accessory1
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
451 accessory
.AccessoryVal
= settings
.FailsafeChannel
.Accessory1
;
452 if (AccessoryDesiredInstSet(1, &accessory
) != 0) {
453 AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER
, SYSTEMALARMS_ALARM_WARNING
);
457 if (settings
.ChannelGroups
.Accessory2
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
458 accessory
.AccessoryVal
= settings
.FailsafeChannel
.Accessory2
;
459 if (AccessoryDesiredInstSet(2, &accessory
) != 0) {
460 AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER
, SYSTEMALARMS_ALARM_WARNING
);
464 if (settings
.ChannelGroups
.Accessory3
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
465 accessory
.AccessoryVal
= settings
.FailsafeChannel
.Accessory3
;
466 if (AccessoryDesiredInstSet(3, &accessory
) != 0) {
467 AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER
, SYSTEMALARMS_ALARM_WARNING
);
470 } else if (valid_input_detected
) {
471 AlarmsClear(SYSTEMALARMS_ALARM_RECEIVER
);
473 // Scale channels to -1 -> +1 range
474 cmd
.Roll
= scaledChannel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL
];
475 cmd
.Pitch
= scaledChannel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH
];
476 cmd
.Yaw
= scaledChannel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW
];
477 cmd
.Throttle
= scaledChannel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE
];
478 // Convert flightMode value into the switch position in the range [0..N-1]
479 cmd
.FlightModeSwitchPosition
= ((int16_t)(scaledChannel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_FLIGHTMODE
] * 256.0f
) + 256) * settings
.FlightModeNumber
>> 9;
480 if (cmd
.FlightModeSwitchPosition
>= settings
.FlightModeNumber
) {
481 cmd
.FlightModeSwitchPosition
= settings
.FlightModeNumber
- 1;
484 uint8_t deadband_checked
= settings
.Deadband
;
485 #ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
486 // AssistedControl must have deadband set for pitch/roll hence
487 // we default to a higher value for badly behaved TXs and also enforce a minimum value
488 // for well behaved TXs
489 uint8_t assistedMode
= isAssistedFlightMode(cmd
.FlightModeSwitchPosition
);
491 deadband_checked
= settings
.DeadbandAssistedControl
;
492 if (deadband_checked
< ASSISTEDCONTROL_DEADBAND_MINIMUM
) {
493 deadband_checked
= ASSISTEDCONTROL_DEADBAND_MINIMUM
;
496 // If user has set settings.Deadband to a higher value...we use that.
497 if (deadband_checked
< settings
.Deadband
) {
498 deadband_checked
= settings
.Deadband
;
501 #endif // PIOS_EXCLUDE_ADVANCED_FEATURES
503 // Apply deadband for Roll/Pitch/Yaw stick inputs
504 if (deadband_checked
> 0) {
505 applyDeadband(&cmd
.Roll
, deadband_checked
);
506 applyDeadband(&cmd
.Pitch
, deadband_checked
);
507 applyDeadband(&cmd
.Yaw
, deadband_checked
);
508 if (frameType
== FRAME_TYPE_GROUND
) { // assumes reversible motors
509 applyDeadband(&cmd
.Throttle
, deadband_checked
);
513 // Apply Low Pass Filter to input channels, time delta between calls in ms
514 portTickType thisSysTime
= xTaskGetTickCount();
515 float dT
= (thisSysTime
> lastSysTimeLPF
) ?
516 (float)((thisSysTime
- lastSysTimeLPF
) * portTICK_RATE_MS
) :
517 (float)UPDATE_PERIOD_MS
;
518 lastSysTimeLPF
= thisSysTime
;
520 applyLPF(&cmd
.Roll
, MANUALCONTROLSETTINGS_RESPONSETIME_ROLL
, &settings
.ResponseTime
, deadband_checked
, dT
);
521 applyLPF(&cmd
.Pitch
, MANUALCONTROLSETTINGS_RESPONSETIME_PITCH
, &settings
.ResponseTime
, deadband_checked
, dT
);
522 applyLPF(&cmd
.Yaw
, MANUALCONTROLSETTINGS_RESPONSETIME_YAW
, &settings
.ResponseTime
, deadband_checked
, dT
);
523 #endif // USE_INPUT_LPF
524 if (cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE
] != (uint16_t)PIOS_RCVR_INVALID
525 && cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE
] != (uint16_t)PIOS_RCVR_NODRIVER
526 && cmd
.Channel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE
] != (uint16_t)PIOS_RCVR_TIMEOUT
) {
527 cmd
.Collective
= scaledChannel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE
];
528 if (settings
.Deadband
> 0) {
529 applyDeadband(&cmd
.Collective
, settings
.Deadband
);
532 applyLPF(&cmd
.Collective
, MANUALCONTROLSETTINGS_RESPONSETIME_COLLECTIVE
, &settings
.ResponseTime
, settings
.Deadband
, dT
);
533 #endif // USE_INPUT_LPF
536 switch (thrustType
) {
537 case SYSTEMSETTINGS_THRUSTCONTROL_THROTTLE
:
538 cmd
.Thrust
= cmd
.Throttle
;
540 case SYSTEMSETTINGS_THRUSTCONTROL_COLLECTIVE
:
541 cmd
.Thrust
= cmd
.Collective
;
547 AccessoryDesiredData accessory
;
549 if (settings
.ChannelGroups
.Accessory0
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
550 accessory
.AccessoryVal
= scaledChannel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY0
];
552 applyLPF(&accessory
.AccessoryVal
, MANUALCONTROLSETTINGS_RESPONSETIME_ACCESSORY0
, &settings
.ResponseTime
, settings
.Deadband
, dT
);
554 if (AccessoryDesiredInstSet(0, &accessory
) != 0) {
555 AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER
, SYSTEMALARMS_ALARM_WARNING
);
559 if (settings
.ChannelGroups
.Accessory1
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
560 accessory
.AccessoryVal
= scaledChannel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY1
];
562 applyLPF(&accessory
.AccessoryVal
, MANUALCONTROLSETTINGS_RESPONSETIME_ACCESSORY1
, &settings
.ResponseTime
, settings
.Deadband
, dT
);
564 if (AccessoryDesiredInstSet(1, &accessory
) != 0) {
565 AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER
, SYSTEMALARMS_ALARM_WARNING
);
569 if (settings
.ChannelGroups
.Accessory2
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
570 accessory
.AccessoryVal
= scaledChannel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY2
];
572 applyLPF(&accessory
.AccessoryVal
, MANUALCONTROLSETTINGS_RESPONSETIME_ACCESSORY2
, &settings
.ResponseTime
, settings
.Deadband
, dT
);
575 if (AccessoryDesiredInstSet(2, &accessory
) != 0) {
576 AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER
, SYSTEMALARMS_ALARM_WARNING
);
580 if (settings
.ChannelGroups
.Accessory3
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
581 accessory
.AccessoryVal
= scaledChannel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY3
];
583 applyLPF(&accessory
.AccessoryVal
, MANUALCONTROLSETTINGS_RESPONSETIME_ACCESSORY3
, &settings
.ResponseTime
, settings
.Deadband
, dT
);
586 if (AccessoryDesiredInstSet(3, &accessory
) != 0) {
587 AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER
, SYSTEMALARMS_ALARM_WARNING
);
591 if (settings
.ChannelGroups
.Rssi
!= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
592 accessory
.AccessoryVal
= scaledChannel
[MANUALCONTROLSETTINGS_CHANNELGROUPS_RSSI
];
594 // Allow some Rssi filtering without deadband
595 applyLPF(&accessory
.AccessoryVal
, MANUALCONTROLSETTINGS_RESPONSETIME_RSSI
, &settings
.ResponseTime
, 0.0f
, dT
);
598 if (AccessoryDesiredInstSet(4, &accessory
) != 0) {
599 AlarmsSet(SYSTEMALARMS_ALARM_RECEIVER
, SYSTEMALARMS_ALARM_WARNING
);
605 ManualControlCommandSet(&cmd
);
608 #if defined(PIOS_INCLUDE_USB_RCTX)
609 if (pios_usb_rctx_id
) {
610 PIOS_USB_RCTX_Update(pios_usb_rctx_id
,
612 ManualControlSettingsChannelMinToArray(settings
.ChannelMin
),
613 ManualControlSettingsChannelMaxToArray(settings
.ChannelMax
),
614 NELEMENTS(cmd
.Channel
));
616 #endif /* PIOS_INCLUDE_USB_RCTX */
620 static void resetRcvrActivity(struct rcvr_activity_fsm
*fsm
)
622 ReceiverActivityData data
;
623 bool updated
= false;
625 /* Clear all channel activity flags */
626 ReceiverActivityGet(&data
);
627 if (data
.ActiveGroup
!= RECEIVERACTIVITY_ACTIVEGROUP_NONE
&& data
.ActiveChannel
!= 255) {
628 data
.ActiveGroup
= RECEIVERACTIVITY_ACTIVEGROUP_NONE
;
629 data
.ActiveChannel
= 255;
633 ReceiverActivitySet(&data
);
636 /* Reset the FSM state */
638 fsm
->sample_count
= 0;
641 static void resetRcvrStatus(struct rcvr_activity_fsm
*fsm
)
643 /* Reset the state */
647 static void updateRcvrActivitySample(uint32_t rcvr_id
, uint16_t samples
[], uint8_t max_channels
)
649 for (uint8_t channel
= 1; channel
<= max_channels
; channel
++) {
650 // Subtract 1 because channels are 1 indexed
651 samples
[channel
- 1] = PIOS_RCVR_Read(rcvr_id
, channel
);
655 static bool updateRcvrActivityCompare(uint32_t rcvr_id
, struct rcvr_activity_fsm
*fsm
)
657 bool activity_updated
= false;
659 ManualControlSettingsChannelGroupsData channelGroups
;
660 ManualControlSettingsChannelNumberData channelNumber
;
662 ManualControlSettingsChannelGroupsGet(&channelGroups
);
663 ManualControlSettingsChannelNumberGet(&channelNumber
);
665 /* Compare the current value to the previous sampled value */
666 for (uint8_t channel
= 1; channel
<= RCVR_ACTIVITY_MONITOR_CHANNELS_PER_GROUP
; channel
++) {
668 uint16_t prev
= fsm
->prev
[channel
- 1]; // Subtract 1 because channels are 1 indexed
669 uint16_t curr
= PIOS_RCVR_Read(rcvr_id
, channel
);
676 // Ignore activity from this Group/Channel because already used/set for Rssi input
677 // Without that, the ReceiverActivity will be saturated just with Rssi value activity.
678 if (channelGroups
.Rssi
== fsm
->group
&& channelNumber
.Rssi
== channel
) {
682 if (delta
> RCVR_ACTIVITY_MONITOR_MIN_RANGE
) {
683 /* Mark this channel as active */
684 ReceiverActivityActiveGroupOptions group
;
686 /* Don't assume manualcontrolsettings and receiveractivity are in the same order. */
687 switch (fsm
->group
) {
688 case MANUALCONTROLSETTINGS_CHANNELGROUPS_PWM
:
689 group
= RECEIVERACTIVITY_ACTIVEGROUP_PWM
;
691 case MANUALCONTROLSETTINGS_CHANNELGROUPS_PPM
:
692 group
= RECEIVERACTIVITY_ACTIVEGROUP_PPM
;
694 case MANUALCONTROLSETTINGS_CHANNELGROUPS_DSMMAINPORT
:
695 group
= RECEIVERACTIVITY_ACTIVEGROUP_DSMMAINPORT
;
697 case MANUALCONTROLSETTINGS_CHANNELGROUPS_DSMFLEXIPORT
:
698 group
= RECEIVERACTIVITY_ACTIVEGROUP_DSMFLEXIPORT
;
700 case MANUALCONTROLSETTINGS_CHANNELGROUPS_DSMRCVRPORT
:
701 group
= RECEIVERACTIVITY_ACTIVEGROUP_DSMRCVRPORT
;
703 case MANUALCONTROLSETTINGS_CHANNELGROUPS_EXBUS
:
704 group
= RECEIVERACTIVITY_ACTIVEGROUP_EXBUS
;
706 case MANUALCONTROLSETTINGS_CHANNELGROUPS_SBUS
:
707 group
= RECEIVERACTIVITY_ACTIVEGROUP_SBUS
;
709 case MANUALCONTROLSETTINGS_CHANNELGROUPS_HOTT
:
710 group
= RECEIVERACTIVITY_ACTIVEGROUP_HOTT
;
712 case MANUALCONTROLSETTINGS_CHANNELGROUPS_SRXL
:
713 group
= RECEIVERACTIVITY_ACTIVEGROUP_SRXL
;
715 case MANUALCONTROLSETTINGS_CHANNELGROUPS_IBUS
:
716 group
= RECEIVERACTIVITY_ACTIVEGROUP_IBUS
;
718 case MANUALCONTROLSETTINGS_CHANNELGROUPS_GCS
:
719 group
= RECEIVERACTIVITY_ACTIVEGROUP_GCS
;
721 case MANUALCONTROLSETTINGS_CHANNELGROUPS_OPLINK
:
722 group
= RECEIVERACTIVITY_ACTIVEGROUP_OPLINK
;
724 case MANUALCONTROLSETTINGS_CHANNELGROUPS_OPENLRS
:
725 group
= RECEIVERACTIVITY_ACTIVEGROUP_OPENLRS
;
732 ReceiverActivityActiveGroupSet((uint8_t *)&group
);
733 ReceiverActivityActiveChannelSet(&channel
);
734 activity_updated
= true;
737 return activity_updated
;
740 static bool updateRcvrActivity(struct rcvr_activity_fsm
*fsm
)
742 bool activity_updated
= false;
744 if (fsm
->group
>= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
745 /* We're out of range, reset things */
746 resetRcvrActivity(fsm
);
747 resetRcvrStatus(fsm
);
750 extern uint32_t pios_rcvr_group_map
[];
751 if (!pios_rcvr_group_map
[fsm
->group
]) {
752 /* Unbound group, skip it */
753 goto group_completed
;
756 if (fsm
->sample_count
== 0) {
757 /* Take a sample of each channel in this group */
758 updateRcvrActivitySample(pios_rcvr_group_map
[fsm
->group
], fsm
->prev
, NELEMENTS(fsm
->prev
));
763 /* Compare with previous sample */
764 activity_updated
= updateRcvrActivityCompare(pios_rcvr_group_map
[fsm
->group
], fsm
);
767 /* Reset the sample counter */
768 fsm
->sample_count
= 0;
770 /* Find the next active group, but limit search so we can't loop forever here */
771 for (uint8_t i
= 0; i
< MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
; i
++) {
772 /* Move to the next group */
774 if (fsm
->group
>= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE
) {
775 /* Wrap back to the first group */
778 if (pios_rcvr_group_map
[fsm
->group
]) {
780 * Found an active group, take a sample here to avoid an
781 * extra 20ms delay in the main thread so we can speed up
784 updateRcvrActivitySample(pios_rcvr_group_map
[fsm
->group
], fsm
->prev
, NELEMENTS(fsm
->prev
));
790 return activity_updated
;
794 * Read signal quality from the specified group or use Rssi input value if any
796 static bool updateRcvrStatus(
797 struct rcvr_activity_fsm
*fsm
,
798 ManualControlSettingsChannelGroupsOptions group
, int8_t rssiValue
)
800 extern uint32_t pios_rcvr_group_map
[];
801 bool activity_updated
= false;
804 quality
= (rssiValue
> 0) ? rssiValue
: PIOS_RCVR_GetQuality(pios_rcvr_group_map
[group
]);
806 /* If no driver is detected or any other error then return */
808 return activity_updated
;
811 /* Compare with previous sample */
812 if (quality
!= fsm
->quality
) {
813 fsm
->quality
= quality
;
814 ReceiverStatusQualitySet(&fsm
->quality
);
815 activity_updated
= true;
818 return activity_updated
;
822 * Convert channel from servo pulse duration (microseconds) to scaled -1/+1 range.
824 static float scaleChannel(int16_t value
, int16_t max
, int16_t min
, int16_t neutral
)
829 if ((max
> min
&& value
>= neutral
) || (min
> max
&& value
<= neutral
)) {
830 if (max
!= neutral
) {
831 valueScaled
= (float)(value
- neutral
) / (float)(max
- neutral
);
836 if (min
!= neutral
) {
837 valueScaled
= (float)(value
- neutral
) / (float)(neutral
- min
);
844 if (valueScaled
> 1.0f
) {
846 } else if (valueScaled
< -1.0f
) {
853 static uint32_t timeDifferenceMs(portTickType start_time
, portTickType end_time
)
855 return (end_time
- start_time
) * portTICK_RATE_MS
;
860 * @brief Determine if the manual input value is within acceptable limits
861 * @returns return TRUE if so, otherwise return FALSE
863 bool validInputRange(int16_t min
, int16_t max
, uint16_t value
)
870 return value
>= min
- CONNECTION_OFFSET
&& value
<= max
+ CONNECTION_OFFSET
;
874 * @brief Apply deadband to Roll/Pitch/Yaw channels
876 static void applyDeadband(float *value
, uint8_t deadband
)
878 float floatDeadband
= ((float)deadband
) * 0.01f
;
880 if (fabsf(*value
) < floatDeadband
) {
882 } else if (*value
> 0.0f
) {
883 *value
= (*value
- floatDeadband
) / (1.0f
- floatDeadband
);
885 *value
= (*value
+ floatDeadband
) / (1.0f
- floatDeadband
);
891 * @brief Apply Low Pass Filter to Throttle/Roll/Pitch/Yaw or Accessory channel
893 static void applyLPF(float *value
, ManualControlSettingsResponseTimeElem channel
, ManualControlSettingsResponseTimeData
*responseTime
, uint8_t deadband
, float dT
)
895 float rt
= (float)ManualControlSettingsResponseTimeToArray((*responseTime
))[channel
];
898 inputFiltered
[channel
] = ((rt
* inputFiltered
[channel
]) + (dT
* (*value
))) / (rt
+ dT
);
900 float floatDeadband
= ((float)deadband
) * 0.01f
;
901 // avoid a long tail of non-zero data. if we have deadband, once the filtered result reduces to 1/10th
902 // of deadband revert to 0. We downstream rely on this to know when sticks are centered.
903 if (floatDeadband
> 0.0f
&& fabsf(inputFiltered
[channel
]) < floatDeadband
* 0.1f
) {
904 inputFiltered
[channel
] = 0.0f
;
906 *value
= inputFiltered
[channel
];
909 #endif // USE_INPUT_LPF
912 * Check and set modes for gps assisted stablised flight modes
914 #ifndef PIOS_EXCLUDE_ADVANCED_FEATURES
915 static uint8_t isAssistedFlightMode(uint8_t position
)
917 uint8_t isAssistedFlag
= STABILIZATIONSETTINGS_FLIGHTMODEASSISTMAP_NONE
;
918 uint8_t FlightModeAssistMap
[STABILIZATIONSETTINGS_FLIGHTMODEASSISTMAP_NUMELEM
];
920 StabilizationSettingsFlightModeAssistMapGet(FlightModeAssistMap
);
922 if (position
< STABILIZATIONSETTINGS_FLIGHTMODEASSISTMAP_NUMELEM
) {
923 isAssistedFlag
= FlightModeAssistMap
[position
];
926 return isAssistedFlag
;