cabana: enhance message heatmap visualization (#34239)

* enhance message heatmap visualization

* TODO

* improve log_factor

* typo

* bit_flip_counts
pull/34215/head
Dean Lee 4 months ago committed by GitHub
parent 9c9b273a3e
commit 9f3c2f0a37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 44
      tools/cabana/binaryview.cc
  2. 11
      tools/cabana/streams/abstractstream.cc
  3. 2
      tools/cabana/streams/abstractstream.h

@ -292,10 +292,15 @@ void BinaryViewModel::updateItem(int row, int col, uint8_t val, const QColor &co
} }
} }
// TODO:
// 1. Detect instability through frequent bit flips and highlight stable bits to indicate steady signals.
// 2. Track message sequence and timestamps to understand how patterns evolve.
// 3. Identify time-based or periodic bit state changes to spot recurring patterns.
// 4. Support multiple time windows for short-term and long-term analysis, helping to observe changes in different time frames.
void BinaryViewModel::updateState() { void BinaryViewModel::updateState() {
const auto &last_msg = can->lastMessage(msg_id); const auto &last_msg = can->lastMessage(msg_id);
const auto &binary = last_msg.dat; const auto &binary = last_msg.dat;
// data size may changed. // Handle size changes in binary data
if (binary.size() > row_count) { if (binary.size() > row_count) {
beginInsertRows({}, row_count, binary.size() - 1); beginInsertRows({}, row_count, binary.size() - 1);
row_count = binary.size(); row_count = binary.size();
@ -303,21 +308,36 @@ void BinaryViewModel::updateState() {
endInsertRows(); endInsertRows();
} }
const double max_f = 255.0; // Find the maximum bit flip count across the message
const double factor = 0.25; uint32_t max_bit_flip_count = 1; // Default to 1 to avoid division by zero
const double scaler = max_f / log2(1.0 + factor); for (const auto &row : last_msg.bit_flip_counts) {
for (int i = 0; i < binary.size(); ++i) { for (auto count : row) {
max_bit_flip_count = std::max(max_bit_flip_count, count);
}
}
const double max_alpha = 255.0;
const double min_alpha_with_signal = 25.0; // Base alpha for small flip counts
const double min_alpha_no_signal = 10.0; // Base alpha for small flip counts for no signal bits
const double log_factor = 1.0 + 0.2; // Factor for logarithmic scaling
const double log_scaler = max_alpha / log2(log_factor * max_bit_flip_count);
for (size_t i = 0; i < binary.size(); ++i) {
for (int j = 0; j < 8; ++j) { for (int j = 0; j < 8; ++j) {
auto &item = items[i * column_count + j]; auto &item = items[i * column_count + j];
int val = ((binary[i] >> (7 - j)) & 1) != 0 ? 1 : 0; int bit_val = (binary[i] >> (7 - j)) & 1;
// Bit update frequency based highlighting
double offset = !item.sigs.empty() ? 50 : 0; double alpha = item.sigs.empty() ? 0 : min_alpha_with_signal;
auto n = last_msg.last_changes[i].bit_change_counts[j]; uint32_t flip_count = last_msg.bit_flip_counts[i][j];
double min_f = n == 0 ? offset : offset + 25; if (flip_count > 0) {
double alpha = std::clamp(offset + log2(1.0 + factor * (double)n / (double)last_msg.count) * scaler, min_f, max_f); double normalized_alpha = log2(1.0 + flip_count * log_factor) * log_scaler;
double min_alpha = item.sigs.empty() ? min_alpha_no_signal : min_alpha_with_signal;
alpha = std::clamp(normalized_alpha, min_alpha, max_alpha);
}
auto color = item.bg_color; auto color = item.bg_color;
color.setAlpha(alpha); color.setAlpha(alpha);
updateItem(i, j, val, color); updateItem(i, j, bit_val, color);
} }
updateItem(i, 8, binary[i], last_msg.colors[i]); updateItem(i, 8, binary[i], last_msg.colors[i]);
} }

@ -39,7 +39,7 @@ void AbstractStream::updateMasks() {
const int size = std::min(mask.size(), m.last_changes.size()); const int size = std::min(mask.size(), m.last_changes.size());
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
for (int j = 0; j < 8; ++j) { for (int j = 0; j < 8; ++j) {
if (((mask[i] >> (7 - j)) & 1) != 0) m.last_changes[i].bit_change_counts[j] = 0; if (((mask[i] >> (7 - j)) & 1) != 0) m.bit_flip_counts[i][j] = 0;
} }
} }
} }
@ -59,9 +59,12 @@ size_t AbstractStream::suppressHighlighted() {
if (dt < 2.0) { if (dt < 2.0) {
last_change.suppressed = true; last_change.suppressed = true;
} }
last_change.bit_change_counts.fill(0);
cnt += last_change.suppressed; cnt += last_change.suppressed;
} }
for (auto &row_bit_flips : m.bit_flip_counts) {
row_bit_flips.fill(0);
}
} }
return cnt; return cnt;
} }
@ -251,6 +254,7 @@ void CanData::compute(const MessageId &msg_id, const uint8_t *can_data, const in
dat.resize(size); dat.resize(size);
colors.assign(size, QColor(0, 0, 0, 0)); colors.assign(size, QColor(0, 0, 0, 0));
last_changes.resize(size); last_changes.resize(size);
bit_flip_counts.resize(size);
std::for_each(last_changes.begin(), last_changes.end(), [current_sec](auto &c) { c.ts = current_sec; }); std::for_each(last_changes.begin(), last_changes.end(), [current_sec](auto &c) { c.ts = current_sec; });
} else { } else {
constexpr int periodic_threshold = 10; constexpr int periodic_threshold = 10;
@ -282,10 +286,11 @@ void CanData::compute(const MessageId &msg_id, const uint8_t *can_data, const in
} }
// Track bit level changes // Track bit level changes
auto &row_bit_flips = bit_flip_counts[i];
const uint8_t diff = (cur ^ last); const uint8_t diff = (cur ^ last);
for (int bit = 0; bit < 8; bit++) { for (int bit = 0; bit < 8; bit++) {
if (diff & (1u << bit)) { if (diff & (1u << bit)) {
++last_change.bit_change_counts[7 - bit]; ++row_bit_flips[7 - bit];
} }
} }

@ -33,9 +33,9 @@ struct CanData {
int delta = 0; int delta = 0;
int same_delta_counter = 0; int same_delta_counter = 0;
bool suppressed = false; bool suppressed = false;
std::array<uint32_t, 8> bit_change_counts;
}; };
std::vector<ByteLastChange> last_changes; std::vector<ByteLastChange> last_changes;
std::vector<std::array<uint32_t, 8>> bit_flip_counts;
double last_freq_update_ts = 0; double last_freq_update_ts = 0;
}; };

Loading…
Cancel
Save