Wideband autonomous SDR analysis engine forked from sdr-visual-suite
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

143 linhas
5.5KB

  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. MonitorMatches []MonitorWindowMatch `json:"monitor_matches,omitempty"`
  22. }
  23. // LevelEvidence captures which analysis level produced a candidate.
  24. // This is intentionally lightweight for later multi-level fusion.
  25. type LevelEvidence struct {
  26. Level AnalysisLevel `json:"level"`
  27. Provenance string `json:"provenance,omitempty"`
  28. }
  29. // MonitorWindow describes a monitoring window to gate candidates.
  30. type MonitorWindow struct {
  31. Index int `json:"index,omitempty"`
  32. Label string `json:"label,omitempty"`
  33. Zone string `json:"zone,omitempty"`
  34. StartHz float64 `json:"start_hz,omitempty"`
  35. EndHz float64 `json:"end_hz,omitempty"`
  36. CenterHz float64 `json:"center_hz,omitempty"`
  37. SpanHz float64 `json:"span_hz,omitempty"`
  38. Source string `json:"source,omitempty"`
  39. Priority float64 `json:"priority,omitempty"`
  40. PriorityBias float64 `json:"priority_bias,omitempty"`
  41. RecordBias float64 `json:"record_bias,omitempty"`
  42. DecodeBias float64 `json:"decode_bias,omitempty"`
  43. AutoRecord bool `json:"auto_record,omitempty"`
  44. AutoDecode bool `json:"auto_decode,omitempty"`
  45. }
  46. // MonitorWindowMatch captures how a candidate overlaps a monitor window.
  47. type MonitorWindowMatch struct {
  48. Index int `json:"index"`
  49. Label string `json:"label,omitempty"`
  50. Zone string `json:"zone,omitempty"`
  51. Source string `json:"source,omitempty"`
  52. StartHz float64 `json:"start_hz,omitempty"`
  53. EndHz float64 `json:"end_hz,omitempty"`
  54. CenterHz float64 `json:"center_hz,omitempty"`
  55. SpanHz float64 `json:"span_hz,omitempty"`
  56. OverlapHz float64 `json:"overlap_hz,omitempty"`
  57. Coverage float64 `json:"coverage,omitempty"`
  58. DistanceHz float64 `json:"distance_hz,omitempty"`
  59. Bias float64 `json:"bias,omitempty"`
  60. RecordBias float64 `json:"record_bias,omitempty"`
  61. DecodeBias float64 `json:"decode_bias,omitempty"`
  62. AutoRecord bool `json:"auto_record,omitempty"`
  63. AutoDecode bool `json:"auto_decode,omitempty"`
  64. }
  65. // MonitorWindowStats summarizes candidate attribution per monitor window.
  66. type MonitorWindowStats struct {
  67. Index int `json:"index"`
  68. Label string `json:"label,omitempty"`
  69. Zone string `json:"zone,omitempty"`
  70. Source string `json:"source,omitempty"`
  71. StartHz float64 `json:"start_hz,omitempty"`
  72. EndHz float64 `json:"end_hz,omitempty"`
  73. CenterHz float64 `json:"center_hz,omitempty"`
  74. SpanHz float64 `json:"span_hz,omitempty"`
  75. Priority float64 `json:"priority,omitempty"`
  76. PriorityBias float64 `json:"priority_bias,omitempty"`
  77. RecordBias float64 `json:"record_bias,omitempty"`
  78. DecodeBias float64 `json:"decode_bias,omitempty"`
  79. AutoRecord bool `json:"auto_record,omitempty"`
  80. AutoDecode bool `json:"auto_decode,omitempty"`
  81. Candidates int `json:"candidates,omitempty"`
  82. Planned int `json:"planned,omitempty"`
  83. Dropped int `json:"dropped,omitempty"`
  84. }
  85. // RefinementWindow describes the local analysis span that refinement should use.
  86. type RefinementWindow struct {
  87. CenterHz float64 `json:"center_hz"`
  88. SpanHz float64 `json:"span_hz"`
  89. Source string `json:"source,omitempty"`
  90. }
  91. // Refinement contains higher-cost local analysis derived from a candidate.
  92. type Refinement struct {
  93. Candidate Candidate `json:"candidate"`
  94. Window RefinementWindow `json:"window"`
  95. Signal detector.Signal `json:"signal"`
  96. SnippetRate int `json:"snippet_rate"`
  97. Class *classifier.Classification `json:"class,omitempty"`
  98. Stage string `json:"stage,omitempty"`
  99. }
  100. func CandidatesFromSignals(signals []detector.Signal, source string) []Candidate {
  101. out := make([]Candidate, 0, len(signals))
  102. for _, s := range signals {
  103. hint := ""
  104. if s.Class != nil {
  105. hint = string(s.Class.ModType)
  106. }
  107. out = append(out, Candidate{
  108. ID: s.ID,
  109. CenterHz: s.CenterHz,
  110. BandwidthHz: s.BWHz,
  111. PeakDb: s.PeakDb,
  112. SNRDb: s.SNRDb,
  113. FirstBin: s.FirstBin,
  114. LastBin: s.LastBin,
  115. NoiseDb: s.NoiseDb,
  116. Source: source,
  117. Hint: hint,
  118. })
  119. }
  120. return out
  121. }
  122. func CandidatesFromSignalsWithLevel(signals []detector.Signal, source string, level AnalysisLevel) []Candidate {
  123. out := CandidatesFromSignals(signals, source)
  124. if level.Name == "" && level.FFTSize == 0 && level.SampleRate == 0 {
  125. return out
  126. }
  127. evidence := LevelEvidence{Level: level, Provenance: source}
  128. for i := range out {
  129. AddCandidateEvidence(&out[i], evidence)
  130. }
  131. return out
  132. }