Wideband autonomous SDR analysis engine forked from sdr-visual-suite
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

220 рядки
5.7KB

  1. package main
  2. import (
  3. "sort"
  4. "strings"
  5. "sdr-wideband-suite/internal/pipeline"
  6. )
  7. type SurveillanceLevelSummary struct {
  8. Name string `json:"name"`
  9. Role string `json:"role,omitempty"`
  10. Truth string `json:"truth,omitempty"`
  11. Kind string `json:"kind,omitempty"`
  12. SampleRate int `json:"sample_rate,omitempty"`
  13. FFTSize int `json:"fft_size,omitempty"`
  14. BinHz float64 `json:"bin_hz,omitempty"`
  15. Decimation int `json:"decimation,omitempty"`
  16. SpanHz float64 `json:"span_hz,omitempty"`
  17. CenterHz float64 `json:"center_hz,omitempty"`
  18. Source string `json:"source,omitempty"`
  19. SpectrumBins int `json:"spectrum_bins,omitempty"`
  20. }
  21. type CandidateEvidenceSummary struct {
  22. Level string `json:"level"`
  23. Role string `json:"role,omitempty"`
  24. Kind string `json:"kind,omitempty"`
  25. Provenance string `json:"provenance,omitempty"`
  26. Count int `json:"count"`
  27. }
  28. type CandidateEvidenceStateSummary struct {
  29. Total int `json:"total"`
  30. WithEvidence int `json:"with_evidence"`
  31. Fused int `json:"fused"`
  32. MultiLevelConfirmed int `json:"multi_level_confirmed"`
  33. DerivedOnly int `json:"derived_only"`
  34. SupportOnly int `json:"support_only"`
  35. PrimaryPresent int `json:"primary_present"`
  36. DerivedPresent int `json:"derived_present"`
  37. SupportPresent int `json:"support_present"`
  38. PrimaryOnly int `json:"primary_only"`
  39. }
  40. func buildSurveillanceLevelSummaries(set pipeline.SurveillanceLevelSet, spectra []pipeline.SurveillanceLevelSpectrum) map[string]SurveillanceLevelSummary {
  41. if set.Primary.Name == "" && len(set.Derived) == 0 && len(set.Support) == 0 && set.Presentation.Name == "" && len(set.All) == 0 {
  42. return nil
  43. }
  44. bins := map[string]int{}
  45. for _, spec := range spectra {
  46. if spec.Level.Name == "" || len(spec.Spectrum) == 0 {
  47. continue
  48. }
  49. bins[spec.Level.Name] = len(spec.Spectrum)
  50. }
  51. levels := set.All
  52. if len(levels) == 0 {
  53. if set.Primary.Name != "" {
  54. levels = append(levels, set.Primary)
  55. }
  56. if len(set.Derived) > 0 {
  57. levels = append(levels, set.Derived...)
  58. }
  59. if len(set.Support) > 0 {
  60. levels = append(levels, set.Support...)
  61. }
  62. if set.Presentation.Name != "" {
  63. levels = append(levels, set.Presentation)
  64. }
  65. }
  66. out := make(map[string]SurveillanceLevelSummary, len(levels))
  67. for _, level := range levels {
  68. name := level.Name
  69. if name == "" {
  70. continue
  71. }
  72. binHz := level.BinHz
  73. if binHz == 0 && level.SampleRate > 0 && level.FFTSize > 0 {
  74. binHz = float64(level.SampleRate) / float64(level.FFTSize)
  75. }
  76. kind := evidenceKind(level)
  77. out[name] = SurveillanceLevelSummary{
  78. Name: name,
  79. Role: level.Role,
  80. Truth: level.Truth,
  81. Kind: kind,
  82. SampleRate: level.SampleRate,
  83. FFTSize: level.FFTSize,
  84. BinHz: binHz,
  85. Decimation: level.Decimation,
  86. SpanHz: level.SpanHz,
  87. CenterHz: level.CenterHz,
  88. Source: level.Source,
  89. SpectrumBins: bins[name],
  90. }
  91. }
  92. if len(out) == 0 {
  93. return nil
  94. }
  95. return out
  96. }
  97. func buildCandidateSourceSummary(candidates []pipeline.Candidate) map[string]int {
  98. if len(candidates) == 0 {
  99. return nil
  100. }
  101. out := map[string]int{}
  102. for _, cand := range candidates {
  103. if cand.Source == "" {
  104. continue
  105. }
  106. out[cand.Source]++
  107. }
  108. if len(out) == 0 {
  109. return nil
  110. }
  111. return out
  112. }
  113. func buildCandidateEvidenceSummary(candidates []pipeline.Candidate) []CandidateEvidenceSummary {
  114. if len(candidates) == 0 {
  115. return nil
  116. }
  117. type key struct {
  118. level string
  119. role string
  120. kind string
  121. provenance string
  122. }
  123. counts := map[key]int{}
  124. for _, cand := range candidates {
  125. for _, ev := range cand.Evidence {
  126. name := ev.Level.Name
  127. if name == "" {
  128. name = "unknown"
  129. }
  130. role := strings.TrimSpace(ev.Level.Role)
  131. kind := evidenceKind(ev.Level)
  132. k := key{level: name, role: role, kind: kind, provenance: ev.Provenance}
  133. counts[k]++
  134. }
  135. }
  136. if len(counts) == 0 {
  137. return nil
  138. }
  139. out := make([]CandidateEvidenceSummary, 0, len(counts))
  140. for k, v := range counts {
  141. out = append(out, CandidateEvidenceSummary{Level: k.level, Role: k.role, Kind: k.kind, Provenance: k.provenance, Count: v})
  142. }
  143. sort.Slice(out, func(i, j int) bool {
  144. if out[i].Count == out[j].Count {
  145. if out[i].Level == out[j].Level {
  146. if out[i].Kind == out[j].Kind {
  147. return out[i].Provenance < out[j].Provenance
  148. }
  149. return out[i].Kind < out[j].Kind
  150. }
  151. return out[i].Level < out[j].Level
  152. }
  153. return out[i].Count > out[j].Count
  154. })
  155. return out
  156. }
  157. func buildCandidateEvidenceStateSummary(candidates []pipeline.Candidate) *CandidateEvidenceStateSummary {
  158. if len(candidates) == 0 {
  159. return nil
  160. }
  161. summary := CandidateEvidenceStateSummary{Total: len(candidates)}
  162. for _, cand := range candidates {
  163. state := pipeline.CandidateEvidenceStateFor(cand)
  164. if state.TotalLevelEntries == 0 {
  165. continue
  166. }
  167. summary.WithEvidence++
  168. if state.Fused {
  169. summary.Fused++
  170. }
  171. if state.MultiLevelConfirmed {
  172. summary.MultiLevelConfirmed++
  173. }
  174. if state.DerivedOnly {
  175. summary.DerivedOnly++
  176. }
  177. if state.SupportOnly {
  178. summary.SupportOnly++
  179. }
  180. if state.PrimaryLevelCount > 0 {
  181. summary.PrimaryPresent++
  182. }
  183. if state.DerivedLevelCount > 0 {
  184. summary.DerivedPresent++
  185. }
  186. if state.SupportLevelCount > 0 {
  187. summary.SupportPresent++
  188. }
  189. if state.PrimaryLevelCount > 0 && state.DerivedLevelCount == 0 {
  190. summary.PrimaryOnly++
  191. }
  192. }
  193. if summary.WithEvidence == 0 {
  194. return nil
  195. }
  196. return &summary
  197. }
  198. func evidenceKind(level pipeline.AnalysisLevel) string {
  199. if pipeline.IsPresentationLevel(level) {
  200. return "presentation"
  201. }
  202. if pipeline.IsSupportLevel(level) {
  203. return "support"
  204. }
  205. if pipeline.IsDetectionLevel(level) {
  206. return "detection"
  207. }
  208. return "unknown"
  209. }