diff --git a/cmd/sdrd/dsp_loop.go b/cmd/sdrd/dsp_loop.go index b1ba8e1..b7d71aa 100644 --- a/cmd/sdrd/dsp_loop.go +++ b/cmd/sdrd/dsp_loop.go @@ -57,12 +57,13 @@ func runDSP(ctx context.Context, srcMgr *sourceManager, cfg config.Config, det * rt.gotSamples = true } state.surveillance = rt.buildSurveillanceResult(art) + state.refinementInput = rt.buildRefinementInput(state.surveillance) finished := state.surveillance.Finished thresholds := state.surveillance.Thresholds noiseFloor := state.surveillance.NoiseFloor var displaySignals []detector.Signal if len(art.iq) > 0 { - state.refinement = rt.refineSignals(art, state.surveillance.Scheduled, extractMgr, rec) + state.refinement = rt.refineSignals(art, state.refinementInput, extractMgr, rec) displaySignals = state.refinement.Signals if rec != nil && len(displaySignals) > 0 && len(art.allIQ) > 0 { aqCfg := extractionConfig{firTaps: rt.cfg.Recorder.ExtractionTaps, bwMult: rt.cfg.Recorder.ExtractionBwMult} diff --git a/cmd/sdrd/phase_state.go b/cmd/sdrd/phase_state.go index 11f053c..dba7007 100644 --- a/cmd/sdrd/phase_state.go +++ b/cmd/sdrd/phase_state.go @@ -3,6 +3,7 @@ package main import "sdr-wideband-suite/internal/pipeline" type phaseState struct { - surveillance pipeline.SurveillanceResult - refinement pipeline.RefinementResult + surveillance pipeline.SurveillanceResult + refinementInput pipeline.RefinementInput + refinement pipeline.RefinementResult } diff --git a/cmd/sdrd/phase_state_test.go b/cmd/sdrd/phase_state_test.go index 7843b3d..ca22d84 100644 --- a/cmd/sdrd/phase_state_test.go +++ b/cmd/sdrd/phase_state_test.go @@ -8,12 +8,16 @@ import ( func TestPhaseStateCarriesPhaseResults(t *testing.T) { ps := &phaseState{ - surveillance: pipeline.SurveillanceResult{NoiseFloor: -90, Scheduled: []pipeline.ScheduledCandidate{{Candidate: pipeline.Candidate{ID: 1}, Priority: 5}}}, - refinement: pipeline.RefinementResult{Decisions: []pipeline.SignalDecision{{ShouldRecord: true}}, Candidates: []pipeline.Candidate{{ID: 1}}}, + surveillance: pipeline.SurveillanceResult{NoiseFloor: -90, Scheduled: []pipeline.ScheduledCandidate{{Candidate: pipeline.Candidate{ID: 1}, Priority: 5}}}, + refinementInput: pipeline.RefinementInput{Scheduled: []pipeline.ScheduledCandidate{{Candidate: pipeline.Candidate{ID: 1}, Priority: 5}}, SampleRate: 2048000, FFTSize: 2048, CenterHz: 7.1e6}, + refinement: pipeline.RefinementResult{Decisions: []pipeline.SignalDecision{{ShouldRecord: true}}, Candidates: []pipeline.Candidate{{ID: 1}}}, } if ps.surveillance.NoiseFloor != -90 || len(ps.surveillance.Scheduled) != 1 { t.Fatalf("unexpected surveillance state: %+v", ps.surveillance) } + if len(ps.refinementInput.Scheduled) != 1 || ps.refinementInput.SampleRate != 2048000 { + t.Fatalf("unexpected refinement input: %+v", ps.refinementInput) + } if len(ps.refinement.Decisions) != 1 || !ps.refinement.Decisions[0].ShouldRecord || len(ps.refinement.Candidates) != 1 { t.Fatalf("unexpected refinement state: %+v", ps.refinement) } diff --git a/cmd/sdrd/pipeline_runtime.go b/cmd/sdrd/pipeline_runtime.go index f30830f..bf2adc7 100644 --- a/cmd/sdrd/pipeline_runtime.go +++ b/cmd/sdrd/pipeline_runtime.go @@ -43,14 +43,14 @@ type dspRuntime struct { } type spectrumArtifacts struct { - allIQ []complex64 - iq []complex64 - spectrum []float64 - finished []detector.Event - detected []detector.Signal - thresholds []float64 - noiseFloor float64 - now time.Time + allIQ []complex64 + iq []complex64 + spectrum []float64 + finished []detector.Event + detected []detector.Signal + thresholds []float64 + noiseFloor float64 + now time.Time } func newDSPRuntime(cfg config.Config, det *detector.Detector, window []float64, gpuState *gpuStatus) *dspRuntime { @@ -229,14 +229,25 @@ func (rt *dspRuntime) buildSurveillanceResult(art *spectrumArtifacts) pipeline.S } } -func (rt *dspRuntime) refineSignals(art *spectrumArtifacts, scheduled []pipeline.ScheduledCandidate, extractMgr *extractionManager, rec *recorder.Manager) pipeline.RefinementResult { +func (rt *dspRuntime) buildRefinementInput(surv pipeline.SurveillanceResult) pipeline.RefinementInput { + return pipeline.RefinementInput{ + Candidates: append([]pipeline.Candidate(nil), surv.Candidates...), + Scheduled: append([]pipeline.ScheduledCandidate(nil), surv.Scheduled...), + SampleRate: rt.cfg.SampleRate, + FFTSize: rt.cfg.FFTSize, + CenterHz: rt.cfg.CenterHz, + Source: "surveillance-detector", + } +} + +func (rt *dspRuntime) refineSignals(art *spectrumArtifacts, input pipeline.RefinementInput, extractMgr *extractionManager, rec *recorder.Manager) pipeline.RefinementResult { if art == nil || len(art.iq) == 0 { return pipeline.RefinementResult{} } policy := pipeline.PolicyFromConfig(rt.cfg) - selectedCandidates := make([]pipeline.Candidate, 0, len(scheduled)) - selectedSignals := make([]detector.Signal, 0, len(scheduled)) - for _, sc := range scheduled { + selectedCandidates := make([]pipeline.Candidate, 0, len(input.Scheduled)) + selectedSignals := make([]detector.Signal, 0, len(input.Scheduled)) + for _, sc := range input.Scheduled { selectedCandidates = append(selectedCandidates, sc.Candidate) selectedSignals = append(selectedSignals, detector.Signal{ ID: sc.Candidate.ID, diff --git a/internal/pipeline/phases.go b/internal/pipeline/phases.go index 2a044d2..2fcc9f9 100644 --- a/internal/pipeline/phases.go +++ b/internal/pipeline/phases.go @@ -11,8 +11,17 @@ type SurveillanceResult struct { Thresholds []float64 `json:"thresholds,omitempty"` } +type RefinementInput struct { + Candidates []Candidate `json:"candidates,omitempty"` + Scheduled []ScheduledCandidate `json:"scheduled,omitempty"` + SampleRate int `json:"sample_rate"` + FFTSize int `json:"fft_size"` + CenterHz float64 `json:"center_hz"` + Source string `json:"source,omitempty"` +} + type RefinementResult struct { - Signals []detector.Signal `json:"signals"` - Decisions []SignalDecision `json:"decisions,omitempty"` - Candidates []Candidate `json:"candidates,omitempty"` + Signals []detector.Signal `json:"signals"` + Decisions []SignalDecision `json:"decisions,omitempty"` + Candidates []Candidate `json:"candidates,omitempty"` } diff --git a/internal/pipeline/phases_test.go b/internal/pipeline/phases_test.go index 141b6fb..44cf9f8 100644 --- a/internal/pipeline/phases_test.go +++ b/internal/pipeline/phases_test.go @@ -26,3 +26,20 @@ func TestSurveillanceResultCarriesScheduledCandidates(t *testing.T) { t.Fatalf("unexpected surveillance result: %+v", res) } } + +func TestRefinementInputCarriesScheduledCandidates(t *testing.T) { + res := RefinementInput{ + Candidates: []Candidate{{ID: 2}}, + Scheduled: []ScheduledCandidate{{Candidate: Candidate{ID: 2}, Priority: 4}}, + SampleRate: 2048000, + FFTSize: 2048, + CenterHz: 7.1e6, + Source: "surveillance-detector", + } + if len(res.Candidates) != 1 || len(res.Scheduled) != 1 { + t.Fatalf("unexpected refinement input: %+v", res) + } + if res.SampleRate != 2048000 || res.FFTSize != 2048 || res.CenterHz != 7.1e6 { + t.Fatalf("unexpected refinement input fields: %+v", res) + } +}