dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
transmitter_control.c
Go to the documentation of this file.
1 
17 /*
18  * This program is free software; you can redistribute it and/or modify
19  * it under the terms of the GNU General Public License as published by
20  * the Free Software Foundation; either version 3 of the License, or
21  * (at your option) any later version.
22  *
23  * This program is distributed in the hope that it will be useful, but
24  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
26  * for more details.
27  *
28  * You should have received a copy of the GNU General Public License along
29  * with this program; if not, see <http://www.gnu.org/licenses/>
30  *
31  * Additional note on redistribution: The copyright and license notices above
32  * must be maintained in each individual source file that is a derivative work
33  * of this source file; otherwise redistribution is prohibited.
34  */
35 
36 #include "openpilot.h"
37 #include "control.h"
38 #include "transmitter_control.h"
39 #include "pios_thread.h"
40 
41 #include "altitudeholdsettings.h"
42 #include "baroaltitude.h"
43 #include "flighttelemetrystats.h"
44 #include "flightstatus.h"
45 #include "loitercommand.h"
46 #include "pathdesired.h"
47 #include "positionactual.h"
48 #include "receiveractivity.h"
49 #include "systemsettings.h"
50 
51 #include "misc_math.h"
52 
53 #if defined(PIOS_INCLUDE_OPENLRS_RCVR)
54 #include "pios_openlrs.h"
55 #endif /* PIOS_INCLUDE_OPENLRS_RCVR */
56 
57 // This is how far "left" you have to deflect for "yaw left" arming, etc.
58 #define ARMED_THRESHOLD 0.50f
59 
60 //safe band to allow a bit of calibration error or trim offset (in microseconds)
61 //these specify how far outside the "permitted range" throttle and other channels
62 //can go. e.g. if range is 1000..2000us, values under 750us or over 2250us
63 //result in failsafe.
64 #define CONNECTION_OFFSET 250
65 
66 #define RCVR_ACTIVITY_MONITOR_CHANNELS_PER_GROUP 12
67 #define RCVR_ACTIVITY_MONITOR_MIN_RANGE 20
68 
69 // If the neutral point on the throttle channel is 35% over the configured
70 // minimum, we assume they're providing bidirectional thrust.
71 #define THRUST_BIDIR_THRESH 0.35f
72 
73 /* All channels must have at least this many counts on each side of neutral.
74  * (Except throttle which must have this many on the positive side). This is
75  * to prevent situations where a partial calibration results in spurious
76  * arming, etc. */
77 #define MIN_MEANINGFUL_RANGE 40
78 
80  ManualControlSettingsChannelGroupsOptions group;
82  uint8_t check_count;
83 };
84 
85 extern uintptr_t pios_rcvr_group_map[];
86 
87 // Private variables
88 static ManualControlCommandData cmd;
89 static ManualControlSettingsData settings;
90 
91 static uint8_t disconnected_count = 0;
92 static uint8_t connected_count = 0;
94 static uint32_t lastActivityTime;
95 static uint32_t lastSysTime;
96 static float flight_mode_value;
98 static bool settings_updated;
99 static bool thrust_is_bidir;
101 
102 // Private functions
103 static float get_thrust_source(ManualControlCommandData *manual_control_command,
104  bool always_fullrange);
105 static void update_stabilization_desired(ManualControlCommandData * manual_control_command, ManualControlSettingsData * settings);
106 static void set_flight_mode();
107 static void process_transmitter_events(ManualControlCommandData * cmd, ManualControlSettingsData * settings, bool valid, bool settings_updated);
108 static void set_manual_control_error(SystemAlarmsManualControlOptions errorCode);
109 static float scaleChannel(int n, int16_t value);
110 static bool validInputRange(int n, uint16_t value, uint16_t offset);
111 static uint32_t timeDifferenceMs(uint32_t start_time, uint32_t end_time);
112 static void resetRcvrActivity(struct rcvr_activity_fsm * fsm);
113 static bool updateRcvrActivity(struct rcvr_activity_fsm * fsm);
114 static void set_loiter_command(ManualControlCommandData *cmd);
115 
116 // Exposed from manualcontrol to prevent attempts to arm when unsafe
117 extern bool ok_to_arm();
118 
121  switch (settings.RssiType) {
122  case MANUALCONTROLSETTINGS_RSSITYPE_PWM:
123  return MANUALCONTROLSETTINGS_CHANNELGROUPS_PWM;
124  case MANUALCONTROLSETTINGS_RSSITYPE_PPM:
125  return MANUALCONTROLSETTINGS_CHANNELGROUPS_PPM;
126  case MANUALCONTROLSETTINGS_RSSITYPE_SBUS:
127  return MANUALCONTROLSETTINGS_CHANNELGROUPS_SBUS;
128  case MANUALCONTROLSETTINGS_RSSITYPE_HOTTSUM:
129  return MANUALCONTROLSETTINGS_CHANNELGROUPS_HOTTSUM;
130  case MANUALCONTROLSETTINGS_RSSITYPE_TBSCROSSFIRE:
131  return MANUALCONTROLSETTINGS_CHANNELGROUPS_TBSCROSSFIRE;
132  case MANUALCONTROLSETTINGS_RSSITYPE_IBUS:
133  return MANUALCONTROLSETTINGS_CHANNELGROUPS_IBUS;
134  default:
135  return -1;
136  }
137 }
138 
141 {
142  if (ManualControlCommandInitialize() == -1
143  || FlightStatusInitialize() == -1
144  || StabilizationDesiredInitialize() == -1
145  || ReceiverActivityInitialize() == -1
146  || LoiterCommandInitialize() == -1
147  || ManualControlSettingsInitialize() == -1) {
148  return -1;
149  }
150 
152 
153  /* Initialize the RcvrActivty FSM */
156 
157  // Use callback to update the settings when they change
158  ManualControlSettingsConnectCallbackCtx(UAVObjCbSetFlag, &settings_updated);
159 
160  settings_updated = true;
161 
162  // Main task loop
164  return 0;
165 }
166 
167 static float get_thrust_source(ManualControlCommandData *manual_control_command,
168  bool always_fullrange)
169 {
170  float thrust = collective_is_thrust ?
171  manual_control_command->Collective :
172  manual_control_command->Throttle;
173 
174  if (thrust_is_bidir) {
175  /* Thrust is bidirectional; apply a deadband and call it good */
176  apply_channel_deadband(&thrust, settings.ThrustDeadband);
177 
178  return thrust;
179  }
180 
181  if (thrust < 0) {
182  /* Thrust is not intended to be bidirectional, squish the
183  * bottom half of the range.
184  */
185  thrust = 0;
186  }
187 
188  if (!always_fullrange) {
189  /* We have a 0..1 value, and they are OK with a 0..1 value */
190  return thrust;
191  }
192 
193  /* We want a -1..1 value, but thrust is squished into a 0..1 range. */
194  thrust = thrust * 2.0f - 1.0f;
195 
196  apply_channel_deadband(&thrust, settings.ThrustDeadband);
197 
198  return thrust;
199 }
200 
202  ManualControlCommandData *manual_control_command)
203 {
204  if (collective_is_thrust) {
205  /* Always use throttle on vehicles with collective; in this
206  * case negative is OK for arming because it means we're below
207  * neutral (and presumably a vehicle with collective can't
208  * actually generate negative throttle).
209  */
210 
211  return manual_control_command->Throttle <= 0;
212  }
213 
214  return get_thrust_source(manual_control_command, false) == 0;
215 }
216 
217 static bool channel_is_configured(int channel_num)
218 {
219  if (settings.ChannelGroups[channel_num] >=
220  MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE) {
221  return false;
222  }
223 
224  if (settings.ChannelNumber[channel_num] == 0) {
225  /* Channel numbers are offset, 0 means "none" */
226  return false;
227  }
228 
229  return true;
230 }
231 
233 {
234  settings_updated = false;
235  ManualControlSettingsGet(&settings);
236 
237  uint8_t thrust_channel;
238 
239  if (channel_is_configured(MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE)) {
240  collective_is_thrust = true;
241  thrust_channel = MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE;
242  } else {
243  collective_is_thrust = false;
244  thrust_channel = MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE;
245  }
246 
247  float minval = settings.ChannelMin[thrust_channel];
248  float maxval = settings.ChannelMax[thrust_channel];
249  float neutral = settings.ChannelNeutral[thrust_channel];
250 
251  float throt_span = (neutral - minval) / (maxval - minval);
252 
253  if (throt_span >= THRUST_BIDIR_THRESH) {
254  thrust_is_bidir = true;
255  } else {
256  thrust_is_bidir = false;
257  }
258 }
259 
269 {
271 
272  float scaledChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_NUMELEM];
273  bool validChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_NUMELEM];
274 
275  static float filter_rssi;
276  bool settings_updated_this_cycle = false;
277 
278  if (settings_updated) {
280 
281  /* TODO: Factor more of the valid-configuration checks up
282  * here, so they don't need to be performed each time.
283  * Also do additonal validation .. e.g. a mostly-negative
284  * throttle range doesn't make sense!
285  */
286 
287  settings_updated_this_cycle = true;
288  }
289 
290  /* Update channel activity monitor */
291  uint8_t arm_status;
292  FlightStatusArmedGet(&arm_status);
293 
294  if (arm_status == FLIGHTSTATUS_ARMED_DISARMED) {
296  /* Reset the aging timer because activity was detected */
298  }
299  }
300 
304  }
305 
306  if (settings.RssiType != MANUALCONTROLSETTINGS_RSSITYPE_NONE) {
307  int32_t value = 0;
308 
309  switch (settings.RssiType) {
310  case MANUALCONTROLSETTINGS_RSSITYPE_ADC:
311 #if defined(PIOS_INCLUDE_ADC)
312  if (settings.RssiChannelNumber > 0) {
313  value = PIOS_ADC_GetChannelRaw(settings.RssiChannelNumber - 1) >> 4;
314  /*
315  * Shift right 4 bits because most targets
316  * return a 16 bit value now but existing configs
317  * expect a 12 bit value.
318  */
319  } else {
320  value = 0;
321  }
322 #endif
323  break;
324  case MANUALCONTROLSETTINGS_RSSITYPE_OPENLRS:
325 #if defined(PIOS_INCLUDE_OPENLRS_RCVR)
326  value = PIOS_OpenLRS_RSSI_Get();
327 #endif /* PIOS_INCLUDE_OPENLRS_RCVR */
328  break;
329  default:
330  (void) 0 ;
331  int mapped = rssitype_to_channelgroup();
332 
333  if (mapped >= 0) {
334  value = PIOS_RCVR_Read(
335  pios_rcvr_group_map[mapped],
336  settings.RssiChannelNumber);
337  }
338 
339  break;
340  }
341 
342  float rssi;
343 
344  if (value < settings.RssiMin) {
345  rssi = 0;
346  } else if (settings.RssiMax == settings.RssiMin) {
347  rssi = 0;
348  } else if (value > settings.RssiMax) {
349  if (value > (settings.RssiMax + settings.RssiMax/4)) {
350  rssi = 0;
351  } else {
352  rssi = 1;
353  }
354  } else {
355  rssi = ((float)(value - settings.RssiMin)/((float)settings.RssiMax-settings.RssiMin));
356  }
357 
358  /* Runs at least every 20ms. Takes 4 updates to go halfway
359  * to value, so makes RSSI on OSD a fair bit less frenetic.
360  * Still gets 90% there in 300ms with slow updates, and
361  * 99% there in 600ms.
362  */
363  filter_rssi = ((filter_rssi * 6) + rssi) / 7;
364 
365  cmd.Rssi = filter_rssi * 100;
366 
367  cmd.RawRssi = value;
368  }
369 
370  bool valid_input_detected = true;
371 
372  // Read channel values in us
373  for (uint8_t n = 0;
374  n < MANUALCONTROLSETTINGS_CHANNELGROUPS_NUMELEM && n < MANUALCONTROLCOMMAND_CHANNEL_NUMELEM;
375  ++n) {
376 
377  if (settings.ChannelGroups[n] >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE) {
378  cmd.Channel[n] = PIOS_RCVR_INVALID;
379  validChannel[n] = false;
380  } else {
381  cmd.Channel[n] = PIOS_RCVR_Read(pios_rcvr_group_map[settings.ChannelGroups[n]],
382  settings.ChannelNumber[n]);
383  }
384 
385  // If a channel has timed out this is not valid data and we shouldn't update anything
386  // until we decide to go to failsafe
387  if(cmd.Channel[n] == (uint16_t) PIOS_RCVR_TIMEOUT) {
388  valid_input_detected = false;
389  validChannel[n] = false;
390  } else {
391  scaledChannel[n] = scaleChannel(n, cmd.Channel[n]);
392  validChannel[n] = validInputRange(n, cmd.Channel[n], CONNECTION_OFFSET);
393  }
394  }
395 
396  // Check settings, if error raise alarm
397  if (settings.ChannelGroups[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL] >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE ||
398  settings.ChannelGroups[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH] >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE ||
399  settings.ChannelGroups[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW] >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE ||
400  settings.ChannelGroups[MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE] >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE ||
401  // Check all channel mappings are valid
402  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL] == (uint16_t) PIOS_RCVR_INVALID ||
403  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH] == (uint16_t) PIOS_RCVR_INVALID ||
404  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW] == (uint16_t) PIOS_RCVR_INVALID ||
405  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE] == (uint16_t) PIOS_RCVR_INVALID ||
406  // Check the driver exists
407  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL] == (uint16_t) PIOS_RCVR_NODRIVER ||
408  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH] == (uint16_t) PIOS_RCVR_NODRIVER ||
409  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW] == (uint16_t) PIOS_RCVR_NODRIVER ||
410  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE] == (uint16_t) PIOS_RCVR_NODRIVER ||
411  // Check the FlightModeNumber is valid
412  settings.FlightModeNumber < 1 || settings.FlightModeNumber > MANUALCONTROLSETTINGS_FLIGHTMODEPOSITION_NUMELEM ||
413  // If we've got more than one possible valid FlightMode, we require a configured FlightMode channel
414  ((settings.FlightModeNumber > 1) && (settings.ChannelGroups[MANUALCONTROLSETTINGS_CHANNELGROUPS_FLIGHTMODE] >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE)) ||
415  // Whenever FlightMode channel is configured, it needs to be valid regardless of FlightModeNumber settings
416  ((settings.ChannelGroups[MANUALCONTROLSETTINGS_CHANNELGROUPS_FLIGHTMODE] < MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE) && (
417  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_FLIGHTMODE] == (uint16_t) PIOS_RCVR_INVALID ||
418  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_FLIGHTMODE] == (uint16_t) PIOS_RCVR_NODRIVER ))) {
419 
420  set_manual_control_error(SYSTEMALARMS_MANUALCONTROL_SETTINGS);
421 
422  cmd.Connected = MANUALCONTROLCOMMAND_CONNECTED_FALSE;
423  ManualControlCommandSet(&cmd);
424 
426 
427  return -1;
428  }
429 
430  // the block above validates the input configuration. this simply checks that the range is valid if flight mode is configured.
431  bool flightmode_valid_input = settings.ChannelGroups[MANUALCONTROLSETTINGS_CHANNELGROUPS_FLIGHTMODE] >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE ||
432  validChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_FLIGHTMODE];
433 
434  // because arming is optional we must determine if it is needed before checking it is valid
435  bool arming_valid_input = (settings.Arming < MANUALCONTROLSETTINGS_ARMING_SWITCH) ||
436  validChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ARMING];
437 
438  // decide if we have valid manual input or not
439  valid_input_detected &= validChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE] &&
440  validChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL] &&
441  validChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW] &&
442  validChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH] &&
443  flightmode_valid_input &&
444  arming_valid_input;
445 
446  // Implement hysteresis loop on connection status
447  if (valid_input_detected && (++connected_count > 10)) {
448  cmd.Connected = MANUALCONTROLCOMMAND_CONNECTED_TRUE;
449  connected_count = 0;
450  disconnected_count = 0;
451  } else if (!valid_input_detected && (++disconnected_count > 10)) {
452  cmd.Connected = MANUALCONTROLCOMMAND_CONNECTED_FALSE;
453  connected_count = 0;
454  disconnected_count = 0;
455  }
456 
457  if (cmd.Connected == MANUALCONTROLCOMMAND_CONNECTED_FALSE) {
458  // These values are not used but just put ManualControlCommand in a sane state. When
459  // Connected is false, then the failsafe submodule will be in control.
460 
461  cmd.Throttle = 0;
462  cmd.Roll = 0;
463  cmd.Yaw = 0;
464  cmd.Pitch = 0;
465  cmd.Collective = 0;
466 
467  /* If all relevant channel groups aren't in error mode, then the receiver seems to be working fine.
468  So if we still reach this point, the channel min/max/neutral values on the input page are faulty. */
469  bool rx_signal_detected = cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE] < (uint16_t)PIOS_RCVR_NODRIVER &&
470  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL] < (uint16_t)PIOS_RCVR_NODRIVER &&
471  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH] < (uint16_t)PIOS_RCVR_NODRIVER &&
472  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW] < (uint16_t)PIOS_RCVR_NODRIVER &&
473  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ARMING] < (uint16_t)PIOS_RCVR_NODRIVER &&
474  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_FLIGHTMODE] < (uint16_t)PIOS_RCVR_NODRIVER;
475 
476  if (rx_signal_detected)
477  set_manual_control_error(SYSTEMALARMS_MANUALCONTROL_CHANNELCONFIGURATION);
478  else
479  set_manual_control_error(SYSTEMALARMS_MANUALCONTROL_NORX);
480 
481  } else if (valid_input_detected) {
482  set_manual_control_error(SYSTEMALARMS_MANUALCONTROL_NONE);
483 
484  // Scale channels to -1 -> +1 range
485  cmd.Roll = scaledChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ROLL];
486  cmd.Pitch = scaledChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_PITCH];
487  cmd.Yaw = scaledChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_YAW];
488  cmd.Throttle = scaledChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE];
489  cmd.ArmSwitch = scaledChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ARMING] > 0 ?
490  MANUALCONTROLCOMMAND_ARMSWITCH_ARMED : MANUALCONTROLCOMMAND_ARMSWITCH_DISARMED;
491  flight_mode_value = scaledChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_FLIGHTMODE];
492 
493  // Apply deadband for Roll/Pitch/Yaw stick inputs
494  if (settings.Deadband) {
495  apply_channel_deadband(&cmd.Roll, settings.Deadband);
496  apply_channel_deadband(&cmd.Pitch, settings.Deadband);
497  apply_channel_deadband(&cmd.Yaw, settings.Deadband);
498  }
499 
500  if(cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE] != (uint16_t) PIOS_RCVR_INVALID &&
501  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE] != (uint16_t) PIOS_RCVR_NODRIVER &&
502  cmd.Channel[MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE] != (uint16_t) PIOS_RCVR_TIMEOUT) {
503  cmd.Collective = scaledChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_COLLECTIVE];
504  }
505 
506  // Set Accessory 0
507  if (settings.ChannelGroups[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY0] !=
508  MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE) {
509  cmd.Accessory[0] = scaledChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY0];
510  }
511 
512  // Set Accessory 1
513  if (settings.ChannelGroups[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY1] !=
514  MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE) {
515  cmd.Accessory[1] = scaledChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY1];
516  }
517  // Set Accessory 2
518  if (settings.ChannelGroups[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY2] !=
519  MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE) {
520  cmd.Accessory[2] = scaledChannel[MANUALCONTROLSETTINGS_CHANNELGROUPS_ACCESSORY2];
521  }
522  }
523 
524  // Process arming outside conditional so system will disarm when disconnected. Notice this
525  // is processed in the _update method instead of _select method so the state system is always
526  // evalulated, even if not detected.
527  process_transmitter_events(&cmd, &settings, valid_input_detected,
528  settings_updated_this_cycle);
529 
530  // Update cmd object
531  ManualControlCommandSet(&cmd);
532 
533  return 0;
534 }
535 
540 int32_t transmitter_control_select(bool reset_controller)
541 {
542  // Activate the flight mode corresponding to the switch position
543  set_flight_mode();
544 
545  ManualControlCommandGet(&cmd);
546 
547  uint8_t flightMode;
548  FlightStatusFlightModeGet(&flightMode);
549 
550  switch(flightMode) {
551  case FLIGHTSTATUS_FLIGHTMODE_MANUAL:
552  case FLIGHTSTATUS_FLIGHTMODE_ACRO:
553  case FLIGHTSTATUS_FLIGHTMODE_ACROPLUS:
554  case FLIGHTSTATUS_FLIGHTMODE_ACRODYNE:
555  case FLIGHTSTATUS_FLIGHTMODE_LEVELING:
556  case FLIGHTSTATUS_FLIGHTMODE_VIRTUALBAR:
557  case FLIGHTSTATUS_FLIGHTMODE_HORIZON:
558  case FLIGHTSTATUS_FLIGHTMODE_AXISLOCK:
559  case FLIGHTSTATUS_FLIGHTMODE_STABILIZED1:
560  case FLIGHTSTATUS_FLIGHTMODE_STABILIZED2:
561  case FLIGHTSTATUS_FLIGHTMODE_STABILIZED3:
562  case FLIGHTSTATUS_FLIGHTMODE_FAILSAFE:
563  case FLIGHTSTATUS_FLIGHTMODE_AUTOTUNE:
564  case FLIGHTSTATUS_FLIGHTMODE_ALTITUDEHOLD:
565  case FLIGHTSTATUS_FLIGHTMODE_LQG:
566  case FLIGHTSTATUS_FLIGHTMODE_LQGLEVELING:
567  case FLIGHTSTATUS_FLIGHTMODE_FLIPREVERSED:
569  break;
570  case FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD:
572  break;
573  case FLIGHTSTATUS_FLIGHTMODE_RETURNTOHOME:
574  // The path planner module processes data here
575  break;
576  case FLIGHTSTATUS_FLIGHTMODE_PATHPLANNER:
577  break;
578  default:
579  set_manual_control_error(SYSTEMALARMS_MANUALCONTROL_UNDEFINED);
580  return -1;
581  }
582 
583  return 0;
584 }
585 
588 {
589  return control_status;
590 }
591 
594 {
595  // Convert flightMode value into the switch position in the range [0..N-1]
596  uint8_t pos = ((int16_t)(flight_mode_value * 256.0f) + 256) * settings.FlightModeNumber >> 9;
597  if (pos >= settings.FlightModeNumber)
598  pos = settings.FlightModeNumber - 1;
599 
600  return settings.FlightModePosition[pos];
601 }
602 
606 static bool arming_position(ManualControlCommandData *cmd,
607  ManualControlSettingsData *settings,
608  bool low_throt) {
609  // If system is not appropriate to arm, do not even attempt
610  if (!ok_to_arm())
611  return false;
612 
613  switch(settings->Arming) {
614  case MANUALCONTROLSETTINGS_ARMING_ALWAYSDISARMED:
615  return false;
616  case MANUALCONTROLSETTINGS_ARMING_ROLLLEFTTHROTTLE:
617  return low_throt && cmd->Roll < -ARMED_THRESHOLD;
618  case MANUALCONTROLSETTINGS_ARMING_ROLLRIGHTTHROTTLE:
619  return low_throt && cmd->Roll > ARMED_THRESHOLD;
620  case MANUALCONTROLSETTINGS_ARMING_YAWLEFTTHROTTLE:
621  return low_throt && cmd->Yaw < -ARMED_THRESHOLD;
622  case MANUALCONTROLSETTINGS_ARMING_YAWRIGHTTHROTTLE:
623  return low_throt && cmd->Yaw > ARMED_THRESHOLD;
624  case MANUALCONTROLSETTINGS_ARMING_CORNERSTHROTTLE:
625  return low_throt && (
626  (cmd->Yaw > ARMED_THRESHOLD || cmd->Yaw < -ARMED_THRESHOLD) &&
627  (cmd->Roll > ARMED_THRESHOLD || cmd->Roll < -ARMED_THRESHOLD) ) &&
628  (cmd->Pitch > ARMED_THRESHOLD);
629  // Note that pulling pitch stick down corresponds to positive values
630  case MANUALCONTROLSETTINGS_ARMING_SWITCHTHROTTLE:
631  if (!low_throt) return false;
632  case MANUALCONTROLSETTINGS_ARMING_SWITCH:
633  return cmd->ArmSwitch == MANUALCONTROLCOMMAND_ARMSWITCH_ARMED;
634  default:
635  return false;
636  }
637 }
638 
642 static bool disarming_position(ManualControlCommandData * cmd, ManualControlSettingsData * settings, bool low_throt)
643 {
644  switch(settings->Arming) {
645  case MANUALCONTROLSETTINGS_ARMING_ALWAYSDISARMED:
646  return true;
647  case MANUALCONTROLSETTINGS_ARMING_ROLLLEFTTHROTTLE:
648  return low_throt && cmd->Roll > ARMED_THRESHOLD;
649  case MANUALCONTROLSETTINGS_ARMING_ROLLRIGHTTHROTTLE:
650  return low_throt && cmd->Roll < -ARMED_THRESHOLD;
651  case MANUALCONTROLSETTINGS_ARMING_YAWLEFTTHROTTLE:
652  return low_throt && cmd->Yaw > ARMED_THRESHOLD;
653  case MANUALCONTROLSETTINGS_ARMING_YAWRIGHTTHROTTLE:
654  return low_throt && cmd->Yaw < -ARMED_THRESHOLD;
655  case MANUALCONTROLSETTINGS_ARMING_CORNERSTHROTTLE:
656  /* Don't handle this here; it's handled in arming_position
657  * because it's a toggle.
658  */
659  return false;
660 
661  case MANUALCONTROLSETTINGS_ARMING_SWITCH:
662  case MANUALCONTROLSETTINGS_ARMING_SWITCHTHROTTLE:
663  return cmd->ArmSwitch != MANUALCONTROLCOMMAND_ARMSWITCH_ARMED;
664  default:
665  return false;
666  }
667 }
668 
669 static bool disarm_commanded(ManualControlCommandData *cmd,
670  ManualControlSettingsData *settings,
671  bool low_throt)
672 {
673  static uint32_t start_time;
674 
675  if (!disarming_position(cmd, settings, low_throt)) {
676  start_time = 0;
677  return false;
678  }
679 
680  uint32_t now = PIOS_Thread_Systime();
681 
682  if (!start_time) {
683  start_time = now;
684  return false;
685  }
686 
687  uint16_t disarm_time = 100;
688 
689  if ((settings->Arming != MANUALCONTROLSETTINGS_ARMING_SWITCH) &&
690  (settings->Arming != MANUALCONTROLSETTINGS_ARMING_SWITCHTHROTTLE)) {
691  disarm_time = (settings->DisarmTime == MANUALCONTROLSETTINGS_DISARMTIME_250) ? 250 :
692  (settings->DisarmTime == MANUALCONTROLSETTINGS_DISARMTIME_500) ? 500 :
693  (settings->DisarmTime == MANUALCONTROLSETTINGS_DISARMTIME_1000) ? 1000 :
694  (settings->DisarmTime == MANUALCONTROLSETTINGS_DISARMTIME_2000) ? 2000 : 1000;
695  }
696 
697  return (now - start_time) >= disarm_time;
698 }
699 
700 #define RECEIVER_TIMER_FIRED 0xffffffff
701 
703 
704 static void reset_receiver_timer() {
705  receiver_timer = 0;
706 }
707 
708 static bool check_receiver_timer(uint32_t threshold) {
709  if (!threshold) {
710  /* 0 threshold means timer disabled */
711 
713 
714  return false;
715  }
716 
718  /* It already went off! We just want a strobe. */
719  return false;
720  }
721 
722  uint32_t now = PIOS_Thread_Systime();
723 
724  if (receiver_timer) {
725  if ((now - receiver_timer) >= threshold) {
727  return true;
728  }
729 
730  return false;
731  }
732 
733  receiver_timer = now;
734 
735  return false;
736 }
737 
746 static void process_transmitter_events(ManualControlCommandData * cmd, ManualControlSettingsData * settings, bool valid, bool settings_updated)
747 {
748  if (settings->Arming == MANUALCONTROLSETTINGS_ARMING_ALWAYSDISARMED) {
749  /*
750  * Disconnected is used to force a "disarmed" position being
751  * reached before arming. e.g. on config change.
752  */
754  }
755 
756  valid &= cmd->Connected == MANUALCONTROLCOMMAND_CONNECTED_TRUE;
757 
758  if (!valid || (cmd->Connected != MANUALCONTROLCOMMAND_CONNECTED_TRUE)) {
759  /* disarm timeout check-- use least favorable timeout.
760  * Note that if things were low throttle, and are now
761  * disconnected, we keep the old timer running.
762  *
763  * (The same is true for vice-versa).
764  */
765  uint32_t timeout = 0;
766 
767  if (settings->InvalidRXArmedTimeout && settings->ArmedTimeout) {
768  timeout = MIN(settings->InvalidRXArmedTimeout,
769  settings->ArmedTimeout);
770  } else if (settings->InvalidRXArmedTimeout) {
771  timeout = settings->InvalidRXArmedTimeout;
772  } else {
773  timeout = settings->ArmedTimeout;
774  }
775 
776  if (check_receiver_timer(timeout)) {
778  } else {
780  }
781 
782  return;
783  }
784 
785  bool low_throt = is_low_throttle_for_arming(cmd);
786 
787  if (low_throt) {
788  /* Determine whether to disarm when throttle is low */
789  uint8_t flt_mode;
790  FlightStatusFlightModeGet(&flt_mode);
791 
792  if (flt_mode == FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD ||
793  flt_mode == FLIGHTSTATUS_FLIGHTMODE_RETURNTOHOME ||
794  flt_mode == FLIGHTSTATUS_FLIGHTMODE_PATHPLANNER ||
795  flt_mode == FLIGHTSTATUS_FLIGHTMODE_ALTITUDEHOLD ||
796  flt_mode == FLIGHTSTATUS_FLIGHTMODE_TABLETCONTROL) {
797  low_throt = false;
798  }
799  }
800 
801  if (low_throt) {
802  if (check_receiver_timer(settings->ArmedTimeout)) {
804  return;
805  }
806  } else {
807  /* Not low throt, not disconnected. */
809  }
810 
811  if (settings_updated) {
812  /* Make sure that we don't arm as a result of a settings
813  * change.
814  */
816  return;
817  }
818 
819  if (disarm_commanded(cmd, settings, low_throt)) {
820  /* Separate timer from the above */
821 
823  return;
824  } else if (arming_position(cmd, settings, low_throt)) {
825  /* Not disarming pos, not low throt timeout, not disconnected. */
826 
827  if (control_status == STATUS_DISARM) {
828  /* Edge detector says it's time to reset the receiver
829  * timer
830  */
832  }
833 
834  if (settings->Arming == MANUALCONTROLSETTINGS_ARMING_SWITCH ||
835  settings->Arming == MANUALCONTROLSETTINGS_ARMING_SWITCHTHROTTLE) {
837  } else if (settings->Arming == MANUALCONTROLSETTINGS_ARMING_CORNERSTHROTTLE) {
839  } else {
841  }
842 
843  return;
844  } else {
845  /* If the arming switch is flipped on, but we don't otherwise
846  * qualify for arming, let the upper layer know.
847  */
848  if (settings->Arming ==
849  MANUALCONTROLSETTINGS_ARMING_SWITCHTHROTTLE) {
850  if (cmd->ArmSwitch == MANUALCONTROLCOMMAND_ARMSWITCH_ARMED) {
852  return;
853  }
854  }
855  }
856 
858 
859  return;
860 
861 }
862 
863 
865 static void set_flight_mode()
866 {
867  uint8_t new_mode = transmitter_control_get_flight_mode();
868 
869  FlightStatusFlightModeOptions cur_mode;
870 
871  FlightStatusFlightModeGet(&cur_mode);
872 
873  if (cur_mode != new_mode) {
874  FlightStatusFlightModeSet(&new_mode);
875  }
876 }
877 
878 
879 static void resetRcvrActivity(struct rcvr_activity_fsm * fsm)
880 {
881  ReceiverActivityData data;
882  bool updated = false;
883 
884  /* Clear all channel activity flags */
885  ReceiverActivityGet(&data);
886  if (data.ActiveGroup != RECEIVERACTIVITY_ACTIVEGROUP_NONE &&
887  data.ActiveChannel != 255) {
888  data.ActiveGroup = RECEIVERACTIVITY_ACTIVEGROUP_NONE;
889  data.ActiveChannel = 255;
890  updated = true;
891  }
892  if (updated) {
893  ReceiverActivitySet(&data);
894  }
895 
896  /* Reset the FSM state */
897  fsm->group = 0;
898  fsm->check_count = 0;
899 }
900 
901 static void updateRcvrActivitySample(uintptr_t rcvr_id, uint16_t samples[], uint8_t max_channels) {
902  for (uint8_t channel = 1; channel <= max_channels; channel++) {
903  // Subtract 1 because channels are 1 indexed
904  samples[channel - 1] = PIOS_RCVR_Read(rcvr_id, channel);
905  }
906 }
907 
908 static bool updateRcvrActivityCompare(uintptr_t rcvr_id, struct rcvr_activity_fsm * fsm)
909 {
910  uint8_t active_channel = 0;
911 
912  int threshold = RCVR_ACTIVITY_MONITOR_MIN_RANGE;
913 
914  int rssi_channel = 0;
915 
916  /* Start off figuring out if there's a receive-rssi-injection channel */
917  int rssi_group = rssitype_to_channelgroup();
918 
919  /* If so, see if it's what we're processing */
920  if ((rssi_group >= 0) && (pios_rcvr_group_map[rssi_group] == rcvr_id)) {
921  // Yup. So let's skip the configured RSSI channel
922  rssi_channel = settings.RssiChannelNumber;
923  }
924 
925  /* Compare the current value to the previous sampled value */
926  for (uint8_t channel = 1;
928  channel++) {
929  if (rssi_channel == channel) {
930  // Don't spot activity on the configured RSSI channel
931  continue;
932  }
933 
934  uint16_t delta;
935  uint16_t prev = fsm->prev[channel - 1]; // Subtract 1 because channels are 1 indexed
936  uint16_t curr = PIOS_RCVR_Read(rcvr_id, channel);
937  if (curr > prev) {
938  delta = curr - prev;
939  } else {
940  delta = prev - curr;
941  }
942 
943  if (delta > threshold) {
944  active_channel = channel;
945 
946  // This is the best channel so far. Only look for
947  // "more active" channels now.
948  threshold = delta;
949  }
950  }
951 
952  if (active_channel) {
953  /* Mark this channel as active */
954  ReceiverActivityActiveGroupSet((uint8_t *)&fsm->group);
955  ReceiverActivityActiveChannelSet(&active_channel);
956 
957  return true;
958  }
959 
960  return false;
961 }
962 
963 static bool updateRcvrActivity(struct rcvr_activity_fsm * fsm)
964 {
965  bool activity_updated = false;
966 
967  if (fsm->group >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE) {
968  /* We're out of range, reset things */
969  resetRcvrActivity(fsm);
970  }
971 
972  if (fsm->check_count && (pios_rcvr_group_map[fsm->group])) {
973  /* Compare with previous sample */
974  activity_updated = updateRcvrActivityCompare(pios_rcvr_group_map[fsm->group], fsm);
975  }
976 
977  if (!activity_updated && fsm->check_count < 3) {
978  // First time through this is 1, so basically be willing
979  // to check twice.
980  fsm->check_count++;
981 
982  return false;
983  }
984 
985  /* Reset the sample counter */
986  fsm->check_count = 0;
987 
988  /* Find the next active group, but limit search so we can't loop forever here */
989  for (uint8_t i = 0; i <= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE; i++) {
990  /* Move to the next group */
991  fsm->group++;
992  if (fsm->group >= MANUALCONTROLSETTINGS_CHANNELGROUPS_NONE) {
993  /* Wrap back to the first group */
994  fsm->group = 0;
995  }
996  if (pios_rcvr_group_map[fsm->group]) {
997  /*
998  * Found an active group, take a sample here to avoid an
999  * extra 20ms delay in the main thread so we can speed up
1000  * this algorithm.
1001  */
1003  fsm->prev,
1004  NELEMENTS(fsm->prev));
1005 
1006  fsm->check_count = 1;
1007 
1008  break;
1009  }
1010  }
1011 
1012  return (activity_updated);
1013 }
1014 
1015 static inline float scale_stabilization(StabilizationSettingsData *stabSettings,
1016  float cmd, SharedDefsStabilizationModeOptions mode,
1017  StabilizationDesiredStabilizationModeElem axis) {
1018  switch (mode) {
1019  case SHAREDDEFS_STABILIZATIONMODE_DISABLED:
1020  case SHAREDDEFS_STABILIZATIONMODE_FAILSAFE:
1021  case SHAREDDEFS_STABILIZATIONMODE_POI:
1022  return 0;
1023  case SHAREDDEFS_STABILIZATIONMODE_MANUAL:
1024  case SHAREDDEFS_STABILIZATIONMODE_VIRTUALBAR:
1025  case SHAREDDEFS_STABILIZATIONMODE_COORDINATEDFLIGHT:
1026  return cmd;
1027  case SHAREDDEFS_STABILIZATIONMODE_SYSTEMIDENTRATE:
1028  case SHAREDDEFS_STABILIZATIONMODE_RATE:
1029  case SHAREDDEFS_STABILIZATIONMODE_WEAKLEVELING:
1030  case SHAREDDEFS_STABILIZATIONMODE_AXISLOCK:
1031  case SHAREDDEFS_STABILIZATIONMODE_LQG:
1032  cmd = expoM(cmd, stabSettings->RateExpo[axis],
1033  stabSettings->RateExponent[axis]*0.1f);
1034  return cmd * stabSettings->ManualRate[axis];
1035  case SHAREDDEFS_STABILIZATIONMODE_ACRODYNE:
1036  // For acrodyne, pass the command through raw.
1037  return cmd;
1038  case SHAREDDEFS_STABILIZATIONMODE_ACROPLUS:
1039  cmd = expoM(cmd, stabSettings->RateExpo[axis],
1040  stabSettings->RateExponent[axis]*0.1f);
1041  return cmd;
1042  case SHAREDDEFS_STABILIZATIONMODE_SYSTEMIDENT:
1043  case SHAREDDEFS_STABILIZATIONMODE_ATTITUDE:
1044  case SHAREDDEFS_STABILIZATIONMODE_ATTITUDELQG:
1045  return cmd * stabSettings->MaxLevelAngle[axis];
1046  case SHAREDDEFS_STABILIZATIONMODE_HORIZON:
1047  cmd = expo3(cmd, stabSettings->HorizonExpo[axis]);
1048  return cmd;
1049  }
1050 
1051  PIOS_Assert(false);
1052  return 0;
1053 }
1054 
1056 static void update_stabilization_desired(ManualControlCommandData * manual_control_command, ManualControlSettingsData * settings)
1057 {
1058  StabilizationDesiredData stabilization;
1059  StabilizationDesiredGet(&stabilization);
1060 
1061  StabilizationSettingsData stabSettings;
1062  StabilizationSettingsGet(&stabSettings);
1063 
1064  const uint8_t MANUAL_SETTINGS[3] = {
1065  STABILIZATIONDESIRED_STABILIZATIONMODE_MANUAL,
1066  STABILIZATIONDESIRED_STABILIZATIONMODE_MANUAL,
1067  STABILIZATIONDESIRED_STABILIZATIONMODE_MANUAL };
1068  const uint8_t RATE_SETTINGS[3] = {
1069  STABILIZATIONDESIRED_STABILIZATIONMODE_RATE,
1070  STABILIZATIONDESIRED_STABILIZATIONMODE_RATE,
1071  STABILIZATIONDESIRED_STABILIZATIONMODE_AXISLOCK};
1072  const uint8_t ATTITUDE_SETTINGS[3] = {
1073  STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE,
1074  STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE,
1075  STABILIZATIONDESIRED_STABILIZATIONMODE_AXISLOCK};
1076  const uint8_t VIRTUALBAR_SETTINGS[3] = {
1077  STABILIZATIONDESIRED_STABILIZATIONMODE_VIRTUALBAR,
1078  STABILIZATIONDESIRED_STABILIZATIONMODE_VIRTUALBAR,
1079  STABILIZATIONDESIRED_STABILIZATIONMODE_AXISLOCK};
1080  const uint8_t HORIZON_SETTINGS[3] = {
1081  STABILIZATIONDESIRED_STABILIZATIONMODE_HORIZON,
1082  STABILIZATIONDESIRED_STABILIZATIONMODE_HORIZON,
1083  STABILIZATIONDESIRED_STABILIZATIONMODE_AXISLOCK};
1084  const uint8_t AXISLOCK_SETTINGS[3] = {
1085  STABILIZATIONDESIRED_STABILIZATIONMODE_AXISLOCK,
1086  STABILIZATIONDESIRED_STABILIZATIONMODE_AXISLOCK,
1087  STABILIZATIONDESIRED_STABILIZATIONMODE_AXISLOCK};
1088  const uint8_t ACROPLUS_SETTINGS[3] = {
1089  STABILIZATIONDESIRED_STABILIZATIONMODE_ACROPLUS,
1090  STABILIZATIONDESIRED_STABILIZATIONMODE_ACROPLUS,
1091  STABILIZATIONDESIRED_STABILIZATIONMODE_RATE};
1092  const uint8_t ACRODYNE_SETTINGS[3] = {
1093  STABILIZATIONDESIRED_STABILIZATIONMODE_ACRODYNE,
1094  STABILIZATIONDESIRED_STABILIZATIONMODE_ACRODYNE,
1095  STABILIZATIONDESIRED_STABILIZATIONMODE_ACRODYNE};
1096  const uint8_t FAILSAFE_SETTINGS[3] = {
1097  STABILIZATIONDESIRED_STABILIZATIONMODE_FAILSAFE,
1098  STABILIZATIONDESIRED_STABILIZATIONMODE_FAILSAFE,
1099  STABILIZATIONDESIRED_STABILIZATIONMODE_FAILSAFE};
1100  const uint8_t SYSTEMIDENT_SETTINGS[3] = {
1101  STABILIZATIONDESIRED_STABILIZATIONMODE_SYSTEMIDENT,
1102  STABILIZATIONDESIRED_STABILIZATIONMODE_SYSTEMIDENT,
1103  STABILIZATIONDESIRED_STABILIZATIONMODE_SYSTEMIDENTRATE };
1104  const uint8_t LQG_SETTINGS[3] = {
1105  STABILIZATIONDESIRED_STABILIZATIONMODE_LQG,
1106  STABILIZATIONDESIRED_STABILIZATIONMODE_LQG,
1107  STABILIZATIONDESIRED_STABILIZATIONMODE_LQG };
1108  const uint8_t ATTITUDELQG_SETTINGS[3] = {
1109  STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDELQG,
1110  STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDELQG,
1111  STABILIZATIONDESIRED_STABILIZATIONMODE_LQG };
1112  const uint8_t FLIPOVER_SETTINGS[3] = {
1113  STABILIZATIONDESIRED_STABILIZATIONMODE_MANUAL,
1114  STABILIZATIONDESIRED_STABILIZATIONMODE_MANUAL,
1115  STABILIZATIONDESIRED_STABILIZATIONMODE_DISABLED };
1116 
1117  const uint8_t * stab_modes = ATTITUDE_SETTINGS;
1118 
1119  uint8_t reprojection = STABILIZATIONDESIRED_REPROJECTIONMODE_NONE;
1120  uint8_t thrust_mode = STABILIZATIONDESIRED_THRUSTMODE_DIRECT;
1121 
1122  uint8_t flightMode;
1123 
1124  FlightStatusFlightModeGet(&flightMode);
1125  switch(flightMode) {
1126  case FLIGHTSTATUS_FLIGHTMODE_MANUAL:
1127  stab_modes = MANUAL_SETTINGS;
1128  break;
1129  case FLIGHTSTATUS_FLIGHTMODE_ACRO:
1130  stab_modes = RATE_SETTINGS;
1131  break;
1132  case FLIGHTSTATUS_FLIGHTMODE_ACRODYNE:
1133  stab_modes = ACRODYNE_SETTINGS;
1134  break;
1135  case FLIGHTSTATUS_FLIGHTMODE_ACROPLUS:
1136  stab_modes = ACROPLUS_SETTINGS;
1137  break;
1138  case FLIGHTSTATUS_FLIGHTMODE_LEVELING:
1139  stab_modes = ATTITUDE_SETTINGS;
1140  break;
1141  case FLIGHTSTATUS_FLIGHTMODE_VIRTUALBAR:
1142  stab_modes = VIRTUALBAR_SETTINGS;
1143  break;
1144  case FLIGHTSTATUS_FLIGHTMODE_HORIZON:
1145  stab_modes = HORIZON_SETTINGS;
1146  break;
1147  case FLIGHTSTATUS_FLIGHTMODE_AXISLOCK:
1148  stab_modes = AXISLOCK_SETTINGS;
1149  break;
1150  case FLIGHTSTATUS_FLIGHTMODE_FAILSAFE:
1151  stab_modes = FAILSAFE_SETTINGS;
1152  break;
1153  case FLIGHTSTATUS_FLIGHTMODE_AUTOTUNE:
1154  stab_modes = SYSTEMIDENT_SETTINGS;
1155  break;
1156  case FLIGHTSTATUS_FLIGHTMODE_STABILIZED1:
1157  stab_modes = settings->Stabilization1Settings;
1158  reprojection = settings->Stabilization1Reprojection;
1159  thrust_mode = settings->Stabilization1Thrust;
1160  break;
1161  case FLIGHTSTATUS_FLIGHTMODE_STABILIZED2:
1162  stab_modes = settings->Stabilization2Settings;
1163  reprojection = settings->Stabilization2Reprojection;
1164  thrust_mode = settings->Stabilization2Thrust;
1165  break;
1166  case FLIGHTSTATUS_FLIGHTMODE_STABILIZED3:
1167  stab_modes = settings->Stabilization3Settings;
1168  reprojection = settings->Stabilization3Reprojection;
1169  thrust_mode = settings->Stabilization3Thrust;
1170  break;
1171 #ifdef TARGET_MAY_HAVE_BARO
1172  case FLIGHTSTATUS_FLIGHTMODE_ALTITUDEHOLD:
1173  stab_modes = ATTITUDE_SETTINGS;
1174  thrust_mode =
1175  STABILIZATIONDESIRED_THRUSTMODE_ALTITUDEWITHSTICKSCALING;
1176  break;
1177 #endif
1178  case FLIGHTSTATUS_FLIGHTMODE_LQG:
1179  stab_modes = LQG_SETTINGS;
1180  break;
1181  case FLIGHTSTATUS_FLIGHTMODE_LQGLEVELING:
1182  stab_modes = ATTITUDELQG_SETTINGS;
1183  break;
1184  case FLIGHTSTATUS_FLIGHTMODE_FLIPREVERSED:
1185  stab_modes = FLIPOVER_SETTINGS;
1186  thrust_mode = STABILIZATIONDESIRED_THRUSTMODE_FLIPOVERMODETHRUSTREVERSED;
1187  break;
1188  default:
1189  {
1190  // Major error, this should not occur because only enter this block when one of these is true
1191  set_manual_control_error(SYSTEMALARMS_MANUALCONTROL_UNDEFINED);
1192  }
1193  return;
1194  }
1195 
1196  stabilization.StabilizationMode[STABILIZATIONDESIRED_STABILIZATIONMODE_ROLL] = stab_modes[0];
1197  stabilization.StabilizationMode[STABILIZATIONDESIRED_STABILIZATIONMODE_PITCH] = stab_modes[1];
1198  stabilization.StabilizationMode[STABILIZATIONDESIRED_STABILIZATIONMODE_YAW] = stab_modes[2];
1199 
1200  stabilization.Roll = scale_stabilization(&stabSettings,
1201  manual_control_command->Roll,
1202  stab_modes[STABILIZATIONDESIRED_STABILIZATIONMODE_ROLL],
1203  STABILIZATIONDESIRED_STABILIZATIONMODE_ROLL);
1204 
1205  stabilization.Pitch = scale_stabilization(&stabSettings,
1206  manual_control_command->Pitch,
1207  stab_modes[STABILIZATIONDESIRED_STABILIZATIONMODE_PITCH],
1208  STABILIZATIONDESIRED_STABILIZATIONMODE_PITCH);
1209 
1210  stabilization.Yaw = scale_stabilization(&stabSettings,
1211  manual_control_command->Yaw,
1212  stab_modes[STABILIZATIONDESIRED_STABILIZATIONMODE_YAW],
1213  STABILIZATIONDESIRED_STABILIZATIONMODE_YAW);
1214 
1215  // get thrust source
1216  stabilization.Thrust = get_thrust_source(manual_control_command,
1217  (thrust_mode == STABILIZATIONDESIRED_THRUSTMODE_ALTITUDEWITHSTICKSCALING));
1218 
1219  // Set the reprojection.
1220  stabilization.ReprojectionMode = reprojection;
1221  stabilization.ThrustMode = thrust_mode;
1222 
1223  StabilizationDesiredSet(&stabilization);
1224 }
1225 
1226 static void set_loiter_command(ManualControlCommandData *cmd)
1227 {
1228  LoiterCommandData loiterCommand;
1229 
1230  loiterCommand.Pitch = cmd->Pitch;
1231  loiterCommand.Roll = cmd->Roll;
1232 
1233  loiterCommand.Thrust = get_thrust_source(cmd, true);
1234 
1235  loiterCommand.Frame = LOITERCOMMAND_FRAME_BODY;
1236 
1237  LoiterCommandSet(&loiterCommand);
1238 }
1239 
1243 static float scaleChannel(int n, int16_t value)
1244 {
1245  // TODO : change these variable names to be safer
1246  int16_t min = settings.ChannelMin[n];
1247  int16_t max = settings.ChannelMax[n];
1248  int16_t neutral = settings.ChannelNeutral[n];
1249 
1250  float valueScaled;
1251 
1252  // Scale
1253  if ((max > min && value >= neutral) || (min > max && value <= neutral))
1254  {
1255  if (max != neutral)
1256  valueScaled = (float)(value - neutral) / (float)(max - neutral);
1257  else
1258  valueScaled = 0;
1259  }
1260  else
1261  {
1262  if (min != neutral)
1263  valueScaled = (float)(value - neutral) / (float)(neutral - min);
1264  else
1265  valueScaled = 0;
1266  }
1267 
1268  // Bound
1269  if (valueScaled > 1.0f) valueScaled = 1.0f;
1270  else
1271  if (valueScaled < -1.0f) valueScaled = -1.0f;
1272 
1273  return valueScaled;
1274 }
1275 
1276 static uint32_t timeDifferenceMs(uint32_t start_time, uint32_t end_time) {
1277  if (end_time >= start_time)
1278  return end_time - start_time;
1279  else
1280  return (uint32_t)PIOS_THREAD_TIMEOUT_MAX - start_time + end_time + 1;
1281 }
1282 
1287 bool validInputRange(int n, uint16_t value, uint16_t offset)
1288 {
1289  int16_t min = settings.ChannelMin[n];
1290  int16_t max = settings.ChannelMax[n];
1291  int16_t neutral = settings.ChannelNeutral[n];
1292  int16_t range;
1293  bool inverted = false;
1294 
1295  if (min > max) {
1296  int16_t tmp = min;
1297  min = max;
1298  max = tmp;
1299 
1300  inverted = true;
1301  }
1302 
1303  if ((neutral > max) || (neutral < min)) {
1304  return false;
1305  }
1306 
1307  if (n == MANUALCONTROLSETTINGS_CHANNELGROUPS_THROTTLE) {
1308  /* Pick just the one that results in the "positive" side of
1309  * the throttle scale */
1310  if (inverted) {
1311  range = neutral - min;
1312  } else {
1313  range = max - neutral;
1314  }
1315  } else {
1316  range = MIN(max - neutral, neutral - min);
1317  }
1318 
1319  if (range < MIN_MEANINGFUL_RANGE) {
1320  return false;
1321  }
1322 
1323  return (value >= (min - offset) && value <= (max + offset));
1324 }
1325 
1330 static void set_manual_control_error(SystemAlarmsManualControlOptions error_code)
1331 {
1332  // Get the severity of the alarm given the error code
1333  SystemAlarmsAlarmOptions severity;
1334  switch (error_code) {
1335  case SYSTEMALARMS_MANUALCONTROL_NONE:
1336  severity = SYSTEMALARMS_ALARM_OK;
1337  break;
1338  case SYSTEMALARMS_MANUALCONTROL_NORX:
1339  case SYSTEMALARMS_MANUALCONTROL_CHANNELCONFIGURATION:
1340  case SYSTEMALARMS_MANUALCONTROL_ACCESSORY:
1341  severity = SYSTEMALARMS_ALARM_WARNING;
1342  break;
1343  case SYSTEMALARMS_MANUALCONTROL_SETTINGS:
1344  severity = SYSTEMALARMS_ALARM_CRITICAL;
1345  break;
1346  case SYSTEMALARMS_MANUALCONTROL_ALTITUDEHOLD:
1347  severity = SYSTEMALARMS_ALARM_ERROR;
1348  break;
1349  case SYSTEMALARMS_MANUALCONTROL_UNDEFINED:
1350  default:
1351  severity = SYSTEMALARMS_ALARM_CRITICAL;
1352  error_code = SYSTEMALARMS_MANUALCONTROL_UNDEFINED;
1353  }
1354 
1355  // Make sure not to set the error code if it didn't change
1356  SystemAlarmsManualControlOptions current_error_code;
1357  SystemAlarmsManualControlGet((uint8_t *) &current_error_code);
1358  if (current_error_code != error_code) {
1359  SystemAlarmsManualControlSet((uint8_t *) &error_code);
1360  }
1361 
1362  // AlarmSet checks only updates on toggle
1363  AlarmsSet(SYSTEMALARMS_ALARM_MANUALCONTROL, (uint8_t) severity);
1364 }
1365 
uint32_t PIOS_Thread_Systime(void)
Definition: pios_thread.c:212
static void set_loiter_command(ManualControlCommandData *cmd)
#define RCVR_ACTIVITY_MONITOR_MIN_RANGE
static bool validInputRange(int n, uint16_t value, uint16_t offset)
Determine if the manual input value is within acceptable limits.
Disconnected timeout has occurred.
Definition: control.h:36
float expoM(float x, int32_t g, float exponent)
Definition: misc_math.c:116
static bool thrust_is_bidir
static bool disarm_commanded(ManualControlCommandData *cmd, ManualControlSettingsData *settings, bool low_throt)
User requested disarm, or low throt timeout.
Definition: control.h:38
static bool collective_is_thrust
uintptr_t pios_rcvr_group_map[]
Definition: pios_hal.c:73
Process transmitter inputs and use as control source.
static uint32_t timeDifferenceMs(uint32_t start_time, uint32_t end_time)
#define NELEMENTS(x)
Definition: pios.h:192
static struct rcvr_activity_fsm activity_fsm
static bool arming_position(ManualControlCommandData *cmd, ManualControlSettingsData *settings, bool low_throt)
#define PIOS_THREAD_TIMEOUT_MAX
Definition: pios_thread.h:29
bool ok_to_arm(void)
Determine if the aircraft is safe to arm based on alarms.
static bool is_low_throttle_for_arming(ManualControlCommandData *manual_control_command)
static bool updateRcvrActivity(struct rcvr_activity_fsm *fsm)
uint16_t rssi
Definition: msp_messages.h:98
void UAVObjCbSetFlag(const UAVObjEvent *objEv, void *ctx, void *obj, int len)
int32_t AlarmsSet(SystemAlarmsAlarmElem alarm, SystemAlarmsAlarmOptions severity)
Definition: alarms.c:97
static bool disarming_position(ManualControlCommandData *cmd, ManualControlSettingsData *settings, bool low_throt)
int32_t transmitter_control_select(bool reset_controller)
Select and use transmitter control.
static float scale_stabilization(StabilizationSettingsData *stabSettings, float cmd, SharedDefsStabilizationModeOptions mode, StabilizationDesiredStabilizationModeElem axis)
int rssitype_to_channelgroup()
Convert a rssi type to the associated channel group.
#define ARMED_THRESHOLD
ManualControlSettingsChannelGroupsOptions group
static void reset_receiver_timer()
uint8_t data[XFER_BYTES_PER_PACKET]
Definition: bl_messages.h:129
static ManualControlSettingsData settings
static void set_manual_control_error(SystemAlarmsManualControlOptions errorCode)
enum control_status transmitter_control_get_status()
Get any control events.
static void update_stabilization_desired(ManualControlCommandData *manual_control_command, ManualControlSettingsData *settings)
In stabilization mode, set stabilization desired.
struct _msp_pid_item pos
Definition: msp_messages.h:100
void apply_channel_deadband(float *value, float deadband)
Apply deadband to Roll/Pitch/Yaw channels.
Definition: misc_math.c:373
static void resetRcvrActivity(struct rcvr_activity_fsm *fsm)
static enum control_status control_status
uint8_t i
Definition: msp_messages.h:97
Something is fundamentally wrong.
Definition: control.h:35
#define THRUST_BIDIR_THRESH
static bool settings_updated
enum channel_mode mode
Definition: pios_servo.c:58
control_status
Definition: control.h:33
#define RCVR_ACTIVITY_MONITOR_CHANNELS_PER_GROUP
static void process_transmitter_events(ManualControlCommandData *cmd, ManualControlSettingsData *settings, bool valid, bool settings_updated)
Process the inputs and determine whether to arm or not.
static uint8_t connected_count
#define MIN_MEANINGFUL_RANGE
#define RECEIVER_TIMER_FIRED
uint16_t value
Definition: storm32bgc.c:155
static float get_thrust_source(ManualControlCommandData *manual_control_command, bool always_fullrange)
#define CONNECTION_OFFSET
uint32_t offset
Definition: uavtalk_priv.h:51
int32_t PIOS_RCVR_Read(uintptr_t rcvr_id, uint8_t channel)
static uint32_t lastActivityTime
RX is disconnected.
Definition: control.h:34
tuple f
Definition: px_mkfw.py:81
static uint8_t disconnected_count
#define MIN(a, b)
Definition: misc_math.h:41
Things are "normal".
Definition: control.h:40
User requested arm, controls in valid pos.
Definition: control.h:43
static void perform_tc_settings_update()
int32_t PIOS_ADC_GetChannelRaw(uint32_t channel)
Includes PiOS and core architecture components.
static uint32_t receiver_timer
int32_t transmitter_control_update()
Process inputs and arming.
uint16_t prev[RCVR_ACTIVITY_MONITOR_CHANNELS_PER_GROUP]
User requested arm, controls in invalid pos.
Definition: control.h:42
int32_t transmitter_control_initialize()
Initialize the transmitter control mode.
Implements an OpenLRS driver for the RFM22B.
static void set_flight_mode()
Determine which of N positions the flight mode switch is in and set flight mode accordingly.
static uint32_t lastSysTime
Arming switch is in a state that doesn't.
Definition: control.h:46
For corners. Request we be the "opposite" mode.
Definition: control.h:41
if(BaroAltitudeHandle()!=NULL)
static bool updateRcvrActivityCompare(uintptr_t rcvr_id, struct rcvr_activity_fsm *fsm)
static ManualControlCommandData cmd
uint8_t transmitter_control_get_flight_mode()
Query what the flight mode would be if this is selected.
#define PIOS_Assert(test)
Definition: pios_debug.h:52
static float flight_mode_value
uint16_t max
Definition: msp_messages.h:97
static float scaleChannel(int n, int16_t value)
static bool check_receiver_timer(uint32_t threshold)
float expo3(float x, int32_t g)
Approximation an exponential scale curve.
Definition: misc_math.c:111
static bool channel_is_configured(int channel_num)
static void updateRcvrActivitySample(uintptr_t rcvr_id, uint16_t samples[], uint8_t max_channels)
uint16_t min
Definition: msp_messages.h:96