From 1e2d6a471253498b6e776c6d571b6bde75271d86 Mon Sep 17 00:00:00 2001 From: Jan Svabenik Date: Thu, 19 Mar 2026 00:32:50 +0100 Subject: [PATCH] Add CFAR legend, classifier scores, and reorder settings --- internal/classifier/rules.go | 1 + internal/classifier/types.go | 11 +++--- web/app.js | 27 +++++++++++++ web/index.html | 73 ++++++++++++++++++++++-------------- web/style.css | 2 + 5 files changed, 81 insertions(+), 33 deletions(-) diff --git a/internal/classifier/rules.go b/internal/classifier/rules.go index b7f3149..1381d7d 100644 --- a/internal/classifier/rules.go +++ b/internal/classifier/rules.go @@ -106,6 +106,7 @@ func RuleClassify(feat Features) Classification { BW3dB: bw, Features: feat, SecondBest: second, + Scores: scores, } } diff --git a/internal/classifier/types.go b/internal/classifier/types.go index d70539f..379e3a6 100644 --- a/internal/classifier/types.go +++ b/internal/classifier/types.go @@ -39,11 +39,12 @@ type Features struct { // Classification is the classifier output attached to signals/events. type Classification struct { - ModType SignalClass `json:"mod_type"` - Confidence float64 `json:"confidence"` - BW3dB float64 `json:"bw_3db_hz"` - Features Features `json:"features,omitempty"` - SecondBest SignalClass `json:"second_best,omitempty"` + ModType SignalClass `json:"mod_type"` + Confidence float64 `json:"confidence"` + BW3dB float64 `json:"bw_3db_hz"` + Features Features `json:"features,omitempty"` + SecondBest SignalClass `json:"second_best,omitempty"` + Scores map[SignalClass]float64 `json:"scores,omitempty"` } // SignalInput is the minimal input needed for classification. diff --git a/web/app.js b/web/app.js index 59a1043..86ed31e 100644 --- a/web/app.js +++ b/web/app.js @@ -86,6 +86,7 @@ const decodeEventBtn = qs('decodeEventBtn'); const decodeModeSelect = qs('decodeMode'); const recordingMetaEl = qs('recordingMeta'); const decodeResultEl = qs('decodeResult'); +const classifierScoresEl = qs('classifierScores'); const recordingMetaLink = qs('recordingMetaLink'); const recordingIQLink = qs('recordingIQLink'); const recordingAudioLink = qs('recordingAudioLink'); @@ -1007,6 +1008,19 @@ function openDrawer(ev) { detailSnrEl.textContent = `${(ev.snr_db || 0).toFixed(1)} dB`; detailDurEl.textContent = fmtMs(ev.duration_ms || 0); detailClassEl.textContent = ev.class?.mod_type || '-'; + if (classifierScoresEl) { + const scores = ev.class?.scores; + if (scores && typeof scores === 'object') { + const rows = Object.entries(scores) + .sort((a, b) => b[1] - a[1]) + .slice(0, 6) + .map(([k, v]) => `${k}:${v.toFixed(2)}`) + .join(' · '); + classifierScoresEl.textContent = rows ? `Classifier scores: ${rows}` : 'Classifier scores: -'; + } else { + classifierScoresEl.textContent = 'Classifier scores: -'; + } + } if (recordingMetaEl) { recordingMetaEl.textContent = 'Recording: -'; } @@ -1427,6 +1441,19 @@ if (recordingList) { const rds = meta.rds_ps ? `RDS: ${meta.rds_ps}` : ''; decodeResultEl.textContent = `Decode: ${rds}`; } + if (classifierScoresEl) { + const scores = meta.classification?.scores; + if (scores && typeof scores === 'object') { + const rows = Object.entries(scores) + .sort((a, b) => b[1] - a[1]) + .slice(0, 6) + .map(([k, v]) => `${k}:${v.toFixed(2)}`) + .join(' · '); + classifierScoresEl.textContent = rows ? `Classifier scores: ${rows}` : 'Classifier scores: -'; + } else { + classifierScoresEl.textContent = 'Classifier scores: -'; + } + } } catch {} }); } diff --git a/web/index.html b/web/index.html index 024b8a1..8c3a3e4 100644 --- a/web/index.html +++ b/web/index.html @@ -80,7 +80,14 @@
-
SpectrumWheel = zoom · Drag = pan · Dbl-click = reset
+
+ Spectrum + Wheel = zoom · Drag = pan · Dbl-click = reset + + + CFAR edge (static threshold) + +
@@ -142,33 +149,38 @@
-
Recorder
-
- - - - - -
- - - -
- -
-
DSP
+
DSP & Display
Gain
dB
+ +
+ + + + + +
+
+ +
+
Detector
Threshold
dB
@@ -179,25 +191,28 @@ -
- - - - -
+ +
+
Recorder
+
+ + + + + +
+ + + +
@@ -312,6 +327,7 @@
Ready for: IQ snippets, clip playback, bookmarks, annotations.
Recording: -
Decode: -
+
Classifier scores: -
meta.json · IQ · @@ -355,3 +371,4 @@ + diff --git a/web/style.css b/web/style.css index 04a3672..c87d5d9 100644 --- a/web/style.css +++ b/web/style.css @@ -207,6 +207,8 @@ button, input, select { font: inherit; } } .viz-card--compact { } .viz-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; } +.viz-legend { display: inline-flex; align-items: center; gap: 6px; font-size: 0.68rem; color: var(--text-dim); } +.legend-swatch { width: 10px; height: 10px; border-radius: 2px; background: rgba(255, 204, 102, 0.35); border: 1px solid rgba(255, 204, 102, 0.35); } .viz-label { font-family: var(--mono); font-size: 0.6rem; font-weight: 600; letter-spacing: 0.1em; color: var(--text-dim); text-transform: uppercase; } .viz-hint { font-family: var(--mono); font-size: 0.58rem; color: var(--text-mute); }