Merge pull request #11198 from SteveCEvans/sce_rc2
[betaflight.git] / src / main / fc / rc_adjustments.c
blobd8fb44a8b141c6e1752c86ffb1517af7865e1b7e
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 "config/config.h"
43 #include "fc/controlrate_profile.h"
44 #include "fc/rc_controls.h"
45 #include "fc/rc.h"
47 #include "flight/pid.h"
48 #include "flight/pid_init.h"
50 #include "io/beeper.h"
51 #include "io/ledstrip.h"
52 #include "io/pidaudio.h"
54 #include "osd/osd.h"
56 #include "pg/pg.h"
57 #include "pg/pg_ids.h"
58 #include "pg/rx.h"
60 #include "rx/rx.h"
62 #include "rc_adjustments.h"
64 #define ADJUSTMENT_RANGE_COUNT_INVALID -1
66 PG_REGISTER_ARRAY(adjustmentRange_t, MAX_ADJUSTMENT_RANGE_COUNT, adjustmentRanges, PG_ADJUSTMENT_RANGE_CONFIG, 2);
68 uint8_t pidAudioPositionToModeMap[7] = {
69 // 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.
70 // current implementation yields RC values as below.
72 PID_AUDIO_PIDSUM_X, // 900 - ~1071 - Min
73 PID_AUDIO_PIDSUM_Y, // ~1071 - ~1242
74 PID_AUDIO_PIDSUM_XY, // ~1242 - ~1414
75 PID_AUDIO_OFF, // ~1414 - ~1585 - Center
76 PID_AUDIO_OFF, // ~1585 - ~1757
77 PID_AUDIO_OFF, // ~1757 - ~1928
78 PID_AUDIO_OFF, // ~1928 - 2100 - Max
80 // Note: Last 3 positions are currently pending implementations and use PID_AUDIO_OFF for now.
83 STATIC_UNIT_TESTED int stepwiseAdjustmentCount = ADJUSTMENT_RANGE_COUNT_INVALID;
84 STATIC_UNIT_TESTED timedAdjustmentState_t stepwiseAdjustments[MAX_ADJUSTMENT_RANGE_COUNT];
86 STATIC_UNIT_TESTED int continuosAdjustmentCount;
87 STATIC_UNIT_TESTED continuosAdjustmentState_t continuosAdjustments[MAX_ADJUSTMENT_RANGE_COUNT];
89 static void blackboxLogInflightAdjustmentEvent(adjustmentFunction_e adjustmentFunction, int32_t newValue)
91 #ifndef USE_BLACKBOX
92 UNUSED(adjustmentFunction);
93 UNUSED(newValue);
94 #else
95 if (blackboxConfig()->device) {
96 flightLogEvent_inflightAdjustment_t eventData;
97 eventData.adjustmentFunction = adjustmentFunction;
98 eventData.newValue = newValue;
99 eventData.floatFlag = false;
100 blackboxLogEvent(FLIGHT_LOG_EVENT_INFLIGHT_ADJUSTMENT, (flightLogEventData_t*)&eventData);
102 #endif
105 // sync with adjustmentFunction_e
106 static const adjustmentConfig_t defaultAdjustmentConfigs[ADJUSTMENT_FUNCTION_COUNT - 1] = {
108 .adjustmentFunction = ADJUSTMENT_RC_RATE,
109 .mode = ADJUSTMENT_MODE_STEP,
110 .data = { .step = 1 }
111 }, {
112 .adjustmentFunction = ADJUSTMENT_RC_EXPO,
113 .mode = ADJUSTMENT_MODE_STEP,
114 .data = { .step = 1 }
115 }, {
116 .adjustmentFunction = ADJUSTMENT_THROTTLE_EXPO,
117 .mode = ADJUSTMENT_MODE_STEP,
118 .data = { .step = 1 }
119 }, {
120 .adjustmentFunction = ADJUSTMENT_PITCH_ROLL_RATE,
121 .mode = ADJUSTMENT_MODE_STEP,
122 .data = { .step = 1 }
123 }, {
124 .adjustmentFunction = ADJUSTMENT_YAW_RATE,
125 .mode = ADJUSTMENT_MODE_STEP,
126 .data = { .step = 1 }
127 }, {
128 .adjustmentFunction = ADJUSTMENT_PITCH_ROLL_P,
129 .mode = ADJUSTMENT_MODE_STEP,
130 .data = { .step = 1 }
131 }, {
132 .adjustmentFunction = ADJUSTMENT_PITCH_ROLL_I,
133 .mode = ADJUSTMENT_MODE_STEP,
134 .data = { .step = 1 }
135 }, {
136 .adjustmentFunction = ADJUSTMENT_PITCH_ROLL_D,
137 .mode = ADJUSTMENT_MODE_STEP,
138 .data = { .step = 1 }
139 }, {
140 .adjustmentFunction = ADJUSTMENT_YAW_P,
141 .mode = ADJUSTMENT_MODE_STEP,
142 .data = { .step = 1 }
143 }, {
144 .adjustmentFunction = ADJUSTMENT_YAW_I,
145 .mode = ADJUSTMENT_MODE_STEP,
146 .data = { .step = 1 }
147 }, {
148 .adjustmentFunction = ADJUSTMENT_YAW_D,
149 .mode = ADJUSTMENT_MODE_STEP,
150 .data = { .step = 1 }
151 }, {
152 .adjustmentFunction = ADJUSTMENT_RATE_PROFILE,
153 .mode = ADJUSTMENT_MODE_SELECT,
154 .data = { .switchPositions = 3 }
155 }, {
156 .adjustmentFunction = ADJUSTMENT_PITCH_RATE,
157 .mode = ADJUSTMENT_MODE_STEP,
158 .data = { .step = 1 }
159 }, {
160 .adjustmentFunction = ADJUSTMENT_ROLL_RATE,
161 .mode = ADJUSTMENT_MODE_STEP,
162 .data = { .step = 1 }
163 }, {
164 .adjustmentFunction = ADJUSTMENT_PITCH_P,
165 .mode = ADJUSTMENT_MODE_STEP,
166 .data = { .step = 1 }
167 }, {
168 .adjustmentFunction = ADJUSTMENT_PITCH_I,
169 .mode = ADJUSTMENT_MODE_STEP,
170 .data = { .step = 1 }
171 }, {
172 .adjustmentFunction = ADJUSTMENT_PITCH_D,
173 .mode = ADJUSTMENT_MODE_STEP,
174 .data = { .step = 1 }
175 }, {
176 .adjustmentFunction = ADJUSTMENT_ROLL_P,
177 .mode = ADJUSTMENT_MODE_STEP,
178 .data = { .step = 1 }
179 }, {
180 .adjustmentFunction = ADJUSTMENT_ROLL_I,
181 .mode = ADJUSTMENT_MODE_STEP,
182 .data = { .step = 1 }
183 }, {
184 .adjustmentFunction = ADJUSTMENT_ROLL_D,
185 .mode = ADJUSTMENT_MODE_STEP,
186 .data = { .step = 1 }
187 }, {
188 .adjustmentFunction = ADJUSTMENT_RC_RATE_YAW,
189 .mode = ADJUSTMENT_MODE_STEP,
190 .data = { .step = 1 }
191 }, {
192 .adjustmentFunction = ADJUSTMENT_PITCH_ROLL_F,
193 .mode = ADJUSTMENT_MODE_STEP,
194 .data = { .step = 1 }
195 }, {
196 .adjustmentFunction = ADJUSTMENT_FEEDFORWARD_TRANSITION,
197 .mode = ADJUSTMENT_MODE_STEP,
198 .data = { .step = 1 }
199 }, {
200 .adjustmentFunction = ADJUSTMENT_HORIZON_STRENGTH,
201 .mode = ADJUSTMENT_MODE_SELECT,
202 .data = { .switchPositions = 255 }
203 }, {
204 .adjustmentFunction = ADJUSTMENT_PID_AUDIO,
205 .mode = ADJUSTMENT_MODE_SELECT,
206 .data = { .switchPositions = ARRAYLEN(pidAudioPositionToModeMap) }
207 }, {
208 .adjustmentFunction = ADJUSTMENT_PITCH_F,
209 .mode = ADJUSTMENT_MODE_STEP,
210 .data = { .step = 1 }
211 }, {
212 .adjustmentFunction = ADJUSTMENT_ROLL_F,
213 .mode = ADJUSTMENT_MODE_STEP,
214 .data = { .step = 1 }
215 }, {
216 .adjustmentFunction = ADJUSTMENT_YAW_F,
217 .mode = ADJUSTMENT_MODE_STEP,
218 .data = { .step = 1 }
219 }, {
220 .adjustmentFunction = ADJUSTMENT_OSD_PROFILE,
221 .mode = ADJUSTMENT_MODE_SELECT,
222 .data = { .switchPositions = 3 }
223 }, {
224 .adjustmentFunction = ADJUSTMENT_LED_PROFILE,
225 .mode = ADJUSTMENT_MODE_SELECT,
226 .data = { .switchPositions = 3 }
230 #if defined(USE_OSD) && defined(USE_OSD_ADJUSTMENTS)
231 static const char * const adjustmentLabels[] = {
232 "RC RATE",
233 "RC EXPO",
234 "THROTTLE EXPO",
235 "ROLL RATE",
236 "YAW RATE",
237 "PITCH/ROLL P",
238 "PITCH/ROLL I",
239 "PITCH/ROLL D",
240 "YAW P",
241 "YAW I",
242 "YAW D",
243 "RATE PROFILE",
244 "PITCH RATE",
245 "ROLL RATE",
246 "PITCH P",
247 "PITCH I",
248 "PITCH D",
249 "ROLL P",
250 "ROLL I",
251 "ROLL D",
252 "RC RATE YAW",
253 "PITCH/ROLL F",
254 "FF TRANSITION",
255 "HORIZON STRENGTH",
256 "ROLL RC RATE",
257 "PITCH RC RATE",
258 "ROLL RC EXPO",
259 "PITCH RC EXPO",
260 "PID AUDIO",
261 "PITCH F",
262 "ROLL F",
263 "YAW F",
264 "OSD PROFILE",
267 static int adjustmentRangeNameIndex = 0;
268 static int adjustmentRangeValue = -1;
269 #endif
271 static int applyStepAdjustment(controlRateConfig_t *controlRateConfig, uint8_t adjustmentFunction, int delta)
273 beeperConfirmationBeeps(delta > 0 ? 2 : 1);
274 int newValue;
275 switch (adjustmentFunction) {
276 case ADJUSTMENT_RC_RATE:
277 case ADJUSTMENT_ROLL_RC_RATE:
278 newValue = constrain((int)controlRateConfig->rcRates[FD_ROLL] + delta, 1, CONTROL_RATE_CONFIG_RC_RATES_MAX);
279 controlRateConfig->rcRates[FD_ROLL] = newValue;
280 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_RC_RATE, newValue);
281 if (adjustmentFunction == ADJUSTMENT_ROLL_RC_RATE) {
282 break;
284 // fall through for combined ADJUSTMENT_RC_EXPO
285 FALLTHROUGH;
286 case ADJUSTMENT_PITCH_RC_RATE:
287 newValue = constrain((int)controlRateConfig->rcRates[FD_PITCH] + delta, 1, CONTROL_RATE_CONFIG_RC_RATES_MAX);
288 controlRateConfig->rcRates[FD_PITCH] = newValue;
289 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_RC_RATE, newValue);
290 break;
291 case ADJUSTMENT_RC_EXPO:
292 case ADJUSTMENT_ROLL_RC_EXPO:
293 newValue = constrain((int)controlRateConfig->rcExpo[FD_ROLL] + delta, 0, CONTROL_RATE_CONFIG_RC_EXPO_MAX);
294 controlRateConfig->rcExpo[FD_ROLL] = newValue;
295 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_RC_EXPO, newValue);
296 if (adjustmentFunction == ADJUSTMENT_ROLL_RC_EXPO) {
297 break;
299 // fall through for combined ADJUSTMENT_RC_EXPO
300 FALLTHROUGH;
301 case ADJUSTMENT_PITCH_RC_EXPO:
302 newValue = constrain((int)controlRateConfig->rcExpo[FD_PITCH] + delta, 0, CONTROL_RATE_CONFIG_RC_EXPO_MAX);
303 controlRateConfig->rcExpo[FD_PITCH] = newValue;
304 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_RC_EXPO, newValue);
305 break;
306 case ADJUSTMENT_THROTTLE_EXPO:
307 newValue = constrain((int)controlRateConfig->thrExpo8 + delta, 0, 100); // FIXME magic numbers repeated in cli.c
308 controlRateConfig->thrExpo8 = newValue;
309 initRcProcessing();
310 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_THROTTLE_EXPO, newValue);
311 break;
312 case ADJUSTMENT_PITCH_ROLL_RATE:
313 case ADJUSTMENT_PITCH_RATE:
314 newValue = constrain((int)controlRateConfig->rates[FD_PITCH] + delta, 0, CONTROL_RATE_CONFIG_RATE_MAX);
315 controlRateConfig->rates[FD_PITCH] = newValue;
316 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_RATE, newValue);
317 if (adjustmentFunction == ADJUSTMENT_PITCH_RATE) {
318 break;
320 // fall through for combined ADJUSTMENT_PITCH_ROLL_RATE
321 FALLTHROUGH;
322 case ADJUSTMENT_ROLL_RATE:
323 newValue = constrain((int)controlRateConfig->rates[FD_ROLL] + delta, 0, CONTROL_RATE_CONFIG_RATE_MAX);
324 controlRateConfig->rates[FD_ROLL] = newValue;
325 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_RATE, newValue);
326 break;
327 case ADJUSTMENT_YAW_RATE:
328 newValue = constrain((int)controlRateConfig->rates[FD_YAW] + delta, 0, CONTROL_RATE_CONFIG_RATE_MAX);
329 controlRateConfig->rates[FD_YAW] = newValue;
330 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_RATE, newValue);
331 break;
332 case ADJUSTMENT_PITCH_ROLL_P:
333 case ADJUSTMENT_PITCH_P:
334 newValue = constrain((int)currentPidProfile->pid[PID_PITCH].P + delta, 0, 200); // FIXME magic numbers repeated in cli.c
335 currentPidProfile->pid[PID_PITCH].P = newValue;
336 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_P, newValue);
338 if (adjustmentFunction == ADJUSTMENT_PITCH_P) {
339 break;
341 // fall through for combined ADJUSTMENT_PITCH_ROLL_P
342 FALLTHROUGH;
343 case ADJUSTMENT_ROLL_P:
344 newValue = constrain((int)currentPidProfile->pid[PID_ROLL].P + delta, 0, 200); // FIXME magic numbers repeated in cli.c
345 currentPidProfile->pid[PID_ROLL].P = newValue;
346 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_P, newValue);
347 break;
348 case ADJUSTMENT_PITCH_ROLL_I:
349 case ADJUSTMENT_PITCH_I:
350 newValue = constrain((int)currentPidProfile->pid[PID_PITCH].I + delta, 0, 200); // FIXME magic numbers repeated in cli.c
351 currentPidProfile->pid[PID_PITCH].I = newValue;
352 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_I, newValue);
353 if (adjustmentFunction == ADJUSTMENT_PITCH_I) {
354 break;
356 // fall through for combined ADJUSTMENT_PITCH_ROLL_I
357 FALLTHROUGH;
358 case ADJUSTMENT_ROLL_I:
359 newValue = constrain((int)currentPidProfile->pid[PID_ROLL].I + delta, 0, 200); // FIXME magic numbers repeated in cli.c
360 currentPidProfile->pid[PID_ROLL].I = newValue;
361 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_I, newValue);
362 break;
363 case ADJUSTMENT_PITCH_ROLL_D:
364 case ADJUSTMENT_PITCH_D:
365 newValue = constrain((int)currentPidProfile->pid[PID_PITCH].D + delta, 0, 200); // FIXME magic numbers repeated in cli.c
366 currentPidProfile->pid[PID_PITCH].D = newValue;
367 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_D, newValue);
368 if (adjustmentFunction == ADJUSTMENT_PITCH_D) {
369 break;
371 // fall through for combined ADJUSTMENT_PITCH_ROLL_D
372 FALLTHROUGH;
373 case ADJUSTMENT_ROLL_D:
374 newValue = constrain((int)currentPidProfile->pid[PID_ROLL].D + delta, 0, 200); // FIXME magic numbers repeated in cli.c
375 currentPidProfile->pid[PID_ROLL].D = newValue;
376 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_D, newValue);
377 break;
378 case ADJUSTMENT_YAW_P:
379 newValue = constrain((int)currentPidProfile->pid[PID_YAW].P + delta, 0, 200); // FIXME magic numbers repeated in cli.c
380 currentPidProfile->pid[PID_YAW].P = newValue;
381 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_P, newValue);
382 break;
383 case ADJUSTMENT_YAW_I:
384 newValue = constrain((int)currentPidProfile->pid[PID_YAW].I + delta, 0, 200); // FIXME magic numbers repeated in cli.c
385 currentPidProfile->pid[PID_YAW].I = newValue;
386 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_I, newValue);
387 break;
388 case ADJUSTMENT_YAW_D:
389 newValue = constrain((int)currentPidProfile->pid[PID_YAW].D + delta, 0, 200); // FIXME magic numbers repeated in cli.c
390 currentPidProfile->pid[PID_YAW].D = newValue;
391 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_D, newValue);
392 break;
393 case ADJUSTMENT_RC_RATE_YAW:
394 newValue = constrain((int)controlRateConfig->rcRates[FD_YAW] + delta, 1, CONTROL_RATE_CONFIG_RC_RATES_MAX);
395 controlRateConfig->rcRates[FD_YAW] = newValue;
396 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_RC_RATE_YAW, newValue);
397 break;
398 case ADJUSTMENT_PITCH_ROLL_F:
399 case ADJUSTMENT_PITCH_F:
400 newValue = constrain(currentPidProfile->pid[PID_PITCH].F + delta, 0, 2000);
401 currentPidProfile->pid[PID_PITCH].F = newValue;
402 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_F, newValue);
404 if (adjustmentFunction == ADJUSTMENT_PITCH_F) {
405 break;
407 // fall through for combined ADJUSTMENT_PITCH_ROLL_F
408 FALLTHROUGH;
409 case ADJUSTMENT_ROLL_F:
410 newValue = constrain(currentPidProfile->pid[PID_ROLL].F + delta, 0, 2000);
411 currentPidProfile->pid[PID_ROLL].F = newValue;
412 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_F, newValue);
413 break;
414 case ADJUSTMENT_YAW_F:
415 newValue = constrain(currentPidProfile->pid[PID_YAW].F + delta, 0, 2000);
416 currentPidProfile->pid[PID_YAW].F = newValue;
417 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_F, newValue);
418 break;
419 #if defined(USE_FEEDFORWARD)
420 case ADJUSTMENT_FEEDFORWARD_TRANSITION:
421 newValue = constrain(currentPidProfile->feedforward_transition + delta, 1, 100); // FIXME magic numbers repeated in cli.c
422 currentPidProfile->feedforward_transition = newValue;
423 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_FEEDFORWARD_TRANSITION, newValue);
424 break;
425 #endif
426 default:
427 newValue = -1;
428 break;
431 return newValue;
434 static int applyAbsoluteAdjustment(controlRateConfig_t *controlRateConfig, adjustmentFunction_e adjustmentFunction, int value)
436 int newValue;
438 switch (adjustmentFunction) {
439 case ADJUSTMENT_RC_RATE:
440 case ADJUSTMENT_ROLL_RC_RATE:
441 newValue = constrain(value, 1, CONTROL_RATE_CONFIG_RC_RATES_MAX);
442 controlRateConfig->rcRates[FD_ROLL] = newValue;
443 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_RC_RATE, newValue);
444 if (adjustmentFunction == ADJUSTMENT_ROLL_RC_RATE) {
445 break;
447 // fall through for combined ADJUSTMENT_RC_EXPO
448 FALLTHROUGH;
449 case ADJUSTMENT_PITCH_RC_RATE:
450 newValue = constrain(value, 1, CONTROL_RATE_CONFIG_RC_RATES_MAX);
451 controlRateConfig->rcRates[FD_PITCH] = newValue;
452 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_RC_RATE, newValue);
453 break;
454 case ADJUSTMENT_RC_EXPO:
455 case ADJUSTMENT_ROLL_RC_EXPO:
456 newValue = constrain(value, 1, CONTROL_RATE_CONFIG_RC_EXPO_MAX);
457 controlRateConfig->rcExpo[FD_ROLL] = newValue;
458 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_RC_EXPO, newValue);
459 if (adjustmentFunction == ADJUSTMENT_ROLL_RC_EXPO) {
460 break;
462 // fall through for combined ADJUSTMENT_RC_EXPO
463 FALLTHROUGH;
464 case ADJUSTMENT_PITCH_RC_EXPO:
465 newValue = constrain(value, 0, CONTROL_RATE_CONFIG_RC_EXPO_MAX);
466 controlRateConfig->rcExpo[FD_PITCH] = newValue;
467 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_RC_EXPO, newValue);
468 break;
469 case ADJUSTMENT_THROTTLE_EXPO:
470 newValue = constrain(value, 0, 100); // FIXME magic numbers repeated in cli.c
471 controlRateConfig->thrExpo8 = newValue;
472 initRcProcessing();
473 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_THROTTLE_EXPO, newValue);
474 break;
475 case ADJUSTMENT_PITCH_ROLL_RATE:
476 case ADJUSTMENT_PITCH_RATE:
477 newValue = constrain(value, 0, CONTROL_RATE_CONFIG_RATE_MAX);
478 controlRateConfig->rates[FD_PITCH] = newValue;
479 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_RATE, newValue);
480 if (adjustmentFunction == ADJUSTMENT_PITCH_RATE) {
481 break;
483 // fall through for combined ADJUSTMENT_PITCH_ROLL_RATE
484 FALLTHROUGH;
485 case ADJUSTMENT_ROLL_RATE:
486 newValue = constrain(value, 0, CONTROL_RATE_CONFIG_RATE_MAX);
487 controlRateConfig->rates[FD_ROLL] = newValue;
488 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_RATE, newValue);
489 break;
490 case ADJUSTMENT_YAW_RATE:
491 newValue = constrain(value, 0, CONTROL_RATE_CONFIG_RATE_MAX);
492 controlRateConfig->rates[FD_YAW] = newValue;
493 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_RATE, newValue);
494 break;
495 case ADJUSTMENT_PITCH_ROLL_P:
496 case ADJUSTMENT_PITCH_P:
497 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
498 currentPidProfile->pid[PID_PITCH].P = newValue;
499 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_P, newValue);
501 if (adjustmentFunction == ADJUSTMENT_PITCH_P) {
502 break;
504 // fall through for combined ADJUSTMENT_PITCH_ROLL_P
505 FALLTHROUGH;
506 case ADJUSTMENT_ROLL_P:
507 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
508 currentPidProfile->pid[PID_ROLL].P = newValue;
509 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_P, newValue);
510 break;
511 case ADJUSTMENT_PITCH_ROLL_I:
512 case ADJUSTMENT_PITCH_I:
513 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
514 currentPidProfile->pid[PID_PITCH].I = newValue;
515 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_I, newValue);
516 if (adjustmentFunction == ADJUSTMENT_PITCH_I) {
517 break;
519 // fall through for combined ADJUSTMENT_PITCH_ROLL_I
520 FALLTHROUGH;
521 case ADJUSTMENT_ROLL_I:
522 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
523 currentPidProfile->pid[PID_ROLL].I = newValue;
524 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_I, newValue);
525 break;
526 case ADJUSTMENT_PITCH_ROLL_D:
527 case ADJUSTMENT_PITCH_D:
528 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
529 currentPidProfile->pid[PID_PITCH].D = newValue;
530 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_D, newValue);
531 if (adjustmentFunction == ADJUSTMENT_PITCH_D) {
532 break;
534 // fall through for combined ADJUSTMENT_PITCH_ROLL_D
535 FALLTHROUGH;
536 case ADJUSTMENT_ROLL_D:
537 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
538 currentPidProfile->pid[PID_ROLL].D = newValue;
539 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_D, newValue);
540 break;
541 case ADJUSTMENT_YAW_P:
542 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
543 currentPidProfile->pid[PID_YAW].P = newValue;
544 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_P, newValue);
545 break;
546 case ADJUSTMENT_YAW_I:
547 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
548 currentPidProfile->pid[PID_YAW].I = newValue;
549 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_I, newValue);
550 break;
551 case ADJUSTMENT_YAW_D:
552 newValue = constrain(value, 0, 200); // FIXME magic numbers repeated in cli.c
553 currentPidProfile->pid[PID_YAW].D = newValue;
554 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_D, newValue);
555 break;
556 case ADJUSTMENT_RC_RATE_YAW:
557 newValue = constrain(value, 1, CONTROL_RATE_CONFIG_RC_RATES_MAX);
558 controlRateConfig->rcRates[FD_YAW] = newValue;
559 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_RC_RATE_YAW, newValue);
560 break;
561 case ADJUSTMENT_PITCH_ROLL_F:
562 case ADJUSTMENT_PITCH_F:
563 newValue = constrain(value, 0, 2000);
564 currentPidProfile->pid[PID_PITCH].F = newValue;
565 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_PITCH_F, newValue);
567 if (adjustmentFunction == ADJUSTMENT_PITCH_F) {
568 break;
570 // fall through for combined ADJUSTMENT_PITCH_ROLL_F
571 FALLTHROUGH;
572 case ADJUSTMENT_ROLL_F:
573 newValue = constrain(value, 0, 2000);
574 currentPidProfile->pid[PID_ROLL].F = newValue;
575 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_ROLL_F, newValue);
576 break;
577 case ADJUSTMENT_YAW_F:
578 newValue = constrain(value, 0, 2000);
579 currentPidProfile->pid[PID_YAW].F = newValue;
580 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_YAW_F, newValue);
581 break;
582 #if defined(USE_FEEDFORWARD)
583 case ADJUSTMENT_FEEDFORWARD_TRANSITION:
584 newValue = constrain(value, 1, 100); // FIXME magic numbers repeated in cli.c
585 currentPidProfile->feedforward_transition = newValue;
586 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_FEEDFORWARD_TRANSITION, newValue);
587 break;
588 #endif
589 default:
590 newValue = -1;
591 break;
594 return newValue;
597 static uint8_t applySelectAdjustment(adjustmentFunction_e adjustmentFunction, uint8_t position)
599 uint8_t beeps = 0;
601 switch (adjustmentFunction) {
602 case ADJUSTMENT_RATE_PROFILE:
603 if (getCurrentControlRateProfileIndex() != position) {
604 changeControlRateProfile(position);
605 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_RATE_PROFILE, position);
607 beeps = position + 1;
609 break;
610 case ADJUSTMENT_HORIZON_STRENGTH:
612 uint8_t newValue = constrain(position, 0, 200); // FIXME magic numbers repeated in serial_cli.c
613 if (currentPidProfile->pid[PID_LEVEL].D != newValue) {
614 beeps = ((newValue - currentPidProfile->pid[PID_LEVEL].D) / 8) + 1;
615 currentPidProfile->pid[PID_LEVEL].D = newValue;
616 blackboxLogInflightAdjustmentEvent(ADJUSTMENT_HORIZON_STRENGTH, position);
619 break;
620 case ADJUSTMENT_PID_AUDIO:
621 #ifdef USE_PID_AUDIO
623 pidAudioModes_e newMode = pidAudioPositionToModeMap[position];
624 if (newMode != pidAudioGetMode()) {
625 pidAudioSetMode(newMode);
628 #endif
629 break;
630 case ADJUSTMENT_OSD_PROFILE:
631 #ifdef USE_OSD_PROFILES
632 if (getCurrentOsdProfileIndex() != (position + 1)) {
633 changeOsdProfileIndex(position+1);
635 #endif
636 break;
637 case ADJUSTMENT_LED_PROFILE:
638 #ifdef USE_LED_STRIP
639 if (getLedProfile() != position) {
640 setLedProfile(position);
642 #endif
643 break;
645 default:
646 break;
649 if (beeps) {
650 beeperConfirmationBeeps(beeps);
653 return position;
656 #define ADJUSTMENT_FUNCTION_CONFIG_INDEX_OFFSET 1
658 static void calcActiveAdjustmentRanges(void)
660 adjustmentRange_t defaultAdjustmentRange;
661 memset(&defaultAdjustmentRange, 0, sizeof(defaultAdjustmentRange));
663 stepwiseAdjustmentCount = 0;
664 continuosAdjustmentCount = 0;
665 for (int i = 0; i < MAX_ADJUSTMENT_RANGE_COUNT; i++) {
666 const adjustmentRange_t * const adjustmentRange = adjustmentRanges(i);
667 if (memcmp(adjustmentRange, &defaultAdjustmentRange, sizeof(defaultAdjustmentRange)) != 0) {
668 const adjustmentConfig_t *adjustmentConfig = &defaultAdjustmentConfigs[adjustmentRange->adjustmentConfig - ADJUSTMENT_FUNCTION_CONFIG_INDEX_OFFSET];
669 if (adjustmentRange->adjustmentCenter == 0 && adjustmentConfig->mode != ADJUSTMENT_MODE_SELECT) {
670 timedAdjustmentState_t *adjustmentState = &stepwiseAdjustments[stepwiseAdjustmentCount++];
671 adjustmentState->adjustmentRangeIndex = i;
672 adjustmentState->timeoutAt = 0;
673 adjustmentState->ready = true;
674 } else {
675 continuosAdjustmentState_t *adjustmentState = &continuosAdjustments[continuosAdjustmentCount++];
676 adjustmentState->adjustmentRangeIndex = i;
677 adjustmentState->lastRcData = 0;
683 #define VALUE_DISPLAY_LATENCY_MS 2000
685 #if defined(USE_OSD) && defined(USE_OSD_ADJUSTMENTS)
686 static void updateOsdAdjustmentData(int newValue, adjustmentFunction_e adjustmentFunction)
688 static timeMs_t lastValueChangeMs;
690 timeMs_t currentTimeMs = millis();
691 if (newValue != -1
692 && adjustmentFunction != ADJUSTMENT_RATE_PROFILE // Rate profile already has an OSD element
693 #ifdef USE_OSD_PROFILES
694 && adjustmentFunction != ADJUSTMENT_OSD_PROFILE
695 #endif
697 adjustmentRangeNameIndex = adjustmentFunction;
698 adjustmentRangeValue = newValue;
700 lastValueChangeMs = currentTimeMs;
703 if (cmp32(currentTimeMs, lastValueChangeMs + VALUE_DISPLAY_LATENCY_MS) >= 0) {
704 adjustmentRangeNameIndex = 0;
707 #endif
709 #define RESET_FREQUENCY_2HZ (1000 / 2)
711 static void processStepwiseAdjustments(controlRateConfig_t *controlRateConfig, const bool canUseRxData)
713 const timeMs_t now = millis();
715 for (int index = 0; index < stepwiseAdjustmentCount; index++) {
716 timedAdjustmentState_t *adjustmentState = &stepwiseAdjustments[index];
717 const adjustmentRange_t *const adjustmentRange = adjustmentRanges(adjustmentState->adjustmentRangeIndex);
718 const adjustmentConfig_t *adjustmentConfig = &defaultAdjustmentConfigs[adjustmentRange->adjustmentConfig - ADJUSTMENT_FUNCTION_CONFIG_INDEX_OFFSET];
719 const adjustmentFunction_e adjustmentFunction = adjustmentConfig->adjustmentFunction;
721 if (!isRangeActive(adjustmentRange->auxChannelIndex, &adjustmentRange->range) ||
722 adjustmentFunction == ADJUSTMENT_NONE) {
723 adjustmentState->timeoutAt = 0;
725 continue;
728 if (cmp32(now, adjustmentState->timeoutAt) >= 0) {
729 adjustmentState->timeoutAt = now + RESET_FREQUENCY_2HZ;
730 adjustmentState->ready = true;
733 if (!canUseRxData) {
734 continue;
737 const uint8_t channelIndex = NON_AUX_CHANNEL_COUNT + adjustmentRange->auxSwitchChannelIndex;
739 if (adjustmentConfig->mode == ADJUSTMENT_MODE_STEP) {
740 int delta;
741 if (rcData[channelIndex] > rxConfig()->midrc + 200) {
742 delta = adjustmentConfig->data.step;
743 } else if (rcData[channelIndex] < rxConfig()->midrc - 200) {
744 delta = -adjustmentConfig->data.step;
745 } else {
746 // returning the switch to the middle immediately resets the ready state
747 adjustmentState->ready = true;
748 adjustmentState->timeoutAt = now + RESET_FREQUENCY_2HZ;
749 continue;
751 if (!adjustmentState->ready) {
752 continue;
755 int newValue = applyStepAdjustment(controlRateConfig, adjustmentFunction, delta);
757 setConfigDirty();
759 pidInitConfig(currentPidProfile);
761 adjustmentState->ready = false;
763 #if defined(USE_OSD) && defined(USE_OSD_ADJUSTMENTS)
764 updateOsdAdjustmentData(newValue, adjustmentConfig->adjustmentFunction);
765 #else
766 UNUSED(newValue);
767 #endif
772 static void setConfigDirtyIfNotPermanent(const channelRange_t *range)
774 if (!(range->startStep == MIN_MODE_RANGE_STEP && range->endStep == MAX_MODE_RANGE_STEP)) {
775 // Only set the configuration dirty if this range is NOT permanently enabled (and the config thus never used).
776 setConfigDirty();
780 static void processContinuosAdjustments(controlRateConfig_t *controlRateConfig)
782 for (int i = 0; i < continuosAdjustmentCount; i++) {
783 continuosAdjustmentState_t *adjustmentState = &continuosAdjustments[i];
784 const adjustmentRange_t * const adjustmentRange = adjustmentRanges(adjustmentState->adjustmentRangeIndex);
785 const uint8_t channelIndex = NON_AUX_CHANNEL_COUNT + adjustmentRange->auxSwitchChannelIndex;
786 const adjustmentConfig_t *adjustmentConfig = &defaultAdjustmentConfigs[adjustmentRange->adjustmentConfig - ADJUSTMENT_FUNCTION_CONFIG_INDEX_OFFSET];
787 const adjustmentFunction_e adjustmentFunction = adjustmentConfig->adjustmentFunction;
789 if (isRangeActive(adjustmentRange->auxChannelIndex, &adjustmentRange->range) &&
790 adjustmentFunction != ADJUSTMENT_NONE) {
792 if (rcData[channelIndex] != adjustmentState->lastRcData) {
793 int newValue = -1;
795 if (adjustmentConfig->mode == ADJUSTMENT_MODE_SELECT) {
796 int switchPositions = adjustmentConfig->data.switchPositions;
797 if (adjustmentFunction == ADJUSTMENT_RATE_PROFILE && systemConfig()->rateProfile6PosSwitch) {
798 switchPositions = 6;
800 const uint16_t rangeWidth = (2100 - 900) / switchPositions;
801 const uint8_t position = (constrain(rcData[channelIndex], 900, 2100 - 1) - 900) / rangeWidth;
802 newValue = applySelectAdjustment(adjustmentFunction, position);
804 setConfigDirtyIfNotPermanent(&adjustmentRange->range);
805 } else {
806 // If setting is defined for step adjustment and center value has been specified, apply values directly (scaled) from aux channel
807 if (adjustmentRange->adjustmentCenter &&
808 (adjustmentConfig->mode == ADJUSTMENT_MODE_STEP)) {
809 int value = (((rcData[channelIndex] - PWM_RANGE_MIDDLE) * adjustmentRange->adjustmentScale) / (PWM_RANGE_MIDDLE - PWM_RANGE_MIN)) + adjustmentRange->adjustmentCenter;
811 newValue = applyAbsoluteAdjustment(controlRateConfig, adjustmentFunction, value);
813 setConfigDirtyIfNotPermanent(&adjustmentRange->range);
815 pidInitConfig(currentPidProfile);
818 #if defined(USE_OSD) && defined(USE_OSD_ADJUSTMENTS)
819 updateOsdAdjustmentData(newValue, adjustmentConfig->adjustmentFunction);
820 #else
821 UNUSED(newValue);
822 #endif
823 adjustmentState->lastRcData = rcData[channelIndex];
825 } else {
826 adjustmentState->lastRcData = 0;
831 void processRcAdjustments(controlRateConfig_t *controlRateConfig)
833 const bool canUseRxData = rxIsReceivingSignal();
835 // Recalculate the new active adjustments if required
836 if (stepwiseAdjustmentCount == ADJUSTMENT_RANGE_COUNT_INVALID) {
837 calcActiveAdjustmentRanges();
840 processStepwiseAdjustments(controlRateConfig, canUseRxData);
842 if (canUseRxData) {
843 processContinuosAdjustments(controlRateConfig);
846 #if defined(USE_OSD) && defined(USE_OSD_ADJUSTMENTS)
847 // Hide the element if there is no change
848 updateOsdAdjustmentData(-1, 0);
849 #endif
852 #if defined(USE_OSD) && defined(USE_OSD_ADJUSTMENTS)
853 const char *getAdjustmentsRangeName(void)
855 if (adjustmentRangeNameIndex > 0) {
856 return &adjustmentLabels[adjustmentRangeNameIndex - 1][0];
857 } else {
858 return NULL;
862 int getAdjustmentsRangeValue(void)
864 return adjustmentRangeValue;
866 #endif
868 void activeAdjustmentRangeReset(void)
870 stepwiseAdjustmentCount = ADJUSTMENT_RANGE_COUNT_INVALID;