| @@ -57,12 +57,13 @@ func runDSP(ctx context.Context, srcMgr *sourceManager, cfg config.Config, det * | |||||
| rt.gotSamples = true | rt.gotSamples = true | ||||
| } | } | ||||
| state.surveillance = rt.buildSurveillanceResult(art) | state.surveillance = rt.buildSurveillanceResult(art) | ||||
| state.refinementInput = rt.buildRefinementInput(state.surveillance) | |||||
| finished := state.surveillance.Finished | finished := state.surveillance.Finished | ||||
| thresholds := state.surveillance.Thresholds | thresholds := state.surveillance.Thresholds | ||||
| noiseFloor := state.surveillance.NoiseFloor | noiseFloor := state.surveillance.NoiseFloor | ||||
| var displaySignals []detector.Signal | var displaySignals []detector.Signal | ||||
| if len(art.iq) > 0 { | 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 | displaySignals = state.refinement.Signals | ||||
| if rec != nil && len(displaySignals) > 0 && len(art.allIQ) > 0 { | if rec != nil && len(displaySignals) > 0 && len(art.allIQ) > 0 { | ||||
| aqCfg := extractionConfig{firTaps: rt.cfg.Recorder.ExtractionTaps, bwMult: rt.cfg.Recorder.ExtractionBwMult} | aqCfg := extractionConfig{firTaps: rt.cfg.Recorder.ExtractionTaps, bwMult: rt.cfg.Recorder.ExtractionBwMult} | ||||
| @@ -3,6 +3,7 @@ package main | |||||
| import "sdr-wideband-suite/internal/pipeline" | import "sdr-wideband-suite/internal/pipeline" | ||||
| type phaseState struct { | type phaseState struct { | ||||
| surveillance pipeline.SurveillanceResult | |||||
| refinement pipeline.RefinementResult | |||||
| surveillance pipeline.SurveillanceResult | |||||
| refinementInput pipeline.RefinementInput | |||||
| refinement pipeline.RefinementResult | |||||
| } | } | ||||
| @@ -8,12 +8,16 @@ import ( | |||||
| func TestPhaseStateCarriesPhaseResults(t *testing.T) { | func TestPhaseStateCarriesPhaseResults(t *testing.T) { | ||||
| ps := &phaseState{ | 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 { | if ps.surveillance.NoiseFloor != -90 || len(ps.surveillance.Scheduled) != 1 { | ||||
| t.Fatalf("unexpected surveillance state: %+v", ps.surveillance) | 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 { | if len(ps.refinement.Decisions) != 1 || !ps.refinement.Decisions[0].ShouldRecord || len(ps.refinement.Candidates) != 1 { | ||||
| t.Fatalf("unexpected refinement state: %+v", ps.refinement) | t.Fatalf("unexpected refinement state: %+v", ps.refinement) | ||||
| } | } | ||||
| @@ -43,14 +43,14 @@ type dspRuntime struct { | |||||
| } | } | ||||
| type spectrumArtifacts 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 { | 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 { | if art == nil || len(art.iq) == 0 { | ||||
| return pipeline.RefinementResult{} | return pipeline.RefinementResult{} | ||||
| } | } | ||||
| policy := pipeline.PolicyFromConfig(rt.cfg) | 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) | selectedCandidates = append(selectedCandidates, sc.Candidate) | ||||
| selectedSignals = append(selectedSignals, detector.Signal{ | selectedSignals = append(selectedSignals, detector.Signal{ | ||||
| ID: sc.Candidate.ID, | ID: sc.Candidate.ID, | ||||
| @@ -11,8 +11,17 @@ type SurveillanceResult struct { | |||||
| Thresholds []float64 `json:"thresholds,omitempty"` | 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 { | 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"` | |||||
| } | } | ||||
| @@ -26,3 +26,20 @@ func TestSurveillanceResultCarriesScheduledCandidates(t *testing.T) { | |||||
| t.Fatalf("unexpected surveillance result: %+v", res) | 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) | |||||
| } | |||||
| } | |||||