Page 2 of 2
Re: Exponential control
Posted: 12 Jun 2019, 10:48
by Phil_G
MaxZ wrote: ↑12 Jun 2019, 08:06Phil, what did you mean by "long casts", too much calculation in a single statement?
No, its not the length of the statement, casts change the 'type' of variable or constant, avrgcc assumes 'int' by default so suffix an 'L' to cast a value as 'long'
As Mike says, feel free to use mine, its an embarrassingly simplistic encoder compared to Mike or Martins work but in its present state hundreds have been built and it does the job. I noticed the other day that the Kraft thread has gone, I'll recreate it when I have a mo. One thing though - if you make any changes, its important to thoroughly test all possible combinations & preconditions before flying it. Its common to spend more time testing than writing! I'm in the process of updating the 7+sc with interrupt driven ppm and Mikes method of doing expo as the existing code (Paul Luby) using floats & exp is heavy on time & resources.
Cheers
Phil
Re: Exponential control
Posted: 13 Jun 2019, 16:34
by Martin
Here's a quick example I knocked up that uses the (cube) formula recommended by Mike_K. This is all integer math and uses the range -1024 to +1024 for the input, output, and expo value. You could easily modify the code to use some other range, but 1024 is plenty accurate enough for radio control. If you increase the range then beware exceeding the limits that a (signed) 16-bit integer can hold: -32768 to +32767
Code: Select all
/* demonstrates a fast integer 'expo' function
* input, output, and expo amount are all integers in range -1024 <= value <= 1024.
* if you wish to use a different range, use bit-shifts to scale to that range, call the function and then bit-shift the result back to the range you want.
*
* ceptimus June 2019
*/
int expo(int input, int xpo) {
if (xpo == 0 || input == 0) {
return input; // no need to calculate when zero xpo is applied
}
// clip values to keep within allowed range
if (input < -1024) {
input = -1024;
} else if (input > 1024) {
input = 1024;
}
if (xpo < -1024) {
xpo = -1024;
} else if (xpo > 1024) {
xpo = 1024;
}
int32_t cube, i; // intermediate values use 32-bits. input values (16-bits) are cast to 32-bit for calculations.
cube = (int32_t)input * (int32_t) input; // first calculate square
cube >>= 10; // shift ten bits (divide by 1024) to normalize
cube *= input; // now cube
cube >>= 10; // re-normalize
cube *= xpo; // now xpo * input^3
cube >>= 10; // re-normalize
i = (int32_t)input * (int32_t)xpo;
i >>= 10;
int32_t output = (int32_t)input - i + cube;
return (int)output;
}
void setup() {
Serial.begin(115200);
Serial.print("Expo\tInput\tOutput\n");
for (int xpo = 0; xpo < 1024; xpo += 102) { // roughly 10% steps
for (int input = -1024; input <= 1024; input += 128) {
int output = expo(input, xpo);
Serial.print(xpo);
Serial.print("\t");
Serial.print(input);
Serial.print("\t");
Serial.print(output);
Serial.print("\n");
}
Serial.print("\n");
}
}
void loop() {
}
...and here's what the program output looks like. The xpo is increasing by roughly 10% for each block (102/1024) and values are tabled for the input range -1024 to 1024 going in steps of 128 within each block. The function obviously works for all (roughly) two million possible input permutations (four million if you count using negative expo), but it would take up too much room to show them all!
Note: although Futaba got it wrong and negative expo is what you (usually) want to use on Futaba, this formula implements expo in the (correct) JR / Spektrum sense.
Code: Select all
Expo Input Output
0 -1024 -1024
0 -896 -896
0 -768 -768
0 -640 -640
0 -512 -512
0 -384 -384
0 -256 -256
0 -128 -128
0 0 0
0 128 128
0 256 256
0 384 384
0 512 512
0 640 640
0 768 768
0 896 896
0 1024 1024
102 -1024 -1024
102 -896 -875
102 -768 -735
102 -640 -601
102 -512 -474
102 -384 -351
102 -256 -232
102 -128 -116
102 0 0
102 128 116
102 256 232
102 384 351
102 512 473
102 640 601
102 768 735
102 896 875
102 1024 1024
204 -1024 -1024
204 -896 -854
204 -768 -702
204 -640 -562
204 -512 -436
204 -384 -318
204 -256 -209
204 -128 -103
204 0 0
204 128 103
204 256 208
204 384 318
204 512 435
204 640 562
204 768 701
204 896 854
204 1024 1024
306 -1024 -1024
306 -896 -833
306 -768 -668
306 -640 -523
306 -512 -398
306 -384 -286
306 -256 -184
306 -128 -90
306 0 0
306 128 90
306 256 184
306 384 286
306 512 397
306 640 523
306 768 668
306 896 833
306 1024 1024
408 -1024 -1024
408 -896 -813
408 -768 -635
408 -640 -485
408 -512 -359
408 -384 -253
408 -256 -161
408 -128 -78
408 0 0
408 128 77
408 256 160
408 384 252
408 512 359
408 640 484
408 768 634
408 896 812
408 1024 1024
510 -1024 -1024
510 -896 -791
510 -768 -601
510 -640 -446
510 -512 -321
510 -384 -219
510 -256 -136
510 -128 -65
510 0 0
510 128 65
510 256 136
510 384 219
510 512 320
510 640 446
510 768 601
510 896 791
510 1024 1024
612 -1024 -1024
612 -896 -770
612 -768 -568
612 -640 -407
612 -512 -283
612 -384 -187
612 -256 -113
612 -128 -53
612 0 0
612 128 53
612 256 112
612 384 187
612 512 282
612 640 407
612 768 567
612 896 770
612 1024 1024
714 -1024 -1024
714 -896 -750
714 -768 -534
714 -640 -368
714 -512 -245
714 -384 -154
714 -256 -89
714 -128 -40
714 0 0
714 128 40
714 256 89
714 384 154
714 512 244
714 640 368
714 768 534
714 896 750
714 1024 1024
816 -1024 -1024
816 -896 -729
816 -768 -501
816 -640 -330
816 -512 -206
816 -384 -122
816 -256 -65
816 -128 -28
816 0 0
816 128 27
816 256 64
816 384 121
816 512 206
816 640 329
816 768 500
816 896 728
816 1024 1024
918 -1024 -1024
918 -896 -707
918 -768 -467
918 -640 -291
918 -512 -168
918 -384 -88
918 -256 -41
918 -128 -15
918 0 0
918 128 15
918 256 41
918 384 88
918 512 167
918 640 291
918 768 467
918 896 707
918 1024 1024
1020 -1024 -1024
1020 -896 -687
1020 -768 -434
1020 -640 -252
1020 -512 -130
1020 -384 -55
1020 -256 -17
1020 -128 -2
1020 0 0
1020 128 2
1020 256 16
1020 384 55
1020 512 129
1020 640 252
1020 768 433
1020 896 687
1020 1024 1024
Re: Exponential control
Posted: 13 Jun 2019, 18:00
by MaxZ
Thanks Martin.
As I said in my previous post, it was meant as a exercise but it got too ambitious for me.
I can follow your example though, it seems the trick is to divide it up in steps and to "normalize" by bit shifting after each step. Not the "throw it all in and see what happens" method I was using, likely to exceed the limits of what values a variable can hold.
What is an int32_t variable type, I can't find anything on the Arduino site, or anywhere else for that matter. I am guessing a 32 bit integer (a.k.a. long?), but what does _t mean?
Cheers,
Max.
P.S. Mike and Phil, don't worry, I will not try to invent the wheel again
Re: Exponential control
Posted: 13 Jun 2019, 18:42
by Martin
Yes, int32_t is the same as long for 8-bit Arduinos. An unsigned long is uint32_t. The _t part just means 'type' and the names are consistent so you can have int16_t for a normal int, uint16_t for an unsigned int, and so on. I find it easier than having char, int, long, long long, and so on, with or without unsigned and the names are consistent across different boards. If you just use 'int' then you'll get 16 bits on most Arduinos, but 32 bits on a Due - an int16_t is always 16 bits on all of them.