Wideband autonomous SDR analysis engine forked from sdr-visual-suite
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

83 líneas
2.8KB

  1. package pipeline
  2. import (
  3. "sdr-wideband-suite/internal/classifier"
  4. "sdr-wideband-suite/internal/detector"
  5. )
  6. // Candidate is the coarse output of the surveillance detector.
  7. // It intentionally stays lightweight and cheap to produce.
  8. type Candidate struct {
  9. ID int64 `json:"id"`
  10. CenterHz float64 `json:"center_hz"`
  11. BandwidthHz float64 `json:"bandwidth_hz"`
  12. PeakDb float64 `json:"peak_db"`
  13. SNRDb float64 `json:"snr_db"`
  14. FirstBin int `json:"first_bin"`
  15. LastBin int `json:"last_bin"`
  16. NoiseDb float64 `json:"noise_db,omitempty"`
  17. Source string `json:"source,omitempty"`
  18. Hint string `json:"hint,omitempty"`
  19. Evidence []LevelEvidence `json:"evidence,omitempty"`
  20. EvidenceState *CandidateEvidenceState `json:"evidence_state,omitempty"`
  21. }
  22. // LevelEvidence captures which analysis level produced a candidate.
  23. // This is intentionally lightweight for later multi-level fusion.
  24. type LevelEvidence struct {
  25. Level AnalysisLevel `json:"level"`
  26. Provenance string `json:"provenance,omitempty"`
  27. }
  28. // RefinementWindow describes the local analysis span that refinement should use.
  29. type RefinementWindow struct {
  30. CenterHz float64 `json:"center_hz"`
  31. SpanHz float64 `json:"span_hz"`
  32. Source string `json:"source,omitempty"`
  33. }
  34. // Refinement contains higher-cost local analysis derived from a candidate.
  35. type Refinement struct {
  36. Candidate Candidate `json:"candidate"`
  37. Window RefinementWindow `json:"window"`
  38. Signal detector.Signal `json:"signal"`
  39. SnippetRate int `json:"snippet_rate"`
  40. Class *classifier.Classification `json:"class,omitempty"`
  41. Stage string `json:"stage,omitempty"`
  42. }
  43. func CandidatesFromSignals(signals []detector.Signal, source string) []Candidate {
  44. out := make([]Candidate, 0, len(signals))
  45. for _, s := range signals {
  46. hint := ""
  47. if s.Class != nil {
  48. hint = string(s.Class.ModType)
  49. }
  50. out = append(out, Candidate{
  51. ID: s.ID,
  52. CenterHz: s.CenterHz,
  53. BandwidthHz: s.BWHz,
  54. PeakDb: s.PeakDb,
  55. SNRDb: s.SNRDb,
  56. FirstBin: s.FirstBin,
  57. LastBin: s.LastBin,
  58. NoiseDb: s.NoiseDb,
  59. Source: source,
  60. Hint: hint,
  61. })
  62. }
  63. return out
  64. }
  65. func CandidatesFromSignalsWithLevel(signals []detector.Signal, source string, level AnalysisLevel) []Candidate {
  66. out := CandidatesFromSignals(signals, source)
  67. if level.Name == "" && level.FFTSize == 0 && level.SampleRate == 0 {
  68. return out
  69. }
  70. evidence := LevelEvidence{Level: level, Provenance: source}
  71. for i := range out {
  72. AddCandidateEvidence(&out[i], evidence)
  73. }
  74. return out
  75. }