feat(gui): PR-Q.5 — 3-PRI CRT Doppler unfolder + cluster extractor (C-5)

Add host-side 3-PRI Chinese-Remainder velocity unfolding and a cluster
extractor that reads the 48-bin Doppler frame, splits it into the 3
sub-frames (SHORT/MEDIUM/LONG), and resolves Doppler aliases across
coprime PRIs.  Resolves the algorithm half of audit C-5; the data is
now in extract_targets_from_frame_crt's hands but workers still call
the legacy single-PRI extractor (PR-Q.6 wires it).

v7/processing.py:
- unfold_velocity_crt(v_meas, v_unamb, v_res, max_alias_k=6,
  tol_factor=0.5) -> (v_est, confidence, alias_set).  Brute-force
  candidate search over PRI-0 fold depth, per-PRI half-bin
  tolerance.  Confidence: CONFIRMED (3-PRI unique), LIKELY (3-PRI
  with 2 cands, or 2-PRI with unique cand), AMBIGUOUS (1-PRI, 3+
  cands, 2-PRI multi-cand, or no fold within tol).
- extract_targets_from_frame_crt(frame, waveform, gps, max_alias_k):
  groups detections by range bin, picks strongest bin per
  (rbin, sf), decodes signed Doppler via sub_frame = dbin // 16 /
  bin_in_sf = dbin % 16, calls unfold_velocity_crt, attaches
  velocity_confidence and alias_set to RadarTarget.  Falls back to
  legacy extract_targets_from_frame for non-48-bin frames.

v7/models.py:
- RadarTarget gains velocity_confidence (str default "UNKNOWN") and
  alias_set (list[float] | None).

v7/__init__.py:
- Re-exports unfold_velocity_crt + extract_targets_from_frame_crt.

test_v7.py (16 new tests, 0 failures):
- TestUnfoldVelocityCRT (8): zero-velocity CONFIRMED, below per-PRI
  v_unamb CONFIRMED, above per-PRI (100 m/s) CONFIRMED, near CRT
  ceiling (~261 m/s) CONFIRMED, negative velocity, 1-PRI AMBIGUOUS,
  2-PRI LIKELY, inconsistent measurements AMBIGUOUS+fallback.
- TestExtractTargetsFromFrameCrt (8): 3-PRI CONFIRMED target,
  LONG-only AMBIGUOUS (the 20-km blindspot regime), 2-PRI LIKELY,
  strongest-bin picking, two targets at distinct ranges, legacy
  32-bin frame fallback, no-detections empty, GPS georef.

Local: test_v7 100/0/0 (9 graceful skips), test_GUI_V65_Tk 117/0/2.
This commit is contained in:
Jason
2026-05-02 15:23:17 +05:45
parent 54627bbbe3
commit 5a7e8b8689
4 changed files with 511 additions and 1 deletions
+9
View File
@@ -90,6 +90,15 @@ class RadarTarget:
timestamp: float = 0.0
track_id: int = -1
classification: str = "unknown"
# PR-Q.5 (audit C-5): 3-PRI Doppler unfolding output.
# velocity_confidence:
# "CONFIRMED" — 3 sub-frames agree on a unique alias fold
# "LIKELY" — 2 sub-frames agree, or 3 sub-frames with 2 candidate folds
# "AMBIGUOUS" — only 1 sub-frame saw the target (no CRT possible), or
# multiple aliases survive within tolerance
# "UNKNOWN" — extractor did not run CRT (legacy single-PRI path)
velocity_confidence: str = "UNKNOWN"
alias_set: list[float] | None = None # Candidate v_true folds (m/s), best first
def to_dict(self) -> dict:
"""Convert to dictionary for JSON serialization."""