From 70b9385f00a21d22113e28b452b334d6f9327253 Mon Sep 17 00:00:00 2001 From: zenith Date: Tue, 17 Feb 2026 15:13:33 -0500 Subject: [PATCH] fix for error stats --- kiss_tnc.cc | 16 +++++++-- modem.hh | 10 ++++++ tnc_ui.hh | 98 ++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 113 insertions(+), 11 deletions(-) diff --git a/kiss_tnc.cc b/kiss_tnc.cc index f60e08a..5a46a26 100644 --- a/kiss_tnc.cc +++ b/kiss_tnc.cc @@ -787,6 +787,18 @@ private: float db = 20.0f * std::log10(rms + 1e-10f); g_ui_state->update_level(db); + + // Copy decoder stats + if (g_ui_state->stats_reset_requested.exchange(false)) { + decoder_->stats_sync_count = 0; + decoder_->stats_preamble_errors = 0; + decoder_->stats_symbol_errors = 0; + decoder_->stats_crc_errors = 0; + } + g_ui_state->sync_count = decoder_->stats_sync_count; + g_ui_state->preamble_errors = decoder_->stats_preamble_errors; + g_ui_state->symbol_errors = decoder_->stats_symbol_errors; + g_ui_state->crc_errors = decoder_->stats_crc_errors; } #endif } @@ -880,7 +892,7 @@ private: std::chrono::steady_clock::time_point tx_lockout_until_; static constexpr float RX_LOCKOUT_SECONDS = 0.5f; - // TX blanking + // TX blanking std::atomic tx_blanking_active_{false}; public: @@ -892,7 +904,7 @@ public: config_.p_persistence = new_config.p_persistence; config_.slot_time_ms = new_config.slot_time_ms; - // TX blanking + // TX blanking config_.tx_blanking_enabled = new_config.tx_blanking_enabled; // Update callsign if changed diff --git a/modem.hh b/modem.hh index 25dda53..e01ac26 100644 --- a/modem.hh +++ b/modem.hh @@ -452,6 +452,11 @@ public: // Get current modulation bits int get_mod_bits() const { return mod_bits; } + // decode statistics + int stats_sync_count = 0; // corelator + int stats_preamble_errors = 0; // preamble decoding failed + int stats_symbol_errors = 0; // seed damage + int stats_crc_errors = 0; // polar CRC failed private: enum class State { SEARCHING, // looking for preamble @@ -621,6 +626,7 @@ private: case State::SEARCHING: if ((*correlator_ptr)(buf_)) { // Sync found + ++stats_sync_count; symbol_pos = correlator_ptr->symbol_pos; cfo_rad = correlator_ptr->cfo_rad; @@ -638,6 +644,8 @@ private: // Need to advance past preamble: symbol_pos + symbol_len + extended_len // Plus extended_len for the first data symbol samples_needed_ = symbol_pos + symbol_len + 2 * extended_len; + } else { + ++stats_preamble_errors; } } break; @@ -651,6 +659,7 @@ private: // Process this symbol if (!process_symbol(symbol_index_)) { // Error, go back to searching + ++stats_symbol_errors; state_ = State::SEARCHING; break; } @@ -956,6 +965,7 @@ private: if (best < 0) { std::cerr << "Decoder: CRC failed" << std::endl; + ++stats_crc_errors; return; } diff --git a/tnc_ui.hh b/tnc_ui.hh index 3d8a475..0ee0f3f 100644 --- a/tnc_ui.hh +++ b/tnc_ui.hh @@ -151,13 +151,19 @@ struct TNCUIState { std::atomic tx_frame_count{0}; std::atomic rx_error_count{0}; + // Decode statistics + std::atomic sync_count{0}; + std::atomic preamble_errors{0}; + std::atomic symbol_errors{0}; + std::atomic crc_errors{0}; + std::atomic stats_reset_requested{false}; + // Signal visualization static constexpr int LEVEL_HISTORY_SIZE = 60; std::mutex level_mutex; float level_history[LEVEL_HISTORY_SIZE]; int level_history_pos = 0; std::atomic decoding_active{false}; - std::atomic sync_count{0}; // SNR history static constexpr int SNR_HISTORY_SIZE = 32; @@ -1835,14 +1841,34 @@ private: attroff(COLOR_PAIR(2) | A_BOLD); addstr(" "); - addstr("Err"); - int errs = state_.rx_error_count.load(); - if (errs > 0) { - attron(COLOR_PAIR(2)); - printw(" %d", errs); - attroff(COLOR_PAIR(2)); + int syncs = state_.sync_count.load(); + int total_errors = state_.preamble_errors.load() + + state_.symbol_errors.load() + + state_.crc_errors.load() + + state_.rx_error_count.load(); + if (syncs > 0) { + float err_pct = 100.0f * total_errors / syncs; + addstr("Err"); + if (total_errors == 0) { + attron(COLOR_PAIR(1)); + printw(" 0/%d", syncs); + attroff(COLOR_PAIR(1)); + } else if (err_pct < 20.0f) { + attron(COLOR_PAIR(3)); + printw(" %d/%d", total_errors, syncs); + attroff(COLOR_PAIR(3)); + } else { + attron(COLOR_PAIR(2)); + printw(" %d/%d", total_errors, syncs); + attroff(COLOR_PAIR(2)); + } + attron(A_DIM); + printw(" (%.0f%%)", err_pct); + attroff(A_DIM); } else { - printw(" %d", errs); + attron(A_DIM); + addstr("Err 0/0"); + attroff(A_DIM); } y++; @@ -2742,6 +2768,55 @@ private: } void draw_log(int y, int h, int cols) { + // Decode stats header + int c1 = 3; + attron(A_DIM); + mvaddstr(y, c1, "DECODE STATS"); + attroff(A_DIM); + y++; + + int syncs = state_.sync_count.load(); + int pre_err = state_.preamble_errors.load(); + int sym_err = state_.symbol_errors.load(); + int crc_err = state_.crc_errors.load(); + int unframe_err = state_.rx_error_count.load(); + int decoded = state_.rx_frame_count.load(); + + mvaddstr(y, c1, "Syncs"); + attron(COLOR_PAIR(4)); + printw(" %d", syncs); + attroff(COLOR_PAIR(4)); + + addstr(" Decoded"); + attron(COLOR_PAIR(1)); + printw(" %d", decoded); + attroff(COLOR_PAIR(1)); + + addstr(" CRC Fail"); + if (crc_err > 0) attron(COLOR_PAIR(2)); + printw(" %d", crc_err); + if (crc_err > 0) attroff(COLOR_PAIR(2)); + + addstr(" Seed Err"); + if (sym_err > 0) attron(COLOR_PAIR(2)); + printw(" %d", sym_err); + if (sym_err > 0) attroff(COLOR_PAIR(2)); + + addstr(" Pre Err"); + if (pre_err > 0) attron(COLOR_PAIR(2)); + printw(" %d", pre_err); + if (pre_err > 0) attroff(COLOR_PAIR(2)); + + if (unframe_err > 0) { + addstr(" Unframe"); + attron(COLOR_PAIR(2)); + printw(" %d", unframe_err); + attroff(COLOR_PAIR(2)); + } + + y += 2; + h -= 3; + auto log = state_.get_log(); int visible = h - 1; int max_scroll = std::max(0, (int)log.size() - visible); @@ -2893,7 +2968,7 @@ private: // Ensure at least 1 grid cell per display cell (prevents striping) int gx_end = std::max(gx + 1, std::min((int)((dx + 1) * scale_x), grid_size)); int gy_end = std::max(gy + 1, std::min((int)((dy + 1) * scale_y), grid_size)); - + int density = 0; for (int sy = gy; sy < gy_end; ++sy) { for (int sx = gx; sx < gx_end; ++sx) { @@ -3229,6 +3304,11 @@ private: state_.rx_frame_count = 0; state_.tx_frame_count = 0; state_.rx_error_count = 0; + state_.sync_count = 0; + state_.preamble_errors = 0; + state_.symbol_errors = 0; + state_.crc_errors = 0; + state_.stats_reset_requested = true; state_.total_tx_time = 0; state_.add_log("S"); break;