package pipeline import ( "sdr-visual-suite/internal/classifier" "sdr-visual-suite/internal/detector" ) // RefineCandidates upgrades coarse detector candidates into refined signals // by attaching local IQ-derived classification and PLL metadata. func RefineCandidates(candidates []Candidate, spectrum []float64, sampleRate int, fftSize int, snippets [][]complex64, snippetRates []int, mode classifier.ClassifierMode) []Refinement { out := make([]Refinement, 0, len(candidates)) for i, c := range candidates { sig := detector.Signal{ ID: c.ID, FirstBin: c.FirstBin, LastBin: c.LastBin, CenterHz: c.CenterHz, BWHz: c.BandwidthHz, PeakDb: c.PeakDb, SNRDb: c.SNRDb, NoiseDb: c.NoiseDb, } var snip []complex64 if i < len(snippets) { snip = snippets[i] } snipRate := sampleRate if i < len(snippetRates) && snippetRates[i] > 0 { snipRate = snippetRates[i] } cls := classifier.Classify(classifier.SignalInput{ FirstBin: sig.FirstBin, LastBin: sig.LastBin, SNRDb: sig.SNRDb, CenterHz: sig.CenterHz, BWHz: sig.BWHz, }, spectrum, sampleRate, fftSize, snip, mode) sig.Class = cls if cls != nil && snip != nil && len(snip) > 256 { pll := classifier.EstimateExactFrequency(snip, snipRate, sig.CenterHz, cls.ModType) cls.PLL = &pll sig.PLL = &pll if cls.ModType == classifier.ClassWFM && pll.Stereo { cls.ModType = classifier.ClassWFMStereo } } out = append(out, Refinement{ Candidate: c, Signal: sig, SnippetRate: snipRate, Class: cls, Stage: "local-iq", }) } return out }