Преглед изворни кода

feat: make pipeline goals influence scheduling

master
Jan Svabenik пре 11 часа
родитељ
комит
427532f61c
7 измењених фајлова са 82 додато и 1 уклоњено
  1. +2
    -0
      README.md
  2. +15
    -0
      cmd/sdrd/http_handlers.go
  3. +29
    -0
      internal/pipeline/goals.go
  4. +19
    -0
      internal/pipeline/goals_test.go
  5. +1
    -1
      internal/pipeline/scheduler.go
  6. +11
    -0
      internal/pipeline/scheduler_test.go
  7. +5
    -0
      internal/pipeline/types.go

+ 2
- 0
README.md Прегледај датотеку

@@ -143,6 +143,8 @@ go build -tags sdrplay ./cmd/sdrd
- `POST /api/config`
- `POST /api/sdr/settings`
- `GET /api/gpu`
- `GET /api/pipeline/policy`
- `GET /api/pipeline/recommendations`

### Signals / Events
- `GET /api/signals` → current live signals


+ 15
- 0
cmd/sdrd/http_handlers.go Прегледај датотеку

@@ -133,6 +133,21 @@ func registerAPIHandlers(mux *http.ServeMux, cfgPath string, cfgManager *runtime
cfg := cfgManager.Snapshot()
_ = json.NewEncoder(w).Encode(pipeline.PolicyFromConfig(cfg))
})
mux.HandleFunc("/api/pipeline/recommendations", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
cfg := cfgManager.Snapshot()
policy := pipeline.PolicyFromConfig(cfg)
recommend := map[string]any{
"mode": policy.Mode,
"intent": policy.Intent,
"monitor_span_hz": policy.MonitorSpanHz,
"signal_priorities": policy.SignalPriorities,
"auto_record_classes": policy.AutoRecordClasses,
"auto_decode_classes": policy.AutoDecodeClasses,
"refinement_jobs": policy.MaxRefinementJobs,
}
_ = json.NewEncoder(w).Encode(recommend)
})
mux.HandleFunc("/api/events", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
limit := 200


+ 29
- 0
internal/pipeline/goals.go Прегледај датотеку

@@ -0,0 +1,29 @@
package pipeline

import "strings"

func WantsClass(values []string, class string) bool {
if len(values) == 0 || class == "" {
return false
}
for _, v := range values {
if strings.EqualFold(strings.TrimSpace(v), class) {
return true
}
}
return false
}

func CandidatePriorityBoost(policy Policy, hint string) float64 {
h := strings.ToLower(strings.TrimSpace(hint))
for i, want := range policy.SignalPriorities {
w := strings.ToLower(strings.TrimSpace(want))
if w == "" {
continue
}
if strings.Contains(h, w) || strings.Contains(w, h) {
return float64(len(policy.SignalPriorities)-i) * 3.0
}
}
return 0
}

+ 19
- 0
internal/pipeline/goals_test.go Прегледај датотеку

@@ -0,0 +1,19 @@
package pipeline

import "testing"

func TestWantsClass(t *testing.T) {
if !WantsClass([]string{"WFM", "DMR"}, "wfm") {
t.Fatalf("expected case-insensitive match")
}
if WantsClass([]string{"DMR"}, "WFM") {
t.Fatalf("unexpected match")
}
}

func TestCandidatePriorityBoost(t *testing.T) {
p := Policy{SignalPriorities: []string{"voice", "digital", "cw"}}
if boost := CandidatePriorityBoost(p, "digital-burst"); boost <= 0 {
t.Fatalf("expected positive boost, got %v", boost)
}
}

+ 1
- 1
internal/pipeline/scheduler.go Прегледај датотеку

@@ -19,7 +19,7 @@ func ScheduleCandidates(candidates []Candidate, policy Policy) []ScheduledCandid
if c.SNRDb < policy.MinCandidateSNRDb {
continue
}
priority := c.SNRDb
priority := c.SNRDb + CandidatePriorityBoost(policy, c.Hint)
if c.BandwidthHz > 0 {
priority += minFloat64(c.BandwidthHz/25000.0, 6)
}


+ 11
- 0
internal/pipeline/scheduler_test.go Прегледај датотеку

@@ -21,3 +21,14 @@ func TestScheduleCandidates(t *testing.T) {
t.Fatalf("expected next strongest candidate second, got id=%d", got[1].Candidate.ID)
}
}

func TestScheduleCandidatesPriorityBoost(t *testing.T) {
policy := Policy{MaxRefinementJobs: 1, MinCandidateSNRDb: 0, SignalPriorities: []string{"digital"}}
got := ScheduleCandidates([]Candidate{
{ID: 1, SNRDb: 15, Hint: "voice"},
{ID: 2, SNRDb: 14, Hint: "digital-burst"},
}, policy)
if len(got) != 1 || got[0].Candidate.ID != 2 {
t.Fatalf("expected priority boost to favor digital candidate, got %+v", got)
}
}

+ 5
- 0
internal/pipeline/types.go Прегледај датотеку

@@ -32,6 +32,10 @@ type Refinement struct {
func CandidatesFromSignals(signals []detector.Signal, source string) []Candidate {
out := make([]Candidate, 0, len(signals))
for _, s := range signals {
hint := ""
if s.Class != nil {
hint = string(s.Class.ModType)
}
out = append(out, Candidate{
ID: s.ID,
CenterHz: s.CenterHz,
@@ -42,6 +46,7 @@ func CandidatesFromSignals(signals []detector.Signal, source string) []Candidate
LastBin: s.LastBin,
NoiseDb: s.NoiseDb,
Source: source,
Hint: hint,
})
}
return out


Loading…
Откажи
Сачувај