From 1e3c1d8ce2184a144effd6782a9d4463c60619a0 Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Thu, 21 Feb 2013 20:17:54 +0000 Subject: [PATCH] Expo EXTENDED function added, SLOW moved to old position, 8 bytes code saved Now the expo function is explained properly. Expo execution speed should be again better. Codesaving this time only 8 bytes. Added EXTENDED_EXPO define for a wider range of expo. So around 0 the curve is more flat and the edges increase faster. This change costs about 84 bytes. I noticed the curve doesn't much change especially for smaller k values. Now the range is extended. From 0 to 80 it gives the old curves from 0 to 100. From 81 to 100 a higher raising curve is used. There is a small break between 80 to 81. If this is a problem I can change it to be smooth and even less code size, but loose the old curve form. I moved SLOW function back to old position, because it might be not a good idea if differential is actived, and the end positions are reached at different times. Therefore the smaller movement needs to be slower. Please try and test and report your opinion. Please also test more about overruns. I was able to generate a overrun by added about 4x a 125% value with 125% offset. (output each above 200%, in total more than 800%) The overruns occurs earlier compared to original version. Could this cause a problem? I can change the internal calculation to be more like the original version, but would cost more flash. --- src/open9x.cpp | 128 +++++++++++++++++++++++++++++++++++-------------- src/open9x.h | 2 +- 2 files changed, 93 insertions(+), 37 deletions(-) diff --git a/src/open9x.cpp b/src/open9x.cpp index a3471f2d5..6560ca6fa 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -470,10 +470,16 @@ int16_t applyCurve(int16_t x, int8_t idx) #define applyCurve(x, idx) (x) #endif +/* currently not needed // maybe used in future? uint16_t divu100(uint32_t A) { return (((A * 0x47AF) >> 16) + A) >> 7; } +*/ + + +// #define EXTENDED_EXPO +// increases range of expo curve but costs about 82 bytes flash // expo-funktion: // --------------- @@ -482,44 +488,90 @@ uint16_t divu100(uint32_t A) { // f(x,k)=x*x*x*k/10 + x*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10] // f(x,k)=x*x*k/10 + x*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10] // 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] +// don't know what this above should be, just confusing in my opinion, +// here is the real explanation +// actually the real formula is +/* + f(x) = exp( ln(x) * 10^k) + 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 + this gives values from 0 to 1 for x and output; k must be between -1 and +1 + 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 + f(x) = 1024 * ( e^( ln(x/1024) * 10^(k/100) ) ) + This would be really hard to be calculated by such a microcontroller + Therefore Thomas Husterer compared a few usual function something like x^3, x^4*something, which look similar + Actually the formula + f(x) = k*x^3+x*(1-k) + gives a similar form and should have even advantages compared to a original exp curve. + This function again expect x from 0 to 1 and k only from 0 to 1 + Therefore rescaling is needed like before: + f(x) = 1024* ((k/100)*(x/1024)^3 + (x/1024)*(100-k)/100) + some mathematical tricks + f(x) = (k*x*x*x/(1024*1024) + x*(100-k)) / 100 + for better rounding results we add the 50 + f(x) = (k*x*x*x/(1024*1024) + x*(100-k) + 50) / 100 + + because we now understand the formula, we can optimize it further + --> calc100to256(k) --> eliminates /100 by replacing with /256 which is just a simple shift right 8 + k is now between 0 and 256 + f(x) = (k*x*x*x/(1024*1024) + x*(256-k) + 128) / 256 + */ + // input parameters; // x 0 to 1024; // k 0 to 100; -// return value = 1024 * (x/1024)^((101+k)/30) -// return value = exp(ln(x/1024)*exp(k/10))*1024 +// output between 0 and 1024 uint16_t expou(uint16_t x, uint16_t k) { - // previous function was this one: - // k*x*x*x + (1-k)*x - // return ((unsigned long)x*x*x/0x10000*k/(RESXul*RESXul/0x10000) + (RESKul-k)*x+RESKul/2)/RESKul; +#ifdef EXTENDED_EXPO + bool extended; + if (k>80) { + extended=true; + } else { + k+=(k>>2); // use bigger values before extend, because the effect is anyway very very low + extended=false; + } // endif k > 50 +#endif + k=calc100to256(k); + uint32_t value = (uint32_t) x*x; value *= (uint32_t)k; value >>= 8; value *= (uint32_t)x; - value >>= 12; - value += (uint32_t)(100-k)*x+50; - // return divu100(value); // would speed up further by needs more code - return value/100; +#ifdef EXTENDED_EXPO + if (extended) { // for higher values do more multiplications to get a stronger expo curve + value>>=16; + value*=(uint32_t)x; + value>>=4; + value*=(uint32_t)x; + } //endif extend +#endif + + value >>= 12; + value += (uint32_t)(256-k)*x+128; + + return value>>8; } int16_t expo(int16_t x, int16_t k) { if (k == 0) return x; int16_t y; - bool neg = x < 0; + bool neg = x < 0; + if (neg) x = -x; if (k<0) { y = RESXu-expou(RESXu-x, -k); - } - else { + } else { y = expou(x, k); } return neg? -y : y; } +// isn't the following useless? wouldn't be called even if EXTENDED_EXPO is defined +/* #ifdef EXTENDED_EXPO /// expo with y-offset class Expo @@ -555,6 +607,7 @@ int16_t Expo::expo(int16_t x) return -expou(-x,c,-d) + drx; } #endif +*/ #ifdef BOLD_FONT ACTIVE_EXPOS_TYPE activeExpos; @@ -592,11 +645,14 @@ void applyExpos(int16_t *anas) else v = expo(v, GET_GVAR(curveParam, -100, 100, s_perout_flight_phase)); } - v = ((int32_t)v * GET_GVAR(ed.weight, 0, 100, s_perout_flight_phase)) / 100; + int16_t weight=GET_GVAR(ed.weight, 0, 100, s_perout_flight_phase); + weight=calc100to256(weight); + v = ((int32_t)v * weight) >> 8; + // v = ((int32_t)v * GET_GVAR(ed.weight, 0, 100, s_perout_flight_phase)) / 100; anas[cur_chn] = v; - } - } - } + } //endif + } //endif getSwitch + } //endfor } #if !defined(CPUARM) @@ -2676,22 +2732,6 @@ void perOut(uint8_t mode, uint8_t tick10ms) // @@@2 recalculate weight to a 256 basis which ease the calculation later a lot int32_t dv = (int32_t) v * calc100to256(weight); - //========== DIFFERENTIAL ========= - if (md->curveMode == MODE_DIFFERENTIAL) { -/* int8_t curveParam = GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase); - if (curveParam > 0 && dv < 0) - dv = (dv * (100 - curveParam)) / 100; - else if (curveParam < 0 && dv > 0) - dv = (dv * (100 + curveParam)) / 100; */ - - // @@@2 also recalculate curveParam to a 256 basis which ease the calculation later a lot - int16_t curveParam=calc100to256(GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase)); - if (curveParam > 0 && dv < 0) - dv = (dv * (256 - curveParam)) >> 8; - else if (curveParam < 0 && dv > 0) - dv = (dv * (256 + curveParam)) >> 8; - } - //========== SPEED =============== if (mode == e_perout_mode_normal && (md->speedUp || md->speedDown)) { // there are delay values @@ -2733,11 +2773,27 @@ void perOut(uint8_t mode, uint8_t tick10ms) dv = act[i]; } //endif diff } //endif speed - + + //========== DIFFERENTIAL ========= + if (md->curveMode == MODE_DIFFERENTIAL) { +/* int8_t curveParam = GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase); + if (curveParam > 0 && dv < 0) + dv = (dv * (100 - curveParam)) / 100; + else if (curveParam < 0 && dv > 0) + dv = (dv * (100 + curveParam)) / 100; */ + + // @@@2 also recalculate curveParam to a 256 basis which ease the calculation later a lot + int16_t curveParam=calc100to256(GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase)); + if (curveParam > 0 && dv < 0) + dv = (dv * (256 - curveParam)) >> 8; + else if (curveParam < 0 && dv > 0) + dv = (dv * (256 + curveParam)) >> 8; + } + int32_t *ptr = &chans[md->destCh]; // Save calculating address several times - if (i == 0 || md->destCh != (md - 1)->destCh) - *ptr = 0; + if (i == 0 || md->destCh != (md - 1)->destCh) *ptr = 0; + // if this is the first calculation for the destination channel, initialize it with 0 (otherwise would be random) switch (md->mltpx) { case MLTPX_REP: @@ -2763,7 +2819,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) tick10ms = 0; dirtyChannels &= passDirtyChannels; - } while (++pass < 5 && dirtyChannels); + } while (++pass < 7 && dirtyChannels); // we increased speed of mixer, therefore allow more passes; old value was 5 mixWarning = lv_mixWarning; } diff --git a/src/open9x.h b/src/open9x.h index 1c432ebab..fabfb7caf 100644 --- a/src/open9x.h +++ b/src/open9x.h @@ -1060,7 +1060,7 @@ extern int16_t ex_chans[NUM_CHNOUT]; // Outputs (before LIMITS) of th extern int16_t g_chans512[NUM_CHNOUT]; extern uint16_t BandGap; -extern uint16_t expou(uint16_t x, uint16_t k); +// extern uint16_t expou(uint16_t x, uint16_t k); extern int16_t expo(int16_t x, int16_t k); extern int16_t intpol(int16_t, uint8_t); extern int16_t applyCurve(int16_t, int8_t);