From ff288468297a013423319492a43b2146432e78e9 Mon Sep 17 00:00:00 2001 From: Nicholas Sherlock Date: Tue, 24 Feb 2015 09:42:38 +1300 Subject: [PATCH 1/3] Save around 300 bytes of flash by improving Blackbox field name storage Also add a bit more Blackbox code documentation --- src/main/blackbox/blackbox.c | 145 ++++++++++++++++++++------------ src/main/blackbox/blackbox_io.c | 17 +++- 2 files changed, 106 insertions(+), 56 deletions(-) diff --git a/src/main/blackbox/blackbox.c b/src/main/blackbox/blackbox.c index 0ee1234768..0461dac74a 100644 --- a/src/main/blackbox/blackbox.c +++ b/src/main/blackbox/blackbox.c @@ -79,6 +79,7 @@ #include "blackbox_io.h" #define BLACKBOX_I_INTERVAL 32 +#define BLACKBOX_SHUTDOWN_TIMEOUT_MILLIS 200 #define ARRAY_LENGTH(x) (sizeof((x))/sizeof((x)[0])) @@ -132,11 +133,17 @@ static const char* const blackboxGPSHHeaderNames[] = { /* All field definition structs should look like this (but with longer arrs): */ typedef struct blackboxFieldDefinition_t { const char *name; + // If the field name has a number to be included in square brackets [1] afterwards, set it here, or -1 for no brackets: + int8_t fieldNameIndex; + + // Each member of this array will be the value to print for this field for the given header index uint8_t arr[1]; } blackboxFieldDefinition_t; typedef struct blackboxMainFieldDefinition_t { const char *name; + int8_t fieldNameIndex; + uint8_t isSigned; uint8_t Ipredict; uint8_t Iencode; @@ -147,6 +154,8 @@ typedef struct blackboxMainFieldDefinition_t { typedef struct blackboxGPSFieldDefinition_t { const char *name; + int8_t fieldNameIndex; + uint8_t isSigned; uint8_t predict; uint8_t encode; @@ -161,73 +170,76 @@ typedef struct blackboxGPSFieldDefinition_t { */ static const blackboxMainFieldDefinition_t blackboxMainFields[] = { /* loopIteration doesn't appear in P frames since it always increments */ - {"loopIteration", UNSIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(UNSIGNED_VB), .Ppredict = PREDICT(INC), .Pencode = FLIGHT_LOG_FIELD_ENCODING_NULL, CONDITION(ALWAYS)}, + {"loopIteration",-1, UNSIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(UNSIGNED_VB), .Ppredict = PREDICT(INC), .Pencode = FLIGHT_LOG_FIELD_ENCODING_NULL, CONDITION(ALWAYS)}, /* Time advances pretty steadily so the P-frame prediction is a straight line */ - {"time", UNSIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(UNSIGNED_VB), .Ppredict = PREDICT(STRAIGHT_LINE), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, - {"axisP[0]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, - {"axisP[1]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, - {"axisP[2]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, + {"time", -1, UNSIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(UNSIGNED_VB), .Ppredict = PREDICT(STRAIGHT_LINE), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, + {"axisP", 0, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, + {"axisP", 1, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, + {"axisP", 2, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, /* I terms get special packed encoding in P frames: */ - {"axisI[0]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG2_3S32), CONDITION(ALWAYS)}, - {"axisI[1]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG2_3S32), CONDITION(ALWAYS)}, - {"axisI[2]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG2_3S32), CONDITION(ALWAYS)}, - {"axisD[0]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(SIGNED_VB), CONDITION(NONZERO_PID_D_0)}, - {"axisD[1]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(SIGNED_VB), CONDITION(NONZERO_PID_D_1)}, - {"axisD[2]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(SIGNED_VB), CONDITION(NONZERO_PID_D_2)}, + {"axisI", 0, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG2_3S32), CONDITION(ALWAYS)}, + {"axisI", 1, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG2_3S32), CONDITION(ALWAYS)}, + {"axisI", 2, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG2_3S32), CONDITION(ALWAYS)}, + {"axisD", 0, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(SIGNED_VB), CONDITION(NONZERO_PID_D_0)}, + {"axisD", 1, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(SIGNED_VB), CONDITION(NONZERO_PID_D_1)}, + {"axisD", 2, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(SIGNED_VB), CONDITION(NONZERO_PID_D_2)}, /* rcCommands are encoded together as a group in P-frames: */ - {"rcCommand[0]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_4S16), CONDITION(ALWAYS)}, - {"rcCommand[1]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_4S16), CONDITION(ALWAYS)}, - {"rcCommand[2]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_4S16), CONDITION(ALWAYS)}, + {"rcCommand", 0, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_4S16), CONDITION(ALWAYS)}, + {"rcCommand", 1, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_4S16), CONDITION(ALWAYS)}, + {"rcCommand", 2, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_4S16), CONDITION(ALWAYS)}, /* Throttle is always in the range [minthrottle..maxthrottle]: */ - {"rcCommand[3]", UNSIGNED, .Ipredict = PREDICT(MINTHROTTLE), .Iencode = ENCODING(UNSIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_4S16), CONDITION(ALWAYS)}, + {"rcCommand", 3, UNSIGNED, .Ipredict = PREDICT(MINTHROTTLE), .Iencode = ENCODING(UNSIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_4S16), CONDITION(ALWAYS)}, + + {"vbatLatest", -1, UNSIGNED, .Ipredict = PREDICT(VBATREF), .Iencode = ENCODING(NEG_14BIT), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_8SVB), FLIGHT_LOG_FIELD_CONDITION_VBAT}, + {"amperageLatest",-1, UNSIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(UNSIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_8SVB), FLIGHT_LOG_FIELD_CONDITION_AMPERAGE}, - {"vbatLatest", UNSIGNED, .Ipredict = PREDICT(VBATREF), .Iencode = ENCODING(NEG_14BIT), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_8SVB), FLIGHT_LOG_FIELD_CONDITION_VBAT}, - {"amperageLatest",UNSIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(UNSIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_8SVB), FLIGHT_LOG_FIELD_CONDITION_AMPERAGE}, #ifdef MAG - {"magADC[0]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_8SVB), FLIGHT_LOG_FIELD_CONDITION_MAG}, - {"magADC[1]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_8SVB), FLIGHT_LOG_FIELD_CONDITION_MAG}, - {"magADC[2]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_8SVB), FLIGHT_LOG_FIELD_CONDITION_MAG}, + {"magADC", 0, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_8SVB), FLIGHT_LOG_FIELD_CONDITION_MAG}, + {"magADC", 1, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_8SVB), FLIGHT_LOG_FIELD_CONDITION_MAG}, + {"magADC", 2, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_8SVB), FLIGHT_LOG_FIELD_CONDITION_MAG}, #endif #ifdef BARO - {"BaroAlt", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_8SVB), FLIGHT_LOG_FIELD_CONDITION_BARO}, + {"BaroAlt", -1, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(TAG8_8SVB), FLIGHT_LOG_FIELD_CONDITION_BARO}, #endif /* Gyros and accelerometers base their P-predictions on the average of the previous 2 frames to reduce noise impact */ - {"gyroData[0]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, - {"gyroData[1]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, - {"gyroData[2]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, - {"accSmooth[0]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, - {"accSmooth[1]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, - {"accSmooth[2]", SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, + {"gyroData", 0, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, + {"gyroData", 1, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, + {"gyroData", 2, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, + {"accSmooth", 0, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, + {"accSmooth", 1, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, + {"accSmooth", 2, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, /* Motors only rarely drops under minthrottle (when stick falls below mincommand), so predict minthrottle for it and use *unsigned* encoding (which is large for negative numbers but more compact for positive ones): */ - {"motor[0]", UNSIGNED, .Ipredict = PREDICT(MINTHROTTLE), .Iencode = ENCODING(UNSIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_1)}, + {"motor", 0, UNSIGNED, .Ipredict = PREDICT(MINTHROTTLE), .Iencode = ENCODING(UNSIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_1)}, /* Subsequent motors base their I-frame values on the first one, P-frame values on the average of last two frames: */ - {"motor[1]", UNSIGNED, .Ipredict = PREDICT(MOTOR_0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_2)}, - {"motor[2]", UNSIGNED, .Ipredict = PREDICT(MOTOR_0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_3)}, - {"motor[3]", UNSIGNED, .Ipredict = PREDICT(MOTOR_0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_4)}, - {"motor[4]", UNSIGNED, .Ipredict = PREDICT(MOTOR_0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_5)}, - {"motor[5]", UNSIGNED, .Ipredict = PREDICT(MOTOR_0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_6)}, - {"motor[6]", UNSIGNED, .Ipredict = PREDICT(MOTOR_0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_7)}, - {"motor[7]", UNSIGNED, .Ipredict = PREDICT(MOTOR_0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_8)}, - {"servo[5]", UNSIGNED, .Ipredict = PREDICT(1500), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(SIGNED_VB), CONDITION(TRICOPTER)} + {"motor", 1, UNSIGNED, .Ipredict = PREDICT(MOTOR_0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_2)}, + {"motor", 2, UNSIGNED, .Ipredict = PREDICT(MOTOR_0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_3)}, + {"motor", 3, UNSIGNED, .Ipredict = PREDICT(MOTOR_0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_4)}, + {"motor", 4, UNSIGNED, .Ipredict = PREDICT(MOTOR_0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_5)}, + {"motor", 5, UNSIGNED, .Ipredict = PREDICT(MOTOR_0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_6)}, + {"motor", 6, UNSIGNED, .Ipredict = PREDICT(MOTOR_0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_7)}, + {"motor", 7, UNSIGNED, .Ipredict = PREDICT(MOTOR_0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), CONDITION(AT_LEAST_MOTORS_8)}, + + /* Tricopter tail servo */ + {"servo", 5, UNSIGNED, .Ipredict = PREDICT(1500), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(PREVIOUS), .Pencode = ENCODING(SIGNED_VB), CONDITION(TRICOPTER)} }; #ifdef GPS // GPS position/vel frame static const blackboxGPSFieldDefinition_t blackboxGpsGFields[] = { - {"time", UNSIGNED, PREDICT(LAST_MAIN_FRAME_TIME), ENCODING(UNSIGNED_VB), CONDITION(NOT_LOGGING_EVERY_FRAME)}, - {"GPS_numSat", UNSIGNED, PREDICT(0), ENCODING(UNSIGNED_VB), CONDITION(ALWAYS)}, - {"GPS_coord[0]", SIGNED, PREDICT(HOME_COORD), ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, - {"GPS_coord[1]", SIGNED, PREDICT(HOME_COORD), ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, - {"GPS_altitude", UNSIGNED, PREDICT(0), ENCODING(UNSIGNED_VB), CONDITION(ALWAYS)}, - {"GPS_speed", UNSIGNED, PREDICT(0), ENCODING(UNSIGNED_VB), CONDITION(ALWAYS)}, - {"GPS_ground_course",UNSIGNED, PREDICT(0), ENCODING(UNSIGNED_VB), CONDITION(ALWAYS)} + {"time", -1, UNSIGNED, PREDICT(LAST_MAIN_FRAME_TIME), ENCODING(UNSIGNED_VB), CONDITION(NOT_LOGGING_EVERY_FRAME)}, + {"GPS_numSat", -1, UNSIGNED, PREDICT(0), ENCODING(UNSIGNED_VB), CONDITION(ALWAYS)}, + {"GPS_coord", 0, SIGNED, PREDICT(HOME_COORD), ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, + {"GPS_coord", 1, SIGNED, PREDICT(HOME_COORD), ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, + {"GPS_altitude", -1, UNSIGNED, PREDICT(0), ENCODING(UNSIGNED_VB), CONDITION(ALWAYS)}, + {"GPS_speed", -1, UNSIGNED, PREDICT(0), ENCODING(UNSIGNED_VB), CONDITION(ALWAYS)}, + {"GPS_ground_course", -1, UNSIGNED, PREDICT(0), ENCODING(UNSIGNED_VB), CONDITION(ALWAYS)} }; // GPS home frame static const blackboxGPSFieldDefinition_t blackboxGpsHFields[] = { - {"GPS_home[0]", SIGNED, PREDICT(0), ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, - {"GPS_home[1]", SIGNED, PREDICT(0), ENCODING(SIGNED_VB), CONDITION(ALWAYS)} + {"GPS_home", 0, SIGNED, PREDICT(0), ENCODING(SIGNED_VB), CONDITION(ALWAYS)}, + {"GPS_home", 1, SIGNED, PREDICT(0), ENCODING(SIGNED_VB), CONDITION(ALWAYS)} }; #endif @@ -284,6 +296,7 @@ static uint32_t blackboxPFrameIndex, blackboxIFrameIndex; * to encode: */ static uint16_t vbatReference; + static gpsState_t gpsHistory; // Keep a history of length 2, plus a buffer for MW to store the new values into @@ -604,6 +617,9 @@ static void validateBlackboxConfig() masterConfig.blackbox_rate_num = 1; masterConfig.blackbox_rate_denom = 1; } else { + /* Reduce the fraction the user entered as much as possible (makes the recorded/skipped frame pattern repeat + * itself more frequently) + */ div = gcd(masterConfig.blackbox_rate_num, masterConfig.blackbox_rate_denom); masterConfig.blackbox_rate_num /= div; @@ -615,6 +631,9 @@ static void validateBlackboxConfig() } } +/** + * Start Blackbox logging if it is not already running. Intended to be called upon arming. + */ void startBlackbox(void) { if (blackboxState == BLACKBOX_STATE_STOPPED) { @@ -820,6 +839,15 @@ static bool sendFieldDefinition(const char * const *headerNames, unsigned int he // The first header is a field name if (xmitState.headerIndex == 0) { charsWritten += blackboxPrint(def->name); + + // Do we need to print an index in brackets after the name? + if (def->fieldNameIndex != -1) { + blackboxWrite('['); + // Assume the field index is a single digit: + blackboxWrite(def->fieldNameIndex + '0'); + blackboxWrite(']'); + charsWritten += 3; + } } else { //The other headers are integers if (def->arr[xmitState.headerIndex - 1] >= 10) { @@ -873,26 +901,24 @@ static bool blackboxWriteSysinfo() //Shouldn't ever get here break; case 1: - blackboxPrintf("H Firmware type:Cleanflight\n"); - - xmitState.u.serialBudget -= strlen("H Firmware type:Cleanflight\n"); + xmitState.u.serialBudget -= blackboxPrint("H Firmware type:Cleanflight\n"); break; case 2: blackboxPrintf("H Firmware revision:%s\n", shortGitRevision); - /* Don't need to be super exact about the budget so don't mind the fact that we're including the length of - * the placeholder "%s" - */ - xmitState.u.serialBudget -= strlen("H Firmware revision:%s\n") + strlen(shortGitRevision); + xmitState.u.serialBudget -= strlen("H Firmware revision:\n") + strlen(shortGitRevision); break; case 3: blackboxPrintf("H Firmware date:%s %s\n", buildDate, buildTime); - xmitState.u.serialBudget -= strlen("H Firmware date:%s %s\n") + strlen(buildDate) + strlen(buildTime); + xmitState.u.serialBudget -= strlen("H Firmware date: \n") + strlen(buildDate) + strlen(buildTime); break; case 4: blackboxPrintf("H P interval:%d/%d\n", masterConfig.blackbox_rate_num, masterConfig.blackbox_rate_denom); + /* Don't need to be super exact about the budget so don't mind the fact that we're using the length of + * the placeholder "%d" instead of the actual integer size. + */ xmitState.u.serialBudget -= strlen("H P interval:%d/%d\n"); break; case 5: @@ -914,7 +940,7 @@ static bool blackboxWriteSysinfo() floatConvert.f = gyro.scale; blackboxPrintf("H gyro.scale:0x%x\n", floatConvert.u); - xmitState.u.serialBudget -= strlen("H gyro.scale:0x%x\n") + 6; + xmitState.u.serialBudget -= strlen("H gyro.scale:0x\n") + 6; break; case 9: blackboxPrintf("H acc_1G:%u\n", acc_1G); @@ -1014,6 +1040,9 @@ static void blackboxPlaySyncBeep() blackboxLogEvent(FLIGHT_LOG_EVENT_SYNC_BEEP, (flightLogEventData_t *) &eventData); } +/** + * Call each flight loop iteration to perform blackbox logging. + */ void handleBlackbox(void) { int i; @@ -1086,6 +1115,9 @@ void handleBlackbox(void) loadBlackboxState(); writeIntraframe(); } else { + /* Adding a magic shift of "masterConfig.blackbox_rate_num - 1" in here creates a better spread of + * recorded / skipped frames when the I frame's position is considered: + */ if ((blackboxPFrameIndex + masterConfig.blackbox_rate_num - 1) % masterConfig.blackbox_rate_denom < masterConfig.blackbox_rate_num) { loadBlackboxState(); writeInterframe(); @@ -1130,7 +1162,7 @@ void handleBlackbox(void) * * Don't wait longer than it could possibly take if something funky happens. */ - if (millis() > xmitState.u.startTime + 200 || blackboxDeviceFlush()) { + if (millis() > xmitState.u.startTime + BLACKBOX_SHUTDOWN_TIMEOUT_MILLIS || blackboxDeviceFlush()) { blackboxDeviceClose(); blackboxSetState(BLACKBOX_STATE_STOPPED); } @@ -1150,6 +1182,9 @@ static bool canUseBlackboxWithCurrentConfiguration(void) return feature(FEATURE_BLACKBOX); } +/** + * Call during system startup to initialize the blackbox. + */ void initBlackbox(void) { if (canUseBlackboxWithCurrentConfiguration()) { diff --git a/src/main/blackbox/blackbox_io.c b/src/main/blackbox/blackbox_io.c index 5c03dc34bc..9ad14bae6c 100644 --- a/src/main/blackbox/blackbox_io.c +++ b/src/main/blackbox/blackbox_io.c @@ -144,13 +144,25 @@ void blackboxWriteUnsignedVB(uint32_t value) blackboxWrite(value); } +/** + * ZigZag encoding maps all values of a signed integer into those of an unsigned integer in such + * a way that numbers of small absolute value correspond to small integers in the result. + * + * (Compared to just casting a signed to an unsigned which creates huge resulting numbers for + * small negative integers). + */ +static uint32_t zigzagEncode(int32_t value) +{ + return (uint32_t)((value << 1) ^ (value >> 31)); +} + /** * Write a signed integer to the blackbox serial port using ZigZig and variable byte encoding. */ void blackboxWriteSignedVB(int32_t value) { //ZigZag encode to make the value always positive - blackboxWriteUnsignedVB((uint32_t)((value << 1) ^ (value >> 31))); + blackboxWriteUnsignedVB(zigzagEncode(value)); } void blackboxWriteS16(int16_t value) @@ -497,6 +509,9 @@ void blackboxDeviceClose(void) mspAllocateSerialPorts(&masterConfig.serialConfig); } break; + case BLACKBOX_DEVICE_FLASH: + // No-op since the flash doesn't have a "close" and there's nobody else to hand control of it to. + break; } } From aae91411200acd61477cb5452b6fd401f6d1cd59 Mon Sep 17 00:00:00 2001 From: Nicholas Sherlock Date: Tue, 24 Feb 2015 10:19:11 +1300 Subject: [PATCH 2/3] Fix blackbox compile bug on targets without flash --- src/main/blackbox/blackbox_io.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/blackbox/blackbox_io.c b/src/main/blackbox/blackbox_io.c index 9ad14bae6c..fa6f66d6d6 100644 --- a/src/main/blackbox/blackbox_io.c +++ b/src/main/blackbox/blackbox_io.c @@ -509,9 +509,11 @@ void blackboxDeviceClose(void) mspAllocateSerialPorts(&masterConfig.serialConfig); } break; +#ifdef USE_FLASHFS case BLACKBOX_DEVICE_FLASH: // No-op since the flash doesn't have a "close" and there's nobody else to hand control of it to. break; +#endif } } From 451d3d7ba2e5381def6ff684471b4a3fbd8e2c68 Mon Sep 17 00:00:00 2001 From: Nicholas Sherlock Date: Thu, 26 Feb 2015 13:27:09 +1300 Subject: [PATCH 3/3] Move zigzag encoding function to new common/encoding.c file, add tests --- Makefile | 1 + src/main/blackbox/blackbox.c | 9 +--- src/main/blackbox/blackbox_io.c | 15 +----- src/main/blackbox/blackbox_io.h | 2 +- src/main/common/encoding.c | 31 +++++++++++ src/main/common/encoding.h | 23 ++++++++ src/test/Makefile | 20 +++++++ src/test/unit/encoding_unittest.cc | 84 ++++++++++++++++++++++++++++++ 8 files changed, 164 insertions(+), 21 deletions(-) create mode 100644 src/main/common/encoding.c create mode 100644 src/main/common/encoding.h create mode 100644 src/test/unit/encoding_unittest.cc diff --git a/Makefile b/Makefile index f3f29edd4d..68f2ee1be5 100644 --- a/Makefile +++ b/Makefile @@ -199,6 +199,7 @@ COMMON_SRC = build_config.c \ common/maths.c \ common/printf.c \ common/typeconversion.c \ + common/encoding.c \ main.c \ mw.c \ flight/altitudehold.c \ diff --git a/src/main/blackbox/blackbox.c b/src/main/blackbox/blackbox.c index 0461dac74a..ae94762975 100644 --- a/src/main/blackbox/blackbox.c +++ b/src/main/blackbox/blackbox.c @@ -26,6 +26,7 @@ #include "common/maths.h" #include "common/axis.h" #include "common/color.h" +#include "common/encoding.h" #include "drivers/gpio.h" #include "drivers/sensor.h" @@ -878,11 +879,6 @@ static bool sendFieldDefinition(const char * const *headerNames, unsigned int he */ static bool blackboxWriteSysinfo() { - union floatConvert_t { - float f; - uint32_t u; - } floatConvert; - if (xmitState.headerIndex == 0) { xmitState.u.serialBudget = 0; xmitState.headerIndex = 1; @@ -937,8 +933,7 @@ static bool blackboxWriteSysinfo() xmitState.u.serialBudget -= strlen("H maxthrottle:%d\n"); break; case 8: - floatConvert.f = gyro.scale; - blackboxPrintf("H gyro.scale:0x%x\n", floatConvert.u); + blackboxPrintf("H gyro.scale:0x%x\n", castFloatBytesToInt(gyro.scale)); xmitState.u.serialBudget -= strlen("H gyro.scale:0x\n") + 6; break; diff --git a/src/main/blackbox/blackbox_io.c b/src/main/blackbox/blackbox_io.c index fa6f66d6d6..ac8bf5ef49 100644 --- a/src/main/blackbox/blackbox_io.c +++ b/src/main/blackbox/blackbox_io.c @@ -4,12 +4,13 @@ #include "blackbox_io.h" -#include "platform.h" #include "version.h" +#include "build_config.h" #include "common/maths.h" #include "common/axis.h" #include "common/color.h" +#include "common/encoding.h" #include "drivers/gpio.h" #include "drivers/sensor.h" @@ -144,18 +145,6 @@ void blackboxWriteUnsignedVB(uint32_t value) blackboxWrite(value); } -/** - * ZigZag encoding maps all values of a signed integer into those of an unsigned integer in such - * a way that numbers of small absolute value correspond to small integers in the result. - * - * (Compared to just casting a signed to an unsigned which creates huge resulting numbers for - * small negative integers). - */ -static uint32_t zigzagEncode(int32_t value) -{ - return (uint32_t)((value << 1) ^ (value >> 31)); -} - /** * Write a signed integer to the blackbox serial port using ZigZig and variable byte encoding. */ diff --git a/src/main/blackbox/blackbox_io.h b/src/main/blackbox/blackbox_io.h index 62d5909986..547850739c 100644 --- a/src/main/blackbox/blackbox_io.h +++ b/src/main/blackbox/blackbox_io.h @@ -20,7 +20,7 @@ #include #include -#include "target.h" +#include "platform.h" typedef enum BlackboxDevice { BLACKBOX_DEVICE_SERIAL = 0, diff --git a/src/main/common/encoding.c b/src/main/common/encoding.c new file mode 100644 index 0000000000..3823e862a7 --- /dev/null +++ b/src/main/common/encoding.c @@ -0,0 +1,31 @@ +#include "encoding.h" + +/** + * Cast the in-memory representation of the given float directly to an int. + * + * This is useful for printing the hex representation of a float number (which is considerably cheaper + * than a full decimal float formatter, in both code size and output length). + */ +uint32_t castFloatBytesToInt(float f) +{ + union floatConvert_t { + float f; + uint32_t u; + } floatConvert; + + floatConvert.f = f; + + return floatConvert.u; +} + +/** + * ZigZag encoding maps all values of a signed integer into those of an unsigned integer in such + * a way that numbers of small absolute value correspond to small integers in the result. + * + * (Compared to just casting a signed to an unsigned which creates huge resulting numbers for + * small negative integers). + */ +uint32_t zigzagEncode(int32_t value) +{ + return (uint32_t)((value << 1) ^ (value >> 31)); +} diff --git a/src/main/common/encoding.h b/src/main/common/encoding.h new file mode 100644 index 0000000000..e0681ed2a6 --- /dev/null +++ b/src/main/common/encoding.h @@ -0,0 +1,23 @@ +/* + * This file is part of Cleanflight. + * + * Cleanflight is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Cleanflight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Cleanflight. If not, see . + */ + +#pragma once + +#include + +uint32_t castFloatBytesToInt(float f); +uint32_t zigzagEncode(int32_t value); diff --git a/src/test/Makefile b/src/test/Makefile index 5ea38fe9f0..6575c48a1a 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -53,6 +53,7 @@ TESTS = \ rc_controls_unittest \ ledstrip_unittest \ ws2811_unittest \ + encoding_unittest \ lowpass_unittest # All Google Test headers. Usually you shouldn't change this @@ -130,6 +131,25 @@ battery_unittest : \ $(OBJECT_DIR)/gtest_main.a $(CXX) $(CXX_FLAGS) -lpthread $^ -o $(OBJECT_DIR)/$@ + +$(OBJECT_DIR)/common/encoding.o : $(USER_DIR)/common/encoding.c $(USER_DIR)/common/encoding.h $(GTEST_HEADERS) + @mkdir -p $(dir $@) + $(CC) $(C_FLAGS) $(TEST_CFLAGS) -c $(USER_DIR)/common/encoding.c -o $@ + +$(OBJECT_DIR)/encoding_unittest.o : \ + $(TEST_DIR)/encoding_unittest.cc \ + $(USER_DIR)/common/encoding.h \ + $(GTEST_HEADERS) + + @mkdir -p $(dir $@) + $(CXX) $(CXX_FLAGS) $(TEST_CFLAGS) -c $(TEST_DIR)/encoding_unittest.cc -o $@ + +encoding_unittest : \ + $(OBJECT_DIR)/common/encoding.o \ + $(OBJECT_DIR)/encoding_unittest.o \ + $(OBJECT_DIR)/gtest_main.a + + $(CXX) $(CXX_FLAGS) -lpthread $^ -o $(OBJECT_DIR)/$@ $(OBJECT_DIR)/flight/imu.o : \ $(USER_DIR)/flight/imu.c \ diff --git a/src/test/unit/encoding_unittest.cc b/src/test/unit/encoding_unittest.cc new file mode 100644 index 0000000000..bf2c70b916 --- /dev/null +++ b/src/test/unit/encoding_unittest.cc @@ -0,0 +1,84 @@ +/* + * This file is part of Cleanflight. + * + * Cleanflight is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Cleanflight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Cleanflight. If not, see . + */ +#include + +extern "C" { + #include "common/encoding.h" +} + +#include "unittest_macros.h" +#include "gtest/gtest.h" + +typedef struct zigzagEncodingExpectation_t { + int32_t input; + uint32_t expected; +} zigzagEncodingExpectation_t; + +typedef struct floatToIntEncodingExpectation_t { + float input; + uint32_t expected; +} floatToIntEncodingExpectation_t; + +TEST(EncodingTest, ZigzagEncodingTest) +{ + // given + zigzagEncodingExpectation_t expectations[] = { + { 0, 0}, + {-1, 1}, + { 1, 2}, + {-2, 3}, + { 2, 4}, + + { 2147483646, 4294967292}, + {-2147483647, 4294967293}, + { 2147483647, 4294967294}, + {-2147483648, 4294967295}, + }; + int expectationCount = sizeof(expectations) / sizeof(expectations[0]); + + // expect + + for (int i = 0; i < expectationCount; i++) { + zigzagEncodingExpectation_t *expectation = &expectations[i]; + + EXPECT_EQ(expectation->expected, zigzagEncode(expectation->input)); + } +} + +TEST(EncodingTest, FloatToIntEncodingTest) +{ + // given + floatToIntEncodingExpectation_t expectations[] = { + {0.0, 0x00000000}, + {2.0, 0x40000000}, // Exponent should be in the top bits + {4.5, 0x40900000} + }; + int expectationCount = sizeof(expectations) / sizeof(expectations[0]); + + // expect + + for (int i = 0; i < expectationCount; i++) { + floatToIntEncodingExpectation_t *expectation = &expectations[i]; + + EXPECT_EQ(expectation->expected, castFloatBytesToInt(expectation->input)); + } +} + +// STUBS + +extern "C" { +}