Explorar el Código

Expose refinement plan scoring details

master
Jan Svabenik hace 5 horas
padre
commit
34830c64fd
Se han modificado 5 ficheros con 90 adiciones y 7 borrados
  1. +3
    -0
      cmd/sdrd/http_handlers.go
  2. +7
    -0
      internal/pipeline/phases.go
  3. +5
    -1
      internal/pipeline/policy.go
  4. +54
    -6
      internal/pipeline/scheduler.go
  5. +21
    -0
      internal/pipeline/scheduler_test.go

+ 3
- 0
cmd/sdrd/http_handlers.go Ver fichero

@@ -140,6 +140,9 @@ func registerAPIHandlers(mux *http.ServeMux, cfgPath string, cfgManager *runtime
recommend := map[string]any{
"mode": policy.Mode,
"intent": policy.Intent,
"monitor_center_hz": policy.MonitorCenterHz,
"monitor_start_hz": policy.MonitorStartHz,
"monitor_end_hz": policy.MonitorEndHz,
"monitor_span_hz": policy.MonitorSpanHz,
"signal_priorities": policy.SignalPriorities,
"auto_record_classes": policy.AutoRecordClasses,


+ 7
- 0
internal/pipeline/phases.go Ver fichero

@@ -27,9 +27,16 @@ type RefinementPlan struct {
TotalCandidates int `json:"total_candidates"`
MinCandidateSNRDb float64 `json:"min_candidate_snr_db"`
Budget int `json:"budget"`
MonitorStartHz float64 `json:"monitor_start_hz,omitempty"`
MonitorEndHz float64 `json:"monitor_end_hz,omitempty"`
MonitorSpanHz float64 `json:"monitor_span_hz,omitempty"`
DroppedByMonitor int `json:"dropped_by_monitor"`
DroppedBySNR int `json:"dropped_by_snr"`
DroppedByBudget int `json:"dropped_by_budget"`
PriorityMin float64 `json:"priority_min,omitempty"`
PriorityMax float64 `json:"priority_max,omitempty"`
PriorityAvg float64 `json:"priority_avg,omitempty"`
PriorityCutoff float64 `json:"priority_cutoff,omitempty"`
Selected []ScheduledCandidate `json:"selected,omitempty"`
}



+ 5
- 1
internal/pipeline/policy.go Ver fichero

@@ -30,7 +30,7 @@ type Policy struct {
}

func PolicyFromConfig(cfg config.Config) Policy {
return Policy{
p := Policy{
Mode: cfg.Pipeline.Mode,
Intent: cfg.Pipeline.Goals.Intent,
MonitorCenterHz: cfg.CenterHz,
@@ -56,6 +56,10 @@ func PolicyFromConfig(cfg config.Config) Policy {
MaxDecodeJobs: cfg.Resources.MaxDecodeJobs,
DecisionHoldMs: cfg.Resources.DecisionHoldMs,
}
if p.MonitorSpanHz <= 0 && p.MonitorStartHz != 0 && p.MonitorEndHz != 0 && p.MonitorEndHz > p.MonitorStartHz {
p.MonitorSpanHz = p.MonitorEndHz - p.MonitorStartHz
}
return p
}

func ApplyNamedProfile(cfg *config.Config, name string) {


+ 54
- 6
internal/pipeline/scheduler.go Ver fichero

@@ -3,8 +3,16 @@ package pipeline
import "sort"

type ScheduledCandidate struct {
Candidate Candidate `json:"candidate"`
Priority float64 `json:"priority"`
Candidate Candidate `json:"candidate"`
Priority float64 `json:"priority"`
Breakdown *PriorityBreakdown `json:"breakdown,omitempty"`
}

type PriorityBreakdown struct {
SNRScore float64 `json:"snr_score"`
BandwidthScore float64 `json:"bandwidth_score"`
PeakScore float64 `json:"peak_score"`
PolicyBoost float64 `json:"policy_boost"`
}

// BuildRefinementPlan scores and budgets candidates for costly local refinement.
@@ -20,6 +28,13 @@ func BuildRefinementPlan(candidates []Candidate, policy Policy) RefinementPlan {
MinCandidateSNRDb: policy.MinCandidateSNRDb,
Budget: budget,
}
if start, end, ok := monitorBounds(policy); ok {
plan.MonitorStartHz = start
plan.MonitorEndHz = end
if end > start {
plan.MonitorSpanHz = end - start
}
}
if len(candidates) == 0 {
return plan
}
@@ -34,14 +49,27 @@ func BuildRefinementPlan(candidates []Candidate, policy Policy) RefinementPlan {
plan.DroppedBySNR++
continue
}
priority := c.SNRDb*snrWeight + CandidatePriorityBoost(policy, c.Hint)
snrScore := c.SNRDb * snrWeight
bwScore := 0.0
peakScore := 0.0
policyBoost := CandidatePriorityBoost(policy, c.Hint)
if c.BandwidthHz > 0 {
priority += minFloat64(c.BandwidthHz/25000.0, 6) * bwWeight
bwScore = minFloat64(c.BandwidthHz/25000.0, 6) * bwWeight
}
if c.PeakDb > 0 {
priority += (c.PeakDb / 20.0) * peakWeight
peakScore = (c.PeakDb / 20.0) * peakWeight
}
scored = append(scored, ScheduledCandidate{Candidate: c, Priority: priority})
priority := snrScore + bwScore + peakScore + policyBoost
scored = append(scored, ScheduledCandidate{
Candidate: c,
Priority: priority,
Breakdown: &PriorityBreakdown{
SNRScore: snrScore,
BandwidthScore: bwScore,
PeakScore: peakScore,
PolicyBoost: policyBoost,
},
})
}
sort.Slice(scored, func(i, j int) bool {
if scored[i].Priority == scored[j].Priority {
@@ -49,11 +77,31 @@ func BuildRefinementPlan(candidates []Candidate, policy Policy) RefinementPlan {
}
return scored[i].Priority > scored[j].Priority
})
if len(scored) > 0 {
minPriority := scored[0].Priority
maxPriority := scored[0].Priority
sumPriority := 0.0
for _, s := range scored {
if s.Priority < minPriority {
minPriority = s.Priority
}
if s.Priority > maxPriority {
maxPriority = s.Priority
}
sumPriority += s.Priority
}
plan.PriorityMin = minPriority
plan.PriorityMax = maxPriority
plan.PriorityAvg = sumPriority / float64(len(scored))
}
limit := plan.Budget
if limit <= 0 || limit > len(scored) {
limit = len(scored)
}
plan.Selected = scored[:limit]
if len(plan.Selected) > 0 {
plan.PriorityCutoff = plan.Selected[len(plan.Selected)-1].Priority
}
plan.DroppedByBudget = len(scored) - len(plan.Selected)
return plan
}


+ 21
- 0
internal/pipeline/scheduler_test.go Ver fichero

@@ -119,3 +119,24 @@ func TestScheduleCandidatesPriorityBoost(t *testing.T) {
t.Fatalf("expected priority boost to favor digital candidate, got %+v", got)
}
}

func TestBuildRefinementPlanPriorityStats(t *testing.T) {
policy := Policy{MaxRefinementJobs: 1, MinCandidateSNRDb: 0}
cands := []Candidate{
{ID: 1, CenterHz: 100, SNRDb: 8, BandwidthHz: 10000, PeakDb: 2},
{ID: 2, CenterHz: 200, SNRDb: 12, BandwidthHz: 20000, PeakDb: 4},
}
plan := BuildRefinementPlan(cands, policy)
if plan.PriorityMax < plan.PriorityMin {
t.Fatalf("priority bounds invalid: %+v", plan)
}
if len(plan.Selected) != 1 {
t.Fatalf("expected 1 selected, got %d", len(plan.Selected))
}
if plan.PriorityCutoff != plan.Selected[0].Priority {
t.Fatalf("expected cutoff to match selection, got %.2f vs %.2f", plan.PriorityCutoff, plan.Selected[0].Priority)
}
if plan.Selected[0].Breakdown == nil {
t.Fatalf("expected breakdown on selected candidate")
}
}

Cargando…
Cancelar
Guardar