3

The limit size of a BLE packet is 20 bytes. I need to transfer the following data over it:

struct Data {
  uint16_t top;
  uint16_t bottom;
  float accX;
  float accY;
  float accZ;
  float qx;
  float qy;
  float qz;
  float qw;
};

Size of Data is 32 bytes. The precision of floats can not be sacrificed, since they represent accelerometers and quaternions, and would create a huge drift error if not represented precisely (data would be integrated over time).

I don't want to send 2 packets as well, as it's really important that whole data is taken at the same time.

I'm planning to take advantage of the range instead.

  • Accelerometer are IEEE floats in the range of [-10, 10]
  • Quaternions are IEEE floats in the range of [-1, 1]. We could remove w, as x^2 + y^2 + z^2 + w^2 = 1
  • Top, and bottom are 10-bit each.

Knowing this information, how can I serialize Data using at most 20 bytes?

Dzung Nguyen
  • 3,794
  • 9
  • 48
  • 86
  • 9
    Umm, if you can't sacrifice precision, then you can't use a `uint16_t` to represent a `float`, even if you limit the range; `float`s of small values just use the extra bits for precision after the decimal instead of before it. A 32 bit `float` can't be shoved into a 16 bit `uint16_t` without losing _some_ precision. – ShadowRanger Aug 30 '16 at 00:23
  • 1
    Lookup [Half-precision floating-point format](https://en.wikipedia.org/wiki/Half-precision_floating-point_format). – dxiv Aug 30 '16 at 00:24
  • Can't I map the floats outside of [-10,10] to represent the missing precision values? Using 2 bytes, I should be able to represent all IEEE floats in the range of [-10, 10]? – Dzung Nguyen Aug 30 '16 at 00:24
  • If you give up *one* bit of precision on the four floats, you can store each with only 24 bits, saving you four bytes in total. – Kerrek SB Aug 30 '16 at 00:31
  • 7
    Given that approximately half of the possible exponents in an ieee754 single-precision float represent values smaller than `1.0f`, the restriction to the interval `[-10, 10]` doesn't save you more than a single bit per float. You'll need to sacrifice precision, unless you have additional implicit information (like a fixed relationship between some of the values). – EOF Aug 30 '16 at 00:32
  • 1
    You can divide the range into 65536 values using fixed point. It may actually be better than using 16-bit floats because floats have extra precision closer to zero, whereas quaternion rotation values will be spread around the unit sphere – samgak Aug 30 '16 at 00:32
  • 4
    What is the range of `top` and `bottom`? – paddy Aug 30 '16 at 00:33
  • 3
    You could also decorrelate the acceleration and store, say, one full-precision magnitude and two low-precision angles. – Kerrek SB Aug 30 '16 at 00:34
  • @paddy: it's an unsigned integer, from 0 to 1023. – Dzung Nguyen Aug 30 '16 at 00:37
  • @EOF: A simple way to say that is to store the value `a / 20 + 1.5`, which is in the range [1, 2), and thus only uses one fixed exponent value. (With perhaps one exception if you want the closed interval [1, 2].) – Kerrek SB Aug 30 '16 at 00:37
  • What's the range of `|top - bottom|`? – Kerrek SB Aug 30 '16 at 00:37
  • @KerrekSB: But those operations *do not preserve the full precision*. Example: If `a/20` is less than `FLT_EPSILON`. – EOF Aug 30 '16 at 00:39
  • @EOF: The OP's constraints were stated mathematically. I'm saying that you consider the mathematical transform a / 20 + 1.5 (which is bijective), and store the result in your on-disk representation. This gives you a nice uniform, fixed-precision representation, and you convert it back to the original range when you need to format the value for display. If your data isn't uniform (but e.g. you need more precision for small-magnitude values), then this approach doesn't help. – Kerrek SB Aug 30 '16 at 00:42
  • 2
    `Using 2 bytes, I should be able to represent all IEEE floats in the range of [-10, 10]?` how about 1e-38? In the end it depends on the format and exponent range. Using half-float's 5-bit exponent it's only possible to go down to 2^-14 – phuclv Aug 30 '16 at 00:44
  • @KerrekSB: The reverse operation `a = (x - 1.5)*20` contains a subtraction that will happily eat *all* of your precision (like in the example I have given) by catastrophic cancellation. You can't cheat if, as the OP stated, "The precision of floats can not be sacrificed". – EOF Aug 30 '16 at 00:45
  • @DzungNguyen Why do you need 16 bits each for `top` and `bottom` if they can only be in 0-1023? – Iskar Jarak Aug 30 '16 at 00:49
  • Yeah, I just realized that top and bottom could be optimized as well. – Dzung Nguyen Aug 30 '16 at 00:49
  • 1
    If you are measuring the rotation of a real object that doesn't stretch or shrink (i.e. it's a normalized quaternion), then you don't need to transmit w, you can compute it from x y z. Then you can use the bits saved for higher precision on your other values. – samgak Aug 30 '16 at 00:56
  • 1
    Can you explain the reasons why you can't use two packets? Is it timing-sensitive? Because without any clever optimizations, you've got two 10-bit values and six 32-bit values = 212 bits (approx 26.5 bytes per packet). If you use the 160-bit packets as a stream and shove these larger packets down it with two packets of latency, you can reassemble them at the other end. The overhead is that 40 full-size packets requires 53 packets of 20 bytes. Every 4 additional bits you can shave off will reduce that overhead by one. – paddy Aug 30 '16 at 01:19

1 Answers1

0

Assuming binary32, code is using 2*16 + 7*32 bits (256 bits) and OP wants to limit to 20*8 bits (160).

Some savings:
1) 10 bit uint16_t,
2) reduced exponent range saves a bit or 2 per float - would save a few more bits if OP stated the _minimum exponent as well. (estimate 4 bits total)
3) Not coding w.

This make make for 2*10 + 6*(32-4) = 188 bits, still not down to 160.

OP says "The precision of floats can not be sacrificed" implying the 24 bit (23- bits explicitly coded) significand is needed. 7 float * 23 bits is 161 bits and that is not counting the sign, exponent nor 2 uint16_t.

So unless some pattern or redundant information can be eliminated, OP is outta luck.


Suggest taking many samples of data and try compressing it using LZ or other compression techniques. If OP ends up with significantly less than 20 bytes per averagedata, then the answer is yes - in theory, else you are SOL.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256