Wideband autonomous SDR analysis engine forked from sdr-visual-suite
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

91 lines
2.5KB

  1. package pipeline
  2. import (
  3. "sdr-wideband-suite/internal/classifier"
  4. "sdr-wideband-suite/internal/detector"
  5. )
  6. // RefineCandidates upgrades coarse detector candidates into refined signals
  7. // by attaching local IQ-derived classification and PLL metadata.
  8. func RefineCandidates(candidates []Candidate, windows []RefinementWindow, spectrum []float64, sampleRate int, fftSize int, snippets [][]complex64, snippetRates []int, mode classifier.ClassifierMode) []Refinement {
  9. out := make([]Refinement, 0, len(candidates))
  10. for i, c := range candidates {
  11. sig := detector.Signal{
  12. ID: c.ID,
  13. FirstBin: c.FirstBin,
  14. LastBin: c.LastBin,
  15. CenterHz: c.CenterHz,
  16. BWHz: c.BandwidthHz,
  17. PeakDb: c.PeakDb,
  18. SNRDb: c.SNRDb,
  19. NoiseDb: c.NoiseDb,
  20. }
  21. var snip []complex64
  22. if i < len(snippets) {
  23. snip = snippets[i]
  24. }
  25. snipRate := sampleRate
  26. if i < len(snippetRates) && snippetRates[i] > 0 {
  27. snipRate = snippetRates[i]
  28. }
  29. cls := classifier.Classify(classifier.SignalInput{
  30. FirstBin: sig.FirstBin,
  31. LastBin: sig.LastBin,
  32. SNRDb: sig.SNRDb,
  33. CenterHz: sig.CenterHz,
  34. BWHz: sig.BWHz,
  35. }, spectrum, sampleRate, fftSize, snip, mode)
  36. sig.Class = cls
  37. if cls != nil && cls.ModType == classifier.ClassWFM {
  38. cls.ModType = classifier.ClassWFMStereo
  39. sig.PlaybackMode = string(classifier.ClassWFMStereo)
  40. sig.DemodName = string(classifier.ClassWFMStereo)
  41. if sig.PLL != nil && sig.PLL.Stereo {
  42. sig.StereoState = "locked"
  43. } else {
  44. sig.StereoState = "searching"
  45. }
  46. }
  47. if cls != nil && snip != nil && len(snip) > 256 {
  48. pll := classifier.EstimateExactFrequency(snip, snipRate, sig.CenterHz, cls.ModType)
  49. cls.PLL = &pll
  50. sig.PLL = &pll
  51. if cls.ModType == classifier.ClassWFMStereo {
  52. if pll.Stereo {
  53. sig.StereoState = "locked"
  54. } else if sig.StereoState == "" {
  55. sig.StereoState = "searching"
  56. }
  57. sig.PlaybackMode = string(classifier.ClassWFMStereo)
  58. sig.DemodName = string(classifier.ClassWFMStereo)
  59. }
  60. }
  61. var window RefinementWindow
  62. if i < len(windows) {
  63. window = windows[i]
  64. }
  65. if window.CenterHz == 0 {
  66. window.CenterHz = c.CenterHz
  67. }
  68. if window.SpanHz <= 0 {
  69. if c.BandwidthHz > 0 {
  70. window.SpanHz = c.BandwidthHz
  71. } else {
  72. window.SpanHz = 12000
  73. }
  74. }
  75. if window.Source == "" {
  76. window.Source = "candidate"
  77. }
  78. out = append(out, Refinement{
  79. Candidate: c,
  80. Window: window,
  81. Signal: sig,
  82. SnippetRate: snipRate,
  83. Class: cls,
  84. Stage: "local-iq",
  85. })
  86. }
  87. return out
  88. }