Auto updated submodule references [18-01-2025]
[betaflight.git] / src / main / blackbox / blackbox_encoding.c
blobdbec91146c29552e5e2917361796edf10e9dd99d
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"
37 static void _putc(void *p, char c)
39 (void)p;
40 blackboxWrite(c);
43 static int blackboxPrintfv(const char *fmt, va_list va)
45 return tfp_format(NULL, _putc, fmt, va);
48 //printf() to the blackbox serial port with no blocking shenanigans (so it's caller's responsibility to not write too fast!)
49 int blackboxPrintf(const char *fmt, ...)
51 va_list va;
53 va_start(va, fmt);
55 const int written = blackboxPrintfv(fmt, va);
57 va_end(va);
59 return written;
63 * printf a Blackbox header line with a leading "H " and trailing "\n" added automatically. blackboxHeaderBudget is
64 * decreased to account for the number of bytes written.
66 void blackboxPrintfHeaderLine(const char *name, const char *fmt, ...)
68 va_list va;
70 blackboxWrite('H');
71 blackboxWrite(' ');
72 blackboxWriteString(name);
73 blackboxWrite(':');
75 va_start(va, fmt);
77 const int written = blackboxPrintfv(fmt, va);
79 va_end(va);
81 blackboxWrite('\n');
83 blackboxHeaderBudget -= written + 3;
86 /**
87 * Write an unsigned integer to the blackbox serial port using variable byte encoding.
89 void blackboxWriteUnsignedVB(uint32_t value)
91 //While this isn't the final byte (we can only write 7 bits at a time)
92 while (value > 127) {
93 blackboxWrite((uint8_t) (value | 0x80)); // Set the high bit to mean "more bytes follow"
94 value >>= 7;
96 blackboxWrite(value);
99 /**
100 * Write a signed integer to the blackbox serial port using ZigZig and variable byte encoding.
102 void blackboxWriteSignedVB(int32_t value)
104 //ZigZag encode to make the value always positive
105 blackboxWriteUnsignedVB(zigzagEncode(value));
108 void blackboxWriteSignedVBArray(int32_t *array, int count)
110 for (int i = 0; i < count; i++) {
111 blackboxWriteSignedVB(array[i]);
115 void blackboxWriteSigned16VBArray(int16_t *array, int count)
117 for (int i = 0; i < count; i++) {
118 blackboxWriteSignedVB(array[i]);
122 void blackboxWriteS16(int16_t value)
124 blackboxWrite(value & 0xFF);
125 blackboxWrite((value >> 8) & 0xFF);
129 * Write a 2 bit tag followed by 3 signed fields of 2, 4, 6 or 32 bits
131 void blackboxWriteTag2_3S32(int32_t *values)
133 static const int NUM_FIELDS = 3;
135 //Need to be enums rather than const ints if we want to switch on them (due to being C)
136 enum {
137 BITS_2 = 0,
138 BITS_4 = 1,
139 BITS_6 = 2,
140 BITS_32 = 3
143 enum {
144 BYTES_1 = 0,
145 BYTES_2 = 1,
146 BYTES_3 = 2,
147 BYTES_4 = 3
150 int selector = BITS_2, selector2;
153 * Find out how many bits the largest value requires to encode, and use it to choose one of the packing schemes
154 * below:
156 * Selector possibilities
158 * 2 bits per field ss11 2233,
159 * 4 bits per field ss00 1111 2222 3333
160 * 6 bits per field ss11 1111 0022 2222 0033 3333
161 * 32 bits per field sstt tttt followed by fields of various byte counts
163 for (int x = 0; x < NUM_FIELDS; x++) {
164 //Require more than 6 bits?
165 if (values[x] >= 32 || values[x] < -32) {
166 selector = BITS_32;
167 break;
170 //Require more than 4 bits?
171 if (values[x] >= 8 || values[x] < -8) {
172 if (selector < BITS_6) {
173 selector = BITS_6;
175 } else if (values[x] >= 2 || values[x] < -2) { //Require more than 2 bits?
176 if (selector < BITS_4) {
177 selector = BITS_4;
182 switch (selector) {
183 case BITS_2:
184 blackboxWrite((selector << 6) | ((values[0] & 0x03) << 4) | ((values[1] & 0x03) << 2) | (values[2] & 0x03));
185 break;
186 case BITS_4:
187 blackboxWrite((selector << 6) | (values[0] & 0x0F));
188 blackboxWrite((values[1] << 4) | (values[2] & 0x0F));
189 break;
190 case BITS_6:
191 blackboxWrite((selector << 6) | (values[0] & 0x3F));
192 blackboxWrite((uint8_t)values[1]);
193 blackboxWrite((uint8_t)values[2]);
194 break;
195 case BITS_32:
197 * Do another round to compute a selector for each field, assuming that they are at least 8 bits each
199 * Selector2 field possibilities
200 * 0 - 8 bits
201 * 1 - 16 bits
202 * 2 - 24 bits
203 * 3 - 32 bits
205 selector2 = 0;
207 //Encode in reverse order so the first field is in the low bits:
208 for (int x = NUM_FIELDS - 1; x >= 0; x--) {
209 selector2 <<= 2;
211 if (values[x] < 128 && values[x] >= -128) {
212 selector2 |= BYTES_1;
213 } else if (values[x] < 32768 && values[x] >= -32768) {
214 selector2 |= BYTES_2;
215 } else if (values[x] < 8388608 && values[x] >= -8388608) {
216 selector2 |= BYTES_3;
217 } else {
218 selector2 |= BYTES_4;
222 //Write the selectors
223 blackboxWrite((selector << 6) | selector2);
225 //And now the values according to the selectors we picked for them
226 for (int x = 0; x < NUM_FIELDS; x++, selector2 >>= 2) {
227 switch (selector2 & 0x03) {
228 case BYTES_1:
229 blackboxWrite(values[x]);
230 break;
231 case BYTES_2:
232 blackboxWrite(values[x]);
233 blackboxWrite(values[x] >> 8);
234 break;
235 case BYTES_3:
236 blackboxWrite(values[x]);
237 blackboxWrite(values[x] >> 8);
238 blackboxWrite(values[x] >> 16);
239 break;
240 case BYTES_4:
241 blackboxWrite(values[x]);
242 blackboxWrite(values[x] >> 8);
243 blackboxWrite(values[x] >> 16);
244 blackboxWrite(values[x] >> 24);
245 break;
248 break;
253 * Write a 2 bit tag followed by 3 signed fields of 2, 554, 877 or 32 bits
255 int blackboxWriteTag2_3SVariable(int32_t *values)
257 static const int FIELD_COUNT = 3;
258 enum {
259 BITS_2 = 0,
260 BITS_554 = 1,
261 BITS_877 = 2,
262 BITS_32 = 3
265 enum {
266 BYTES_1 = 0,
267 BYTES_2 = 1,
268 BYTES_3 = 2,
269 BYTES_4 = 3
273 * Find out how many bits the largest value requires to encode, and use it to choose one of the packing schemes
274 * below:
276 * Selector possibilities
278 * 2 bits per field ss11 2233,
279 * 554 bits per field ss11 1112 2222 3333
280 * 877 bits per field ss11 1111 1122 2222 2333 3333
281 * 32 bits per field sstt tttt followed by fields of various byte counts
283 int selector = BITS_2;
284 int selector2 = 0;
285 // Require more than 877 bits?
286 if (values[0] >= 256 || values[0] < -256
287 || values[1] >= 128 || values[1] < -128
288 || values[2] >= 128 || values[2] < -128) {
289 selector = BITS_32;
290 // Require more than 554 bits?
291 } else if (values[0] >= 16 || values[0] < -16
292 || values[1] >= 16 || values[1] < -16
293 || values[2] >= 8 || values[2] < -8) {
294 selector = BITS_877;
295 // Require more than 2 bits?
296 } else if (values[0] >= 2 || values[0] < -2
297 || values[1] >= 2 || values[1] < -2
298 || values[2] >= 2 || values[2] < -2) {
299 selector = BITS_554;
302 switch (selector) {
303 case BITS_2:
304 blackboxWrite((selector << 6) | ((values[0] & 0x03) << 4) | ((values[1] & 0x03) << 2) | (values[2] & 0x03));
305 break;
306 case BITS_554:
307 // 554 bits per field ss11 1112 2222 3333
308 blackboxWrite((selector << 6) | ((values[0] & 0x1F) << 1) | ((values[1] & 0x1F) >> 4));
309 blackboxWrite(((values[1] & 0x0F) << 4) | (values[2] & 0x0F));
310 break;
311 case BITS_877:
312 // 877 bits per field ss11 1111 1122 2222 2333 3333
313 blackboxWrite((selector << 6) | ((values[0] & 0xFF) >> 2));
314 blackboxWrite(((values[0] & 0x03) << 6) | ((values[1] & 0x7F) >> 1));
315 blackboxWrite(((values[1] & 0x01) << 7) | (values[2] & 0x7F));
316 break;
317 case BITS_32:
319 * Do another round to compute a selector for each field, assuming that they are at least 8 bits each
321 * Selector2 field possibilities
322 * 0 - 8 bits
323 * 1 - 16 bits
324 * 2 - 24 bits
325 * 3 - 32 bits
327 selector2 = 0;
328 //Encode in reverse order so the first field is in the low bits:
329 for (int x = FIELD_COUNT - 1; x >= 0; x--) {
330 selector2 <<= 2;
332 if (values[x] < 128 && values[x] >= -128) {
333 selector2 |= BYTES_1;
334 } else if (values[x] < 32768 && values[x] >= -32768) {
335 selector2 |= BYTES_2;
336 } else if (values[x] < 8388608 && values[x] >= -8388608) {
337 selector2 |= BYTES_3;
338 } else {
339 selector2 |= BYTES_4;
343 //Write the selectors
344 blackboxWrite((selector << 6) | selector2);
346 //And now the values according to the selectors we picked for them
347 for (int x = 0; x < FIELD_COUNT; x++, selector2 >>= 2) {
348 switch (selector2 & 0x03) {
349 case BYTES_1:
350 blackboxWrite(values[x]);
351 break;
352 case BYTES_2:
353 blackboxWrite(values[x]);
354 blackboxWrite(values[x] >> 8);
355 break;
356 case BYTES_3:
357 blackboxWrite(values[x]);
358 blackboxWrite(values[x] >> 8);
359 blackboxWrite(values[x] >> 16);
360 break;
361 case BYTES_4:
362 blackboxWrite(values[x]);
363 blackboxWrite(values[x] >> 8);
364 blackboxWrite(values[x] >> 16);
365 blackboxWrite(values[x] >> 24);
366 break;
369 break;
371 return selector;
375 * Write an 8-bit selector followed by four signed fields of size 0, 4, 8 or 16 bits.
377 void blackboxWriteTag8_4S16(int32_t *values)
380 //Need to be enums rather than const ints if we want to switch on them (due to being C)
381 enum {
382 FIELD_ZERO = 0,
383 FIELD_4BIT = 1,
384 FIELD_8BIT = 2,
385 FIELD_16BIT = 3
388 uint8_t selector = 0;
389 //Encode in reverse order so the first field is in the low bits:
390 for (int x = 3; x >= 0; x--) {
391 selector <<= 2;
393 if (values[x] == 0) {
394 selector |= FIELD_ZERO;
395 } else if (values[x] < 8 && values[x] >= -8) {
396 selector |= FIELD_4BIT;
397 } else if (values[x] < 128 && values[x] >= -128) {
398 selector |= FIELD_8BIT;
399 } else {
400 selector |= FIELD_16BIT;
404 blackboxWrite(selector);
406 int nibbleIndex = 0;
407 uint8_t buffer = 0;
408 for (int x = 0; x < 4; x++, selector >>= 2) {
409 switch (selector & 0x03) {
410 case FIELD_ZERO:
411 //No-op
412 break;
413 case FIELD_4BIT:
414 if (nibbleIndex == 0) {
415 //We fill high-bits first
416 buffer = values[x] << 4;
417 nibbleIndex = 1;
418 } else {
419 blackboxWrite(buffer | (values[x] & 0x0F));
420 nibbleIndex = 0;
422 break;
423 case FIELD_8BIT:
424 if (nibbleIndex == 0) {
425 blackboxWrite(values[x]);
426 } else {
427 //Write the high bits of the value first (mask to avoid sign extension)
428 blackboxWrite(buffer | ((values[x] >> 4) & 0x0F));
429 //Now put the leftover low bits into the top of the next buffer entry
430 buffer = values[x] << 4;
432 break;
433 case FIELD_16BIT:
434 if (nibbleIndex == 0) {
435 //Write high byte first
436 blackboxWrite(values[x] >> 8);
437 blackboxWrite(values[x]);
438 } else {
439 //First write the highest 4 bits
440 blackboxWrite(buffer | ((values[x] >> 12) & 0x0F));
441 // Then the middle 8
442 blackboxWrite(values[x] >> 4);
443 //Only the smallest 4 bits are still left to write
444 buffer = values[x] << 4;
446 break;
449 //Anything left over to write?
450 if (nibbleIndex == 1) {
451 blackboxWrite(buffer);
456 * Write `valueCount` fields from `values` to the Blackbox using signed variable byte encoding. A 1-byte header is
457 * written first which specifies which fields are non-zero (so this encoding is compact when most fields are zero).
459 * valueCount must be 8 or less.
461 void blackboxWriteTag8_8SVB(int32_t *values, int valueCount)
463 uint8_t header;
465 if (valueCount > 0) {
466 //If we're only writing one field then we can skip the header
467 if (valueCount == 1) {
468 blackboxWriteSignedVB(values[0]);
469 } else {
470 //First write a one-byte header that marks which fields are non-zero
471 header = 0;
473 // First field should be in low bits of header
474 for (int i = valueCount - 1; i >= 0; i--) {
475 header <<= 1;
477 if (values[i] != 0) {
478 header |= 0x01;
482 blackboxWrite(header);
484 for (int i = 0; i < valueCount; i++) {
485 if (values[i] != 0) {
486 blackboxWriteSignedVB(values[i]);
493 /** Write unsigned integer **/
494 void blackboxWriteU32(int32_t value)
496 blackboxWrite(value & 0xFF);
497 blackboxWrite((value >> 8) & 0xFF);
498 blackboxWrite((value >> 16) & 0xFF);
499 blackboxWrite((value >> 24) & 0xFF);
502 /** Write float value in the integer form **/
503 void blackboxWriteFloat(float value)
505 blackboxWriteU32(castFloatBytesToInt(value));
507 #endif // BLACKBOX