Exponential control

Arduino projects on the go
User avatar
Phil_G
Posts: 597
Joined: 15 Feb 2018, 23:32
Contact:

Re: Exponential control

Post 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'


long_casts.jpg


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
Martin
Posts: 744
Joined: 16 Feb 2018, 14:11
Location: Warwickshire

Re: Exponential control

Post 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! :D

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
Attachments
expo.zip
(942 Bytes) Downloaded 174 times
MaxZ
Posts: 330
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Re: Exponential control

Post 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 :D
Martin
Posts: 744
Joined: 16 Feb 2018, 14:11
Location: Warwickshire

Re: Exponential control

Post 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.
Post Reply