Merge pull request #11198 from SteveCEvans/sce_rc2
[betaflight.git] / src / main / blackbox / blackbox_encoding.c
blob200c00b5d772e6122c496eccdc6e14d6217e16c8
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 #include <stdint.h>
22 #include <stdbool.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <string.h>
27 #include "platform.h"
29 #ifdef USE_BLACKBOX
31 #include "blackbox_encoding.h"
32 #include "blackbox_io.h"
34 #include "common/encoding.h"
35 #include "common/printf.h"
38 static void _putc(void *p, char c)
40 (void)p;
41 blackboxWrite(c);
44 static int blackboxPrintfv(const char *fmt, va_list va)
46 return tfp_format(NULL, _putc, fmt, va);
50 //printf() to the blackbox serial port with no blocking shenanigans (so it's caller's responsibility to not write too fast!)
51 int blackboxPrintf(const char *fmt, ...)
53 va_list va;
55 va_start(va, fmt);
57 const int written = blackboxPrintfv(fmt, va);
59 va_end(va);
61 return written;
65 * printf a Blackbox header line with a leading "H " and trailing "\n" added automatically. blackboxHeaderBudget is
66 * decreased to account for the number of bytes written.
68 void blackboxPrintfHeaderLine(const char *name, const char *fmt, ...)
70 va_list va;
72 blackboxWrite('H');
73 blackboxWrite(' ');
74 blackboxWriteString(name);
75 blackboxWrite(':');
77 va_start(va, fmt);
79 const int written = blackboxPrintfv(fmt, va);
81 va_end(va);
83 blackboxWrite('\n');
85 blackboxHeaderBudget -= written + 3;
88 /**
89 * Write an unsigned integer to the blackbox serial port using variable byte encoding.
91 void blackboxWriteUnsignedVB(uint32_t value)
93 //While this isn't the final byte (we can only write 7 bits at a time)
94 while (value > 127) {
95 blackboxWrite((uint8_t) (value | 0x80)); // Set the high bit to mean "more bytes follow"
96 value >>= 7;
98 blackboxWrite(value);
102 * Write a signed integer to the blackbox serial port using ZigZig and variable byte encoding.
104 void blackboxWriteSignedVB(int32_t value)
106 //ZigZag encode to make the value always positive
107 blackboxWriteUnsignedVB(zigzagEncode(value));
110 void blackboxWriteSignedVBArray(int32_t *array, int count)
112 for (int i = 0; i < count; i++) {
113 blackboxWriteSignedVB(array[i]);
117 void blackboxWriteSigned16VBArray(int16_t *array, int count)
119 for (int i = 0; i < count; i++) {
120 blackboxWriteSignedVB(array[i]);
124 void blackboxWriteS16(int16_t value)
126 blackboxWrite(value & 0xFF);
127 blackboxWrite((value >> 8) & 0xFF);
131 * Write a 2 bit tag followed by 3 signed fields of 2, 4, 6 or 32 bits
133 void blackboxWriteTag2_3S32(int32_t *values)
135 static const int NUM_FIELDS = 3;
137 //Need to be enums rather than const ints if we want to switch on them (due to being C)
138 enum {
139 BITS_2 = 0,
140 BITS_4 = 1,
141 BITS_6 = 2,
142 BITS_32 = 3
145 enum {
146 BYTES_1 = 0,
147 BYTES_2 = 1,
148 BYTES_3 = 2,
149 BYTES_4 = 3
152 int selector = BITS_2, selector2;
155 * Find out how many bits the largest value requires to encode, and use it to choose one of the packing schemes
156 * below:
158 * Selector possibilities
160 * 2 bits per field ss11 2233,
161 * 4 bits per field ss00 1111 2222 3333
162 * 6 bits per field ss11 1111 0022 2222 0033 3333
163 * 32 bits per field sstt tttt followed by fields of various byte counts
165 for (int x = 0; x < NUM_FIELDS; x++) {
166 //Require more than 6 bits?
167 if (values[x] >= 32 || values[x] < -32) {
168 selector = BITS_32;
169 break;
172 //Require more than 4 bits?
173 if (values[x] >= 8 || values[x] < -8) {
174 if (selector < BITS_6) {
175 selector = BITS_6;
177 } else if (values[x] >= 2 || values[x] < -2) { //Require more than 2 bits?
178 if (selector < BITS_4) {
179 selector = BITS_4;
184 switch (selector) {
185 case BITS_2:
186 blackboxWrite((selector << 6) | ((values[0] & 0x03) << 4) | ((values[1] & 0x03) << 2) | (values[2] & 0x03));
187 break;
188 case BITS_4:
189 blackboxWrite((selector << 6) | (values[0] & 0x0F));
190 blackboxWrite((values[1] << 4) | (values[2] & 0x0F));
191 break;
192 case BITS_6:
193 blackboxWrite((selector << 6) | (values[0] & 0x3F));
194 blackboxWrite((uint8_t)values[1]);
195 blackboxWrite((uint8_t)values[2]);
196 break;
197 case BITS_32:
199 * Do another round to compute a selector for each field, assuming that they are at least 8 bits each
201 * Selector2 field possibilities
202 * 0 - 8 bits
203 * 1 - 16 bits
204 * 2 - 24 bits
205 * 3 - 32 bits
207 selector2 = 0;
209 //Encode in reverse order so the first field is in the low bits:
210 for (int x = NUM_FIELDS - 1; x >= 0; x--) {
211 selector2 <<= 2;
213 if (values[x] < 128 && values[x] >= -128) {
214 selector2 |= BYTES_1;
215 } else if (values[x] < 32768 && values[x] >= -32768) {
216 selector2 |= BYTES_2;
217 } else if (values[x] < 8388608 && values[x] >= -8388608) {
218 selector2 |= BYTES_3;
219 } else {
220 selector2 |= BYTES_4;
224 //Write the selectors
225 blackboxWrite((selector << 6) | selector2);
227 //And now the values according to the selectors we picked for them
228 for (int x = 0; x < NUM_FIELDS; x++, selector2 >>= 2) {
229 switch (selector2 & 0x03) {
230 case BYTES_1:
231 blackboxWrite(values[x]);
232 break;
233 case BYTES_2:
234 blackboxWrite(values[x]);
235 blackboxWrite(values[x] >> 8);
236 break;
237 case BYTES_3:
238 blackboxWrite(values[x]);
239 blackboxWrite(values[x] >> 8);
240 blackboxWrite(values[x] >> 16);
241 break;
242 case BYTES_4:
243 blackboxWrite(values[x]);
244 blackboxWrite(values[x] >> 8);
245 blackboxWrite(values[x] >> 16);
246 blackboxWrite(values[x] >> 24);
247 break;
250 break;
255 * Write a 2 bit tag followed by 3 signed fields of 2, 554, 877 or 32 bits
257 int blackboxWriteTag2_3SVariable(int32_t *values)
259 static const int FIELD_COUNT = 3;
260 enum {
261 BITS_2 = 0,
262 BITS_554 = 1,
263 BITS_877 = 2,
264 BITS_32 = 3
267 enum {
268 BYTES_1 = 0,
269 BYTES_2 = 1,
270 BYTES_3 = 2,
271 BYTES_4 = 3
276 * Find out how many bits the largest value requires to encode, and use it to choose one of the packing schemes
277 * below:
279 * Selector possibilities
281 * 2 bits per field ss11 2233,
282 * 554 bits per field ss11 1112 2222 3333
283 * 877 bits per field ss11 1111 1122 2222 2333 3333
284 * 32 bits per field sstt tttt followed by fields of various byte counts
286 int selector = BITS_2;
287 int selector2 = 0;
288 // Require more than 877 bits?
289 if (values[0] >= 256 || values[0] < -256
290 || values[1] >= 128 || values[1] < -128
291 || values[2] >= 128 || values[2] < -128) {
292 selector = BITS_32;
293 // Require more than 554 bits?
294 } else if (values[0] >= 16 || values[0] < -16
295 || values[1] >= 16 || values[1] < -16
296 || values[2] >= 8 || values[2] < -8) {
297 selector = BITS_877;
298 // Require more than 2 bits?
299 } else if (values[0] >= 2 || values[0] < -2
300 || values[1] >= 2 || values[1] < -2
301 || values[2] >= 2 || values[2] < -2) {
302 selector = BITS_554;
305 switch (selector) {
306 case BITS_2:
307 blackboxWrite((selector << 6) | ((values[0] & 0x03) << 4) | ((values[1] & 0x03) << 2) | (values[2] & 0x03));
308 break;
309 case BITS_554:
310 // 554 bits per field ss11 1112 2222 3333
311 blackboxWrite((selector << 6) | ((values[0] & 0x1F) << 1) | ((values[1] & 0x1F) >> 4));
312 blackboxWrite(((values[1] & 0x0F) << 4) | (values[2] & 0x0F));
313 break;
314 case BITS_877:
315 // 877 bits per field ss11 1111 1122 2222 2333 3333
316 blackboxWrite((selector << 6) | ((values[0] & 0xFF) >> 2));
317 blackboxWrite(((values[0] & 0x03) << 6) | ((values[1] & 0x7F) >> 1));
318 blackboxWrite(((values[1] & 0x01) << 7) | (values[2] & 0x7F));
319 break;
320 case BITS_32:
322 * Do another round to compute a selector for each field, assuming that they are at least 8 bits each
324 * Selector2 field possibilities
325 * 0 - 8 bits
326 * 1 - 16 bits
327 * 2 - 24 bits
328 * 3 - 32 bits
330 selector2 = 0;
331 //Encode in reverse order so the first field is in the low bits:
332 for (int x = FIELD_COUNT - 1; x >= 0; x--) {
333 selector2 <<= 2;
335 if (values[x] < 128 && values[x] >= -128) {
336 selector2 |= BYTES_1;
337 } else if (values[x] < 32768 && values[x] >= -32768) {
338 selector2 |= BYTES_2;
339 } else if (values[x] < 8388608 && values[x] >= -8388608) {
340 selector2 |= BYTES_3;
341 } else {
342 selector2 |= BYTES_4;
346 //Write the selectors
347 blackboxWrite((selector << 6) | selector2);
349 //And now the values according to the selectors we picked for them
350 for (int x = 0; x < FIELD_COUNT; x++, selector2 >>= 2) {
351 switch (selector2 & 0x03) {
352 case BYTES_1:
353 blackboxWrite(values[x]);
354 break;
355 case BYTES_2:
356 blackboxWrite(values[x]);
357 blackboxWrite(values[x] >> 8);
358 break;
359 case BYTES_3:
360 blackboxWrite(values[x]);
361 blackboxWrite(values[x] >> 8);
362 blackboxWrite(values[x] >> 16);
363 break;
364 case BYTES_4:
365 blackboxWrite(values[x]);
366 blackboxWrite(values[x] >> 8);
367 blackboxWrite(values[x] >> 16);
368 blackboxWrite(values[x] >> 24);
369 break;
372 break;
374 return selector;
378 * Write an 8-bit selector followed by four signed fields of size 0, 4, 8 or 16 bits.
380 void blackboxWriteTag8_4S16(int32_t *values)
383 //Need to be enums rather than const ints if we want to switch on them (due to being C)
384 enum {
385 FIELD_ZERO = 0,
386 FIELD_4BIT = 1,
387 FIELD_8BIT = 2,
388 FIELD_16BIT = 3
391 uint8_t selector = 0;
392 //Encode in reverse order so the first field is in the low bits:
393 for (int x = 3; x >= 0; x--) {
394 selector <<= 2;
396 if (values[x] == 0) {
397 selector |= FIELD_ZERO;
398 } else if (values[x] < 8 && values[x] >= -8) {
399 selector |= FIELD_4BIT;
400 } else if (values[x] < 128 && values[x] >= -128) {
401 selector |= FIELD_8BIT;
402 } else {
403 selector |= FIELD_16BIT;
407 blackboxWrite(selector);
409 int nibbleIndex = 0;
410 uint8_t buffer = 0;
411 for (int x = 0; x < 4; x++, selector >>= 2) {
412 switch (selector & 0x03) {
413 case FIELD_ZERO:
414 //No-op
415 break;
416 case FIELD_4BIT:
417 if (nibbleIndex == 0) {
418 //We fill high-bits first
419 buffer = values[x] << 4;
420 nibbleIndex = 1;
421 } else {
422 blackboxWrite(buffer | (values[x] & 0x0F));
423 nibbleIndex = 0;
425 break;
426 case FIELD_8BIT:
427 if (nibbleIndex == 0) {
428 blackboxWrite(values[x]);
429 } else {
430 //Write the high bits of the value first (mask to avoid sign extension)
431 blackboxWrite(buffer | ((values[x] >> 4) & 0x0F));
432 //Now put the leftover low bits into the top of the next buffer entry
433 buffer = values[x] << 4;
435 break;
436 case FIELD_16BIT:
437 if (nibbleIndex == 0) {
438 //Write high byte first
439 blackboxWrite(values[x] >> 8);
440 blackboxWrite(values[x]);
441 } else {
442 //First write the highest 4 bits
443 blackboxWrite(buffer | ((values[x] >> 12) & 0x0F));
444 // Then the middle 8
445 blackboxWrite(values[x] >> 4);
446 //Only the smallest 4 bits are still left to write
447 buffer = values[x] << 4;
449 break;
452 //Anything left over to write?
453 if (nibbleIndex == 1) {
454 blackboxWrite(buffer);
459 * Write `valueCount` fields from `values` to the Blackbox using signed variable byte encoding. A 1-byte header is
460 * written first which specifies which fields are non-zero (so this encoding is compact when most fields are zero).
462 * valueCount must be 8 or less.
464 void blackboxWriteTag8_8SVB(int32_t *values, int valueCount)
466 uint8_t header;
468 if (valueCount > 0) {
469 //If we're only writing one field then we can skip the header
470 if (valueCount == 1) {
471 blackboxWriteSignedVB(values[0]);
472 } else {
473 //First write a one-byte header that marks which fields are non-zero
474 header = 0;
476 // First field should be in low bits of header
477 for (int i = valueCount - 1; i >= 0; i--) {
478 header <<= 1;
480 if (values[i] != 0) {
481 header |= 0x01;
485 blackboxWrite(header);
487 for (int i = 0; i < valueCount; i++) {
488 if (values[i] != 0) {
489 blackboxWriteSignedVB(values[i]);
496 /** Write unsigned integer **/
497 void blackboxWriteU32(int32_t value)
499 blackboxWrite(value & 0xFF);
500 blackboxWrite((value >> 8) & 0xFF);
501 blackboxWrite((value >> 16) & 0xFF);
502 blackboxWrite((value >> 24) & 0xFF);
505 /** Write float value in the integer form **/
506 void blackboxWriteFloat(float value)
508 blackboxWriteU32(castFloatBytesToInt(value));
510 #endif // BLACKBOX