feat(gui): M-1 / PR-Q.7 — dashboard CRT confidence column + alias-fold tooltip (C-5)

The CRT extractor (PR-Q.5/PR-Q.6) tags every target with a velocity_confidence
("CONFIRMED" / "LIKELY" / "AMBIGUOUS" / "UNKNOWN") and an optional alias_set
of candidate v_true folds. Until now the operator-facing targets table on the
Main View tab dropped that signal, so a single-PRI-only AMBIGUOUS reading
looked identical to a 3-PRI CONFIRMED one.

Changes:
  - Targets table column count 5 -> 6; new "Confidence" column between
    Velocity and Magnitude.
  - Module helper _confidence_display(label) -> (text, QColor):
      CONFIRMED  green  (DARK_SUCCESS)
      LIKELY     amber  (DARK_WARNING)
      AMBIGUOUS  red    (DARK_ERROR), prefixed with "? " so the row stands
                        out even when the operator's eyes skip the colour.
      UNKNOWN    gray   (DARK_TEXT) — legacy 32-bin / no CRT.
    Unrecognised future labels fall through to UNKNOWN.
  - Velocity cell carries a tooltip listing the CRT alias_set folds when
    present, so hovering reveals all plausible v_true candidates.
  - QColor pulled in from PyQt6.QtGui for the foreground tint.

Tests (TestDashboardConfidenceDisplay, +5):
  - CONFIRMED/LIKELY/AMBIGUOUS/UNKNOWN each map to expected text + colour.
  - AMBIGUOUS leads with "?" so it's visible without colour.
  - Unrecognised label "BANANA" falls back to UNKNOWN/gray.

Regression: 237/237 (test_v7 120 + test_GUI_V65_Tk 117). Ruff clean.

This closes audit M-1 / task PR-Q.7. The C-5 thread is end-to-end functional:
RTL emits 3 sub-frames (PR-Q.1) -> cosim agrees (PR-Q.2) -> v7 models carry
per-subframe params (PR-Q.4) -> processing.py runs CRT (PR-Q.5) -> workers
route through it (PR-Q.6) -> dashboard surfaces the confidence (PR-Q.7).
This commit is contained in:
Jason
2026-05-02 16:51:58 +05:45
parent 3401d05eca
commit 115c5f0778
2 changed files with 95 additions and 7 deletions
+46
View File
@@ -1581,6 +1581,52 @@ class TestWorkersRouteThroughCrt(unittest.TestCase):
self.assertIs(worker._extract_targets, extract_targets_from_frame_crt)
# =============================================================================
# Test: PR-Q.7 / audit M-1 — dashboard confidence column display helper
# =============================================================================
@unittest.skipUnless(_pyqt6_available(), "PyQt6 not installed")
class TestDashboardConfidenceDisplay(unittest.TestCase):
"""_confidence_display maps RadarTarget.velocity_confidence to (text, QColor)."""
def test_confirmed_is_green_no_prefix(self):
from v7.dashboard import _confidence_display
from v7.models import DARK_SUCCESS
text, color = _confidence_display("CONFIRMED")
self.assertEqual(text, "CONFIRMED")
self.assertEqual(color.name().upper(), DARK_SUCCESS.upper())
def test_likely_is_amber(self):
from v7.dashboard import _confidence_display
from v7.models import DARK_WARNING
text, color = _confidence_display("LIKELY")
self.assertEqual(text, "LIKELY")
self.assertEqual(color.name().upper(), DARK_WARNING.upper())
def test_ambiguous_gets_question_mark_prefix_and_red(self):
from v7.dashboard import _confidence_display
from v7.models import DARK_ERROR
text, color = _confidence_display("AMBIGUOUS")
self.assertTrue(text.startswith("?"),
"AMBIGUOUS must lead with '?' so it's visible without color")
self.assertIn("AMBIGUOUS", text)
self.assertEqual(color.name().upper(), DARK_ERROR.upper())
def test_unknown_falls_back_to_text_color(self):
from v7.dashboard import _confidence_display
from v7.models import DARK_TEXT
text, color = _confidence_display("UNKNOWN")
self.assertEqual(text, "UNKNOWN")
self.assertEqual(color.name().upper(), DARK_TEXT.upper())
def test_unrecognised_label_falls_through_to_unknown(self):
from v7.dashboard import _confidence_display
from v7.models import DARK_TEXT
text, color = _confidence_display("BANANA")
self.assertEqual(text, "UNKNOWN")
self.assertEqual(color.name().upper(), DARK_TEXT.upper())
# =============================================================================
# Helper: lazy import of v7.models
# =============================================================================