Sfoglia il codice sorgente

feat: separate surveillance analysis and display settings

master
Jan Svabenik 13 ore fa
parent
commit
f09ed00d1d
8 ha cambiato i file con 60 aggiunte e 2 eliminazioni
  1. +2
    -0
      README.md
  2. +17
    -0
      cmd/sdrd/surveillance_test.go
  3. +2
    -0
      config.yaml
  4. +10
    -0
      internal/config/config.go
  5. +4
    -0
      internal/pipeline/policy.go
  6. +3
    -1
      internal/pipeline/policy_test.go
  7. +16
    -0
      internal/runtime/runtime.go
  8. +6
    -1
      internal/runtime/runtime_test.go

+ 2
- 0
README.md Vedi File

@@ -65,6 +65,8 @@ Edit `config.yaml` (autosave goes to `config.autosave.yaml`).
- `surveillance.analysis_fft_size` — analysis FFT size used by the surveillance layer
- `surveillance.frame_rate` — surveillance cadence target
- `surveillance.strategy` — currently `single-resolution`, reserved for future multi-resolution modes
- `surveillance.display_bins` — preferred presentation density for clients/UI
- `surveillance.display_fps` — preferred presentation cadence for clients/UI
- `refinement.enabled` — enables explicit candidate refinement stage
- `refinement.max_concurrent` — refinement budget hint
- `refinement.min_candidate_snr_db` — floor for future scheduling decisions


+ 17
- 0
cmd/sdrd/surveillance_test.go Vedi File

@@ -0,0 +1,17 @@
package main

import (
"testing"

"sdr-wideband-suite/internal/config"
)

func TestSurveillanceDisplayDefaults(t *testing.T) {
cfg := config.Default()
if cfg.Surveillance.DisplayBins != cfg.FFTSize {
t.Fatalf("expected display bins to default to fft size, got %d vs %d", cfg.Surveillance.DisplayBins, cfg.FFTSize)
}
if cfg.Surveillance.DisplayFPS != cfg.FrameRate {
t.Fatalf("expected display fps to default to frame rate, got %d vs %d", cfg.Surveillance.DisplayFPS, cfg.FrameRate)
}
}

+ 2
- 0
config.yaml Vedi File

@@ -25,6 +25,8 @@ surveillance:
analysis_fft_size: 2048
frame_rate: 15
strategy: single-resolution
display_bins: 2048
display_fps: 15
refinement:
enabled: true
max_concurrent: 8


+ 10
- 0
internal/config/config.go Vedi File

@@ -89,6 +89,8 @@ type SurveillanceConfig struct {
AnalysisFFTSize int `yaml:"analysis_fft_size" json:"analysis_fft_size"`
FrameRate int `yaml:"frame_rate" json:"frame_rate"`
Strategy string `yaml:"strategy" json:"strategy"`
DisplayBins int `yaml:"display_bins" json:"display_bins"`
DisplayFPS int `yaml:"display_fps" json:"display_fps"`
}

type RefinementConfig struct {
@@ -164,6 +166,8 @@ func Default() Config {
AnalysisFFTSize: 2048,
FrameRate: 15,
Strategy: "single-resolution",
DisplayBins: 2048,
DisplayFPS: 15,
},
Refinement: RefinementConfig{
Enabled: true,
@@ -330,6 +334,12 @@ func applyDefaults(cfg Config) Config {
if cfg.Surveillance.Strategy == "" {
cfg.Surveillance.Strategy = "single-resolution"
}
if cfg.Surveillance.DisplayBins <= 0 {
cfg.Surveillance.DisplayBins = cfg.FFTSize
}
if cfg.Surveillance.DisplayFPS <= 0 {
cfg.Surveillance.DisplayFPS = cfg.FrameRate
}
if !cfg.Refinement.Enabled {
// keep explicit false if user disabled it; enable by default only when unset-like zero config
if cfg.Refinement.MaxConcurrent == 0 && cfg.Refinement.MinCandidateSNRDb == 0 {


+ 4
- 0
internal/pipeline/policy.go Vedi File

@@ -13,6 +13,8 @@ type Policy struct {
AutoDecodeClasses []string `json:"auto_decode_classes,omitempty"`
SurveillanceFFTSize int `json:"surveillance_fft_size"`
SurveillanceFPS int `json:"surveillance_fps"`
DisplayBins int `json:"display_bins"`
DisplayFPS int `json:"display_fps"`
RefinementEnabled bool `json:"refinement_enabled"`
MaxRefinementJobs int `json:"max_refinement_jobs"`
MinCandidateSNRDb float64 `json:"min_candidate_snr_db"`
@@ -31,6 +33,8 @@ func PolicyFromConfig(cfg config.Config) Policy {
AutoDecodeClasses: append([]string(nil), cfg.Pipeline.Goals.AutoDecodeClasses...),
SurveillanceFFTSize: cfg.Surveillance.AnalysisFFTSize,
SurveillanceFPS: cfg.Surveillance.FrameRate,
DisplayBins: cfg.Surveillance.DisplayBins,
DisplayFPS: cfg.Surveillance.DisplayFPS,
RefinementEnabled: cfg.Refinement.Enabled,
MaxRefinementJobs: cfg.Resources.MaxRefinementJobs,
MinCandidateSNRDb: cfg.Refinement.MinCandidateSNRDb,


+ 3
- 1
internal/pipeline/policy_test.go Vedi File

@@ -36,12 +36,14 @@ func TestPolicyFromConfig(t *testing.T) {
cfg.Pipeline.Goals.SignalPriorities = []string{"broadcast-fm", "rds"}
cfg.Surveillance.AnalysisFFTSize = 8192
cfg.Surveillance.FrameRate = 9
cfg.Surveillance.DisplayBins = 1200
cfg.Surveillance.DisplayFPS = 6
cfg.Refinement.Enabled = true
cfg.Resources.MaxRefinementJobs = 5
cfg.Refinement.MinCandidateSNRDb = 2.5
cfg.Resources.PreferGPU = true
p := PolicyFromConfig(cfg)
if p.Mode != "archive" || p.Intent != "archive-and-triage" || p.SurveillanceFFTSize != 8192 || p.SurveillanceFPS != 9 {
if p.Mode != "archive" || p.Intent != "archive-and-triage" || p.SurveillanceFFTSize != 8192 || p.SurveillanceFPS != 9 || p.DisplayBins != 1200 || p.DisplayFPS != 6 {
t.Fatalf("unexpected policy: %+v", p)
}
if p.MonitorSpanHz != 20e6 || len(p.SignalPriorities) != 2 {


+ 16
- 0
internal/runtime/runtime.go Vedi File

@@ -18,6 +18,8 @@ type SurveillanceUpdate struct {
AnalysisFFTSize *int `json:"analysis_fft_size"`
FrameRate *int `json:"frame_rate"`
Strategy *string `json:"strategy"`
DisplayBins *int `json:"display_bins"`
DisplayFPS *int `json:"display_fps"`
}

type RefinementUpdate struct {
@@ -187,6 +189,20 @@ func (m *Manager) ApplyConfig(update ConfigUpdate) (config.Config, error) {
if update.Surveillance.Strategy != nil {
next.Surveillance.Strategy = *update.Surveillance.Strategy
}
if update.Surveillance.DisplayBins != nil {
v := *update.Surveillance.DisplayBins
if v <= 0 {
return m.cfg, errors.New("surveillance.display_bins must be > 0")
}
next.Surveillance.DisplayBins = v
}
if update.Surveillance.DisplayFPS != nil {
v := *update.Surveillance.DisplayFPS
if v <= 0 {
return m.cfg, errors.New("surveillance.display_fps must be > 0")
}
next.Surveillance.DisplayFPS = v
}
}
if update.Refinement != nil {
if update.Refinement.Enabled != nil {


+ 6
- 1
internal/runtime/runtime_test.go Vedi File

@@ -25,6 +25,8 @@ func TestApplyConfigUpdate(t *testing.T) {
mode := "wideband-balanced"
profile := "wideband-balanced"
survFPS := 12
displayBins := 1024
displayFPS := 8
maxRefJobs := 24
updated, err := mgr.ApplyConfig(ConfigUpdate{
CenterHz: &center,
@@ -32,7 +34,7 @@ func TestApplyConfigUpdate(t *testing.T) {
FFTSize: &fftSize,
TunerBwKHz: &bw,
Pipeline: &PipelineUpdate{Mode: &mode, Profile: &profile},
Surveillance: &SurveillanceUpdate{FrameRate: &survFPS},
Surveillance: &SurveillanceUpdate{FrameRate: &survFPS, DisplayBins: &displayBins, DisplayFPS: &displayFPS},
Resources: &ResourcesUpdate{MaxRefinementJobs: &maxRefJobs},
Detector: &DetectorUpdate{
ThresholdDb: &threshold,
@@ -92,6 +94,9 @@ func TestApplyConfigUpdate(t *testing.T) {
if updated.Resources.MaxRefinementJobs != maxRefJobs {
t.Fatalf("max refinement jobs: %v", updated.Resources.MaxRefinementJobs)
}
if updated.Surveillance.DisplayBins != displayBins || updated.Surveillance.DisplayFPS != displayFPS {
t.Fatalf("display settings not applied: bins=%d fps=%d", updated.Surveillance.DisplayBins, updated.Surveillance.DisplayFPS)
}
}

func TestApplyConfigRejectsInvalid(t *testing.T) {


Loading…
Annulla
Salva