Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/Blackbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,14 @@ The CLI command `blackbox` allows setting which Blackbox fields are recorded to
* `PEAKS_Y` - Yaw axis noise peak
* `SERVOS` - Servo outputs (for planes, tris, etc.)

On a board with two IMUs, `gyro_secondary_enabled` adds `gyroRaw2[0..2]`, the second
sensor's rates in deg/s, in the same body frame as `gyroRaw` and with that sensor's
own alignment already applied. It has no flag of its own here: that setting is the
switch, and nothing else puts the field in the log, not even another feature that
starts the second sensor for its own reasons. With the setting on, the flight
controller reads that sensor only while a log is being written. Include `GYRO_RAW` as well to compare the two sensors, since
`gyroADC` is filtered and `gyroRaw2` is not.

Usage:

* `blackbox` currently enabled Blackbox fields
Expand Down
10 changes: 10 additions & 0 deletions docs/Settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -2176,6 +2176,16 @@ Software based gyro main lowpass filter. Value is cutoff frequency (Hz)

---

### gyro_secondary_enabled

On a board with two IMUs, also log the one `gyro_to_use` did not select, to Blackbox as `gyroRaw2`. It is read only while it measures its zero after power-up and while a log is being written, one extra SPI transaction per gyro cycle, and never reaches attitude estimation or the PID loops. While this is off the second IMU is not initialised at all. Log `GYRO_RAW` as well to compare the two sensors.

| Default | Min | Max |
| --- | --- | --- |
| OFF | OFF | ON |

---

### gyro_to_use

On multi-gyro targets, allows to choose which gyro to use. 0 = first gyro, 1 = second gyro
Expand Down
39 changes: 38 additions & 1 deletion src/main/blackbox/blackbox.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,11 @@ static const blackboxDeltaFieldDefinition_t blackboxMainFields[] = {
{"gyroRaw", 0, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_GYRO_RAW},
{"gyroRaw", 1, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_GYRO_RAW},
{"gyroRaw", 2, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_GYRO_RAW},
#ifdef USE_DUAL_GYRO
{"gyroRaw2", 0, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_GYRO_SECONDARY},
{"gyroRaw2", 1, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_GYRO_SECONDARY},
{"gyroRaw2", 2, SIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(SIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_GYRO_SECONDARY},
#endif

{"gyroPeakRoll", 0, UNSIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(UNSIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_GYRO_PEAKS_ROLL},
{"gyroPeakRoll", 1, UNSIGNED, .Ipredict = PREDICT(0), .Iencode = ENCODING(UNSIGNED_VB), .Ppredict = PREDICT(AVERAGE_2), .Pencode = ENCODING(SIGNED_VB), FLIGHT_LOG_FIELD_CONDITION_GYRO_PEAKS_ROLL},
Expand Down Expand Up @@ -565,6 +570,9 @@ typedef struct blackboxMainState_s {
int16_t rcCommand[4];
int16_t gyroADC[XYZ_AXIS_COUNT];
int16_t gyroRaw[XYZ_AXIS_COUNT];
#ifdef USE_DUAL_GYRO
int16_t gyroRaw2[XYZ_AXIS_COUNT];
#endif

int16_t gyroPeaksRoll[DYN_NOTCH_PEAK_COUNT];
int16_t gyroPeaksPitch[DYN_NOTCH_PEAK_COUNT];
Expand Down Expand Up @@ -674,7 +682,9 @@ static struct {
// Cache for FLIGHT_LOG_FIELD_CONDITION_* test results:
static uint64_t blackboxConditionCache;

STATIC_ASSERT((sizeof(blackboxConditionCache) * 8) >= FLIGHT_LOG_FIELD_CONDITION_LAST, too_many_flight_log_conditions);
// The cache holds a bit for every condition up to and including LAST, which is NEVER, so
// it needs LAST + 1 bits. A dual-gyro target takes the 64th
STATIC_ASSERT((sizeof(blackboxConditionCache) * 8) > FLIGHT_LOG_FIELD_CONDITION_LAST, too_many_flight_log_conditions);

static uint32_t blackboxIFrameInterval;
static uint32_t blackboxIteration;
Expand Down Expand Up @@ -853,6 +863,13 @@ static bool testBlackboxConditionUncached(FlightLogFieldCondition condition)
case FLIGHT_LOG_FIELD_CONDITION_GYRO_RAW:
return blackboxIncludeFlag(BLACKBOX_FEATURE_GYRO_RAW);

#ifdef USE_DUAL_GYRO
case FLIGHT_LOG_FIELD_CONDITION_GYRO_SECONDARY:
// The setting that asks for the field, not merely a second sensor that is
// running: anything else that starts one would put it in the log as well
return gyroConfig()->gyro_secondary_enabled && gyro.secondaryInitialized;
#endif

case FLIGHT_LOG_FIELD_CONDITION_GYRO_PEAKS_ROLL:
return blackboxIncludeFlag(BLACKBOX_FEATURE_GYRO_PEAKS_ROLL);

Expand Down Expand Up @@ -921,6 +938,11 @@ static void blackboxSetState(BlackboxState newState)
;
}
blackboxState = newState;

#ifdef USE_DUAL_GYRO
// The second gyro is read only for the log, so only while there is one
gyroSetSecondaryLogging(newState > BLACKBOX_STATE_STOPPED);
Comment on lines +942 to +944

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

4. Paused logs still read the second gyro 🐞 Bug ➹ Performance

blackboxSetState() enables gyroSecondaryLogging for every state numerically above
BLACKBOX_STATE_STOPPED, rather than only BLACKBOX_STATE_RUNNING. Header generation, paused
logging, and shutdown therefore keep gyroUpdateSecondary() issuing SPI reads even though paused
and shutdown paths write no main frames that could contain gyroRaw2.
Agent Prompt
## Issue description
`gyroSetSecondaryLogging(newState > BLACKBOX_STATE_STOPPED)` enables the secondary IMU while headers are emitted, while Blackbox is paused, and while it is shutting down. Those states do not write main log frames, so the extra gyro transaction cannot produce `gyroRaw2` data and contradicts the intended logging-only sampling behavior.

## Fix Focus Areas
- src/main/blackbox/blackbox.c[942-944]

## Recommended Fix
Enable secondary sampling only when entering `BLACKBOX_STATE_RUNNING`, and disable it for every other state. Preserve the existing calibration behavior: `gyroUpdateSecondary()` already continues reading an incomplete secondary calibration even when logging is disabled.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

#endif
}

static void writeIntraframe(void)
Expand Down Expand Up @@ -1032,6 +1054,12 @@ static void writeIntraframe(void)
blackboxWriteSigned16VBArray(blackboxCurrent->gyroRaw, XYZ_AXIS_COUNT);
}

#ifdef USE_DUAL_GYRO
if (testBlackboxCondition(FLIGHT_LOG_FIELD_CONDITION_GYRO_SECONDARY)) {
blackboxWriteSigned16VBArray(blackboxCurrent->gyroRaw2, XYZ_AXIS_COUNT);
}
#endif

if (testBlackboxCondition(FLIGHT_LOG_FIELD_CONDITION_GYRO_PEAKS_ROLL)) {
blackboxWriteUnsignedVB(blackboxCurrent->gyroPeaksRoll[0]);
blackboxWriteUnsignedVB(blackboxCurrent->gyroPeaksRoll[1]);
Expand Down Expand Up @@ -1306,6 +1334,12 @@ static void writeInterframe(void)
blackboxWriteArrayUsingAveragePredictor16(offsetof(blackboxMainState_t, gyroRaw), XYZ_AXIS_COUNT);
}

#ifdef USE_DUAL_GYRO
if (testBlackboxCondition(FLIGHT_LOG_FIELD_CONDITION_GYRO_SECONDARY)) {
blackboxWriteArrayUsingAveragePredictor16(offsetof(blackboxMainState_t, gyroRaw2), XYZ_AXIS_COUNT);
}
#endif

if (testBlackboxCondition(FLIGHT_LOG_FIELD_CONDITION_GYRO_PEAKS_ROLL)) {
blackboxWriteArrayUsingAveragePredictor16(offsetof(blackboxMainState_t, gyroPeaksRoll), DYN_NOTCH_PEAK_COUNT);
}
Expand Down Expand Up @@ -1759,6 +1793,9 @@ static void loadMainState(timeUs_t currentTimeUs)
blackboxCurrent->gyroADC[i] = lrintf(gyro.gyroADCf[i]);
blackboxCurrent->accADC[i] = constrain(lrintf(acc.accADCf[i] * acc.dev.acc_1G), -32678, 32767);
blackboxCurrent->gyroRaw[i] = lrintf(gyro.gyroRaw[i]);
#ifdef USE_DUAL_GYRO
blackboxCurrent->gyroRaw2[i] = lrintf(gyro.gyroRaw2[i]);
#endif

#ifdef USE_DYNAMIC_FILTERS
for (uint8_t i = 0; i < DYN_NOTCH_PEAK_COUNT ; i++) {
Expand Down
3 changes: 3 additions & 0 deletions src/main/blackbox/blackbox_fielddefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ typedef enum FlightLogFieldCondition {
FLIGHT_LOG_FIELD_CONDITION_RC_DATA,
FLIGHT_LOG_FIELD_CONDITION_RC_COMMAND,
FLIGHT_LOG_FIELD_CONDITION_GYRO_RAW,
#ifdef USE_DUAL_GYRO
FLIGHT_LOG_FIELD_CONDITION_GYRO_SECONDARY,
#endif

FLIGHT_LOG_FIELD_CONDITION_GYRO_PEAKS_ROLL,
FLIGHT_LOG_FIELD_CONDITION_GYRO_PEAKS_PITCH,
Expand Down
6 changes: 6 additions & 0 deletions src/main/fc/settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,12 @@ groups:
min: 0
max: 2
default_value: 0
- name: gyro_secondary_enabled
description: "On a board with two IMUs, also log the one `gyro_to_use` did not select, to Blackbox as `gyroRaw2`. It is read only while it measures its zero after power-up and while a log is being written, one extra SPI transaction per gyro cycle, and never reaches attitude estimation or the PID loops. While this is off the second IMU is not initialised at all. Log `GYRO_RAW` as well to compare the two sensors."
default_value: OFF
condition: USE_DUAL_GYRO
field: gyro_secondary_enabled
type: bool
- name: setpoint_kalman_enabled
description: "Enable Kalman filter on the gyro data"
default_value: ON
Expand Down
Loading
Loading