5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
24 int8_t virtualInputsTrims
[MAX_INPUTS
];
25 int16_t anas
[MAX_INPUTS
] = {0};
26 int16_t trims
[NUM_TRIMS
] = {0};
27 int32_t chans
[MAX_OUTPUT_CHANNELS
] = {0};
28 BeepANACenter bpanaCenter
= 0;
30 int32_t act
[MAX_MIXERS
] = {0};
31 SwOn swOn
[MAX_MIXERS
]; // TODO better name later...
36 int16_t calibratedAnalogs
[NUM_CALIBRATED_ANALOGS
];
37 int16_t channelOutputs
[MAX_OUTPUT_CHANNELS
] = {0};
38 int16_t ex_chans
[MAX_OUTPUT_CHANNELS
] = {0}; // Outputs (before LIMITS) of the last perMain;
41 int16_t cyc_anas
[3] = {0};
44 // #define EXTENDED_EXPO
45 // increases range of expo curve but costs about 82 bytes flash
50 // f(x,k)=exp(ln(x)*k/10) ;P[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
51 // f(x,k)=x*x*x*k/10 + x*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10]
52 // f(x,k)=x*x*k/10 + x*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10]
53 // f(x,k)=1+(x-1)*(x-1)*(x-1)*k/10 + (x-1)*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10]
54 // don't know what this above should be, just confusing in my opinion,
56 // here is the real explanation
57 // actually the real formula is
59 f(x) = exp( ln(x) * 10^k)
60 if it is 10^k or e^k or 2^k etc. just defines the max distortion of the expo curve; I think 10 is useful
61 this gives values from 0 to 1 for x and output; k must be between -1 and +1
62 we do not like to calculate with floating point. Therefore we rescale for x from 0 to 1024 and for k from -100 to +100
63 f(x) = 1024 * ( e^( ln(x/1024) * 10^(k/100) ) )
64 This would be really hard to be calculated by such a microcontroller
65 Therefore Thomas Husterer compared a few usual function something like x^3, x^4*something, which look similar
68 gives a similar form and should have even advantages compared to a original exp curve.
69 This function again expect x from 0 to 1 and k only from 0 to 1
70 Therefore rescaling is needed like before:
71 f(x) = 1024* ((k/100)*(x/1024)^3 + (x/1024)*(100-k)/100)
72 some mathematical tricks
73 f(x) = (k*x*x*x/(1024*1024) + x*(100-k)) / 100
74 for better rounding results we add the 50
75 f(x) = (k*x*x*x/(1024*1024) + x*(100-k) + 50) / 100
77 because we now understand the formula, we can optimize it further
78 --> calc100to256(k) --> eliminates /100 by replacing with /256 which is just a simple shift right 8
79 k is now between 0 and 256
80 f(x) = (k*x*x*x/(1024*1024) + x*(256-k) + 128) / 256
86 // output between 0 and 1024
87 unsigned int expou(unsigned int x
, unsigned int k
)
89 #if defined(EXTENDED_EXPO)
95 k
+= (k
>>2); // use bigger values before extend, because the effect is anyway very very low
102 uint32_t value
= (uint32_t) x
*x
;
103 value
*= (uint32_t)k
;
105 value
*= (uint32_t)x
;
107 #if defined(EXTENDED_EXPO)
108 if (extended
) { // for higher values do more multiplications to get a stronger expo curve
110 value
*= (uint32_t)x
;
112 value
*= (uint32_t)x
;
117 value
+= (uint32_t)(256-k
) * x
+ 128;
122 int expo(int x
, int k
)
134 if (x
> (int)RESXu
) {
138 y
= RESXu
- expou(RESXu
-x
, -k
);
146 void applyExpos(int16_t * anas
, uint8_t mode
, uint8_t ovwrIdx
, int16_t ovwrValue
)
150 for (uint8_t i
=0; i
<MAX_EXPOS
; i
++) {
151 #if defined(BOLD_FONT)
152 if (mode
==e_perout_mode_normal
) swOn
[i
].activeExpo
= false;
154 ExpoData
* ed
= expoAddress(i
);
155 if (!EXPO_VALID(ed
)) break; // end of list
156 if (ed
->chn
== cur_chn
)
158 if (ed
->flightModes
& (1<<mixerCurrentFlightMode
))
160 if (ed
->srcRaw
>= MIXSRC_FIRST_TRAINER
&& ed
->srcRaw
<= MIXSRC_LAST_TRAINER
&& !IS_TRAINER_INPUT_VALID())
162 if (getSwitch(ed
->swtch
)) {
164 if (ed
->srcRaw
== ovwrIdx
) {
168 v
= getValue(ed
->srcRaw
);
169 if (ed
->srcRaw
>= MIXSRC_FIRST_TELEM
&& ed
->scale
> 0) {
170 v
= (v
* 1024) / convertTelemValue(ed
->srcRaw
-MIXSRC_FIRST_TELEM
+1, ed
->scale
);
172 v
= limit
<int32_t>(-1024, v
, 1024);
174 if (EXPO_MODE_ENABLE(ed
, v
)) {
175 #if defined(BOLD_FONT)
176 if (mode
==e_perout_mode_normal
) swOn
[i
].activeExpo
= true;
180 //========== CURVE=================
181 if (ed
->curve
.value
) {
182 v
= applyCurve(v
, ed
->curve
);
185 //========== WEIGHT ===============
186 int32_t weight
= GET_GVAR_PREC1(ed
->weight
, MIN_EXPO_WEIGHT
, 100, mixerCurrentFlightMode
);
187 v
= div_and_round((int32_t)v
* weight
, 1000);
189 //========== OFFSET ===============
190 int32_t offset
= GET_GVAR_PREC1(ed
->offset
, -100, 100, mixerCurrentFlightMode
);
191 if (offset
) v
+= div_and_round(calc100toRESX(offset
), 10);
193 //========== TRIMS ================
194 if (ed
->carryTrim
< TRIM_ON
)
195 virtualInputsTrims
[cur_chn
] = -ed
->carryTrim
- 1;
196 else if (ed
->carryTrim
== TRIM_ON
&& ed
->srcRaw
>= MIXSRC_Rud
&& ed
->srcRaw
<= MIXSRC_Ail
)
197 virtualInputsTrims
[cur_chn
] = ed
->srcRaw
- MIXSRC_Rud
;
199 virtualInputsTrims
[cur_chn
] = -1;
206 // #define PREVENT_ARITHMETIC_OVERFLOW
207 // because of optimizations the reserves before overruns occurs is only the half
208 // this defines enables some checks the greatly improves this situation
209 // It should nearly prevent all overruns (is still a chance for it, but quite low)
210 // negative side is code cost 96 bytes flash
212 // we do it now half way, only in applyLimits, which costs currently 50bytes
213 // according opinion poll this topic is currently not very important
214 // the change below improves already the situation
215 // the check inside mixer would slow down mix a little bit and costs additionally flash
216 // also the check inside mixer still is not bulletproof, there may be still situations a overflow could occur
217 // a bulletproof implementation would take about additional 100bytes flash
218 // therefore with go with this compromize, interested people could activate this define
220 // @@@2 open.20.fsguruh ;
221 // channel = channelnumber -1;
222 // value = outputvalue with 100 mulitplied usual range -102400 to 102400; output -1024 to 1024
223 // changed rescaling from *100 to *256 to optimize performance
224 // rescaled from -262144 to 262144
225 int16_t applyLimits(uint8_t channel
, int32_t value
)
227 #if defined(OVERRIDE_CHANNEL_FUNCTION)
228 if (safetyCh
[channel
] != OVERRIDE_CHANNEL_UNDEFINED
) {
229 // safety channel available for channel check
230 return calc100toRESX(safetyCh
[channel
]);
234 if (isFunctionActive(FUNCTION_TRAINER_CHANNELS
) && IS_TRAINER_INPUT_VALID()) {
235 return ppmInput
[channel
] * 2;
238 LimitData
* lim
= limitAddress(channel
);
241 // TODO we loose precision here, applyCustomCurve could work with int32_t on ARM boards...
243 value
= 256 * applyCustomCurve(value
/256, lim
->curve
-1);
245 value
= 256 * applyCustomCurve(-value
/256, -lim
->curve
-1);
248 int16_t ofs
= LIMIT_OFS_RESX(lim
);
249 int16_t lim_p
= LIMIT_MAX_RESX(lim
);
250 int16_t lim_n
= LIMIT_MIN_RESX(lim
);
252 if (ofs
> lim_p
) ofs
= lim_p
;
253 if (ofs
< lim_n
) ofs
= lim_n
;
255 // because the rescaling optimization would reduce the calculation reserve we activate this for all builds
256 // it increases the calculation reserve from factor 20,25x to 32x, which it slightly better as original
257 // without it we would only have 16x which is slightly worse as original, we should not do this
259 // thanks to gbirkus, he motivated this change, which greatly reduces overruns
260 // unfortunately the constants and 32bit compares generates about 50 bytes codes; didn't find a way to get it down.
261 value
= limit(int32_t(-RESXl
*256), value
, int32_t(RESXl
*256)); // saves 2 bytes compared to other solutions up to now
263 #if defined(PPM_LIMITS_SYMETRICAL)
267 tmp
= (value
> 0) ? (lim_p
) : (-lim_n
);
269 tmp
= (value
> 0) ? (lim_p
- ofs
) : (-lim_n
+ ofs
);
270 value
= (int32_t) value
* tmp
; // div by 1024*256 -> output = -1024..1024
273 int16_t tmp
= (value
> 0) ? (lim_p
- ofs
) : (-lim_n
+ ofs
);
274 value
= (int32_t) value
* tmp
; // div by 1024*256 -> output = -1024..1024
277 #ifdef CORRECT_NEGATIVE_SHIFTS
278 int8_t sign
= (value
<0?1:0);
280 tmp
= value
>>16; // that's quite tricky: the shiftright 16 operation is assmbled just with addressmove; just forget the two least significant bytes;
281 tmp
>>= 2; // now one simple shift right for two bytes does the rest
284 tmp
= value
>>16; // that's quite tricky: the shiftright 16 operation is assmbled just with addressmove; just forget the two least significant bytes;
285 tmp
>>= 2; // now one simple shift right for two bytes does the rest
288 ofs
+= tmp
; // ofs can to added directly because already recalculated,
296 ofs
= -ofs
; // finally do the reverse.
301 // TODO same naming convention than the drawSource
303 getvalue_t
getValue(mixsrc_t i
)
305 if (i
== MIXSRC_NONE
) {
308 else if (i
<= MIXSRC_LAST_INPUT
) {
309 return anas
[i
-MIXSRC_FIRST_INPUT
];
311 #if defined(LUA_INPUTS)
312 else if (i
<= MIXSRC_LAST_LUA
) {
313 #if defined(LUA_MODEL_SCRIPTS)
314 div_t qr
= div(i
-MIXSRC_FIRST_LUA
, MAX_SCRIPT_OUTPUTS
);
315 return scriptInputsOutputs
[qr
.quot
].outputs
[qr
.rem
].value
;
322 else if (i
<= MIXSRC_LAST_POT
+ NUM_MOUSE_ANALOGS
) {
323 return calibratedAnalogs
[i
- MIXSRC_Rud
];
327 else if (i
== MIXSRC_GYRO1
) {
328 return gyro
.scaledX();
330 else if (i
== MIXSRC_GYRO2
) {
331 return gyro
.scaledY();
335 else if (i
== MIXSRC_MAX
) {
339 else if (i
<= MIXSRC_CYC3
) {
341 return cyc_anas
[i
- MIXSRC_CYC1
];
347 else if (i
<= MIXSRC_LAST_TRIM
) {
348 return calc1000toRESX((int16_t)8 * getTrimValue(mixerCurrentFlightMode
, i
-MIXSRC_FIRST_TRIM
));
351 #if defined(PCBTARANIS) || defined(PCBHORUS)
352 else if (i
>= MIXSRC_FIRST_SWITCH
&& i
<= MIXSRC_LAST_SWITCH
) {
353 mixsrc_t sw
= i
- MIXSRC_FIRST_SWITCH
;
354 if (SWITCH_EXISTS(sw
)) {
355 return (switchState(3*sw
) ? -1024 : (IS_CONFIG_3POS(sw
) && switchState(3*sw
+1) ? 0 : 1024));
362 else if (i
== MIXSRC_3POS
) {
363 return (getSwitch(SW_ID0
+1) ? -1024 : (getSwitch(SW_ID1
+1) ? 0 : 1024));
365 // don't use switchState directly to give getSwitch possibility to hack values if needed for switch warning
366 else if (i
< MIXSRC_SW1
) {
367 return getSwitch(SWSRC_THR
+i
-MIXSRC_THR
) ? 1024 : -1024;
371 else if (i
<= MIXSRC_LAST_LOGICAL_SWITCH
) {
372 return getSwitch(SWSRC_FIRST_LOGICAL_SWITCH
+ i
- MIXSRC_FIRST_LOGICAL_SWITCH
) ? 1024 : -1024;
374 else if (i
<= MIXSRC_LAST_TRAINER
) {
375 int16_t x
= ppmInput
[i
- MIXSRC_FIRST_TRAINER
];
376 if (i
< MIXSRC_FIRST_TRAINER
+ NUM_CAL_PPM
) {
377 x
-= g_eeGeneral
.trainer
.calib
[i
- MIXSRC_FIRST_TRAINER
];
381 else if (i
<= MIXSRC_LAST_CH
) {
382 return ex_chans
[i
- MIXSRC_CH1
];
385 else if (i
<= MIXSRC_LAST_GVAR
) {
387 return GVAR_VALUE(i
- MIXSRC_GVAR1
, getGVarFlightMode(mixerCurrentFlightMode
, i
- MIXSRC_GVAR1
));
393 else if (i
== MIXSRC_TX_VOLTAGE
) {
396 else if (i
< MIXSRC_FIRST_TIMER
) {
399 return (g_rtcTime
% SECS_PER_DAY
) / 60; // number of minutes from midnight
404 else if (i
<= MIXSRC_LAST_TIMER
) {
405 return timersStates
[i
- MIXSRC_FIRST_TIMER
].val
;
408 else if (i
<= MIXSRC_LAST_TELEM
) {
409 if (IS_FAI_FORBIDDEN(i
)) {
412 i
-= MIXSRC_FIRST_TELEM
;
413 div_t qr
= div(i
, 3);
414 TelemetryItem
& telemetryItem
= telemetryItems
[qr
.quot
];
417 return telemetryItem
.valueMin
;
419 return telemetryItem
.valueMax
;
421 return telemetryItem
.value
;
427 void evalInputs(uint8_t mode
)
429 BeepANACenter anaCenter
= 0;
431 for (uint8_t i
= 0; i
< NUM_STICKS
+ NUM_POTS
+ NUM_SLIDERS
; i
++) {
432 // normalization [0..2048] -> [-1024..1024]
433 uint8_t ch
= (i
< NUM_STICKS
? CONVERT_MODE(i
) : i
);
434 int16_t v
= anaIn(i
);
436 if (IS_POT_MULTIPOS(i
)) {
441 CalibData
* calib
= &g_eeGeneral
.calib
[i
];
443 v
= v
* (int32_t) RESX
/ (max((int16_t) 100, (v
> 0 ? calib
->spanPos
: calib
->spanNeg
)));
447 if (v
< -RESX
) v
= -RESX
;
448 if (v
> RESX
) v
= RESX
;
450 if (g_model
.throttleReversed
&& ch
==THR_STICK
) {
454 BeepANACenter mask
= (BeepANACenter
)1 << ch
;
456 calibratedAnalogs
[ch
] = v
; // for show in expo
458 // filtering for center beep
459 uint8_t tmp
= (uint16_t)abs(v
) / 16;
460 if (mode
== e_perout_mode_normal
) {
461 if (tmp
==0 || (tmp
==1 && (bpanaCenter
& mask
))) {
463 if ((g_model
.beepANACenter
& mask
) && !(bpanaCenter
& mask
) && !menuCalibrationState
) {
464 if (!IS_POT(i
) || IS_POT_SLIDER_AVAILABLE(i
)) {
471 if (ch
< NUM_STICKS
) { // only do this for sticks
472 if (mode
& e_perout_mode_nosticks
) {
476 if (mode
<= e_perout_mode_inactive_flight_mode
&& isFunctionActive(FUNCTION_TRAINER_STICK1
+ch
) && IS_TRAINER_INPUT_VALID()) {
478 TrainerMix
* td
= &g_eeGeneral
.trainer
.mix
[ch
];
480 uint8_t chStud
= td
->srcChn
;
481 int32_t vStud
= (ppmInput
[chStud
] - g_eeGeneral
.trainer
.calib
[chStud
]);
482 vStud
*= td
->studWeight
;
487 v
= limit
<int16_t>(-RESX
, v
+vStud
, RESX
);
496 calibratedAnalogs
[ch
] = v
;
500 #if NUM_MOUSE_ANALOGS > 0
501 for (uint8_t i
=0; i
<NUM_MOUSE_ANALOGS
; i
++) {
502 uint8_t ch
= NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
+i
;
503 int16_t v
= anaIn(MOUSE1
+i
);
504 CalibData
* calib
= &g_eeGeneral
.calib
[ch
];
506 v
= v
* (int32_t) RESX
/ (max((int16_t) 100, (v
> 0 ? calib
->spanPos
: calib
->spanNeg
)));
507 if (v
< -RESX
) v
= -RESX
;
508 if (v
> RESX
) v
= RESX
;
509 calibratedAnalogs
[ch
] = v
;
514 applyExpos(anas
, mode
);
517 evalTrims(); // when no virtual inputs, the trims need the anas array calculated above (when throttle trim enabled)
519 if (mode
== e_perout_mode_normal
) {
520 bpanaCenter
= anaCenter
;
524 int getStickTrimValue(int stick
, int stickValue
)
529 int trim
= trims
[stick
];
530 if (stick
== THR_STICK
) {
531 if (g_model
.thrTrim
) {
532 int trimMin
= g_model
.extendedTrims
? 2*TRIM_EXTENDED_MIN
: 2*TRIM_MIN
;
533 trim
= ((g_model
.throttleReversed
? (trim
+trimMin
) : (trim
-trimMin
)) * (RESX
-stickValue
)) >> (RESX_SHIFT
+1);
535 if (g_model
.throttleReversed
) {
542 int getSourceTrimValue(int source
, int stickValue
=0)
544 if (source
>= MIXSRC_Rud
&& source
<= MIXSRC_Ail
)
545 return getStickTrimValue(source
- MIXSRC_Rud
, stickValue
);
546 else if (source
>= MIXSRC_FIRST_INPUT
&& source
<= MIXSRC_LAST_INPUT
)
547 return getStickTrimValue(virtualInputsTrims
[source
- MIXSRC_FIRST_INPUT
], stickValue
);
552 uint8_t mixerCurrentFlightMode
;
553 void evalFlightModeMixes(uint8_t mode
, uint8_t tick10ms
)
558 evalLogicalSwitches(mode
==e_perout_mode_normal
);
561 int heliEleValue
= getValue(g_model
.swashR
.elevatorSource
);
562 int heliAilValue
= getValue(g_model
.swashR
.aileronSource
);
563 if (g_model
.swashR
.value
) {
564 uint32_t v
= ((int32_t)heliEleValue
*heliEleValue
+ (int32_t)heliAilValue
*heliAilValue
);
565 uint32_t q
= calc100toRESX(g_model
.swashR
.value
);
568 uint16_t d
= isqrt32(v
);
569 int16_t tmp
= calc100toRESX(g_model
.swashR
.value
);
570 heliEleValue
= (int32_t) heliEleValue
*tmp
/d
;
571 heliAilValue
= (int32_t) heliAilValue
*tmp
/d
;
575 #define REZ_SWASH_X(x) ((x) - (x)/8 - (x)/128 - (x)/512) // 1024*sin(60) ~= 886
576 #define REZ_SWASH_Y(x) ((x)) // 1024 => 1024
578 if (g_model
.swashR
.type
) {
579 getvalue_t vp
= heliEleValue
+ getSourceTrimValue(g_model
.swashR
.elevatorSource
);
580 getvalue_t vr
= heliAilValue
+ getSourceTrimValue(g_model
.swashR
.aileronSource
);
582 if (g_model
.swashR
.collectiveSource
)
583 vc
= getValue(g_model
.swashR
.collectiveSource
);
585 vp
= (vp
* g_model
.swashR
.elevatorWeight
) / 100;
586 vr
= (vr
* g_model
.swashR
.aileronWeight
) / 100;
587 vc
= (vc
* g_model
.swashR
.collectiveWeight
) / 100;
589 switch (g_model
.swashR
.type
) {
591 vp
= REZ_SWASH_Y(vp
);
592 vr
= REZ_SWASH_X(vr
);
593 cyc_anas
[0] = vc
- vp
;
594 cyc_anas
[1] = vc
+ vp
/2 + vr
;
595 cyc_anas
[2] = vc
+ vp
/2 - vr
;
597 case SWASH_TYPE_120X
:
598 vp
= REZ_SWASH_X(vp
);
599 vr
= REZ_SWASH_Y(vr
);
600 cyc_anas
[0] = vc
- vr
;
601 cyc_anas
[1] = vc
+ vr
/2 + vp
;
602 cyc_anas
[2] = vc
+ vr
/2 - vp
;
605 vp
= REZ_SWASH_Y(vp
);
606 vr
= REZ_SWASH_Y(vr
);
607 cyc_anas
[0] = vc
- vp
;
608 cyc_anas
[1] = vc
+ vp
+ vr
;
609 cyc_anas
[2] = vc
+ vp
- vr
;
612 vp
= REZ_SWASH_Y(vp
);
613 vr
= REZ_SWASH_Y(vr
);
614 cyc_anas
[0] = vc
- vp
;
615 cyc_anas
[1] = vc
+ vr
;
616 cyc_anas
[2] = vc
- vr
;
624 memclear(chans
, sizeof(chans
)); // all outputs to 0
626 //========== MIXER LOOP ===============
627 uint8_t lv_mixWarning
= 0;
631 bitfield_channels_t dirtyChannels
= (bitfield_channels_t
)-1; // all dirty when mixer starts
634 bitfield_channels_t passDirtyChannels
= 0;
636 for (uint8_t i
=0; i
<MAX_MIXERS
; i
++) {
637 #if defined(BOLD_FONT)
638 if (mode
== e_perout_mode_normal
&& pass
== 0)
639 swOn
[i
].activeMix
= 0;
642 MixData
* md
= mixAddress(i
);
647 mixsrc_t stickIndex
= md
->srcRaw
- MIXSRC_Rud
;
649 if (!(dirtyChannels
& ((bitfield_channels_t
)1 << md
->destCh
)))
652 // if this is the first calculation for the destination channel, initialize it with 0 (otherwise would be random)
653 if (i
== 0 || md
->destCh
!= (md
-1)->destCh
)
654 chans
[md
->destCh
] = 0;
656 //========== FLIGHT MODE && SWITCH =====
657 bool mixCondition
= (md
->flightModes
!= 0 || md
->swtch
);
658 delayval_t mixEnabled
= (!(md
->flightModes
& (1 << mixerCurrentFlightMode
)) && getSwitch(md
->swtch
)) ? DELAY_POS_MARGIN
+1 : 0;
660 #define MIXER_LINE_DISABLE() (mixCondition = true, mixEnabled = 0)
662 if (mixEnabled
&& md
->srcRaw
>= MIXSRC_FIRST_TRAINER
&& md
->srcRaw
<= MIXSRC_LAST_TRAINER
&& !IS_TRAINER_INPUT_VALID()) {
663 MIXER_LINE_DISABLE();
666 #if defined(LUA_MODEL_SCRIPTS)
667 // disable mixer if Lua script is used as source and script was killed
668 if (mixEnabled
&& md
->srcRaw
>= MIXSRC_FIRST_LUA
&& md
->srcRaw
<= MIXSRC_LAST_LUA
) {
669 div_t qr
= div(md
->srcRaw
-MIXSRC_FIRST_LUA
, MAX_SCRIPT_OUTPUTS
);
670 if (scriptInternalData
[qr
.quot
].state
!= SCRIPT_OK
) {
671 MIXER_LINE_DISABLE();
676 //========== VALUE ===============
678 if (mode
> e_perout_mode_inactive_flight_mode
) {
680 v
= getValue(md
->srcRaw
);
685 mixsrc_t srcRaw
= MIXSRC_Rud
+ stickIndex
;
686 v
= getValue(srcRaw
);
687 srcRaw
-= MIXSRC_CH1
;
688 if (srcRaw
<= MIXSRC_LAST_CH
-MIXSRC_CH1
&& md
->destCh
!= srcRaw
) {
689 if (dirtyChannels
& ((bitfield_channels_t
)1 << srcRaw
) & (passDirtyChannels
|~(((bitfield_channels_t
) 1 << md
->destCh
)-1)))
690 passDirtyChannels
|= (bitfield_channels_t
) 1 << md
->destCh
;
691 if (srcRaw
< md
->destCh
|| pass
> 0)
692 v
= chans
[srcRaw
] >> 8;
699 bool applyOffsetAndCurve
= true;
701 //========== DELAYS ===============
702 delayval_t _swOn
= swOn
[i
].now
;
703 delayval_t _swPrev
= swOn
[i
].prev
;
704 bool swTog
= (mixEnabled
> _swOn
+DELAY_POS_MARGIN
|| mixEnabled
< _swOn
-DELAY_POS_MARGIN
);
705 if (mode
== e_perout_mode_normal
&& swTog
) {
708 swOn
[i
].delay
= (mixEnabled
> _swOn
? md
->delayUp
: md
->delayDown
) * 10;
709 swOn
[i
].now
= mixEnabled
;
710 swOn
[i
].prev
= _swPrev
;
712 if (mode
== e_perout_mode_normal
&& swOn
[i
].delay
> 0) {
713 swOn
[i
].delay
= max
<int16_t>(0, (int16_t)swOn
[i
].delay
- tick10ms
);
720 if (mode
==e_perout_mode_normal
) {
721 swOn
[i
].now
= swOn
[i
].prev
= mixEnabled
;
724 if ((md
->speedDown
|| md
->speedUp
) && md
->mltpx
!=MLTPX_REP
) {
726 v
= (md
->mltpx
== MLTPX_ADD
? 0 : RESX
);
727 applyOffsetAndCurve
= false;
730 else if (mixCondition
) {
736 if (mode
==e_perout_mode_normal
&& (!mixCondition
|| mixEnabled
|| swOn
[i
].delay
)) {
737 if (md
->mixWarn
) lv_mixWarning
|= 1 << (md
->mixWarn
- 1);
738 #if defined(BOLD_FONT)
739 swOn
[i
].activeMix
= true;
743 if (applyOffsetAndCurve
) {
745 //========== TRIMS ================
746 if (!(mode
& e_perout_mode_notrims
)) {
747 if (md
->carryTrim
== 0) {
748 v
+= getSourceTrimValue(md
->srcRaw
, v
);
753 int32_t weight
= GET_GVAR_PREC1(MD_WEIGHT(md
), GV_RANGELARGE_NEG
, GV_RANGELARGE
, mixerCurrentFlightMode
);
754 weight
= calc100to256_16Bits(weight
);
755 //========== SPEED ===============
756 // now its on input side, but without weight compensation. More like other remote controls
757 // lower weight causes slower movement
759 if (mode
<= e_perout_mode_inactive_flight_mode
&& (md
->speedUp
|| md
->speedDown
)) { // there are delay values
760 #define DEL_MULT_SHIFT 8
761 // we recale to a mult 256 higher value for calculation
762 int32_t tact
= act
[i
];
763 int16_t diff
= v
- (tact
>>DEL_MULT_SHIFT
);
765 // open.20.fsguruh: speed is defined in % movement per second; In menu we specify the full movement (-100% to 100%) = 200% in total
766 // the unit of the stored value is the value from md->speedUp or md->speedDown * 0.1s; e.g. value 4 means 0.4 seconds
767 // because we get a tick each 10msec, we need 100 ticks for one second
768 // the value in md->speedXXX gives the time it should take to do a full movement from -100 to 100 therefore 200%. This equals 2048 in recalculated internal range
769 if (tick10ms
|| !s_mixer_first_run_done
) {
770 // only if already time is passed add or substract a value according the speed configured
771 int32_t rate
= (int32_t) tick10ms
<< (DEL_MULT_SHIFT
+11); // = DEL_MULT*2048*tick10ms
772 // rate equals a full range for one second; if less time is passed rate is accordingly smaller
773 // if one second passed, rate would be 2048 (full motion)*256(recalculated weight)*100(100 ticks needed for one second)
774 int32_t currentValue
= ((int32_t) v
<<DEL_MULT_SHIFT
);
776 if (s_mixer_first_run_done
&& md
->speedUp
> 0) {
777 // if a speed upwards is defined recalculate the new value according configured speed; the higher the speed the smaller the add value is
778 int32_t newValue
= tact
+rate
/((int16_t)10*md
->speedUp
);
779 if (newValue
<currentValue
) currentValue
= newValue
; // Endposition; prevent toggling around the destination
782 else { // if is <0 because ==0 is not possible
783 if (s_mixer_first_run_done
&& md
->speedDown
> 0) {
784 // see explanation in speedUp
785 int32_t newValue
= tact
-rate
/((int16_t)10*md
->speedDown
);
786 if (newValue
>currentValue
) currentValue
= newValue
; // Endposition; prevent toggling around the destination
789 act
[i
] = tact
= currentValue
;
790 // open.20.fsguruh: this implementation would save about 50 bytes code
791 } // endif tick10ms ; in case no time passed assign the old value, not the current value from source
792 v
= (tact
>> DEL_MULT_SHIFT
);
796 //========== CURVES ===============
797 if (applyOffsetAndCurve
&& md
->curve
.type
!= CURVE_REF_DIFF
&& md
->curve
.value
) {
798 v
= applyCurve(v
, md
->curve
);
801 //========== WEIGHT ===============
802 int32_t dv
= (int32_t)v
* weight
;
803 dv
= div_and_round(dv
, 10);
805 //========== OFFSET / AFTER ===============
806 if (applyOffsetAndCurve
) {
807 int32_t offset
= GET_GVAR_PREC1(MD_OFFSET(md
), GV_RANGELARGE_NEG
, GV_RANGELARGE
, mixerCurrentFlightMode
);
808 if (offset
) dv
+= div_and_round(calc100toRESX_16Bits(offset
), 10) << 8;
811 //========== DIFFERENTIAL =========
812 if (md
->curve
.type
== CURVE_REF_DIFF
&& md
->curve
.value
) {
813 dv
= applyCurve(dv
, md
->curve
);
816 int32_t * ptr
= &chans
[md
->destCh
]; // Save calculating address several times
821 #if defined(BOLD_FONT)
822 if (mode
==e_perout_mode_normal
) {
823 for (uint8_t m
=i
-1; m
<MAX_MIXERS
&& mixAddress(m
)->destCh
==md
->destCh
; m
--)
824 swOn
[m
].activeMix
= false;
829 // @@@2 we have to remove the weight factor of 256 in case of 100%; now we use the new base of 256
832 dv
>>= RESX_SHIFT
; // same as dv /= RESXl;
835 default: // MLTPX_ADD
836 *ptr
+= dv
; //Mixer output add up to the line (dv + (dv>0 ? 100/2 : -100/2))/(100);
838 } // endswitch md->mltpx
839 #ifdef PREVENT_ARITHMETIC_OVERFLOW
841 // a lot of assumptions must be true, for this kind of check; not really worth for only 4 bytes flash savings
842 // this solution would save again 4 bytes flash
843 int8_t testVar=(*ptr<<1)>>24;
844 if ( (testVar!=-1) && (testVar!=0 ) ) {
845 // this devices by 64 which should give a good balance between still over 100% but lower then 32x100%; should be OK
846 *ptr >>= 6; // this is quite tricky, reduces the value a lot but should be still over 100% and reduces flash need
850 PACK( union u_int16int32_t
{
862 if ((tmp
.words_t
.hi
&0xFF80)!=0xFF80) tmp
.words_t
.hi
=0xFF86; // set to min nearly
865 if ((tmp
.words_t
.hi
|0x007F)!=0x007F) tmp
.words_t
.hi
=0x0079; // set to max nearly
868 // this implementation saves 18bytes flash
871 if (dv>(32767-RESXl)) {
872 *ptr=(32767-RESXl)<<8;
873 } else if (dv<(-32767+RESXl)) {
874 *ptr=(-32767+RESXl)<<8;
876 // *ptr=limit( int32_t(int32_t(-1)<<23), *ptr, int32_t(int32_t(1)<<23)); // limit code cost 72 bytes
877 // *ptr=limit( int32_t((-32767+RESXl)<<8), *ptr, int32_t((32767-RESXl)<<8)); // limit code cost 80 bytes
883 dirtyChannels
&= passDirtyChannels
;
885 } while (++pass
< 5 && dirtyChannels
);
887 mixWarning
= lv_mixWarning
;
892 #define MAX_ACT 0xffff
893 uint8_t lastFlightMode
= 255; // TODO reinit everything here when the model changes, no???
895 tmr10ms_t flightModeTransitionTime
;
896 uint8_t flightModeTransitionLast
= 255;
898 void evalMixes(uint8_t tick10ms
)
900 int32_t sum_chans512
[MAX_OUTPUT_CHANNELS
];
902 static uint16_t fp_act
[MAX_FLIGHT_MODES
] = {0};
903 static uint16_t delta
= 0;
904 static ACTIVE_PHASES_TYPE flightModesFade
= 0;
906 LS_RECURSIVE_EVALUATION_RESET();
908 uint8_t fm
= getFlightMode();
910 if (lastFlightMode
!= fm
) {
911 flightModeTransitionTime
= get_tmr10ms();
913 if (lastFlightMode
== 255) {
914 fp_act
[fm
] = MAX_ACT
;
917 uint8_t fadeTime
= max(g_model
.flightModeData
[lastFlightMode
].fadeOut
, g_model
.flightModeData
[fm
].fadeIn
);
918 ACTIVE_PHASES_TYPE transitionMask
= ((ACTIVE_PHASES_TYPE
)1 << lastFlightMode
) + ((ACTIVE_PHASES_TYPE
)1 << fm
);
920 flightModesFade
|= transitionMask
;
921 delta
= (MAX_ACT
/ 10) / fadeTime
;
924 flightModesFade
&= ~transitionMask
;
925 fp_act
[lastFlightMode
] = 0;
926 fp_act
[fm
] = MAX_ACT
;
928 logicalSwitchesCopyState(lastFlightMode
, fm
); // push last logical switches state from old to new flight mode
933 if (flightModeTransitionTime
&& get_tmr10ms() > flightModeTransitionTime
+SWITCHES_DELAY()) {
934 flightModeTransitionTime
= 0;
935 if (fm
!= flightModeTransitionLast
) {
936 if (flightModeTransitionLast
!= 255) {
937 PLAY_PHASE_OFF(flightModeTransitionLast
);
940 flightModeTransitionLast
= fm
;
945 if (flightModesFade
) {
946 memclear(sum_chans512
, sizeof(sum_chans512
));
947 for (uint8_t p
=0; p
<MAX_FLIGHT_MODES
; p
++) {
948 LS_RECURSIVE_EVALUATION_RESET();
949 if (flightModesFade
& ((ACTIVE_PHASES_TYPE
)1 << p
)) {
950 mixerCurrentFlightMode
= p
;
951 evalFlightModeMixes(p
==fm
? e_perout_mode_normal
: e_perout_mode_inactive_flight_mode
, p
==fm
? tick10ms
: 0);
952 for (uint8_t i
=0; i
<MAX_OUTPUT_CHANNELS
; i
++)
953 sum_chans512
[i
] += (chans
[i
] >> 4) * fp_act
[p
];
956 LS_RECURSIVE_EVALUATION_RESET();
959 mixerCurrentFlightMode
= fm
;
962 mixerCurrentFlightMode
= fm
;
963 evalFlightModeMixes(e_perout_mode_normal
, tick10ms
);
966 //========== FUNCTIONS ===============
967 // must be done after mixing because some functions use the inputs/channels values
968 // must be done before limits because of the applyLimit function: it checks for safety switches which would be not initialized otherwise
970 requiredSpeakerVolume
= g_eeGeneral
.speakerVolume
+ VOLUME_LEVEL_DEF
;
972 if (!g_model
.noGlobalFunctions
) {
973 evalFunctions(g_eeGeneral
.customFn
, globalFunctionsContext
);
975 evalFunctions(g_model
.customFn
, modelFunctionsContext
);
978 //========== LIMITS ===============
979 for (uint8_t i
=0; i
<MAX_OUTPUT_CHANNELS
; i
++) {
980 // chans[i] holds data from mixer. chans[i] = v*weight => 1024*256
981 // later we multiply by the limit (up to 100) and then we need to normalize
982 // at the end chans[i] = chans[i]/256 => -1024..1024
983 // interpolate value with min/max so we get smooth motion from center to stop
984 // this limits based on v original values and min=-1024, max=1024 RESX=1024
985 int32_t q
= (flightModesFade
? (sum_chans512
[i
] / weight
) << 4 : chans
[i
]);
987 ex_chans
[i
] = q
/ 256;
989 int16_t value
= applyLimits(i
, q
); // applyLimits will remove the 256 100% basis
991 channelOutputs
[i
] = value
; // copy consistent word to int-level
994 if (tick10ms
&& flightModesFade
) {
995 uint16_t tick_delta
= delta
* tick10ms
;
996 for (uint8_t p
=0; p
<MAX_FLIGHT_MODES
; p
++) {
997 ACTIVE_PHASES_TYPE flightModeMask
= ((ACTIVE_PHASES_TYPE
)1 << p
);
998 if (flightModesFade
& flightModeMask
) {
1000 if (MAX_ACT
- fp_act
[p
] > tick_delta
)
1001 fp_act
[p
] += tick_delta
;
1003 fp_act
[p
] = MAX_ACT
;
1004 flightModesFade
-= flightModeMask
;
1008 if (fp_act
[p
] > tick_delta
)
1009 fp_act
[p
] -= tick_delta
;
1012 flightModesFade
-= flightModeMask
;