Line data Source code
1 : /*
2 : This program is free software: you can redistribute it and/or modify
3 : it under the terms of the GNU General Public License as published by
4 : the Free Software Foundation, either version 3 of the License, or
5 : (at your option) any later version.
6 :
7 : This program is distributed in the hope that it will be useful,
8 : but WITHOUT ANY WARRANTY; without even the implied warranty of
9 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 : GNU General Public License for more details.
11 :
12 : You should have received a copy of the GNU General Public License
13 : along with this program. If not, see <http://www.gnu.org/licenses/>.
14 : */
15 :
16 : /*
17 : * RC_Channel.cpp - class for one RC channel input
18 : */
19 :
20 : #include "RC_Channel_config.h"
21 :
22 : #if AP_RC_CHANNEL_ENABLED
23 :
24 : #include <stdlib.h>
25 : #include <cmath>
26 :
27 : #include <AP_HAL/AP_HAL.h>
28 : extern const AP_HAL::HAL& hal;
29 :
30 : #include <AP_Math/AP_Math.h>
31 :
32 : #include "RC_Channel.h"
33 : #include <GCS_MAVLink/GCS.h>
34 :
35 : #include <AC_Avoidance/AC_Avoid.h>
36 : #include <AC_Sprayer/AC_Sprayer.h>
37 : #include <AP_Camera/AP_Camera.h>
38 : #include <AP_Camera/AP_RunCam.h>
39 : #include <AP_Compass/AP_Compass.h>
40 : #include <AP_Generator/AP_Generator.h>
41 : #include <AP_Gripper/AP_Gripper.h>
42 : #include <AP_GyroFFT/AP_GyroFFT.h>
43 : #include <AP_ADSB/AP_ADSB.h>
44 : #include <AP_BoardConfig/AP_BoardConfig.h>
45 : #include <AP_BattMonitor/AP_BattMonitor.h>
46 : #include <AP_LandingGear/AP_LandingGear.h>
47 : #include <AP_Logger/AP_Logger.h>
48 : #include <AP_ServoRelayEvents/AP_ServoRelayEvents.h>
49 : #include <SRV_Channel/SRV_Channel.h>
50 : #include <AP_Arming/AP_Arming.h>
51 : #include <AP_Avoidance/AP_Avoidance.h>
52 : #include <AP_GPS/AP_GPS.h>
53 : #include <AC_Fence/AC_Fence.h>
54 : #include <AP_OpticalFlow/AP_OpticalFlow.h>
55 : #include <AP_VisualOdom/AP_VisualOdom.h>
56 : #include <AP_AHRS/AP_AHRS.h>
57 : #include <AP_Mount/AP_Mount.h>
58 : #include <AP_Notify/AP_Notify.h>
59 : #include <AP_VideoTX/AP_VideoTX.h>
60 : #include <AP_Torqeedo/AP_Torqeedo.h>
61 : #include <AP_Vehicle/AP_Vehicle_Type.h>
62 : #include <AP_Parachute/AP_Parachute_config.h>
63 : #include <AP_Scripting/AP_Scripting.h>
64 : #define SWITCH_DEBOUNCE_TIME_MS 200
65 :
66 : const AP_Param::GroupInfo RC_Channel::var_info[] = {
67 : // @Param: MIN
68 : // @DisplayName: RC min PWM
69 : // @Description: RC minimum PWM pulse width in microseconds. Typically 1000 is lower limit, 1500 is neutral and 2000 is upper limit.
70 : // @Units: PWM
71 : // @Range: 800 2200
72 : // @Increment: 1
73 : // @User: Advanced
74 : AP_GROUPINFO("MIN", 1, RC_Channel, radio_min, 1100),
75 :
76 : // @Param: TRIM
77 : // @DisplayName: RC trim PWM
78 : // @Description: RC trim (neutral) PWM pulse width in microseconds. Typically 1000 is lower limit, 1500 is neutral and 2000 is upper limit.
79 : // @Units: PWM
80 : // @Range: 800 2200
81 : // @Increment: 1
82 : // @User: Advanced
83 : AP_GROUPINFO("TRIM", 2, RC_Channel, radio_trim, 1500),
84 :
85 : // @Param: MAX
86 : // @DisplayName: RC max PWM
87 : // @Description: RC maximum PWM pulse width in microseconds. Typically 1000 is lower limit, 1500 is neutral and 2000 is upper limit.
88 : // @Units: PWM
89 : // @Range: 800 2200
90 : // @Increment: 1
91 : // @User: Advanced
92 : AP_GROUPINFO("MAX", 3, RC_Channel, radio_max, 1900),
93 :
94 : // @Param: REVERSED
95 : // @DisplayName: RC reversed
96 : // @Description: Reverse channel input. Set to 0 for normal operation. Set to 1 to reverse this input channel.
97 : // @Values: 0:Normal,1:Reversed
98 : // @User: Advanced
99 : AP_GROUPINFO("REVERSED", 4, RC_Channel, reversed, 0),
100 :
101 : // @Param: DZ
102 : // @DisplayName: RC dead-zone
103 : // @Description: PWM dead zone in microseconds around trim or bottom
104 : // @Units: PWM
105 : // @Range: 0 200
106 : // @User: Advanced
107 : AP_GROUPINFO("DZ", 5, RC_Channel, dead_zone, 0),
108 :
109 : // @Param: OPTION
110 : // @DisplayName: RC input option
111 : // @Description: Function assigned to this RC channel
112 : // @SortValues: AlphabeticalZeroAtTop
113 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 0:Do Nothing
114 : // @Values{Copter}: 2:FLIP Mode
115 : // @Values{Copter}: 3:Simple Mode
116 : // @Values{Copter, Rover, Plane}: 4:RTL
117 : // @Values{Copter}: 5:Save Trim
118 : // @Values{Rover}: 5:Save Trim (4.1 and lower)
119 : // @Values{Copter, Rover}: 7:Save WP
120 : // @Values{Copter, Rover, Plane, Sub}: 9:Camera Trigger
121 : // @Values{Copter}: 10:RangeFinder Enable
122 : // @Values{Copter, Rover, Plane, Sub}: 11:Fence Enable
123 : // @Values{Copter}: 13:Super Simple Mode
124 : // @Values{Copter}: 14:Acro Trainer
125 : // @Values{Copter}: 15:Sprayer Enable
126 : // @Values{Copter, Rover, Plane}: 16:AUTO Mode
127 : // @Values{Copter}: 17:AUTOTUNE Mode
128 : // @Values{Copter, Blimp}: 18:LAND Mode
129 : // @Values{Copter, Rover}: 19:Gripper
130 : // @Values{Copter}: 21:Parachute Enable
131 : // @Values{Copter, Plane}: 22:Parachute Release
132 : // @Values{Copter}: 23:Parachute 3pos
133 : // @Values{Copter, Rover, Plane, Sub}: 24:Auto Mission Reset
134 : // @Values{Copter}: 25:AttCon Feed Forward
135 : // @Values{Copter}: 26:AttCon Accel Limits
136 : // @Values{Copter, Rover, Plane, Sub}: 27:Retract Mount1
137 : // @Values{Copter, Rover, Plane, Sub}: 28:Relay1 On/Off
138 : // @Values{Copter, Plane}: 29:Landing Gear
139 : // @Values{Copter}: 30:Lost Copter Sound
140 : // @Values{Rover}: 30:Lost Rover Sound
141 : // @Values{Plane}: 30:Lost Plane Sound
142 : // @Values{Copter, Rover, Plane, Sub}: 31:Motor Emergency Stop
143 : // @Values{Copter}: 32:Motor Interlock
144 : // @Values{Copter}: 33:BRAKE Mode
145 : // @Values{Copter, Rover, Plane, Sub}: 34:Relay2 On/Off, 35:Relay3 On/Off, 36:Relay4 On/Off
146 : // @Values{Copter}: 37:THROW Mode
147 : // @Values{Copter, Plane}: 38:ADSB Avoidance Enable
148 : // @Values{Copter}: 39:PrecLoiter Enable
149 : // @Values{Copter, Rover}: 40:Proximity Avoidance Enable
150 : // @Values{Copter, Rover, Plane}: 41:ArmDisarm (4.1 and lower)
151 : // @Values{Copter, Rover}: 42:SMARTRTL Mode
152 : // @Values{Copter, Plane}: 43:InvertedFlight Enable
153 : // @Values{Copter}: 44:Winch Enable, 45:Winch Control
154 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 46:RC Override Enable
155 : // @Values{Copter}: 47:User Function 1, 48:User Function 2, 49:User Function 3
156 : // @Values{Rover}: 50:LearnCruise Speed
157 : // @Values{Rover, Plane}: 51:MANUAL Mode
158 : // @Values{Copter, Rover, Plane}: 52:ACRO Mode
159 : // @Values{Rover}: 53:STEERING Mode
160 : // @Values{Rover}: 54:HOLD Mode
161 : // @Values{Copter, Rover, Plane}: 55:GUIDED Mode
162 : // @Values{Copter, Rover, Plane}: 56:LOITER Mode
163 : // @Values{Copter, Rover}: 57:FOLLOW Mode
164 : // @Values{Copter, Rover, Plane, Sub}: 58:Clear Waypoints
165 : // @Values{Rover}: 59:Simple Mode
166 : // @Values{Copter}: 60:ZigZag Mode
167 : // @Values{Copter}: 61:ZigZag SaveWP
168 : // @Values{Copter, Rover, Plane, Sub}: 62:Compass Learn
169 : // @Values{Rover}: 63:Sailboat Tack
170 : // @Values{Plane}: 64:Reverse Throttle
171 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 65:GPS Disable
172 : // @Values{Copter, Rover, Plane, Sub}: 66:Relay5 On/Off, 67:Relay6 On/Off
173 : // @Values{Copter}: 68:STABILIZE Mode
174 : // @Values{Copter}: 69:POSHOLD Mode
175 : // @Values{Copter}: 70:ALTHOLD Mode
176 : // @Values{Copter}: 71:FLOWHOLD Mode
177 : // @Values{Copter,Rover,Plane}: 72:CIRCLE Mode
178 : // @Values{Copter}: 73:DRIFT Mode
179 : // @Values{Rover}: 74:Sailboat motoring 3pos
180 : // @Values{Copter}: 75:SurfaceTrackingUpDown
181 : // @Values{Copter}: 76:STANDBY Mode
182 : // @Values{Plane}: 77:TAKEOFF Mode
183 : // @Values{Copter, Rover, Plane, Sub}: 78:RunCam Control
184 : // @Values{Copter, Rover, Plane, Sub}: 79:RunCam OSD Control
185 : // @Values{Copter}: 80:VisOdom Align
186 : // @Values{Rover}: 80:VisoOdom Align
187 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 81:Disarm
188 : // @Values{Plane}: 82:QAssist 3pos
189 : // @Values{Copter}: 83:ZigZag Auto
190 : // @Values{Copter, Plane}: 84:AirMode
191 : // @Values{Copter, Plane}: 85:Generator
192 : // @Values{Plane}: 86:Non Auto Terrain Follow Disable
193 : // @Values{Plane}: 87:Crow Select
194 : // @Values{Plane}: 88:Soaring Enable
195 : // @Values{Plane}: 89:Landing Flare
196 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 90:EKF Source Set
197 : // @Values{Plane}: 91:Airspeed Ratio Calibration
198 : // @Values{Plane}: 92:FBWA Mode
199 : // @Values{Copter, Rover, Plane, Sub}: 94:VTX Power
200 : // @Values{Plane}: 95:FBWA taildragger takeoff mode
201 : // @Values{Plane}: 96:Trigger re-reading of mode switch
202 : // @Values{Rover}: 97:Windvane home heading direction offset
203 : // @Values{Plane}: 98:TRAINING Mode
204 : // @Values{Copter}: 99:AUTO RTL
205 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 100:KillIMU1, 101:KillIMU2
206 : // @Values{Copter, Rover, Plane, Sub}: 102:Camera Mode Toggle
207 : // @Values{Copter, Rover, Plane, Blimp, Sub, Tracker}: 103: EKF lane switch attempt
208 : // @Values{Copter, Rover, Plane, Blimp, Sub, Tracker}: 104: EKF yaw reset
209 : // @Values{Copter, Rover, Plane, Sub}: 105:GPS Disable Yaw
210 : // @Values{Rover, Plane}: 106:Disable Airspeed Use
211 : // @Values{Plane}: 107:Enable FW Autotune
212 : // @Values{Plane}: 108:QRTL Mode
213 : // @Values{Copter}: 109:use Custom Controller
214 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 110:KillIMU3
215 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 111:Loweheiser starter
216 : // @Values{Copter,Plane,Rover,Blimp,Sub,Tracker}: 112:SwitchExternalAHRS
217 : // @Values{Copter, Rover, Plane, Sub}: 113:Retract Mount2
218 : // @Values{Plane}: 150:CRUISE Mode
219 : // @Values{Copter}: 151:TURTLE Mode
220 : // @Values{Copter}: 152:SIMPLE heading reset
221 : // @Values{Copter, Rover, Plane, Sub}: 153:ArmDisarm (4.2 and higher)
222 : // @Values{Blimp}: 153:ArmDisarm
223 : // @Values{Copter}: 154:ArmDisarm with AirMode (4.2 and higher)
224 : // @Values{Plane}: 154:ArmDisarm with Quadplane AirMode (4.2 and higher)
225 : // @Values{Rover}: 155:Set steering trim to current servo and RC
226 : // @Values{Plane}: 155:Set roll pitch and yaw trim to current servo and RC
227 : // @Values{Rover}: 156:Torqeedo Clear Err
228 : // @Values{Plane}: 157:Force FS Action to FBWA
229 : // @Values{Copter, Plane}: 158:Optflow Calibration
230 : // @Values{Copter}: 159:Force IS_Flying
231 : // @Values{Plane}: 160:Weathervane Enable
232 : // @Values{Copter}: 161:Turbine Start(heli)
233 : // @Values{Copter, Rover, Plane}: 162:FFT Tune
234 : // @Values{Copter, Rover, Plane, Sub}: 163:Mount Yaw Lock
235 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 164:Pause Stream Logging
236 : // @Values{Copter, Rover, Plane, Sub}: 165:Arm/Emergency Motor Stop
237 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 166:Camera Record Video, 167:Camera Zoom, 168:Camera Manual Focus, 169:Camera Auto Focus
238 : // @Values{Plane}: 170:QSTABILIZE Mode
239 : // @Values{Copter, Rover, Plane, Blimp}: 171:Calibrate Compasses
240 : // @Values{Copter, Rover, Plane, Blimp}: 172:Battery MPPT Enable
241 : // @Values{Plane}: 173:Plane AUTO Mode Landing Abort
242 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 174:Camera Image Tracking
243 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 175:Camera Lens
244 : // @Values{Plane}: 176:Quadplane Fwd Throttle Override enable
245 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 177:Mount LRF enable
246 : // @Values{Copter}: 178:FlightMode Pause/Resume
247 : // @Values{Plane}: 179:ICEngine start / stop
248 : // @Values{Copter, Plane}: 180:Test autotuned gains after tune is complete
249 : // @Values{Plane}: 181: QuickTune
250 : // @Values{Copter}: 182: AHRS AutoTrim
251 : // @Values{Plane}: 183: AUTOLAND mode
252 : // @Values{Plane}: 184: System ID Chirp
253 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 185:Mount Roll/Pitch Lock
254 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 186:Mount POI Lock
255 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 187:EKF Reset
256 : // @Values{Rover}: 201:Roll
257 : // @Values{Rover}: 202:Pitch
258 : // @Values{Rover}: 207:MainSail
259 : // @Values{Rover, Plane}: 208:Flap
260 : // @Values{Plane}: 209:VTOL Forward Throttle
261 : // @Values{Plane}: 210:Airbrakes
262 : // @Values{Rover}: 211:Walking Height
263 : // @Values{Copter, Rover, Plane, Sub}: 212:Mount1 Roll, 213:Mount1 Pitch, 214:Mount1 Yaw, 215:Mount2 Roll, 216:Mount2 Pitch, 217:Mount2 Yaw
264 : // @Values{Copter, Rover, Plane, Blimp, Sub}: 218:Loweheiser throttle
265 : // @Values{Copter}: 219:Transmitter Tuning
266 : // @Values{All-Vehicles}: 300:Scripting1, 301:Scripting2, 302:Scripting3, 303:Scripting4, 304:Scripting5, 305:Scripting6, 306:Scripting7, 307:Scripting8, 308:Scripting9, 309:Scripting10, 310:Scripting11, 311:Scripting12, 312:Scripting13, 313:Scripting14, 314:Scripting15, 315:Scripting16
267 : // @Values{All-Vehicles}: 316:Stop-Restart Scripting
268 : // @User: Standard
269 : AP_GROUPINFO("OPTION", 6, RC_Channel, option, 0),
270 :
271 : AP_GROUPEND
272 : };
273 :
274 :
275 : // constructor
276 35697 : RC_Channel::RC_Channel(void)
277 : {
278 35697 : AP_Param::setup_object_defaults(this, var_info);
279 35697 : }
280 :
281 56138 : void RC_Channel::set_range(uint16_t high)
282 : {
283 56138 : type_in = ControlType::RANGE;
284 56138 : high_in = high;
285 56138 : }
286 :
287 206998 : void RC_Channel::set_angle(uint16_t angle)
288 : {
289 206998 : type_in = ControlType::ANGLE;
290 206998 : high_in = angle;
291 206998 : }
292 :
293 8121 : void RC_Channel::set_default_dead_zone(int16_t dzone)
294 : {
295 8121 : dead_zone.set_default(abs(dzone));
296 8121 : }
297 :
298 40421032 : bool RC_Channel::get_reverse(void) const
299 : {
300 40421032 : return bool(reversed.get());
301 : }
302 :
303 : // read input from hal.rcin or overrides
304 119958672 : bool RC_Channel::update(void)
305 : {
306 119958672 : raw_radio_in = hal.rcin->read(ch_in);
307 119958672 : if (has_override() && !rc().option_is_enabled(RC_Channels::Option::IGNORE_OVERRIDES)) {
308 55180 : radio_in = override_value;
309 119903492 : } else if (rc().has_had_rc_receiver() && !rc().option_is_enabled(RC_Channels::Option::IGNORE_RECEIVER)) {
310 119899256 : radio_in = raw_radio_in;
311 : } else {
312 4236 : return false;
313 : }
314 :
315 119954436 : if (type_in == ControlType::RANGE) {
316 6809620 : control_in = pwm_to_range();
317 : } else {
318 : // ControlType::ANGLE
319 113144816 : control_in = pwm_to_angle();
320 : }
321 :
322 119954436 : return true;
323 : }
324 :
325 : /*
326 : return the center stick position expressed as a control_in value
327 : used for thr_mid in copter
328 : */
329 14958411 : int16_t RC_Channel::get_control_mid() const
330 : {
331 14958411 : if (type_in == ControlType::RANGE) {
332 14958411 : int16_t r_in = (radio_min.get() + radio_max.get())/2;
333 :
334 14958411 : int16_t radio_trim_low = radio_min + dead_zone;
335 :
336 14958411 : return (((int32_t)(high_in) * (int32_t)(r_in - radio_trim_low)) / (int32_t)(radio_max - radio_trim_low));
337 : } else {
338 0 : return 0;
339 : }
340 : }
341 :
342 : /*
343 : return an "angle in centidegrees" (normally -4500 to 4500) from
344 : the current radio_in value using the specified dead_zone
345 : */
346 116127266 : float RC_Channel::pwm_to_angle_dz_trim(uint16_t _dead_zone, uint16_t _trim) const
347 : {
348 116127266 : int16_t radio_trim_high = _trim + _dead_zone;
349 116127266 : int16_t radio_trim_low = _trim - _dead_zone;
350 :
351 116127266 : float reverse_mul = (reversed?-1:1);
352 :
353 : // don't allow out of range values
354 116127266 : int16_t r_in = constrain_int16(radio_in, radio_min.get(), radio_max.get());
355 :
356 116127266 : if (r_in > radio_trim_high && radio_max != radio_trim_high) {
357 7350829 : return reverse_mul * ((float)high_in * (float)(r_in - radio_trim_high)) / (float)(radio_max - radio_trim_high);
358 108776437 : } else if (r_in < radio_trim_low && radio_trim_low != radio_min) {
359 937349 : return reverse_mul * ((float)high_in * (float)(r_in - radio_trim_low)) / (float)(radio_trim_low - radio_min);
360 : } else {
361 107839088 : return 0;
362 : }
363 : }
364 :
365 : /*
366 : return an "angle in centidegrees" (normally -4500 to 4500) from
367 : the current radio_in value using the specified dead_zone
368 : */
369 115172981 : float RC_Channel::pwm_to_angle_dz(uint16_t _dead_zone) const
370 : {
371 115172981 : return pwm_to_angle_dz_trim(_dead_zone, radio_trim);
372 : }
373 :
374 : /*
375 : return an "angle in centidegrees" (normally -4500 to 4500) from
376 : the current radio_in value
377 : */
378 113144816 : float RC_Channel::pwm_to_angle() const
379 : {
380 113144816 : return pwm_to_angle_dz(dead_zone);
381 : }
382 :
383 :
384 : /*
385 : convert a pulse width modulation value to a value in the configured
386 : range, using the specified deadzone
387 : */
388 12327142 : float RC_Channel::pwm_to_range_dz(uint16_t _dead_zone) const
389 : {
390 12327142 : int16_t r_in = constrain_int16(radio_in, radio_min.get(), radio_max.get());
391 :
392 12327142 : if (reversed) {
393 0 : r_in = radio_max.get() - (r_in - radio_min.get());
394 : }
395 :
396 12327142 : int16_t radio_trim_low = radio_min + _dead_zone;
397 :
398 12327142 : if (r_in > radio_trim_low) {
399 3934259 : return (((float)(high_in) * (float)(r_in - radio_trim_low)) / (float)(radio_max - radio_trim_low));
400 : }
401 8392883 : return 0;
402 : }
403 :
404 : /*
405 : convert a pulse width modulation value to a value in the configured
406 : range
407 : */
408 6809620 : float RC_Channel::pwm_to_range() const
409 : {
410 6809620 : return pwm_to_range_dz(dead_zone);
411 : }
412 :
413 :
414 7545687 : float RC_Channel::get_control_in_zero_dz(void) const
415 : {
416 7545687 : if (type_in == ControlType::RANGE) {
417 5517522 : return pwm_to_range_dz(0);
418 : }
419 2028165 : return pwm_to_angle_dz(0);
420 : }
421 :
422 : // ------------------------------------------
423 :
424 15612637 : float RC_Channel::norm_input() const
425 : {
426 : float ret;
427 15612637 : int16_t reverse_mul = (reversed?-1:1);
428 15612637 : if (radio_in < radio_trim) {
429 245731 : if (radio_min >= radio_trim) {
430 0 : return 0.0f;
431 : }
432 245731 : ret = reverse_mul * (float)(radio_in - radio_trim) / (float)(radio_trim - radio_min);
433 : } else {
434 15366906 : if (radio_max <= radio_trim) {
435 0 : return 0.0f;
436 : }
437 15366906 : ret = reverse_mul * (float)(radio_in - radio_trim) / (float)(radio_max - radio_trim);
438 : }
439 15612637 : return constrain_float(ret, -1.0f, 1.0f);
440 : }
441 :
442 75876353 : float RC_Channel::norm_input_dz() const
443 : {
444 75876353 : int16_t dz_min = radio_trim - dead_zone;
445 75876353 : int16_t dz_max = radio_trim + dead_zone;
446 : float ret;
447 75876353 : int16_t reverse_mul = (reversed?-1:1);
448 75876353 : if (radio_in < dz_min && dz_min > radio_min) {
449 819444 : ret = reverse_mul * (float)(radio_in - dz_min) / (float)(dz_min - radio_min);
450 75056909 : } else if (radio_in > dz_max && radio_max > dz_max) {
451 736847 : ret = reverse_mul * (float)(radio_in - dz_max) / (float)(radio_max - dz_max);
452 : } else {
453 74320062 : ret = 0;
454 : }
455 75876353 : return constrain_float(ret, -1.0f, 1.0f);
456 : }
457 :
458 : // return a normalised input for a channel, in range -1 to 1,
459 : // ignores trim and deadzone
460 10112 : float RC_Channel::norm_input_ignore_trim() const
461 : {
462 : // sanity check min and max to avoid divide by zero
463 10112 : if (radio_max <= radio_min) {
464 0 : return 0.0f;
465 : }
466 10112 : const float ret = (reversed ? -2.0f : 2.0f) * (((float)(radio_in - radio_min) / (float)(radio_max - radio_min)) - 0.5f);
467 10112 : return constrain_float(ret, -1.0f, 1.0f);
468 : }
469 :
470 :
471 1386 : bool RC_Channel::norm_input_ignore_trim(float &norm_in) const
472 : {
473 1386 : if (!rc().has_valid_input()) {
474 0 : return false;
475 : }
476 1386 : if (radio_in == 0) {
477 0 : return false;
478 : }
479 1386 : if (radio_max <= radio_min) {
480 : // sanity check min and max to avoid divide by zero
481 0 : return false;
482 : }
483 1386 : norm_in = norm_input_ignore_trim();
484 1386 : return true;
485 : }
486 :
487 : /*
488 : get percentage input from 0 to 100. This ignores the trim value.
489 : */
490 13517 : uint8_t RC_Channel::percent_input() const
491 : {
492 13517 : if (radio_in <= radio_min) {
493 13242 : return reversed?100:0;
494 : }
495 275 : if (radio_in >= radio_max) {
496 70 : return reversed?0:100;
497 : }
498 205 : uint8_t ret = 100.0f * (radio_in - radio_min) / (float)(radio_max - radio_min);
499 205 : if (reversed) {
500 0 : ret = 100 - ret;
501 : }
502 205 : return ret;
503 : }
504 :
505 : /*
506 : return true if input is within deadzone of trim
507 : */
508 0 : bool RC_Channel::in_trim_dz() const
509 : {
510 0 : return is_bounded_int32(radio_in, radio_trim - dead_zone, radio_trim + dead_zone);
511 : }
512 :
513 : /*
514 : return true if raw input is within deadzone of trim
515 : */
516 1041 : bool RC_Channel::in_raw_trim_dz() const
517 : {
518 1041 : return is_bounded_int32(raw_radio_in, radio_trim - dead_zone, radio_trim + dead_zone);
519 : }
520 :
521 : /*
522 : return trues if input is within deadzone of min
523 : */
524 0 : bool RC_Channel::in_min_dz() const
525 : {
526 0 : return radio_in < radio_min + dead_zone;
527 : }
528 :
529 4110 : void RC_Channel::set_override(const uint16_t v, const uint32_t timestamp_ms)
530 : {
531 4110 : if (!rc().gcs_overrides_enabled()) {
532 374 : return;
533 : }
534 :
535 : // the channel that controls override-enable must always reflect the
536 : // physical RC switch; accepting a GCS override here would let the GCS
537 : // disable its own overrides and create an oscillation
538 3736 : if ((AUX_FUNC)option.get() == AUX_FUNC::RC_OVERRIDE_ENABLE) {
539 151 : return;
540 : }
541 :
542 : // store throttle pwm at the start of an override session
543 3585 : if (!rc().has_active_overrides()) {
544 116 : rc().set_override_start_throttle(rc().get_throttle_channel().get_raw_radio_in());
545 : }
546 :
547 3585 : last_override_time = timestamp_ms != 0 ? timestamp_ms : AP_HAL::millis();
548 3585 : override_value = v;
549 3585 : rc().new_override_received();
550 : }
551 :
552 967824 : void RC_Channel::clear_override()
553 : {
554 967824 : last_override_time = 0;
555 967824 : override_value = 0;
556 : // only forget the throttle override-start pwm once no overrides remain
557 967824 : if (!rc().has_active_overrides()) {
558 967816 : rc().set_override_start_throttle(-1);
559 : }
560 967824 : }
561 :
562 170704413 : bool RC_Channel::has_override() const
563 : {
564 170704413 : if (override_value == 0) {
565 168013394 : return false;
566 : }
567 :
568 : uint32_t override_timeout_ms;
569 2691019 : if (!rc().get_override_timeout_ms(override_timeout_ms)) {
570 : // timeouts are disabled
571 3981 : return true;
572 : }
573 :
574 2687038 : if (override_timeout_ms == 0) {
575 : // overrides are explicitly disabled by a zero value
576 112 : return false;
577 : }
578 :
579 2686926 : return (AP_HAL::millis() - last_override_time < override_timeout_ms);
580 : }
581 :
582 : /*
583 : perform stick mixing on one channel
584 : This type of stick mixing reduces the influence of the auto
585 : controller as it increases the influence of the users stick input,
586 : allowing the user full deflection if needed
587 : */
588 126941 : float RC_Channel::stick_mixing(const float servo_in)
589 : {
590 126941 : float ch_inf = (float)(radio_in - radio_trim);
591 126941 : ch_inf = fabsf(ch_inf);
592 126941 : ch_inf = MIN(ch_inf, 400.0f);
593 126941 : ch_inf = ((400.0f - ch_inf) / 400.0f);
594 :
595 126941 : float servo_out = servo_in;
596 126941 : servo_out *= ch_inf;
597 126941 : servo_out += control_in;
598 :
599 126941 : return servo_out;
600 : }
601 :
602 : //
603 : // support for auxiliary switches:
604 : //
605 :
606 2707 : void RC_Channel::reset_mode_switch()
607 : {
608 2707 : switch_state.current_position = -1;
609 2707 : switch_state.debounce_position = -1;
610 2707 : read_mode_switch();
611 2707 : }
612 :
613 : // read a 6 position switch
614 30421640 : bool RC_Channel::read_6pos_switch(int8_t& position)
615 : {
616 : // calculate position of 6 pos switch
617 30421640 : const uint16_t pulsewidth = get_radio_in();
618 30421640 : if (pulsewidth <= RC_MIN_LIMIT_PWM || pulsewidth >= RC_MAX_LIMIT_PWM) {
619 30570 : return false; // This is an error condition
620 : }
621 :
622 30391070 : if (pulsewidth < 1231) {
623 49567 : position = 0;
624 30341503 : } else if (pulsewidth < 1361) {
625 22256 : position = 1;
626 30319247 : } else if (pulsewidth < 1491) {
627 1205 : position = 2;
628 30318042 : } else if (pulsewidth < 1621) {
629 153324 : position = 3;
630 30164718 : } else if (pulsewidth < 1750) {
631 1269 : position = 4;
632 : } else {
633 30163449 : position = 5;
634 : }
635 :
636 30391070 : if (!debounce_completed(position)) {
637 30389027 : return false;
638 : }
639 :
640 2043 : return true;
641 : }
642 :
643 30421640 : void RC_Channel::read_mode_switch()
644 : {
645 : int8_t position;
646 30421640 : if (read_6pos_switch(position)) {
647 : // set flight mode and simple mode setting
648 2043 : mode_switch_changed(modeswitch_pos_t(position));
649 : }
650 30421640 : }
651 :
652 31398111 : bool RC_Channel::debounce_completed(int8_t position)
653 : {
654 : // switch change not detected
655 31398111 : if (switch_state.current_position == position) {
656 : // reset debouncing
657 31305823 : switch_state.debounce_position = position;
658 : } else {
659 : // switch change detected
660 92288 : const uint32_t tnow_ms = AP_HAL::millis();
661 :
662 : // position not established yet
663 92288 : if (switch_state.debounce_position != position) {
664 4025 : switch_state.debounce_position = position;
665 4025 : switch_state.last_edge_time_ms = tnow_ms;
666 88263 : } else if (tnow_ms - switch_state.last_edge_time_ms >= SWITCH_DEBOUNCE_TIME_MS) {
667 : // position estabilished; debounce completed
668 3999 : switch_state.current_position = position;
669 3999 : return true;
670 : }
671 : }
672 :
673 31394112 : return false;
674 : }
675 :
676 : //
677 : // support for auxiliary switches:
678 : //
679 :
680 : // init_aux_switch_function - initialize aux functions
681 32425 : void RC_Channel::init_aux_function(const AUX_FUNC ch_option, const AuxSwitchPos ch_flag)
682 : {
683 : // init channel options
684 32425 : switch (ch_option) {
685 : // the following functions do not need to be initialised:
686 : #if AP_ARMING_ENABLED
687 32406 : case AUX_FUNC::ARMDISARM:
688 : case AUX_FUNC::ARMDISARM_AIRMODE:
689 : #endif
690 : #if AP_BATTERY_ENABLED
691 : case AUX_FUNC::BATTERY_MPPT_ENABLE:
692 : #endif
693 : #if AP_CAMERA_ENABLED
694 : case AUX_FUNC::CAMERA_TRIGGER:
695 : #endif
696 : #if AP_MISSION_ENABLED
697 : case AUX_FUNC::CLEAR_WP:
698 : #endif
699 : case AUX_FUNC::COMPASS_LEARN:
700 : #if AP_ARMING_ENABLED
701 : case AUX_FUNC::DISARM:
702 : #endif
703 : case AUX_FUNC::DO_NOTHING:
704 : #if AP_GRIPPER_ENABLED
705 : case AUX_FUNC::GRIPPER:
706 : #endif
707 : #if AP_LANDINGGEAR_ENABLED
708 : case AUX_FUNC::LANDING_GEAR:
709 : #endif
710 : case AUX_FUNC::LOST_VEHICLE_SOUND:
711 : #if AP_SERVORELAYEVENTS_ENABLED && AP_RELAY_ENABLED
712 : case AUX_FUNC::RELAY:
713 : case AUX_FUNC::RELAY2:
714 : case AUX_FUNC::RELAY3:
715 : case AUX_FUNC::RELAY4:
716 : case AUX_FUNC::RELAY5:
717 : case AUX_FUNC::RELAY6:
718 : #endif
719 : #if HAL_VISUALODOM_ENABLED
720 : case AUX_FUNC::VISODOM_ALIGN:
721 : #endif
722 : #if AP_AHRS_ENABLED
723 : case AUX_FUNC::EKF_LANE_SWITCH:
724 : case AUX_FUNC::EKF_YAW_RESET:
725 : #endif
726 : #if AP_AHRS_EKF_RESET_ENABLED
727 : case AUX_FUNC::EKF_RESET:
728 : #endif
729 : #if HAL_GENERATOR_ENABLED
730 : case AUX_FUNC::GENERATOR: // don't turn generator on or off initially
731 : #endif
732 : #if AP_AHRS_ENABLED
733 : case AUX_FUNC::EKF_SOURCE_SET:
734 : #endif
735 : #if HAL_TORQEEDO_ENABLED
736 : case AUX_FUNC::TORQEEDO_CLEAR_ERR:
737 : #endif
738 : #if AP_SCRIPTING_ENABLED
739 : case AUX_FUNC::SCRIPTING_1:
740 : case AUX_FUNC::SCRIPTING_2:
741 : case AUX_FUNC::SCRIPTING_3:
742 : case AUX_FUNC::SCRIPTING_4:
743 : case AUX_FUNC::SCRIPTING_5:
744 : case AUX_FUNC::SCRIPTING_6:
745 : case AUX_FUNC::SCRIPTING_7:
746 : case AUX_FUNC::SCRIPTING_8:
747 : case AUX_FUNC::SCRIPTING_9:
748 : case AUX_FUNC::SCRIPTING_10:
749 : case AUX_FUNC::SCRIPTING_11:
750 : case AUX_FUNC::SCRIPTING_12:
751 : case AUX_FUNC::SCRIPTING_13:
752 : case AUX_FUNC::SCRIPTING_14:
753 : case AUX_FUNC::SCRIPTING_15:
754 : case AUX_FUNC::SCRIPTING_16:
755 : case AUX_FUNC::STOP_RESTART_SCRIPTING:
756 : #endif
757 : #if AP_VIDEOTX_ENABLED
758 : case AUX_FUNC::VTX_POWER:
759 : #endif
760 : #if AP_OPTICALFLOW_CALIBRATOR_ENABLED
761 : case AUX_FUNC::OPTFLOW_CAL:
762 : #endif
763 : case AUX_FUNC::TURBINE_START:
764 : #if HAL_MOUNT_ENABLED
765 : case AUX_FUNC::MOUNT1_ROLL:
766 : case AUX_FUNC::MOUNT1_PITCH:
767 : case AUX_FUNC::MOUNT1_YAW:
768 : case AUX_FUNC::MOUNT2_ROLL:
769 : case AUX_FUNC::MOUNT2_PITCH:
770 : case AUX_FUNC::MOUNT2_YAW:
771 : #endif
772 : #if HAL_GENERATOR_ENABLED
773 : case AUX_FUNC::LOWEHEISER_STARTER:
774 : #endif
775 : #if COMPASS_CAL_ENABLED
776 : case AUX_FUNC::MAG_CAL:
777 : #endif
778 : #if AP_CAMERA_ENABLED
779 : case AUX_FUNC::CAMERA_IMAGE_TRACKING:
780 : #endif
781 : #if HAL_MOUNT_ENABLED
782 : case AUX_FUNC::MOUNT_LRF_ENABLE:
783 : #endif
784 : #if HAL_GENERATOR_ENABLED
785 : case AUX_FUNC::LOWEHEISER_THROTTLE:
786 : #endif
787 32406 : break;
788 :
789 : // these functions require explicit initialization
790 : #if HAL_ADSB_ENABLED
791 19 : case AUX_FUNC::AVOID_ADSB:
792 : #endif
793 : case AUX_FUNC::AVOID_PROXIMITY:
794 : #if AP_FENCE_ENABLED
795 : case AUX_FUNC::FENCE:
796 : #endif
797 : #if AP_GPS_ENABLED
798 : case AUX_FUNC::GPS_DISABLE:
799 : case AUX_FUNC::GPS_DISABLE_YAW:
800 : #endif
801 : #if AP_INERTIALSENSOR_KILL_IMU_ENABLED
802 : case AUX_FUNC::KILL_IMU1:
803 : case AUX_FUNC::KILL_IMU2:
804 : case AUX_FUNC::KILL_IMU3:
805 : #endif
806 : #if AP_MISSION_ENABLED
807 : case AUX_FUNC::MISSION_RESET:
808 : #endif
809 : case AUX_FUNC::MOTOR_ESTOP:
810 : case AUX_FUNC::RC_OVERRIDE_ENABLE:
811 : #if AP_CAMERA_RUNCAM_ENABLED
812 : case AUX_FUNC::RUNCAM_CONTROL:
813 : case AUX_FUNC::RUNCAM_OSD_CONTROL:
814 : #endif
815 : #if HAL_SPRAYER_ENABLED
816 : case AUX_FUNC::SPRAYER:
817 : #endif
818 : #if AP_AIRSPEED_ENABLED
819 : case AUX_FUNC::DISABLE_AIRSPEED_USE:
820 : #endif
821 : case AUX_FUNC::FFT_NOTCH_TUNE:
822 : #if HAL_MOUNT_ENABLED
823 : case AUX_FUNC::RETRACT_MOUNT1:
824 : case AUX_FUNC::RETRACT_MOUNT2:
825 : case AUX_FUNC::MOUNT_YAW_LOCK:
826 : case AUX_FUNC::MOUNT_RP_LOCK:
827 : #if AP_MOUNT_POI_LOCK_ENABLED
828 : case AUX_FUNC::MOUNT_POI_LOCK:
829 : #endif //AP_MOUNT_POI_LOCK_ENABLED
830 : #endif //HAL_MOUNT_ENABLED
831 : #if HAL_LOGGING_ENABLED
832 : case AUX_FUNC::LOG_PAUSE:
833 : #endif
834 : case AUX_FUNC::ARM_EMERGENCY_STOP:
835 : #if AP_CAMERA_ENABLED
836 : case AUX_FUNC::CAMERA_REC_VIDEO:
837 : case AUX_FUNC::CAMERA_ZOOM:
838 : case AUX_FUNC::CAMERA_MANUAL_FOCUS:
839 : case AUX_FUNC::CAMERA_AUTO_FOCUS:
840 : case AUX_FUNC::CAMERA_LENS:
841 : #endif
842 : #if AP_AHRS_ENABLED
843 : case AUX_FUNC::AHRS_TYPE:
844 : #endif
845 19 : run_aux_function(ch_option, ch_flag, AuxFuncTrigger::Source::INIT, ch_in);
846 19 : break;
847 0 : default:
848 0 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Failed to init: RC%u_OPTION: %u",
849 : (unsigned)(this->ch_in+1), (unsigned)ch_option);
850 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
851 0 : AP_BoardConfig::config_error("Failed to init: RC%u_OPTION: %u",
852 0 : (unsigned)(this->ch_in+1), (unsigned)ch_option);
853 : #endif
854 0 : break;
855 : }
856 32425 : }
857 :
858 : #if AP_RC_CHANNEL_AUX_FUNCTION_STRINGS_ENABLED
859 :
860 : const RC_Channel::LookupTable RC_Channel::lookuptable[] = {
861 : #if AP_MISSION_ENABLED
862 : { AUX_FUNC::SAVE_WP,"SaveWaypoint"},
863 : #endif
864 : #if AP_CAMERA_ENABLED
865 : { AUX_FUNC::CAMERA_TRIGGER,"CameraTrigger"},
866 : #endif
867 : #if AP_RANGEFINDER_ENABLED
868 : { AUX_FUNC::RANGEFINDER,"Rangefinder"},
869 : #endif
870 : #if AP_FENCE_ENABLED
871 : { AUX_FUNC::FENCE,"Fence"},
872 : #endif
873 : #if HAL_SPRAYER_ENABLED
874 : { AUX_FUNC::SPRAYER,"Sprayer"},
875 : #endif
876 : #if HAL_PARACHUTE_ENABLED
877 : { AUX_FUNC::PARACHUTE_ENABLE,"ParachuteEnable"},
878 : { AUX_FUNC::PARACHUTE_RELEASE,"ParachuteRelease"},
879 : { AUX_FUNC::PARACHUTE_3POS,"Parachute3Position"},
880 : #endif
881 : #if AP_MISSION_ENABLED
882 : { AUX_FUNC::MISSION_RESET,"MissionReset"},
883 : #endif
884 : #if HAL_MOUNT_ENABLED
885 : { AUX_FUNC::RETRACT_MOUNT1,"RetractMount1"},
886 : { AUX_FUNC::RETRACT_MOUNT2,"RetractMount2"},
887 : #endif
888 : #if AP_SERVORELAYEVENTS_ENABLED && AP_RELAY_ENABLED
889 : { AUX_FUNC::RELAY,"Relay1"},
890 : #endif
891 : { AUX_FUNC::MOTOR_ESTOP,"MotorEStop"},
892 : { AUX_FUNC::MOTOR_INTERLOCK,"MotorInterlock"},
893 : #if AP_SERVORELAYEVENTS_ENABLED && AP_RELAY_ENABLED
894 : { AUX_FUNC::RELAY2,"Relay2"},
895 : { AUX_FUNC::RELAY3,"Relay3"},
896 : { AUX_FUNC::RELAY4,"Relay4"},
897 : #endif
898 : { AUX_FUNC::PRECISION_LOITER,"PrecisionLoiter"},
899 : { AUX_FUNC::AVOID_PROXIMITY,"AvoidProximity"},
900 : #if AP_WINCH_ENABLED
901 : { AUX_FUNC::WINCH_ENABLE,"WinchEnable"},
902 : { AUX_FUNC::WINCH_CONTROL,"WinchControl"},
903 : #endif
904 : #if AP_MISSION_ENABLED
905 : { AUX_FUNC::CLEAR_WP,"ClearWaypoint"},
906 : #endif
907 : { AUX_FUNC::COMPASS_LEARN,"CompassLearn"},
908 : { AUX_FUNC::SAILBOAT_TACK,"SailboatTack"},
909 : #if AP_GPS_ENABLED
910 : { AUX_FUNC::GPS_DISABLE,"GPSDisable"},
911 : { AUX_FUNC::GPS_DISABLE_YAW,"GPSDisableYaw"},
912 : #endif
913 : #if AP_AIRSPEED_ENABLED
914 : { AUX_FUNC::DISABLE_AIRSPEED_USE,"DisableAirspeedUse"},
915 : #endif
916 : #if AP_SERVORELAYEVENTS_ENABLED && AP_RELAY_ENABLED
917 : { AUX_FUNC::RELAY5,"Relay5"},
918 : { AUX_FUNC::RELAY6,"Relay6"},
919 : #endif
920 : { AUX_FUNC::SAILBOAT_MOTOR_3POS,"SailboatMotor"},
921 : { AUX_FUNC::SURFACE_TRACKING,"SurfaceTracking"},
922 : #if AP_CAMERA_RUNCAM_ENABLED
923 : { AUX_FUNC::RUNCAM_CONTROL,"RunCamControl"},
924 : { AUX_FUNC::RUNCAM_OSD_CONTROL,"RunCamOSDControl"},
925 : #endif
926 : #if HAL_VISUALODOM_ENABLED
927 : { AUX_FUNC::VISODOM_ALIGN,"VisOdomAlign"},
928 : #endif
929 : { AUX_FUNC::AIRMODE, "AirMode"},
930 : #if AP_CAMERA_ENABLED
931 : { AUX_FUNC::CAM_MODE_TOGGLE,"CamModeToggle"},
932 : #endif
933 : #if HAL_GENERATOR_ENABLED
934 : { AUX_FUNC::GENERATOR,"Generator"},
935 : #endif
936 : #if AP_BATTERY_ENABLED
937 : { AUX_FUNC::BATTERY_MPPT_ENABLE,"Battery MPPT Enable"},
938 : #endif
939 : #if AP_AIRSPEED_AUTOCAL_ENABLE
940 : { AUX_FUNC::ARSPD_CALIBRATE,"Calibrate Airspeed"},
941 : #endif
942 : #if HAL_TORQEEDO_ENABLED
943 : { AUX_FUNC::TORQEEDO_CLEAR_ERR, "Torqeedo Clear Err"},
944 : #endif
945 : { AUX_FUNC::EMERGENCY_LANDING_EN, "Emergency Landing"},
946 : { AUX_FUNC::WEATHER_VANE_ENABLE, "Weathervane"},
947 : { AUX_FUNC::TURBINE_START, "Turbine Start"},
948 : { AUX_FUNC::FFT_NOTCH_TUNE, "FFT Notch Tuning"},
949 : #if HAL_MOUNT_ENABLED
950 : { AUX_FUNC::MOUNT_YAW_LOCK, "Mount Yaw Lock"},
951 : { AUX_FUNC::MOUNT_RP_LOCK, "Mount Roll/Pitch Lock"},
952 : #endif //HAL_MOUNT_ENABLED
953 : #if HAL_LOGGING_ENABLED
954 : { AUX_FUNC::LOG_PAUSE, "Pause Stream Logging"},
955 : #endif
956 : #if AP_CAMERA_ENABLED
957 : { AUX_FUNC::CAMERA_REC_VIDEO, "Camera Record Video"},
958 : { AUX_FUNC::CAMERA_ZOOM, "Camera Zoom"},
959 : { AUX_FUNC::CAMERA_MANUAL_FOCUS, "Camera Manual Focus"},
960 : { AUX_FUNC::CAMERA_AUTO_FOCUS, "Camera Auto Focus"},
961 : { AUX_FUNC::CAMERA_IMAGE_TRACKING, "Camera Image Tracking"},
962 : { AUX_FUNC::CAMERA_LENS, "Camera Lens"},
963 : #endif
964 : #if HAL_MOUNT_ENABLED
965 : { AUX_FUNC::MOUNT_LRF_ENABLE, "Mount LRF Enable"},
966 : #endif
967 : };
968 :
969 : /* lookup the announcement for switch change */
970 1968 : const char *RC_Channel::string_for_aux_function(AUX_FUNC function) const
971 : {
972 44524 : for (const struct LookupTable &entry : lookuptable) {
973 43856 : if (entry.option == function) {
974 1300 : return entry.announcement;
975 : }
976 : }
977 668 : return nullptr;
978 : }
979 :
980 : /* find string for position */
981 1300 : const char *RC_Channel::string_for_aux_pos(AuxSwitchPos pos) const
982 : {
983 1300 : switch (pos) {
984 132 : case AuxSwitchPos::HIGH:
985 132 : return "HIGH";
986 940 : case AuxSwitchPos::MIDDLE:
987 940 : return "MIDDLE";
988 228 : case AuxSwitchPos::LOW:
989 228 : return "LOW";
990 : }
991 0 : return "";
992 : }
993 :
994 : #endif // AP_RC_CHANNEL_AUX_FUNCTION_STRINGS_ENABLED
995 :
996 : /*
997 : read an aux channel. Return true if a switch has changed
998 : */
999 23606768 : bool RC_Channel::read_aux()
1000 : {
1001 23606768 : const AUX_FUNC _option = (AUX_FUNC)option.get();
1002 23606768 : if (_option == AUX_FUNC::DO_NOTHING) {
1003 : // may wish to add special cases for other "AUXSW" things
1004 : // here e.g. RCMAP_ROLL etc once they become options
1005 22599013 : return false;
1006 : #if AP_VIDEOTX_ENABLED
1007 1007755 : } else if (_option == AUX_FUNC::VTX_POWER) {
1008 : int8_t position;
1009 0 : if (read_6pos_switch(position)) {
1010 0 : AP::vtx().change_power(position);
1011 0 : return true;
1012 : }
1013 0 : return false;
1014 : #endif // AP_VIDEOTX_ENABLED
1015 : }
1016 :
1017 : AuxSwitchPos new_position;
1018 1007755 : if (!read_3pos_switch(new_position)) {
1019 714 : return false;
1020 : }
1021 :
1022 1007041 : if (!switch_state.initialised) {
1023 1403 : switch_state.initialised = true;
1024 1403 : if (init_position_on_first_radio_read((AUX_FUNC)option.get())) {
1025 14 : switch_state.current_position = (int8_t)new_position;
1026 14 : switch_state.debounce_position = (int8_t)new_position;
1027 : }
1028 : }
1029 :
1030 1007041 : if (!debounce_completed((int8_t)new_position)) {
1031 1005085 : return false;
1032 : }
1033 :
1034 : #if AP_RC_CHANNEL_AUX_FUNCTION_STRINGS_ENABLED
1035 : // announce the change to the GCS:
1036 1956 : const char *aux_string = string_for_aux_function(_option);
1037 1956 : if (aux_string != nullptr) {
1038 1296 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "RC%i: %s %s", ch_in+1, aux_string, string_for_aux_pos(new_position));
1039 : }
1040 : #endif
1041 :
1042 : // debounced; undertake the action:
1043 1956 : run_aux_function(_option, new_position, AuxFuncTrigger::Source::RC, ch_in);
1044 1956 : return true;
1045 : }
1046 :
1047 : // returns true if the first time we successfully read the
1048 : // channel's three-position-switch position we should record that
1049 : // position as the current position *without* executing the
1050 : // associated auxiliary function. e.g. do not attempt to arm a
1051 : // vehicle when the user turns on their transmitter with the arm
1052 : // switch high!
1053 1403 : bool RC_Channel::init_position_on_first_radio_read(AUX_FUNC func) const
1054 : {
1055 1403 : switch (func) {
1056 : #if AP_ARMING_ENABLED
1057 14 : case AUX_FUNC::ARMDISARM_AIRMODE:
1058 : case AUX_FUNC::ARMDISARM:
1059 : case AUX_FUNC::ARM_EMERGENCY_STOP:
1060 : #endif
1061 : #if HAL_PARACHUTE_ENABLED
1062 : case AUX_FUNC::PARACHUTE_RELEASE:
1063 : #endif
1064 :
1065 : // we do not want to process
1066 14 : return true;
1067 1389 : default:
1068 1389 : return false;
1069 : }
1070 : }
1071 :
1072 69 : void RC_Channel::do_aux_function_armdisarm(const AuxSwitchPos ch_flag)
1073 : {
1074 : // arm or disarm the vehicle
1075 69 : switch (ch_flag) {
1076 32 : case AuxSwitchPos::HIGH:
1077 32 : AP::arming().arm(AP_Arming::Method::AUXSWITCH, true);
1078 32 : break;
1079 0 : case AuxSwitchPos::MIDDLE:
1080 : // nothing
1081 0 : break;
1082 37 : case AuxSwitchPos::LOW:
1083 37 : AP::arming().disarm(AP_Arming::Method::AUXSWITCH);
1084 37 : break;
1085 : }
1086 69 : }
1087 :
1088 : #if AP_ADSB_AVOIDANCE_ENABLED
1089 5 : void RC_Channel::do_aux_function_avoid_adsb(const AuxSwitchPos ch_flag)
1090 : {
1091 5 : AP_Avoidance *avoidance = AP::ap_avoidance();
1092 5 : if (avoidance == nullptr) {
1093 0 : return;
1094 : }
1095 5 : if (ch_flag == AuxSwitchPos::HIGH) {
1096 3 : AP_ADSB *adsb = AP::ADSB();
1097 3 : if (adsb == nullptr) {
1098 0 : return;
1099 : }
1100 : // try to enable AP_Avoidance
1101 3 : if (!adsb->enabled() || !adsb->healthy()) {
1102 1 : GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "ADSB not available");
1103 1 : return;
1104 : }
1105 2 : avoidance->enable();
1106 2 : LOGGER_WRITE_EVENT(LogEvent::AVOIDANCE_ADSB_ENABLE);
1107 2 : GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "ADSB Avoidance Enabled");
1108 2 : return;
1109 : }
1110 :
1111 : // disable AP_Avoidance
1112 2 : avoidance->disable();
1113 2 : LOGGER_WRITE_EVENT(LogEvent::AVOIDANCE_ADSB_DISABLE);
1114 2 : GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "ADSB Avoidance Disabled");
1115 : }
1116 : #endif // AP_ADSB_AVOIDANCE_ENABLED
1117 :
1118 11 : void RC_Channel::do_aux_function_avoid_proximity(const AuxSwitchPos ch_flag)
1119 : {
1120 : #if AP_AVOIDANCE_ENABLED && !APM_BUILD_TYPE(APM_BUILD_ArduPlane)
1121 11 : AC_Avoid *avoid = AP::ac_avoid();
1122 11 : if (avoid == nullptr) {
1123 0 : return;
1124 : }
1125 :
1126 11 : switch (ch_flag) {
1127 3 : case AuxSwitchPos::HIGH:
1128 3 : avoid->proximity_avoidance_enable(true);
1129 3 : break;
1130 5 : case AuxSwitchPos::MIDDLE:
1131 : // nothing
1132 5 : break;
1133 3 : case AuxSwitchPos::LOW:
1134 3 : avoid->proximity_avoidance_enable(false);
1135 3 : break;
1136 : }
1137 : #endif // !APM_BUILD_ArduPlane
1138 0 : }
1139 :
1140 : #if AP_CAMERA_ENABLED
1141 16 : void RC_Channel::do_aux_function_camera_trigger(const AuxSwitchPos ch_flag)
1142 : {
1143 16 : if (ch_flag == AuxSwitchPos::HIGH) {
1144 6 : AP_Camera *camera = AP::camera();
1145 6 : if (camera == nullptr) {
1146 0 : return;
1147 : }
1148 6 : camera->take_picture();
1149 : }
1150 : }
1151 :
1152 0 : bool RC_Channel::do_aux_function_record_video(const AuxSwitchPos ch_flag)
1153 : {
1154 0 : AP_Camera *camera = AP::camera();
1155 0 : if (camera == nullptr) {
1156 0 : return false;
1157 : }
1158 0 : return camera->record_video(ch_flag == AuxSwitchPos::HIGH);
1159 : }
1160 :
1161 0 : bool RC_Channel::do_aux_function_camera_zoom(const AuxSwitchPos ch_flag)
1162 : {
1163 0 : AP_Camera *camera = AP::camera();
1164 0 : if (camera == nullptr) {
1165 0 : return false;
1166 : }
1167 0 : int8_t zoom_step = 0; // zoom out = -1, hold = 0, zoom in = 1
1168 0 : switch (ch_flag) {
1169 0 : case AuxSwitchPos::HIGH:
1170 0 : zoom_step = 1; // zoom in
1171 0 : break;
1172 0 : case AuxSwitchPos::MIDDLE:
1173 0 : zoom_step = 0; // zoom hold
1174 0 : break;
1175 0 : case AuxSwitchPos::LOW:
1176 0 : zoom_step = -1; // zoom out
1177 0 : break;
1178 : }
1179 0 : return camera->set_zoom(ZoomType::RATE, zoom_step);
1180 : }
1181 :
1182 0 : bool RC_Channel::do_aux_function_camera_manual_focus(const AuxSwitchPos ch_flag)
1183 : {
1184 0 : AP_Camera *camera = AP::camera();
1185 0 : if (camera == nullptr) {
1186 0 : return false;
1187 : }
1188 0 : int8_t focus_step = 0; // focus in = -1, focus hold = 0, focus out = 1
1189 0 : switch (ch_flag) {
1190 0 : case AuxSwitchPos::HIGH:
1191 : // wide shot, focus out
1192 0 : focus_step = 1;
1193 0 : break;
1194 0 : case AuxSwitchPos::MIDDLE:
1195 0 : focus_step = 0;
1196 0 : break;
1197 0 : case AuxSwitchPos::LOW:
1198 : // close shot, focus in
1199 0 : focus_step = -1;
1200 0 : break;
1201 : }
1202 0 : return camera->set_focus(FocusType::RATE, focus_step) == SetFocusResult::ACCEPTED;
1203 : }
1204 :
1205 0 : bool RC_Channel::do_aux_function_camera_auto_focus(const AuxSwitchPos ch_flag)
1206 : {
1207 0 : if (ch_flag == AuxSwitchPos::HIGH) {
1208 0 : AP_Camera *camera = AP::camera();
1209 0 : if (camera == nullptr) {
1210 0 : return false;
1211 : }
1212 0 : return camera->set_focus(FocusType::AUTO, 0) == SetFocusResult::ACCEPTED;
1213 : }
1214 0 : return false;
1215 : }
1216 :
1217 0 : bool RC_Channel::do_aux_function_camera_image_tracking(const AuxSwitchPos ch_flag)
1218 : {
1219 0 : AP_Camera *camera = AP::camera();
1220 0 : if (camera == nullptr) {
1221 0 : return false;
1222 : }
1223 : // High position enables tracking a POINT in middle of image
1224 : // Low or Mediums disables tracking. (0.5,0.5) is still passed in but ignored
1225 0 : return camera->set_tracking(ch_flag == AuxSwitchPos::HIGH ? TrackingType::TRK_POINT : TrackingType::TRK_NONE, Vector2f{0.5, 0.5}, Vector2f{});
1226 : }
1227 :
1228 0 : bool RC_Channel::do_aux_function_camera_lens(const AuxSwitchPos ch_flag)
1229 : {
1230 : #if AP_CAMERA_SET_CAMERA_SOURCE_ENABLED
1231 0 : AP_Camera *camera = AP::camera();
1232 0 : if (camera == nullptr) {
1233 0 : return false;
1234 : }
1235 : // Low selects lens 0 (default), Mediums selects lens1, High selects lens2
1236 0 : return camera->set_lens((uint8_t)ch_flag);
1237 : #else
1238 : return false;
1239 : #endif // AP_CAMERA_SET_CAMERA_SOURCE_ENABLED
1240 : }
1241 : #endif // AP_CAMERA_ENABLED
1242 :
1243 : #if AP_CAMERA_RUNCAM_ENABLED
1244 0 : void RC_Channel::do_aux_function_runcam_control(const AuxSwitchPos ch_flag)
1245 : {
1246 0 : AP_RunCam *runcam = AP::runcam();
1247 0 : if (runcam == nullptr) {
1248 0 : return;
1249 : }
1250 :
1251 0 : switch (ch_flag) {
1252 0 : case AuxSwitchPos::HIGH:
1253 0 : runcam->start_recording();
1254 0 : break;
1255 0 : case AuxSwitchPos::MIDDLE:
1256 0 : runcam->osd_option();
1257 0 : break;
1258 0 : case AuxSwitchPos::LOW:
1259 0 : runcam->stop_recording();
1260 0 : break;
1261 : }
1262 : }
1263 :
1264 0 : void RC_Channel::do_aux_function_runcam_osd_control(const AuxSwitchPos ch_flag)
1265 : {
1266 0 : AP_RunCam *runcam = AP::runcam();
1267 0 : if (runcam == nullptr) {
1268 0 : return;
1269 : }
1270 :
1271 0 : switch (ch_flag) {
1272 0 : case AuxSwitchPos::HIGH:
1273 0 : runcam->enter_osd();
1274 0 : break;
1275 0 : case AuxSwitchPos::MIDDLE:
1276 : case AuxSwitchPos::LOW:
1277 0 : runcam->exit_osd();
1278 0 : break;
1279 : }
1280 : }
1281 : #endif
1282 :
1283 : #if AP_FENCE_ENABLED
1284 : // enable or disable the fence
1285 14 : void RC_Channel::do_aux_function_fence(const AuxSwitchPos ch_flag)
1286 : {
1287 14 : AC_Fence *fence = AP::fence();
1288 14 : if (fence == nullptr) {
1289 0 : return;
1290 : }
1291 :
1292 14 : fence->enable_configured(ch_flag == AuxSwitchPos::HIGH);
1293 : }
1294 : #endif
1295 :
1296 : #if AP_MISSION_ENABLED
1297 9 : void RC_Channel::do_aux_function_clear_wp(const AuxSwitchPos ch_flag)
1298 : {
1299 9 : if (ch_flag == AuxSwitchPos::HIGH) {
1300 3 : AP::mission().clear();
1301 : }
1302 9 : }
1303 : #endif // AP_MISSION_ENABLED
1304 :
1305 : #if AP_SERVORELAYEVENTS_ENABLED && AP_RELAY_ENABLED
1306 9 : void RC_Channel::do_aux_function_relay(const uint8_t relay, bool val)
1307 : {
1308 9 : AP_ServoRelayEvents *servorelayevents = AP::servorelayevents();
1309 9 : if (servorelayevents == nullptr) {
1310 0 : return;
1311 : }
1312 9 : servorelayevents->do_set_relay(relay, val);
1313 : }
1314 : #endif
1315 :
1316 : #if HAL_GENERATOR_ENABLED
1317 46 : void RC_Channel::do_aux_function_generator(const AuxSwitchPos ch_flag)
1318 : {
1319 46 : AP_Generator *generator = AP::generator();
1320 46 : if (generator == nullptr) {
1321 0 : return;
1322 : }
1323 :
1324 46 : switch (ch_flag) {
1325 17 : case AuxSwitchPos::LOW:
1326 17 : generator->stop();
1327 17 : break;
1328 13 : case AuxSwitchPos::MIDDLE:
1329 13 : generator->idle();
1330 13 : break;
1331 16 : case AuxSwitchPos::HIGH:
1332 16 : generator->run();
1333 16 : break;
1334 : }
1335 : }
1336 : #endif
1337 :
1338 : #if HAL_SPRAYER_ENABLED
1339 14 : void RC_Channel::do_aux_function_sprayer(const AuxSwitchPos ch_flag)
1340 : {
1341 14 : AC_Sprayer *sprayer = AP::sprayer();
1342 14 : if (sprayer == nullptr) {
1343 0 : return;
1344 : }
1345 14 : sprayer->run(ch_flag == AuxSwitchPos::HIGH);
1346 : // if we are disarmed the pilot must want to test the pump
1347 14 : sprayer->test_pump((ch_flag == AuxSwitchPos::HIGH) && !hal.util->get_soft_armed());
1348 : }
1349 : #endif // HAL_SPRAYER_ENABLED
1350 :
1351 : #if AP_GRIPPER_ENABLED
1352 30 : void RC_Channel::do_aux_function_gripper(const AuxSwitchPos ch_flag)
1353 : {
1354 30 : AP_Gripper &gripper = AP::gripper();
1355 :
1356 30 : switch (ch_flag) {
1357 9 : case AuxSwitchPos::LOW:
1358 9 : gripper.release();
1359 9 : break;
1360 11 : case AuxSwitchPos::MIDDLE:
1361 : // nothing
1362 11 : break;
1363 10 : case AuxSwitchPos::HIGH:
1364 10 : gripper.grab();
1365 10 : break;
1366 : }
1367 30 : }
1368 : #endif // AP_GRIPPER_ENABLED
1369 :
1370 0 : void RC_Channel::do_aux_function_lost_vehicle_sound(const AuxSwitchPos ch_flag)
1371 : {
1372 0 : switch (ch_flag) {
1373 0 : case AuxSwitchPos::HIGH:
1374 0 : AP_Notify::flags.vehicle_lost = true;
1375 0 : break;
1376 0 : case AuxSwitchPos::MIDDLE:
1377 : // nothing
1378 0 : break;
1379 0 : case AuxSwitchPos::LOW:
1380 0 : AP_Notify::flags.vehicle_lost = false;
1381 0 : break;
1382 : }
1383 0 : }
1384 :
1385 47 : void RC_Channel::do_aux_function_rc_override_enable(const AuxSwitchPos ch_flag)
1386 : {
1387 47 : switch (ch_flag) {
1388 19 : case AuxSwitchPos::HIGH: {
1389 19 : rc().set_gcs_overrides_enabled(true);
1390 19 : break;
1391 : }
1392 9 : case AuxSwitchPos::MIDDLE:
1393 : // nothing
1394 9 : break;
1395 19 : case AuxSwitchPos::LOW: {
1396 19 : rc().set_gcs_overrides_enabled(false);
1397 19 : break;
1398 : }
1399 : }
1400 47 : }
1401 :
1402 : #if AP_MISSION_ENABLED
1403 2 : void RC_Channel::do_aux_function_mission_reset(const AuxSwitchPos ch_flag)
1404 : {
1405 2 : if (ch_flag != AuxSwitchPos::HIGH) {
1406 1 : return;
1407 : }
1408 1 : AP::mission().reset();
1409 : }
1410 : #endif
1411 :
1412 8 : void RC_Channel::do_aux_function_fft_notch_tune(const AuxSwitchPos ch_flag)
1413 : {
1414 : #if HAL_GYROFFT_ENABLED
1415 8 : AP_GyroFFT *fft = AP::fft();
1416 8 : if (fft == nullptr) {
1417 0 : return;
1418 : }
1419 :
1420 8 : switch (ch_flag) {
1421 2 : case AuxSwitchPos::HIGH:
1422 2 : fft->start_notch_tune();
1423 2 : break;
1424 6 : case AuxSwitchPos::MIDDLE:
1425 : case AuxSwitchPos::LOW:
1426 6 : fft->stop_notch_tune();
1427 6 : break;
1428 : }
1429 : #endif
1430 0 : }
1431 :
1432 : /**
1433 : * Perform the RETRACT_MOUNT 1/2 process.
1434 : *
1435 : * @param [in] ch_flag Position of the switch. HIGH, MIDDLE and LOW.
1436 : * @param [in] instance 0: RETRACT MOUNT 1 <br>
1437 : * 1: RETRACT MOUNT 2
1438 : */
1439 : #if HAL_MOUNT_ENABLED
1440 0 : void RC_Channel::do_aux_function_retract_mount(const AuxSwitchPos ch_flag, const uint8_t instance)
1441 : {
1442 0 : AP_Mount *mount = AP::mount();
1443 0 : if (mount == nullptr) {
1444 0 : return;
1445 : }
1446 0 : switch (ch_flag) {
1447 0 : case AuxSwitchPos::HIGH:
1448 0 : mount->set_mode(instance,MAV_MOUNT_MODE_RETRACT);
1449 0 : break;
1450 0 : case AuxSwitchPos::MIDDLE:
1451 : // nothing
1452 0 : break;
1453 0 : case AuxSwitchPos::LOW:
1454 0 : mount->set_mode_to_default(instance);
1455 0 : break;
1456 : }
1457 : }
1458 : #endif // HAL_MOUNT_ENABLED
1459 :
1460 2139 : bool RC_Channel::run_aux_function(AUX_FUNC ch_option, AuxSwitchPos pos, AuxFuncTrigger::Source source, uint16_t source_index)
1461 : {
1462 : #if AP_SCRIPTING_ENABLED
1463 2139 : rc().set_aux_cached(ch_option, pos);
1464 : #endif
1465 :
1466 : const AuxFuncTrigger trigger {
1467 : func: ch_option,
1468 : pos: pos,
1469 : source: source,
1470 : source_index: source_index,
1471 2139 : };
1472 :
1473 2139 : const bool ret = do_aux_function(trigger);
1474 :
1475 : #if HAL_LOGGING_ENABLED
1476 : // @LoggerMessage: AUXF
1477 : // @Description: Auxiliary function invocation information
1478 : // @Field: TimeUS: Time since system startup
1479 : // @Field: function: ID of triggered function
1480 : // @FieldValueEnum: function: RC_Channel::AUX_FUNC
1481 : // @Field: pos: switch position when function triggered
1482 : // @FieldValueEnum: pos: RC_Channel::AuxSwitchPos
1483 : // @Field: source: source of auxiliary function invocation
1484 : // @FieldValueEnum: source: RC_Channel::AuxFuncTrigger::Source
1485 : // @Field: index: index within source. 0 indexed. Invalid for scripting.
1486 : // @Field: result: true if function was successful
1487 2139 : AP::logger().Write(
1488 : "AUXF",
1489 : "TimeUS,function,pos,source,index,result",
1490 : "s#----",
1491 : "F-----",
1492 : "QHBBHB",
1493 : AP_HAL::micros64(),
1494 : uint16_t(ch_option),
1495 : uint8_t(pos),
1496 : uint8_t(source),
1497 : source_index,
1498 : uint8_t(ret)
1499 : );
1500 : #endif
1501 :
1502 2139 : return ret;
1503 : }
1504 :
1505 618 : bool RC_Channel::do_aux_function(const AuxFuncTrigger &trigger)
1506 : {
1507 618 : const AUX_FUNC &ch_option = trigger.func;
1508 618 : const AuxSwitchPos &ch_flag = trigger.pos;
1509 :
1510 618 : switch (ch_option) {
1511 : #if AP_FENCE_ENABLED
1512 14 : case AUX_FUNC::FENCE:
1513 14 : do_aux_function_fence(ch_flag);
1514 14 : break;
1515 : #endif
1516 :
1517 : #if AP_GRIPPER_ENABLED
1518 30 : case AUX_FUNC::GRIPPER:
1519 30 : do_aux_function_gripper(ch_flag);
1520 30 : break;
1521 : #endif
1522 :
1523 47 : case AUX_FUNC::RC_OVERRIDE_ENABLE:
1524 : // Allow or disallow RC_Override
1525 47 : do_aux_function_rc_override_enable(ch_flag);
1526 47 : break;
1527 :
1528 11 : case AUX_FUNC::AVOID_PROXIMITY:
1529 11 : do_aux_function_avoid_proximity(ch_flag);
1530 11 : break;
1531 :
1532 : #if AP_SERVORELAYEVENTS_ENABLED && AP_RELAY_ENABLED
1533 9 : case AUX_FUNC::RELAY:
1534 9 : do_aux_function_relay(0, ch_flag == AuxSwitchPos::HIGH);
1535 9 : break;
1536 0 : case AUX_FUNC::RELAY2:
1537 0 : do_aux_function_relay(1, ch_flag == AuxSwitchPos::HIGH);
1538 0 : break;
1539 0 : case AUX_FUNC::RELAY3:
1540 0 : do_aux_function_relay(2, ch_flag == AuxSwitchPos::HIGH);
1541 0 : break;
1542 0 : case AUX_FUNC::RELAY4:
1543 0 : do_aux_function_relay(3, ch_flag == AuxSwitchPos::HIGH);
1544 0 : break;
1545 0 : case AUX_FUNC::RELAY5:
1546 0 : do_aux_function_relay(4, ch_flag == AuxSwitchPos::HIGH);
1547 0 : break;
1548 0 : case AUX_FUNC::RELAY6:
1549 0 : do_aux_function_relay(5, ch_flag == AuxSwitchPos::HIGH);
1550 0 : break;
1551 : #endif // AP_SERVORELAYEVENTS_ENABLED && AP_RELAY_ENABLED
1552 :
1553 : #if AP_CAMERA_RUNCAM_ENABLED
1554 0 : case AUX_FUNC::RUNCAM_CONTROL:
1555 0 : do_aux_function_runcam_control(ch_flag);
1556 0 : break;
1557 :
1558 0 : case AUX_FUNC::RUNCAM_OSD_CONTROL:
1559 0 : do_aux_function_runcam_osd_control(ch_flag);
1560 0 : break;
1561 : #endif
1562 :
1563 : #if AP_MISSION_ENABLED
1564 9 : case AUX_FUNC::CLEAR_WP:
1565 9 : do_aux_function_clear_wp(ch_flag);
1566 9 : break;
1567 2 : case AUX_FUNC::MISSION_RESET:
1568 2 : do_aux_function_mission_reset(ch_flag);
1569 2 : break;
1570 : #endif
1571 :
1572 : #if AP_ADSB_AVOIDANCE_ENABLED
1573 5 : case AUX_FUNC::AVOID_ADSB:
1574 5 : do_aux_function_avoid_adsb(ch_flag);
1575 5 : break;
1576 : #endif // AP_ADSB_AVOIDANCE_ENABLED
1577 :
1578 8 : case AUX_FUNC::FFT_NOTCH_TUNE:
1579 8 : do_aux_function_fft_notch_tune(ch_flag);
1580 8 : break;
1581 :
1582 : #if HAL_GENERATOR_ENABLED
1583 46 : case AUX_FUNC::GENERATOR:
1584 46 : do_aux_function_generator(ch_flag);
1585 46 : break;
1586 : #endif
1587 :
1588 : #if AP_BATTERY_ENABLED
1589 0 : case AUX_FUNC::BATTERY_MPPT_ENABLE:
1590 0 : if (ch_flag != AuxSwitchPos::MIDDLE) {
1591 0 : AP::battery().set_powered_state_to_all(ch_flag == AuxSwitchPos::HIGH);
1592 : }
1593 0 : break;
1594 : #endif
1595 :
1596 : #if HAL_SPRAYER_ENABLED
1597 14 : case AUX_FUNC::SPRAYER:
1598 14 : do_aux_function_sprayer(ch_flag);
1599 14 : break;
1600 : #endif
1601 :
1602 0 : case AUX_FUNC::LOST_VEHICLE_SOUND:
1603 0 : do_aux_function_lost_vehicle_sound(ch_flag);
1604 0 : break;
1605 :
1606 : #if AP_ARMING_ENABLED
1607 33 : case AUX_FUNC::ARMDISARM:
1608 33 : do_aux_function_armdisarm(ch_flag);
1609 33 : break;
1610 :
1611 0 : case AUX_FUNC::DISARM:
1612 0 : if (ch_flag == AuxSwitchPos::HIGH) {
1613 0 : AP::arming().disarm(AP_Arming::Method::AUXSWITCH);
1614 : }
1615 0 : break;
1616 : #endif
1617 :
1618 0 : case AUX_FUNC::COMPASS_LEARN:
1619 0 : if (ch_flag == AuxSwitchPos::HIGH) {
1620 0 : Compass &compass = AP::compass();
1621 0 : compass.set_learn_type(Compass::LearnType::INFLIGHT, false);
1622 : }
1623 0 : break;
1624 :
1625 : #if AP_LANDINGGEAR_ENABLED
1626 0 : case AUX_FUNC::LANDING_GEAR: {
1627 0 : AP_LandingGear *lg = AP_LandingGear::get_singleton();
1628 0 : if (lg == nullptr) {
1629 0 : break;
1630 : }
1631 0 : switch (ch_flag) {
1632 0 : case AuxSwitchPos::LOW:
1633 0 : lg->set_position(AP_LandingGear::LandingGear_Deploy);
1634 0 : break;
1635 0 : case AuxSwitchPos::MIDDLE:
1636 : // nothing
1637 0 : break;
1638 0 : case AuxSwitchPos::HIGH:
1639 0 : lg->set_position(AP_LandingGear::LandingGear_Retract);
1640 0 : break;
1641 : }
1642 0 : break;
1643 : }
1644 : #endif
1645 :
1646 : #if AP_GPS_ENABLED
1647 6 : case AUX_FUNC::GPS_DISABLE:
1648 6 : AP::gps().force_disable(ch_flag == AuxSwitchPos::HIGH);
1649 : #if AP_EXTERNAL_AHRS_ENABLED
1650 6 : AP::externalAHRS().set_gnss_disable(ch_flag == AuxSwitchPos::HIGH);
1651 : #endif
1652 6 : break;
1653 :
1654 0 : case AUX_FUNC::GPS_DISABLE_YAW:
1655 0 : AP::gps().set_force_disable_yaw(ch_flag == AuxSwitchPos::HIGH);
1656 0 : break;
1657 : #endif // AP_GPS_ENABLED
1658 :
1659 : #if AP_AIRSPEED_ENABLED
1660 14 : case AUX_FUNC::DISABLE_AIRSPEED_USE: {
1661 14 : AP_Airspeed *airspeed = AP::airspeed();
1662 14 : if (airspeed == nullptr) {
1663 0 : break;
1664 : }
1665 14 : switch (ch_flag) {
1666 6 : case AuxSwitchPos::HIGH:
1667 6 : airspeed->force_disable_use(true);
1668 6 : break;
1669 1 : case AuxSwitchPos::MIDDLE:
1670 1 : break;
1671 7 : case AuxSwitchPos::LOW:
1672 7 : airspeed->force_disable_use(false);
1673 7 : break;
1674 : }
1675 14 : break;
1676 : }
1677 : #endif
1678 :
1679 7 : case AUX_FUNC::MOTOR_ESTOP:
1680 7 : switch (ch_flag) {
1681 5 : case AuxSwitchPos::HIGH: {
1682 5 : SRV_Channels::set_emergency_stop(true);
1683 5 : break;
1684 : }
1685 0 : case AuxSwitchPos::MIDDLE:
1686 : // nothing
1687 0 : break;
1688 2 : case AuxSwitchPos::LOW: {
1689 2 : SRV_Channels::set_emergency_stop(false);
1690 2 : break;
1691 : }
1692 : }
1693 7 : break;
1694 :
1695 : #if HAL_VISUALODOM_ENABLED
1696 2 : case AUX_FUNC::VISODOM_ALIGN:
1697 2 : if (ch_flag == AuxSwitchPos::HIGH) {
1698 1 : AP_VisualOdom *visual_odom = AP::visualodom();
1699 1 : if (visual_odom != nullptr) {
1700 1 : visual_odom->request_align_yaw_to_ahrs();
1701 : }
1702 : }
1703 2 : break;
1704 : #endif
1705 :
1706 : #if AP_AHRS_ENABLED
1707 12 : case AUX_FUNC::EKF_SOURCE_SET: {
1708 12 : AP_NavEKF_Source::SourceSetSelection source_set = AP_NavEKF_Source::SourceSetSelection::PRIMARY;
1709 12 : switch (ch_flag) {
1710 2 : case AuxSwitchPos::LOW:
1711 : // low switches to primary source
1712 2 : source_set = AP_NavEKF_Source::SourceSetSelection::PRIMARY;
1713 2 : break;
1714 7 : case AuxSwitchPos::MIDDLE:
1715 : // middle switches to secondary source
1716 7 : source_set = AP_NavEKF_Source::SourceSetSelection::SECONDARY;
1717 7 : break;
1718 3 : case AuxSwitchPos::HIGH:
1719 : // high switches to tertiary source
1720 3 : source_set = AP_NavEKF_Source::SourceSetSelection::TERTIARY;
1721 3 : break;
1722 : }
1723 12 : AP::ahrs().set_posvelyaw_source_set(source_set);
1724 12 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Using EKF Source Set %u", uint8_t(source_set)+1);
1725 12 : break;
1726 : }
1727 : #endif // AP_AHRS_ENABLED
1728 :
1729 : #if AP_OPTICALFLOW_CALIBRATOR_ENABLED
1730 5 : case AUX_FUNC::OPTFLOW_CAL: {
1731 5 : AP_OpticalFlow *optflow = AP::opticalflow();
1732 5 : if (optflow == nullptr) {
1733 0 : GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "OptFlow Cal: failed sensor not enabled");
1734 0 : break;
1735 : }
1736 5 : if (ch_flag == AuxSwitchPos::HIGH) {
1737 1 : optflow->start_calibration();
1738 : } else {
1739 4 : optflow->stop_calibration();
1740 : }
1741 5 : break;
1742 : }
1743 : #endif
1744 :
1745 : #if AP_INERTIALSENSOR_KILL_IMU_ENABLED
1746 0 : case AUX_FUNC::KILL_IMU1:
1747 0 : AP::ins().kill_imu(0, ch_flag == AuxSwitchPos::HIGH);
1748 0 : break;
1749 :
1750 0 : case AUX_FUNC::KILL_IMU2:
1751 0 : AP::ins().kill_imu(1, ch_flag == AuxSwitchPos::HIGH);
1752 0 : break;
1753 :
1754 0 : case AUX_FUNC::KILL_IMU3:
1755 0 : AP::ins().kill_imu(2, ch_flag == AuxSwitchPos::HIGH);
1756 0 : break;
1757 : #endif // AP_INERTIALSENSOR_KILL_IMU_ENABLED
1758 :
1759 : #if AP_CAMERA_ENABLED
1760 16 : case AUX_FUNC::CAMERA_TRIGGER:
1761 16 : do_aux_function_camera_trigger(ch_flag);
1762 16 : break;
1763 :
1764 0 : case AUX_FUNC::CAM_MODE_TOGGLE: {
1765 : // Momentary switch to for cycling camera modes
1766 0 : AP_Camera *camera = AP_Camera::get_singleton();
1767 0 : if (camera == nullptr) {
1768 0 : break;
1769 : }
1770 0 : switch (ch_flag) {
1771 0 : case AuxSwitchPos::LOW:
1772 : // nothing
1773 0 : break;
1774 0 : case AuxSwitchPos::MIDDLE:
1775 : // nothing
1776 0 : break;
1777 0 : case AuxSwitchPos::HIGH:
1778 0 : camera->cam_mode_toggle();
1779 0 : break;
1780 : }
1781 0 : break;
1782 : }
1783 0 : case AUX_FUNC::CAMERA_REC_VIDEO:
1784 0 : return do_aux_function_record_video(ch_flag);
1785 :
1786 0 : case AUX_FUNC::CAMERA_ZOOM:
1787 0 : return do_aux_function_camera_zoom(ch_flag);
1788 :
1789 0 : case AUX_FUNC::CAMERA_MANUAL_FOCUS:
1790 0 : return do_aux_function_camera_manual_focus(ch_flag);
1791 :
1792 0 : case AUX_FUNC::CAMERA_AUTO_FOCUS:
1793 0 : return do_aux_function_camera_auto_focus(ch_flag);
1794 :
1795 0 : case AUX_FUNC::CAMERA_IMAGE_TRACKING:
1796 0 : return do_aux_function_camera_image_tracking(ch_flag);
1797 :
1798 : #if AP_CAMERA_SET_CAMERA_SOURCE_ENABLED
1799 0 : case AUX_FUNC::CAMERA_LENS:
1800 0 : return do_aux_function_camera_lens(ch_flag);
1801 : #endif // AP_CAMERA_SET_CAMERA_SOURCE_ENABLED
1802 : #endif // AP_CAMERA_ENABLED
1803 :
1804 : #if HAL_MOUNT_ENABLED
1805 0 : case AUX_FUNC::RETRACT_MOUNT1:
1806 0 : do_aux_function_retract_mount(ch_flag, 0);
1807 0 : break;
1808 :
1809 0 : case AUX_FUNC::RETRACT_MOUNT2:
1810 0 : do_aux_function_retract_mount(ch_flag, 1);
1811 0 : break;
1812 :
1813 0 : case AUX_FUNC::MOUNT_YAW_LOCK: {
1814 0 : AP_Mount *mount = AP::mount();
1815 0 : if (mount == nullptr) {
1816 0 : break;
1817 : }
1818 0 : mount->set_yaw_lock(ch_flag == AuxSwitchPos::HIGH);
1819 0 : break;
1820 : }
1821 :
1822 0 : case AUX_FUNC::MOUNT_RP_LOCK: {
1823 0 : AP_Mount *mount = AP::mount();
1824 0 : if (mount == nullptr) {
1825 0 : break;
1826 : }
1827 : //low is FPV:no ef locks,high is HORIZON lock:roll/pitch ef lock,middle is only pitch ef lock
1828 0 : switch (ch_flag) {
1829 0 : case AuxSwitchPos::HIGH:
1830 0 : mount->set_roll_lock(true);
1831 0 : mount->set_pitch_lock(true);
1832 0 : break;
1833 0 : case AuxSwitchPos::MIDDLE:
1834 0 : mount->set_roll_lock(false);
1835 0 : mount->set_pitch_lock(true);
1836 0 : break;
1837 0 : case AuxSwitchPos::LOW:
1838 0 : mount->set_roll_lock(false);
1839 0 : mount->set_pitch_lock(false);
1840 0 : break;
1841 : }
1842 0 : break;
1843 : }
1844 : #if AP_MOUNT_POI_LOCK_ENABLED
1845 13 : case AUX_FUNC::MOUNT_POI_LOCK: {
1846 13 : AP_Mount *mount = AP::mount();
1847 13 : if (mount == nullptr) {
1848 0 : break;
1849 : }
1850 13 : switch (ch_flag) {
1851 4 : case AuxSwitchPos::HIGH:
1852 4 : mount->set_poi_lock();
1853 4 : break;
1854 7 : case AuxSwitchPos::MIDDLE:
1855 7 : mount->suspend_poi_lock();
1856 7 : break;
1857 2 : case AuxSwitchPos::LOW:
1858 2 : mount->clear_poi_lock();
1859 2 : break;
1860 : }
1861 13 : break;
1862 : }
1863 : #endif // AP_MOUNT_POI_LOCK_ENABLED
1864 :
1865 0 : case AUX_FUNC::MOUNT_LRF_ENABLE: {
1866 0 : AP_Mount *mount = AP::mount();
1867 0 : if (mount == nullptr) {
1868 0 : break;
1869 : }
1870 0 : mount->set_rangefinder_enable(0, ch_flag == AuxSwitchPos::HIGH);
1871 0 : break;
1872 : }
1873 : #endif
1874 :
1875 : #if HAL_LOGGING_ENABLED
1876 0 : case AUX_FUNC::LOG_PAUSE: {
1877 0 : AP_Logger *logger = AP_Logger::get_singleton();
1878 0 : switch (ch_flag) {
1879 0 : case AuxSwitchPos::LOW:
1880 0 : logger->log_pause(false);
1881 0 : break;
1882 0 : case AuxSwitchPos::MIDDLE:
1883 : // nothing
1884 0 : break;
1885 0 : case AuxSwitchPos::HIGH:
1886 0 : logger->log_pause(true);
1887 0 : break;
1888 : }
1889 0 : break;
1890 : }
1891 : #endif
1892 :
1893 : #if COMPASS_CAL_ENABLED
1894 0 : case AUX_FUNC::MAG_CAL: {
1895 0 : Compass &compass = AP::compass();
1896 0 : switch (ch_flag) {
1897 0 : case AuxSwitchPos::LOW:
1898 0 : compass.cancel_calibration_all();
1899 0 : break;
1900 0 : case AuxSwitchPos::MIDDLE:
1901 : // nothing
1902 0 : break;
1903 0 : case AuxSwitchPos::HIGH:
1904 0 : if (!hal.util->get_soft_armed()) {
1905 0 : const bool retry = true;
1906 0 : const bool autosave = true;
1907 0 : const float delay = 5.0;
1908 0 : const bool autoreboot = false;
1909 0 : compass.start_calibration_all(retry, autosave, delay, autoreboot);
1910 : } else {
1911 0 : GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "Disarm to allow compass calibration");
1912 : }
1913 0 : break;
1914 : }
1915 0 : break;
1916 : }
1917 : #endif
1918 :
1919 : #if AP_ARMING_ENABLED
1920 3 : case AUX_FUNC::ARM_EMERGENCY_STOP: {
1921 3 : switch (ch_flag) {
1922 1 : case AuxSwitchPos::HIGH:
1923 : // request arm, disable emergency motor stop
1924 1 : SRV_Channels::set_emergency_stop(false);
1925 1 : AP::arming().arm(AP_Arming::Method::AUXSWITCH, true);
1926 1 : break;
1927 0 : case AuxSwitchPos::MIDDLE:
1928 : // disable emergency motor stop
1929 0 : SRV_Channels::set_emergency_stop(false);
1930 0 : break;
1931 2 : case AuxSwitchPos::LOW:
1932 : // enable emergency motor stop
1933 2 : SRV_Channels::set_emergency_stop(true);
1934 2 : break;
1935 : }
1936 3 : break;
1937 : }
1938 : #endif // AP_ARMING_ENABLED
1939 :
1940 : #if AP_AHRS_ENABLED
1941 0 : case AUX_FUNC::EKF_LANE_SWITCH:
1942 : // used to test emergency lane switch
1943 0 : AP::ahrs().check_lane_switch();
1944 0 : break;
1945 :
1946 0 : case AUX_FUNC::EKF_YAW_RESET:
1947 : // used to test emergency yaw reset
1948 0 : AP::ahrs().request_yaw_reset();
1949 0 : break;
1950 :
1951 : #if AP_AHRS_EKF_RESET_ENABLED
1952 6 : case AUX_FUNC::EKF_RESET:
1953 6 : if (ch_flag == AuxSwitchPos::HIGH) {
1954 : // only allowed while disarmed - resetting the EKF in flight would
1955 : // discard the in-flight state estimate
1956 2 : if (hal.util->get_soft_armed()) {
1957 1 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "EKF reset ignored: vehicle armed");
1958 1 : } else if (AP::ahrs().reset_configured_backend()) {
1959 1 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "EKF bootstrap reset performed");
1960 : } else {
1961 0 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "EKF bootstrap reset failed");
1962 : }
1963 : }
1964 6 : break;
1965 : #endif // AP_AHRS_EKF_RESET_ENABLED
1966 :
1967 0 : case AUX_FUNC::AHRS_TYPE: {
1968 : #if HAL_NAVEKF3_AVAILABLE && AP_EXTERNAL_AHRS_ENABLED
1969 0 : AP::ahrs().set_ekf_type(ch_flag==AuxSwitchPos::HIGH? AP_AHRS::EKFType::EXTERNAL : AP_AHRS::EKFType::THREE);
1970 : #endif
1971 0 : break;
1972 : }
1973 : #endif // AP_AHRS_ENABLED
1974 :
1975 : #if HAL_TORQEEDO_ENABLED
1976 : // clear torqeedo error
1977 0 : case AUX_FUNC::TORQEEDO_CLEAR_ERR: {
1978 0 : if (ch_flag == AuxSwitchPos::HIGH) {
1979 0 : AP_Torqeedo *torqeedo = AP_Torqeedo::get_singleton();
1980 0 : if (torqeedo != nullptr) {
1981 0 : torqeedo->clear_motor_error();
1982 : }
1983 : }
1984 0 : break;
1985 : }
1986 : #endif
1987 :
1988 : #if AP_SCRIPTING_ENABLED
1989 0 : case AUX_FUNC::STOP_RESTART_SCRIPTING: {
1990 0 : AP_Scripting *scr = AP::scripting();
1991 0 : if (scr != nullptr) {
1992 0 : switch (ch_flag) {
1993 0 : case AuxSwitchPos::HIGH:
1994 0 : scr->stop();
1995 0 : break;
1996 0 : case AuxSwitchPos::MIDDLE:
1997 0 : break;
1998 0 : case AuxSwitchPos::LOW:
1999 0 : scr->restart_all();
2000 0 : break;
2001 : }
2002 : }
2003 0 : break;
2004 : }
2005 : #endif
2006 :
2007 : // do nothing for these functions
2008 : #if HAL_MOUNT_ENABLED
2009 277 : case AUX_FUNC::MOUNT1_ROLL:
2010 : case AUX_FUNC::MOUNT1_PITCH:
2011 : case AUX_FUNC::MOUNT1_YAW:
2012 : case AUX_FUNC::MOUNT2_ROLL:
2013 : case AUX_FUNC::MOUNT2_PITCH:
2014 : case AUX_FUNC::MOUNT2_YAW:
2015 : #endif
2016 : #if AP_SCRIPTING_ENABLED
2017 : case AUX_FUNC::SCRIPTING_1:
2018 : case AUX_FUNC::SCRIPTING_2:
2019 : case AUX_FUNC::SCRIPTING_3:
2020 : case AUX_FUNC::SCRIPTING_4:
2021 : case AUX_FUNC::SCRIPTING_5:
2022 : case AUX_FUNC::SCRIPTING_6:
2023 : case AUX_FUNC::SCRIPTING_7:
2024 : case AUX_FUNC::SCRIPTING_8:
2025 : case AUX_FUNC::SCRIPTING_9:
2026 : case AUX_FUNC::SCRIPTING_10:
2027 : case AUX_FUNC::SCRIPTING_11:
2028 : case AUX_FUNC::SCRIPTING_12:
2029 : case AUX_FUNC::SCRIPTING_13:
2030 : case AUX_FUNC::SCRIPTING_14:
2031 : case AUX_FUNC::SCRIPTING_15:
2032 : case AUX_FUNC::SCRIPTING_16:
2033 : #endif
2034 277 : break;
2035 :
2036 : #if HAL_GENERATOR_ENABLED
2037 25 : case AUX_FUNC::LOWEHEISER_THROTTLE:
2038 : case AUX_FUNC::LOWEHEISER_STARTER:
2039 : // monitored by the library itself
2040 25 : break;
2041 : #endif
2042 :
2043 4 : default:
2044 4 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Invalid channel option (%u)", (unsigned int)ch_option);
2045 4 : return false;
2046 : }
2047 :
2048 614 : return true;
2049 : }
2050 :
2051 33456 : void RC_Channel::init_aux()
2052 : {
2053 : AuxSwitchPos position;
2054 33456 : if (!read_3pos_switch(position)) {
2055 33456 : position = AuxSwitchPos::LOW;
2056 : }
2057 :
2058 33456 : init_aux_function((AUX_FUNC)option.get(), position);
2059 33456 : }
2060 :
2061 : // read_3pos_switch
2062 1041532 : bool RC_Channel::read_3pos_switch(RC_Channel::AuxSwitchPos &ret) const
2063 : {
2064 1041532 : const uint16_t in = get_radio_in();
2065 1041532 : if (in <= RC_MIN_LIMIT_PWM || in >= RC_MAX_LIMIT_PWM) {
2066 34170 : return false;
2067 : }
2068 :
2069 : // switch is reversed if 'reversed' option set on channel and switches reverse is allowed by RC_OPTIONS
2070 1007362 : bool switch_reversed = reversed && rc().option_is_enabled(RC_Channels::Option::ALLOW_SWITCH_REV);
2071 :
2072 1007362 : if (in < AUX_SWITCH_PWM_TRIGGER_LOW) {
2073 102256 : ret = switch_reversed ? AuxSwitchPos::HIGH : AuxSwitchPos::LOW;
2074 905106 : } else if (in > AUX_SWITCH_PWM_TRIGGER_HIGH) {
2075 112606 : ret = switch_reversed ? AuxSwitchPos::LOW : AuxSwitchPos::HIGH;
2076 : } else {
2077 792500 : ret = AuxSwitchPos::MIDDLE;
2078 : }
2079 1007362 : return true;
2080 : }
2081 :
2082 : // return switch position value as LOW, MIDDLE, HIGH
2083 : // if reading the switch fails then it returns LOW
2084 321 : RC_Channel::AuxSwitchPos RC_Channel::get_aux_switch_pos() const
2085 : {
2086 321 : AuxSwitchPos position = AuxSwitchPos::LOW;
2087 321 : UNUSED_RESULT(read_3pos_switch(position));
2088 :
2089 321 : return position;
2090 : }
2091 :
2092 : // return stick gesture pos as LOW, MIDDLE, HIGH
2093 : // this function uses different threshold values to RC_Chanel::get_aux_switch_pos()
2094 : // to avoid glitching on the stick travel and also always honours channel reversal
2095 0 : RC_Channel::AuxSwitchPos RC_Channel::get_stick_gesture_pos() const
2096 : {
2097 0 : const uint16_t in = get_radio_in();
2098 0 : if (in <= 900 || in >= 2200) {
2099 0 : return RC_Channel::AuxSwitchPos::LOW;
2100 : }
2101 :
2102 : // switch is reversed if 'reversed' option set on channel and switches reverse is allowed by RC_OPTIONS
2103 0 : bool switch_reversed = get_reverse();
2104 :
2105 0 : if (in < RC_Channel::AUX_PWM_TRIGGER_LOW) {
2106 0 : return switch_reversed ? RC_Channel::AuxSwitchPos::HIGH : RC_Channel::AuxSwitchPos::LOW;
2107 : }
2108 0 : if (in > RC_Channel::AUX_PWM_TRIGGER_HIGH) {
2109 0 : return switch_reversed ? RC_Channel::AuxSwitchPos::LOW : RC_Channel::AuxSwitchPos::HIGH;
2110 : }
2111 0 : return RC_Channel::AuxSwitchPos::MIDDLE;
2112 : }
2113 :
2114 4610257 : RC_Channel *RC_Channels::find_channel_for_option(const RC_Channel::AUX_FUNC option)
2115 : {
2116 66491492 : for (uint8_t i=0; i<NUM_RC_CHANNELS; i++) {
2117 63128299 : RC_Channel *c = channel(i);
2118 63128299 : if (c == nullptr) {
2119 : // odd?
2120 0 : continue;
2121 : }
2122 63128299 : if ((RC_Channel::AUX_FUNC)c->option.get() == option) {
2123 1247064 : return c;
2124 : }
2125 : }
2126 3363193 : return nullptr;
2127 : }
2128 :
2129 : // duplicate_options_exist - returns true if any options are duplicated
2130 1028 : bool RC_Channels::duplicate_options_exist()
2131 : {
2132 1028 : Bitmask<(uint16_t)RC_Channel::AUX_FUNC::AUX_FUNCTION_MAX> used_auxsw_options;
2133 17466 : for (uint8_t i=0; i<NUM_RC_CHANNELS; i++) {
2134 16439 : const RC_Channel *c = channel(i);
2135 16439 : if (c == nullptr) {
2136 : // odd?
2137 0 : continue;
2138 : }
2139 16439 : const uint16_t option = c->option.get();
2140 16439 : if (option == (uint16_t)RC_Channel::AUX_FUNC::DO_NOTHING) {
2141 15628 : continue;
2142 : }
2143 811 : if (option >= used_auxsw_options.size()) {
2144 0 : continue;
2145 : }
2146 811 : if (used_auxsw_options.get(option)) {
2147 1 : return true;
2148 : }
2149 810 : used_auxsw_options.set(option);
2150 : }
2151 1027 : return false;
2152 : }
2153 :
2154 : #endif // AP_RC_CHANNEL_ENABLED
|