I am trying to build an algorithm for compressing 4 byte floats into a custom 2 byte uint16_t. I plan on sending a lot of data through the serial line from one Arduino to Another via I2c. I thought that if I "compress" the float into a uint16_t and have all the necessary information in the uint16_t to decode on the other end of the serial line, then I2c communications will be faster. This is for a button box for Microsoft Flight Simulator 2020. The numbers I need to send fall into certain categories:
1. Com/Nav Frequencies are positive and below 255 and have fractional components
-> 108.45, 112.95
2. Vertical Speed can be positive or negative and have no fractional component
-> -2000, 5000
3. Transponder codes need to be accurate and are between 0 and 7777
4. Anything above 255 does not have fractional component.
5. Anything value above 7777 does not need to be precise but better precision is preferred.
--> Doesn't matter if altitude is 40000 or 40050.
I think I have the code to satisfy the above requirements.
Can you guys look at this Encode and Decode snippet and see if there are any optimizations that could be done.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
|
uint16_t Encode(float fnumber) {
uint16_t inumber=abs(fnumber);
//Lose precision, always positive -> store as square root -> first bit will be 1 and next 7 bits approximate the nearest 1/27 value using numbers 100-127
if ((inumber > 16384) && (fnumber > 0)) {
float sroot=sqrt(fnumber);
//Extract whole number and set the first byte to its value
inumber=(uint8_t) sroot;
//Extract fractinal number set the second byte to its value
//First Method, not very accurate but faster
uint16_t ifract = (uint16_t) ((((sroot * 100) - (inumber*100)))/27)+100; //Extract the fractional part as an int and divice by 27 and add to 100
//second Method, more steps but better accuracy -->takes an extra 20 microseconds to finnish
//uint16_t ifract = (uint16_t) (round(((sroot * 100) - (inumber*100))/27))+100;
inumber <<= 8;
ifract <<= 1;
inumber += ifract;
inumber |= 1UL << 0; //Set the first bit
return inumber;
// Precise and with a sign if no fractional component -> store as 14 bit number with first bit 0 and second bit sign with 0 positive and 1 negative
} else if ((inumber <= 16384 ) && ((int)fnumber == fnumber )) {
inumber <<= 2; //Shift 2 bits to the left, first bit is automatically zero
//Extract the sign of the float and put it in the second bit
if ((int)fnumber <0) {
inumber |= 1UL << 1;
}
return inumber;
// Precise and with a fractional component, always positive
} else if ((inumber < 256 ) && (fnumber > inumber)) {
//The whole number is already inumber
//Extract the fractional number
uint16_t ifract=(uint16_t) ((fnumber * 100)-(inumber*100));
inumber <<= 8;
if (ifract > 0) {
ifract <<= 1;
inumber += ifract;
}
inumber |= 1UL << 0; //Set the first bit
return inumber;
} else { //Error. Unhandled case
return 0; //error condition
}
}
float Decode(uint16_t inumber) {
//check first bit
uint8_t bit = inumber & 1U;
if (bit) { // whole + fract
//Extract whole number
uint16_t whole= inumber >> 8;
//Extract the fraction
uint16_t fract = inumber & 0xFF;
fract >>= 1;
if (fract > 99) {
//Round away the inaccuracy and report in thousands only
return (round((sq(whole + ((fract - 100 ) * 27)/(float)100))/1000))*1000;
//Don't round away the inaccuracy
//return sq(whole + ((fract - 100 ) * 27)/(float)100);
} else {
whole *= 100;
return ((float)whole + fract)/100;
}
} else {
//Extract the sign from second bit
uint8_t sign = inumber & 0x2;
if (inumber & 0x2)
return ((float)(inumber >> 2))*-1;
else
return (float)(inumber >> 2);
}
}
|
Testing the code yields:
Encode 65000.00 ==> 65231 ==> 65000.00 Encode time: 108 Decode time 112
Encode 44000.00 ==> 53709 ==> 44000.00 Encode time: 116 Decode time 112
Encode 44501.00 ==> 53967 ==> 44000.00 Encode time: 108 Decode time 112
Encode 15101.00 ==> 60404 ==> 15101.00 Encode time: 24 Decode time 8
Encode -5000.00 ==> 20002 ==> -5000.00 Encode time: 24 Decode time 8
Encode 250.00 ==> 1000 ==> 250.00 Encode time: 20 Decode time 8
Encode -25.00 ==> 102 ==> -25.00 Encode time: 24 Decode time 12
Encode -25.12 ==> 0 ==> 0.00 Encode time: 32 Decode time 4
Encode 108.45 ==> 27739 ==> 108.45 Encode time: 56 Decode time 44
Encode -100.21 ==> 0 ==> 0.00 Encode time: 28 Decode time 4
Encode -32000.00 ==> 0 ==> 0.00 Encode time: 12 Decode time 4
Times measured with micros().
If the code fails, the worst thing that could happen is my airplane crashes. But I can reload the flight :)
Thanks,
Chris