Fixed unit tests using 'ledstrip.c'.
[betaflight.git] / src / main / fc / rc_adjustments.c
blob41f7b048c58f23a48d464f9d025aa5eb26bfa96d
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <string.h>
25 #include <math.h>
27 #include "platform.h"
29 #include "blackbox/blackbox.h"
30 #include "blackbox/blackbox_fielddefs.h"
32 #include "build/build_config.h"
34 #include "common/axis.h"
35 #include "common/maths.h"
36 #include "common/utils.h"
38 #include "config/feature.h"
40 #include "drivers/time.h"
42 #include "flight/pid.h"
44 #include "io/beeper.h"
45 #include "io/ledstrip.h"
46 #include "io/motors.h"
47 #include "io/pidaudio.h"
48 #include "io/osd.h"
50 #include "fc/config.h"
51 #include "fc/controlrate_profile.h"
52 #include "fc/rc_controls.h"
53 #include "fc/rc.h"
55 #include "pg/pg.h"
56 #include "pg/pg_ids.h"
57 #include "pg/rx.h"
59 #include "rx/rx.h"
61 #include "rc_adjustments.h"
63 #define ADJUSTMENT_RANGE_COUNT_INVALID -1
65 PG_REGISTER_ARRAY(adjustmentRange_t, MAX_ADJUSTMENT_RANGE_COUNT, adjustmentRanges, PG_ADJUSTMENT_RANGE_CONFIG, 1);
67 uint8_t pidAudioPositionToModeMap[7] = {
68 // on a pot with a center detent, it's easy to have center area for off/default, then three positions to the left and three to the right.
69 // current implementation yields RC values as below.
71 PID_AUDIO_PIDSUM_X, // 900 - ~1071 - Min
72 PID_AUDIO_PIDSUM_Y, // ~1071 - ~1242
73 PID_AUDIO_PIDSUM_XY, // ~1242 - ~1414
74 PID_AUDIO_OFF, // ~1414 - ~1585 - Center
75 PID_AUDIO_OFF, // ~1585 - ~1757
76 PID_AUDIO_OFF, // ~1757 - ~1928
77 PID_AUDIO_OFF, // ~1928 - 2100 - Max
79 // Note: Last 3 positions are currently pending implementations and use PID_AUDIO_OFF for now.
82 static int activeAdjustmentCount = ADJUSTMENT_RANGE_COUNT_INVALID;
83 static uint8_t activeAdjustmentArray[MAX_ADJUSTMENT_RANGE_COUNT];
84 static int activeAbsoluteAdjustmentCount;
85 static uint8_t activeAbsoluteAdjustmentArray[MAX_ADJUSTMENT_RANGE_COUNT];
87 static void blackboxLogInflightAdjustmentEvent(adjustmentFunction_e adjustmentFunction, int32_t newValue)
89 #ifndef USE_BLACKBOX
90 UNUSED(adjustmentFunction);
91 UNUSED(newValue);
92 #else
93 if (blackboxConfig()->device) {
94 flightLogEvent_inflightAdjustment_t eventData;
95 eventData.adjustmentFunction = adjustmentFunction;
96 eventData.newValue = newValue;
97 eventData.floatFlag = false;
98 blackboxLogEvent(FLIGHT_LOG_EVENT_INFLIGHT_ADJUSTMENT, (flightLogEventData_t*)&eventData);
100 #endif
103 #if 0
104 static void blackboxLogInflightAdjustmentEventFloat(adjustmentFunction_e adjustmentFunction, float newFloatValue)
106 #ifndef USE_BLACKBOX
107 UNUSED(adjustmentFunction);
108 UNUSED(newFloatValue);
109 #else
110 if (blackboxConfig()->device) {
111 flightLogEvent_inflightAdjustment_t eventData;
112 eventData.adjustmentFunction = adjustmentFunction;
113 eventData.newFloatValue = newFloatValue;
114 eventData.floatFlag = true;
115 blackboxLogEvent(FLIGHT_LOG_EVENT_INFLIGHT_ADJUSTMENT, (flightLogEventData_t*)&eventData);
117 #endif
119 #endif
121 STATIC_UNIT_TESTED uint8_t adjustmentStateMask = 0;
123 #define MARK_ADJUSTMENT_FUNCTION_AS_BUSY(adjustmentIndex) adjustmentStateMask |= (1 << adjustmentIndex)
124 #define MARK_ADJUSTMENT_FUNCTION_AS_READY(adjustmentIndex) adjustmentStateMask &= ~(1 << adjustmentIndex)
126 #define IS_ADJUSTMENT_FUNCTION_BUSY(adjustmentIndex) (adjustmentStateMask & (1 << adjustmentIndex))
128 // sync with adjustmentFunction_e
129 static const adjustmentConfig_t defaultAdjustmentConfigs[ADJUSTMENT_FUNCTION_COUNT - 1] = {
131 .adjustmentFunction = ADJUSTMENT_RC_RATE,
132 .mode = ADJUSTMENT_MODE_STEP,
133 .data = { .step = 1 }
134 }, {
135 .adjustmentFunction = ADJUSTMENT_RC_EXPO,
136 .mode = ADJUSTMENT_MODE_STEP,
137 .data = { .step = 1 }
138 }, {
139 .adjustmentFunction = ADJUSTMENT_THROTTLE_EXPO,
140 .mode = ADJUSTMENT_MODE_STEP,
141 .data = { .step = 1 }
142 }, {
143 .adjustmentFunction = ADJUSTMENT_PITCH_ROLL_RATE,
144 .mode = ADJUSTMENT_MODE_STEP,
145 .data = { .step = 1 }
146 }, {
147 .adjustmentFunction = ADJUSTMENT_YAW_RATE,
148 .mode = ADJUSTMENT_MODE_STEP,
149 .data = { .step = 1 }
150 }, {
151 .adjustmentFunction = ADJUSTMENT_PITCH_ROLL_P,
152 .mode = ADJUSTMENT_MODE_STEP,
153 .data = { .step = 1 }
154 }, {
155 .adjustmentFunction = ADJUSTMENT_PITCH_ROLL_I,
156 .mode = ADJUSTMENT_MODE_STEP,
157 .data = { .step = 1 }
158 }, {
159 .adjustmentFunction = ADJUSTMENT_PITCH_ROLL_D,
160 .mode = ADJUSTMENT_MODE_STEP,
161 .data = { .step = 1 }
162 }, {
163 .adjustmentFunction = ADJUSTMENT_YAW_P,
164 .mode = ADJUSTMENT_MODE_STEP,
165 .data = { .step = 1 }
166 }, {
167 .adjustmentFunction = ADJUSTMENT_YAW_I,
168 .mode = ADJUSTMENT_MODE_STEP,
169 .data = { .step = 1 }
170 }, {
171 .adjustmentFunction = ADJUSTMENT_YAW_D,
172 .mode = ADJUSTMENT_MODE_STEP,
173 .data = { .step = 1 }
174 }, {
175 .adjustmentFunction = ADJUSTMENT_RATE_PROFILE,
176 .mode = ADJUSTMENT_MODE_SELECT,
177 .data = { .switchPositions = 3 }
178 }, {
179 .adjustmentFunction = ADJUSTMENT_PITCH_RATE,
180 .mode = ADJUSTMENT_MODE_STEP,
181 .data = { .step = 1 }
182 }, {
183 .adjustmentFunction = ADJUSTMENT_ROLL_RATE,
184 .mode = ADJUSTMENT_MODE_STEP,
185 .data = { .step = 1 }
186 }, {
187 .adjustmentFunction = ADJUSTMENT_PITCH_P,
188 .mode = ADJUSTMENT_MODE_STEP,
189 .data = { .step = 1 }
190 }, {
191 .adjustmentFunction = ADJUSTMENT_PITCH_I,
192 .mode = ADJUSTMENT_MODE_STEP,
193 .data = { .step = 1 }
194 }, {
195 .adjustmentFunction = ADJUSTMENT_PITCH_D,
196 .mode = ADJUSTMENT_MODE_STEP,
197 .data = { .step = 1 }
198 }, {
199 .adjustmentFunction = ADJUSTMENT_ROLL_P,
200 .mode = ADJUSTMENT_MODE_STEP,
201 .data = { .step = 1 }
202 }, {
203 .adjustmentFunction = ADJUSTMENT_ROLL_I,
204 .mode = ADJUSTMENT_MODE_STEP,
205 .data = { .step = 1 }
206 }, {
207 .adjustmentFunction = ADJUSTMENT_ROLL_D,
208 .mode = ADJUSTMENT_MODE_STEP,
209 .data = { .step = 1 }
210 }, {
211 .adjustmentFunction = ADJUSTMENT_RC_RATE_YAW,
212 .mode = ADJUSTMENT_MODE_STEP,
213 .data = { .step = 1 }
214 }, {
215 .adjustmentFunction = ADJUSTMENT_PITCH_ROLL_F,
216 .mode = ADJUSTMENT_MODE_STEP,
217 .data = { .step = 1 }
218 }, {
219 .adjustmentFunction = ADJUSTMENT_FEEDFORWARD_TRANSITION,
220 .mode = ADJUSTMENT_MODE_STEP,
221 .data = { .step = 1 }
222 }, {
223 .adjustmentFunction = ADJUSTMENT_HORIZON_STRENGTH,
224 .mode = ADJUSTMENT_MODE_SELECT,
225 .data = { .switchPositions = 255 }
226 }, {
227 .adjustmentFunction = ADJUSTMENT_PID_AUDIO,
228 .mode = ADJUSTMENT_MODE_SELECT,
229 .data = { .switchPositions = ARRAYLEN(pidAudioPositionToModeMap) }
230 }, {
231 .adjustmentFunction = ADJUSTMENT_PITCH_F,
232 .mode = ADJUSTMENT_MODE_STEP,
233 .data = { .step = 1 }
234 }, {
235 .adjustmentFunction = ADJUSTMENT_ROLL_F,
236 .mode = ADJUSTMENT_MODE_STEP,
237 .data = { .step = 1 }
238 }, {
239 .adjustmentFunction = ADJUSTMENT_YAW_F,
240 .mode = ADJUSTMENT_MODE_STEP,
241 .data = { .step = 1 }
242 }, {
243 .adjustmentFunction = ADJUSTMENT_OSD_PROFILE,
244 .mode = ADJUSTMENT_MODE_SELECT,
245 .data = { .switchPositions = 3 }
246 }, {
247 .adjustmentFunction = ADJUSTMENT_LED_PROFILE,
248 .mode = ADJUSTMENT_MODE_SELECT,
249 .data = { .switchPositions = 3 }
253 #if defined(USE_OSD) && defined(USE_OSD_ADJUSTMENTS)
254 static const char * const adjustmentLabels[] = {
255 "RC RATE",
256 "RC EXPO",
257 "THROTTLE EXPO",
258 "ROLL RATE",
259 "YAW RATE",
260 "PITCH/ROLL P",
261 "PITCH/ROLL I",
262 "PITCH/ROLL D",
263 "YAW P",
264 "YAW I",
265 "YAW D",
266 "RATE PROFILE",
267 "PITCH RATE",
268 "ROLL RATE",
269 "PITCH P",
270 "PITCH I",
271 "PITCH D",
272 "ROLL P",
273 "ROLL I",
274 "ROLL D",
275 "RC RATE YAW",
276 "PITCH/ROLL F",
277 "FF TRANSITION",
278 "HORIZON STRENGTH",
279 "ROLL RC RATE",
280 "PITCH RC RATE",
281 "ROLL RC EXPO",
282 "PITCH RC EXPO",
283 "PID AUDIO",
284 "PITCH F",
285 "ROLL F",
286 "YAW F",
287 "OSD PROFILE",
290 static int adjustmentRangeNameIndex = 0;
291 static int adjustmentRangeValue = -1;
292 #endif
294 #define ADJUSTMENT_FUNCTION_CONFIG_INDEX_OFFSET 1
296 STATIC_UNIT_TESTED adjustmentState_t adjustmentStates[MAX_SIMULTANEOUS_ADJUSTMENT_COUNT];
298 STATIC_UNIT_TESTED void configureAdjustment(uint8_t index, uint8_t auxSwitchChannelIndex, const adjustmentConfig_t *adjustmentConfig)
300 adjustmentState_t *adjustmentState = &adjustmentStates[index];
302 if (adjustmentState->config == adjustmentConfig) {
303 // already configured
304 return;
306 adjustmentState->auxChannelIndex = auxSwitchChannelIndex;
307 adjustmentState->config = adjustmentConfig;
308 adjustmentState->timeoutAt = 0;
310 MARK_ADJUSTMENT_FUNCTION_AS_READY(index);
313 static int applyStepAdjustment(controlRateConfig_t *controlRateConfig, uint8_t adjustmentFunction, int delta)
316 beeperConfirmationBeeps(delta > 0 ? 2 : 1);
317 int newValue;
318 switch (adjustmentFunction) {
319 case ADJUSTMENT_RC_RATE:
320 case ADJUSTMENT_ROLL_RC_RATE:
321 newValue = constrain((int)controlRateConfig->rcRates[FD_ROLL] + delta, 1, CONTROL_RATE_CONFIG_RC_RATES_MAX);
322 controlRateConfig->rcRates[FD_ROLL] = newValue;
323 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_RC_RATE, newValue);
324 if (adjustmentFunction == ADJUSTMENT_ROLL_RC_RATE) {
325 break;
327 // fall through for combined ADJUSTMENT_RC_EXPO
328 FALLTHROUGH;
329 case ADJUSTMENT_PITCH_RC_RATE:
330 newValue = constrain((int)controlRateConfig->rcRates[FD_PITCH] + delta, 1, CONTROL_RATE_CONFIG_RC_RATES_MAX);
331 controlRateConfig->rcRates[FD_PITCH] = newValue;
332 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_RC_RATE, newValue);
333 break;
334 case ADJUSTMENT_RC_EXPO:
335 case ADJUSTMENT_ROLL_RC_EXPO:
336 newValue = constrain((int)controlRateConfig->rcExpo[FD_ROLL] + delta, 0, CONTROL_RATE_CONFIG_RC_EXPO_MAX);
337 controlRateConfig->rcExpo[FD_ROLL] = newValue;
338 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_RC_EXPO, newValue);
339 if (adjustmentFunction == ADJUSTMENT_ROLL_RC_EXPO) {
340 break;
342 // fall through for combined ADJUSTMENT_RC_EXPO
343 FALLTHROUGH;
344 case ADJUSTMENT_PITCH_RC_EXPO:
345 newValue = constrain((int)controlRateConfig->rcExpo[FD_PITCH] + delta, 0, CONTROL_RATE_CONFIG_RC_EXPO_MAX);
346 controlRateConfig->rcExpo[FD_PITCH] = newValue;
347 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_RC_EXPO, newValue);
348 break;
349 case ADJUSTMENT_THROTTLE_EXPO:
350 newValue = constrain((int)controlRateConfig->thrExpo8 + delta, 0, 100); // FIXME magic numbers repeated in cli.c
351 controlRateConfig->thrExpo8 = newValue;
352 initRcProcessing();
353 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_THROTTLE_EXPO, newValue);
354 break;
355 case ADJUSTMENT_PITCH_ROLL_RATE:
356 case ADJUSTMENT_PITCH_RATE:
357 newValue = constrain((int)controlRateConfig->rates[FD_PITCH] + delta, 0, CONTROL_RATE_CONFIG_RATE_MAX);
358 controlRateConfig->rates[FD_PITCH] = newValue;
359 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_RATE, newValue);
360 if (adjustmentFunction == ADJUSTMENT_PITCH_RATE) {
361 break;
363 // fall through for combined ADJUSTMENT_PITCH_ROLL_RATE
364 FALLTHROUGH;
365 case ADJUSTMENT_ROLL_RATE:
366 newValue = constrain((int)controlRateConfig->rates[FD_ROLL] + delta, 0, CONTROL_RATE_CONFIG_RATE_MAX);
367 controlRateConfig->rates[FD_ROLL] = newValue;
368 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_RATE, newValue);
369 break;
370 case ADJUSTMENT_YAW_RATE:
371 newValue = constrain((int)controlRateConfig->rates[FD_YAW] + delta, 0, CONTROL_RATE_CONFIG_RATE_MAX);
372 controlRateConfig->rates[FD_YAW] = newValue;
373 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_RATE, newValue);
374 break;
375 case ADJUSTMENT_PITCH_ROLL_P:
376 case ADJUSTMENT_PITCH_P:
377 newValue = constrain((int)currentPidProfile->pid[PID_PITCH].P + delta, 0, 200); // FIXME magic numbers repeated in cli.c
378 currentPidProfile->pid[PID_PITCH].P = newValue;
379 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_P, newValue);
381 if (adjustmentFunction == ADJUSTMENT_PITCH_P) {
382 break;
384 // fall through for combined ADJUSTMENT_PITCH_ROLL_P
385 FALLTHROUGH;
386 case ADJUSTMENT_ROLL_P:
387 newValue = constrain((int)currentPidProfile->pid[PID_ROLL].P + delta, 0, 200); // FIXME magic numbers repeated in cli.c
388 currentPidProfile->pid[PID_ROLL].P = newValue;
389 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_P, newValue);
390 break;
391 case ADJUSTMENT_PITCH_ROLL_I:
392 case ADJUSTMENT_PITCH_I:
393 newValue = constrain((int)currentPidProfile->pid[PID_PITCH].I + delta, 0, 200); // FIXME magic numbers repeated in cli.c
394 currentPidProfile->pid[PID_PITCH].I = newValue;
395 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_I, newValue);
396 if (adjustmentFunction == ADJUSTMENT_PITCH_I) {
397 break;
399 // fall through for combined ADJUSTMENT_PITCH_ROLL_I
400 FALLTHROUGH;
401 case ADJUSTMENT_ROLL_I:
402 newValue = constrain((int)currentPidProfile->pid[PID_ROLL].I + delta, 0, 200); // FIXME magic numbers repeated in cli.c
403 currentPidProfile->pid[PID_ROLL].I = newValue;
404 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_I, newValue);
405 break;
406 case ADJUSTMENT_PITCH_ROLL_D:
407 case ADJUSTMENT_PITCH_D:
408 newValue = constrain((int)currentPidProfile->pid[PID_PITCH].D + delta, 0, 200); // FIXME magic numbers repeated in cli.c
409 currentPidProfile->pid[PID_PITCH].D = newValue;
410 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_D, newValue);
411 if (adjustmentFunction == ADJUSTMENT_PITCH_D) {
412 break;
414 // fall through for combined ADJUSTMENT_PITCH_ROLL_D
415 FALLTHROUGH;
416 case ADJUSTMENT_ROLL_D:
417 newValue = constrain((int)currentPidProfile->pid[PID_ROLL].D + delta, 0, 200); // FIXME magic numbers repeated in cli.c
418 currentPidProfile->pid[PID_ROLL].D = newValue;
419 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_D, newValue);
420 break;
421 case ADJUSTMENT_YAW_P:
422 newValue = constrain((int)currentPidProfile->pid[PID_YAW].P + delta, 0, 200); // FIXME magic numbers repeated in cli.c
423 currentPidProfile->pid[PID_YAW].P = newValue;
424 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_P, newValue);
425 break;
426 case ADJUSTMENT_YAW_I:
427 newValue = constrain((int)currentPidProfile->pid[PID_YAW].I + delta, 0, 200); // FIXME magic numbers repeated in cli.c
428 currentPidProfile->pid[PID_YAW].I = newValue;
429 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_I, newValue);
430 break;
431 case ADJUSTMENT_YAW_D:
432 newValue = constrain((int)currentPidProfile->pid[PID_YAW].D + delta, 0, 200); // FIXME magic numbers repeated in cli.c
433 currentPidProfile->pid[PID_YAW].D = newValue;
434 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_D, newValue);
435 break;
436 case ADJUSTMENT_RC_RATE_YAW:
437 newValue = constrain((int)controlRateConfig->rcRates[FD_YAW] + delta, 1, CONTROL_RATE_CONFIG_RC_RATES_MAX);
438 controlRateConfig->rcRates[FD_YAW] = newValue;
439 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_RC_RATE_YAW, newValue);
440 break;
441 case ADJUSTMENT_PITCH_ROLL_F:
442 case ADJUSTMENT_PITCH_F:
443 newValue = constrain(currentPidProfile->pid[PID_PITCH].F + delta, 0, 2000);
444 currentPidProfile->pid[PID_PITCH].F = newValue;
445 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_F, newValue);
447 if (adjustmentFunction == ADJUSTMENT_PITCH_F) {
448 break;
450 // fall through for combined ADJUSTMENT_PITCH_ROLL_F
451 FALLTHROUGH;
452 case ADJUSTMENT_ROLL_F:
453 newValue = constrain(currentPidProfile->pid[PID_ROLL].F + delta, 0, 2000);
454 currentPidProfile->pid[PID_ROLL].F = newValue;
455 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_F, newValue);
456 break;
457 case ADJUSTMENT_YAW_F:
458 newValue = constrain(currentPidProfile->pid[PID_YAW].F + delta, 0, 2000);
459 currentPidProfile->pid[PID_YAW].F = newValue;
460 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_F, newValue);
461 break;
462 case ADJUSTMENT_FEEDFORWARD_TRANSITION:
463 newValue = constrain(currentPidProfile->feedForwardTransition + delta, 1, 100); // FIXME magic numbers repeated in cli.c
464 currentPidProfile->feedForwardTransition = newValue;
465 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_FEEDFORWARD_TRANSITION, newValue);
466 break;
467 default:
468 newValue = -1;
469 break;
472 return newValue;
475 static int applyAbsoluteAdjustment(controlRateConfig_t *controlRateConfig, adjustmentFunction_e adjustmentFunction, int value)
477 int newValue;
479 if ( !controlRateConfig || !currentPidProfile) {
480 return 0;
483 switch (adjustmentFunction) {
484 case ADJUSTMENT_RC_RATE:
485 case ADJUSTMENT_ROLL_RC_RATE:
486 newValue = constrain(value, 1, CONTROL_RATE_CONFIG_RC_RATES_MAX);
487 controlRateConfig->rcRates[FD_ROLL] = newValue;
488 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_RC_RATE, newValue);
489 if (adjustmentFunction == ADJUSTMENT_ROLL_RC_RATE) {
490 break;
492 // fall through for combined ADJUSTMENT_RC_EXPO
493 FALLTHROUGH;
494 case ADJUSTMENT_PITCH_RC_RATE:
495 newValue = constrain(value, 1, CONTROL_RATE_CONFIG_RC_RATES_MAX);
496 controlRateConfig->rcRates[FD_PITCH] = newValue;
497 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_RC_RATE, newValue);
498 break;
499 case ADJUSTMENT_RC_EXPO:
500 case ADJUSTMENT_ROLL_RC_EXPO:
501 newValue = constrain(value, 1, CONTROL_RATE_CONFIG_RC_EXPO_MAX);
502 controlRateConfig->rcExpo[FD_ROLL] = newValue;
503 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_RC_EXPO, newValue);
504 if (adjustmentFunction == ADJUSTMENT_ROLL_RC_EXPO) {
505 break;
507 // fall through for combined ADJUSTMENT_RC_EXPO
508 FALLTHROUGH;
509 case ADJUSTMENT_PITCH_RC_EXPO:
510 newValue = constrain(value, 0, CONTROL_RATE_CONFIG_RC_EXPO_MAX);
511 controlRateConfig->rcExpo[FD_PITCH] = newValue;
512 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_RC_EXPO, newValue);
513 break;
514 case ADJUSTMENT_THROTTLE_EXPO:
515 newValue = constrain(value, 0, 100); // FIXME magic numbers repeated in cli.c
516 controlRateConfig->thrExpo8 = newValue;
517 initRcProcessing();
518 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_THROTTLE_EXPO, newValue);
519 break;
520 case ADJUSTMENT_PITCH_ROLL_RATE:
521 case ADJUSTMENT_PITCH_RATE:
522 newValue = constrain(value, 0, CONTROL_RATE_CONFIG_RATE_MAX);
523 controlRateConfig->rates[FD_PITCH] = newValue;
524 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_RATE, newValue);
525 if (adjustmentFunction == ADJUSTMENT_PITCH_RATE) {
526 break;
528 // fall through for combined ADJUSTMENT_PITCH_ROLL_RATE
529 FALLTHROUGH;
530 case ADJUSTMENT_ROLL_RATE:
531 newValue = constrain(value, 0, CONTROL_RATE_CONFIG_RATE_MAX);
532 controlRateConfig->rates[FD_ROLL] = newValue;
533 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_RATE, newValue);
534 break;
535 case ADJUSTMENT_YAW_RATE:
536 newValue = constrain(value, 0, CONTROL_RATE_CONFIG_RATE_MAX);
537 controlRateConfig->rates[FD_YAW] = newValue;
538 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_RATE, newValue);
539 break;
540 case ADJUSTMENT_PITCH_ROLL_P:
541 case ADJUSTMENT_PITCH_P:
542 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
543 currentPidProfile->pid[PID_PITCH].P = newValue;
544 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_P, newValue);
546 if (adjustmentFunction == ADJUSTMENT_PITCH_P) {
547 break;
549 // fall through for combined ADJUSTMENT_PITCH_ROLL_P
550 FALLTHROUGH;
551 case ADJUSTMENT_ROLL_P:
552 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
553 currentPidProfile->pid[PID_ROLL].P = newValue;
554 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_P, newValue);
555 break;
556 case ADJUSTMENT_PITCH_ROLL_I:
557 case ADJUSTMENT_PITCH_I:
558 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
559 currentPidProfile->pid[PID_PITCH].I = newValue;
560 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_I, newValue);
561 if (adjustmentFunction == ADJUSTMENT_PITCH_I) {
562 break;
564 // fall through for combined ADJUSTMENT_PITCH_ROLL_I
565 FALLTHROUGH;
566 case ADJUSTMENT_ROLL_I:
567 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
568 currentPidProfile->pid[PID_ROLL].I = newValue;
569 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_I, newValue);
570 break;
571 case ADJUSTMENT_PITCH_ROLL_D:
572 case ADJUSTMENT_PITCH_D:
573 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
574 currentPidProfile->pid[PID_PITCH].D = newValue;
575 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_D, newValue);
576 if (adjustmentFunction == ADJUSTMENT_PITCH_D) {
577 break;
579 // fall through for combined ADJUSTMENT_PITCH_ROLL_D
580 FALLTHROUGH;
581 case ADJUSTMENT_ROLL_D:
582 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
583 currentPidProfile->pid[PID_ROLL].D = newValue;
584 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_D, newValue);
585 break;
586 case ADJUSTMENT_YAW_P:
587 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
588 currentPidProfile->pid[PID_YAW].P = newValue;
589 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_P, newValue);
590 break;
591 case ADJUSTMENT_YAW_I:
592 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
593 currentPidProfile->pid[PID_YAW].I = newValue;
594 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_I, newValue);
595 break;
596 case ADJUSTMENT_YAW_D:
597 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
598 currentPidProfile->pid[PID_YAW].D = newValue;
599 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_D, newValue);
600 break;
601 case ADJUSTMENT_RC_RATE_YAW:
602 newValue = constrain(value, 1, CONTROL_RATE_CONFIG_RC_RATES_MAX);
603 controlRateConfig->rcRates[FD_YAW] = newValue;
604 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_RC_RATE_YAW, newValue);
605 break;
606 case ADJUSTMENT_PITCH_ROLL_F:
607 case ADJUSTMENT_PITCH_F:
608 newValue = constrain(value, 0, 2000);
609 currentPidProfile->pid[PID_PITCH].F = newValue;
610 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_F, newValue);
612 if (adjustmentFunction == ADJUSTMENT_PITCH_F) {
613 break;
615 // fall through for combined ADJUSTMENT_PITCH_ROLL_F
616 FALLTHROUGH;
617 case ADJUSTMENT_ROLL_F:
618 newValue = constrain(value, 0, 2000);
619 currentPidProfile->pid[PID_ROLL].F = newValue;
620 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_F, newValue);
621 break;
622 case ADJUSTMENT_YAW_F:
623 newValue = constrain(value, 0, 2000);
624 currentPidProfile->pid[PID_YAW].F = newValue;
625 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_F, newValue);
626 break;
627 case ADJUSTMENT_FEEDFORWARD_TRANSITION:
628 newValue = constrain(value, 1, 100); // FIXME magic numbers repeated in cli.c
629 currentPidProfile->feedForwardTransition = newValue;
630 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_FEEDFORWARD_TRANSITION, newValue);
631 break;
632 default:
633 newValue = -1;
634 break;
637 return newValue;
640 static uint8_t applySelectAdjustment(adjustmentFunction_e adjustmentFunction, uint8_t position)
642 uint8_t beeps = 0;
644 switch (adjustmentFunction) {
645 case ADJUSTMENT_RATE_PROFILE:
646 if (getCurrentControlRateProfileIndex() != position) {
647 changeControlRateProfile(position);
648 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_RATE_PROFILE, position);
650 beeps = position + 1;
652 break;
653 case ADJUSTMENT_HORIZON_STRENGTH:
655 uint8_t newValue = constrain(position, 0, 200); // FIXME magic numbers repeated in serial_cli.c
656 if (currentPidProfile->pid[PID_LEVEL].D != newValue) {
657 beeps = ((newValue - currentPidProfile->pid[PID_LEVEL].D) / 8) + 1;
658 currentPidProfile->pid[PID_LEVEL].D = newValue;
659 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_HORIZON_STRENGTH, position);
662 break;
663 case ADJUSTMENT_PID_AUDIO:
664 #ifdef USE_PID_AUDIO
666 pidAudioModes_e newMode = pidAudioPositionToModeMap[position];
667 if (newMode != pidAudioGetMode()) {
668 pidAudioSetMode(newMode);
671 #endif
672 break;
673 case ADJUSTMENT_OSD_PROFILE:
674 #ifdef USE_OSD_PROFILES
675 if (getCurrentOsdProfileIndex() != (position + 1)) {
676 changeOsdProfileIndex(position+1);
678 #endif
679 break;
680 case ADJUSTMENT_LED_PROFILE:
681 #ifdef USE_LED_STRIP
682 if (getLedProfile() != position) {
683 setLedProfile(position);
685 #endif
686 break;
688 default:
689 break;
692 if (beeps) {
693 beeperConfirmationBeeps(beeps);
696 return position;
699 static void calcActiveAdjustmentRanges(void)
701 adjustmentRange_t defaultAdjustmentRange;
702 memset(&defaultAdjustmentRange, 0, sizeof(defaultAdjustmentRange));
704 activeAdjustmentCount = 0;
705 activeAbsoluteAdjustmentCount = 0;
706 for (int i = 0; i < MAX_ADJUSTMENT_RANGE_COUNT; i++) {
707 const adjustmentRange_t * const adjustmentRange = adjustmentRanges(i);
708 if (memcmp(adjustmentRange, &defaultAdjustmentRange, sizeof(defaultAdjustmentRange)) != 0) {
709 if (adjustmentRange->adjustmentCenter == 0) {
710 activeAdjustmentArray[activeAdjustmentCount++] = i;
711 } else {
712 activeAbsoluteAdjustmentArray[activeAbsoluteAdjustmentCount++] = i;
718 static void updateAdjustmentStates(void)
720 for (int index = 0; index < activeAdjustmentCount; index++) {
721 const adjustmentRange_t * const adjustmentRange = adjustmentRanges(activeAdjustmentArray[index]);
722 // Only use slots if center value has not been specified, otherwise apply values directly (scaled) from aux channel
723 if (isRangeActive(adjustmentRange->auxChannelIndex, &adjustmentRange->range) &&
724 (adjustmentRange->adjustmentCenter == 0)) {
725 const adjustmentConfig_t *adjustmentConfig = &defaultAdjustmentConfigs[adjustmentRange->adjustmentConfig - ADJUSTMENT_FUNCTION_CONFIG_INDEX_OFFSET];
726 configureAdjustment(adjustmentRange->adjustmentIndex, adjustmentRange->auxSwitchChannelIndex, adjustmentConfig);
731 #define RESET_FREQUENCY_2HZ (1000 / 2)
733 void processRcAdjustments(controlRateConfig_t *controlRateConfig)
735 const uint32_t now = millis();
737 int newValue = -1;
739 const bool canUseRxData = rxIsReceivingSignal();
741 // Recalculate the new active adjustments if required
742 if (activeAdjustmentCount == ADJUSTMENT_RANGE_COUNT_INVALID) {
743 calcActiveAdjustmentRanges();
746 updateAdjustmentStates();
748 // Process Increment/Decrement adjustments
749 for (int adjustmentIndex = 0; adjustmentIndex < MAX_SIMULTANEOUS_ADJUSTMENT_COUNT; adjustmentIndex++) {
750 adjustmentState_t *adjustmentState = &adjustmentStates[adjustmentIndex];
752 if (!adjustmentState->config) {
753 continue;
755 const adjustmentFunction_e adjustmentFunction = adjustmentState->config->adjustmentFunction;
756 if (adjustmentFunction == ADJUSTMENT_NONE) {
757 continue;
760 if (cmp32(now, adjustmentState->timeoutAt) >= 0) {
761 adjustmentState->timeoutAt = now + RESET_FREQUENCY_2HZ;
762 MARK_ADJUSTMENT_FUNCTION_AS_READY(adjustmentIndex);
764 #if defined(USE_OSD) && defined(USE_OSD_ADJUSTMENTS)
765 adjustmentRangeValue = -1;
766 #endif
769 if (!canUseRxData) {
770 continue;
773 const uint8_t channelIndex = NON_AUX_CHANNEL_COUNT + adjustmentState->auxChannelIndex;
775 if (adjustmentState->config->mode == ADJUSTMENT_MODE_STEP) {
776 int delta;
777 if (rcData[channelIndex] > rxConfig()->midrc + 200) {
778 delta = adjustmentState->config->data.step;
779 } else if (rcData[channelIndex] < rxConfig()->midrc - 200) {
780 delta = -adjustmentState->config->data.step;
781 } else {
782 // returning the switch to the middle immediately resets the ready state
783 MARK_ADJUSTMENT_FUNCTION_AS_READY(adjustmentIndex);
784 adjustmentState->timeoutAt = now + RESET_FREQUENCY_2HZ;
785 continue;
787 if (IS_ADJUSTMENT_FUNCTION_BUSY(adjustmentIndex)) {
788 continue;
791 newValue = applyStepAdjustment(controlRateConfig, adjustmentFunction, delta);
792 pidInitConfig(currentPidProfile);
793 } else if (adjustmentState->config->mode == ADJUSTMENT_MODE_SELECT) {
794 int switchPositions = adjustmentState->config->data.switchPositions;
795 if (adjustmentFunction == ADJUSTMENT_RATE_PROFILE && systemConfig()->rateProfile6PosSwitch) {
796 switchPositions = 6;
798 const uint16_t rangeWidth = (2100 - 900) / switchPositions;
799 const uint8_t position = (constrain(rcData[channelIndex], 900, 2100 - 1) - 900) / rangeWidth;
800 newValue = applySelectAdjustment(adjustmentFunction, position);
803 #if defined(USE_OSD) && defined(USE_OSD_ADJUSTMENTS)
804 if (newValue != -1
805 && adjustmentState->config->adjustmentFunction != ADJUSTMENT_RATE_PROFILE // Rate profile already has an OSD element
806 #ifdef USE_OSD_PROFILES
807 && adjustmentState->config->adjustmentFunction != ADJUSTMENT_OSD_PROFILE
808 #endif
809 #ifdef USE_LED_STRIP
810 && adjustmentState->config->adjustmentFunction != ADJUSTMENT_LED_PROFILE
811 #endif
813 adjustmentRangeNameIndex = adjustmentFunction;
814 adjustmentRangeValue = newValue;
816 #else
817 UNUSED(newValue);
818 #endif
819 MARK_ADJUSTMENT_FUNCTION_AS_BUSY(adjustmentIndex);
822 // Process Absolute adjustments
823 for (int i = 0; i < activeAbsoluteAdjustmentCount; i++) {
824 static int16_t lastRcData[MAX_ADJUSTMENT_RANGE_COUNT] = { 0 };
825 int index = activeAbsoluteAdjustmentArray[i];
826 const adjustmentRange_t * const adjustmentRange = adjustmentRanges(index);
827 const uint8_t channelIndex = NON_AUX_CHANNEL_COUNT + adjustmentRange->auxSwitchChannelIndex;
828 const adjustmentConfig_t *adjustmentConfig = &defaultAdjustmentConfigs[adjustmentRange->adjustmentConfig - ADJUSTMENT_FUNCTION_CONFIG_INDEX_OFFSET];
830 // If setting is defined for step adjustment and center value has been specified, apply values directly (scaled) from aux channel
831 if ((rcData[channelIndex] != lastRcData[index]) &&
832 adjustmentRange->adjustmentCenter &&
833 (adjustmentConfig->mode == ADJUSTMENT_MODE_STEP) &&
834 isRangeActive(adjustmentRange->auxChannelIndex, &adjustmentRange->range)) {
835 int value = (((rcData[channelIndex] - PWM_RANGE_MIDDLE) * adjustmentRange->adjustmentScale) / (PWM_RANGE_MIDDLE - PWM_RANGE_MIN)) + adjustmentRange->adjustmentCenter;
837 lastRcData[index] = rcData[channelIndex];
838 applyAbsoluteAdjustment(controlRateConfig, adjustmentConfig->adjustmentFunction, value);
839 pidInitConfig(currentPidProfile);
844 void resetAdjustmentStates(void)
846 memset(adjustmentStates, 0, sizeof(adjustmentStates));
849 #if defined(USE_OSD) && defined(USE_OSD_ADJUSTMENTS)
850 const char *getAdjustmentsRangeName(void)
852 if (adjustmentRangeNameIndex > 0) {
853 return &adjustmentLabels[adjustmentRangeNameIndex - 1][0];
854 } else {
855 return NULL;
859 int getAdjustmentsRangeValue(void)
861 return adjustmentRangeValue;
863 #endif
865 void activeAdjustmentRangeReset(void)
867 activeAdjustmentCount = ADJUSTMENT_RANGE_COUNT_INVALID;