Wideband autonomous SDR analysis engine forked from sdr-visual-suite
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123 lines
2.7KB

  1. package pipeline
  2. import "math"
  3. func AddCandidateEvidence(candidate *Candidate, evidence LevelEvidence) {
  4. if candidate == nil {
  5. return
  6. }
  7. levelName := evidence.Level.Name
  8. if levelName == "" {
  9. levelName = "unknown"
  10. }
  11. for _, ev := range candidate.Evidence {
  12. evLevel := ev.Level.Name
  13. if evLevel == "" {
  14. evLevel = "unknown"
  15. }
  16. if evLevel == levelName && ev.Provenance == evidence.Provenance {
  17. RefreshCandidateEvidenceState(candidate)
  18. return
  19. }
  20. }
  21. candidate.Evidence = append(candidate.Evidence, evidence)
  22. RefreshCandidateEvidenceState(candidate)
  23. }
  24. func MergeCandidateEvidence(dst *Candidate, src Candidate) {
  25. if dst == nil || len(src.Evidence) == 0 {
  26. return
  27. }
  28. for _, ev := range src.Evidence {
  29. AddCandidateEvidence(dst, ev)
  30. }
  31. }
  32. func CandidateEvidenceLevelCount(candidate Candidate) int {
  33. state := CandidateEvidenceStateFor(candidate)
  34. return state.DetectionLevelCount
  35. }
  36. func FuseCandidates(primary []Candidate, derived []Candidate) []Candidate {
  37. if len(primary) == 0 && len(derived) == 0 {
  38. return nil
  39. }
  40. out := make([]Candidate, 0, len(primary)+len(derived))
  41. out = append(out, primary...)
  42. if len(derived) == 0 {
  43. return out
  44. }
  45. used := make([]bool, len(derived))
  46. for i := range out {
  47. for j, cand := range derived {
  48. if used[j] {
  49. continue
  50. }
  51. if !candidatesOverlap(out[i], cand) {
  52. continue
  53. }
  54. MergeCandidateEvidence(&out[i], cand)
  55. used[j] = true
  56. }
  57. }
  58. for j, cand := range derived {
  59. if used[j] {
  60. continue
  61. }
  62. out = append(out, cand)
  63. }
  64. for i := range out {
  65. RefreshCandidateEvidenceState(&out[i])
  66. }
  67. return out
  68. }
  69. func candidatesOverlap(a Candidate, b Candidate) bool {
  70. spanA := candidateSpanHz(a)
  71. spanB := candidateSpanHz(b)
  72. if spanA <= 0 {
  73. spanA = 25000
  74. }
  75. if spanB <= 0 {
  76. spanB = 25000
  77. }
  78. guard := 0.0
  79. if binA, binB := candidateBinHz(a), candidateBinHz(b); binA > 0 || binB > 0 {
  80. guard = 0.5 * math.Max(binA, binB)
  81. }
  82. leftA := a.CenterHz - spanA/2 - guard
  83. rightA := a.CenterHz + spanA/2 + guard
  84. leftB := b.CenterHz - spanB/2 - guard
  85. rightB := b.CenterHz + spanB/2 + guard
  86. return leftA <= rightB && leftB <= rightA
  87. }
  88. func candidateSpanHz(candidate Candidate) float64 {
  89. if candidate.BandwidthHz > 0 {
  90. return candidate.BandwidthHz
  91. }
  92. if candidate.LastBin < candidate.FirstBin {
  93. return 0
  94. }
  95. binHz := candidateBinHz(candidate)
  96. if binHz <= 0 {
  97. return 0
  98. }
  99. return float64(candidate.LastBin-candidate.FirstBin+1) * binHz
  100. }
  101. func candidateBinHz(candidate Candidate) float64 {
  102. for _, ev := range candidate.Evidence {
  103. if IsPresentationLevel(ev.Level) || !IsDetectionLevel(ev.Level) {
  104. continue
  105. }
  106. if ev.Level.BinHz > 0 {
  107. return ev.Level.BinHz
  108. }
  109. if ev.Level.SampleRate > 0 && ev.Level.FFTSize > 0 {
  110. return float64(ev.Level.SampleRate) / float64(ev.Level.FFTSize)
  111. }
  112. }
  113. return 0
  114. }