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

168 строки
3.3KB

  1. package classifier
  2. import "math"
  3. func RuleClassify(feat Features) Classification {
  4. bw := feat.BW3dB
  5. flat := feat.SpectralFlat
  6. sym := feat.Symmetry
  7. p2a := feat.PeakToAvg
  8. scores := map[SignalClass]float64{}
  9. add := func(c SignalClass, w float64) {
  10. if w <= 0 {
  11. return
  12. }
  13. scores[c] += w
  14. }
  15. switch {
  16. case bw >= 80e3:
  17. add(ClassWFM, 2.2)
  18. case bw >= 25e3 && bw < 80e3:
  19. add(ClassWFM, 1.4)
  20. add(ClassNFM, 0.8)
  21. case bw >= 6e3 && bw < 25e3:
  22. add(ClassNFM, 2.0)
  23. case bw >= 3e3 && bw < 6e3:
  24. add(ClassSSBUSB, 0.6)
  25. add(ClassSSBLSB, 0.6)
  26. if p2a > 2.5 && flat < 0.5 {
  27. add(ClassAM, 1.1)
  28. }
  29. case bw >= 500 && bw < 3e3:
  30. add(ClassSSBUSB, 0.8)
  31. add(ClassSSBLSB, 0.8)
  32. if p2a > 3 && flat < 0.4 {
  33. add(ClassAM, 1.0)
  34. }
  35. case bw >= 150 && bw < 500:
  36. add(ClassFSK, 0.5)
  37. add(ClassPSK, 0.5)
  38. case bw < 150:
  39. add(ClassCW, 1.6)
  40. }
  41. if sym > 0.2 {
  42. add(ClassSSBUSB, 1.2)
  43. } else if sym < -0.2 {
  44. add(ClassSSBLSB, 1.2)
  45. }
  46. if feat.EnvVariance < 0.6 && feat.InstFreqStd < 0.7 && bw >= 2000 && bw < 3000 {
  47. add(ClassFT8, 1.4)
  48. }
  49. if feat.EnvVariance < 0.4 && feat.InstFreqStd < 0.5 && bw >= 150 && bw < 500 {
  50. add(ClassWSPR, 1.3)
  51. }
  52. if feat.InstFreqStd > 0.9 {
  53. add(ClassFSK, 1.2)
  54. } else if feat.InstFreqStd < 0.25 {
  55. add(ClassPSK, 1.0)
  56. }
  57. if p2a > 2.5 && flat < 0.5 {
  58. add(ClassAM, 0.8)
  59. }
  60. if flat > 0.85 && bw > 2e3 {
  61. add(ClassNoise, 1.0)
  62. }
  63. if feat.InstFreqStd < 0.5 && feat.EnvVariance < 0.3 && bw >= 6e3 && bw < 25e3 {
  64. add(ClassDMR, 0.7)
  65. }
  66. best, _, second, _ := top2(scores)
  67. if best == "" {
  68. best = ClassUnknown
  69. }
  70. if second == "" {
  71. second = ClassUnknown
  72. }
  73. conf := softmaxConfidence(scores, best)
  74. if best == ClassNFM || best == ClassWFM {
  75. conf = conf * (0.8 + 0.2*clamp01(1-flat))
  76. }
  77. if best == ClassAM {
  78. conf = conf * (0.7 + 0.3*clamp01(p2a/6.0))
  79. }
  80. if math.IsNaN(conf) || conf <= 0 {
  81. conf = 0.1
  82. }
  83. if (best == ClassSSBUSB || best == ClassSSBLSB) && second == ClassUnknown {
  84. if best == ClassSSBUSB {
  85. second = ClassSSBLSB
  86. } else {
  87. second = ClassSSBUSB
  88. }
  89. }
  90. return Classification{
  91. ModType: best,
  92. Confidence: conf,
  93. BW3dB: bw,
  94. Features: feat,
  95. SecondBest: second,
  96. Scores: scores,
  97. }
  98. }
  99. func softmaxConfidence(scores map[SignalClass]float64, best SignalClass) float64 {
  100. if len(scores) == 0 || best == "" || best == ClassUnknown {
  101. return 0.1
  102. }
  103. maxScore := math.Inf(-1)
  104. for _, v := range scores {
  105. if v > maxScore {
  106. maxScore = v
  107. }
  108. }
  109. if math.IsInf(maxScore, -1) {
  110. return 0.1
  111. }
  112. var expSum float64
  113. var expBest float64
  114. for k, v := range scores {
  115. e := math.Exp(v - maxScore)
  116. expSum += e
  117. if k == best {
  118. expBest = e
  119. }
  120. }
  121. if expSum <= 0 {
  122. return 0.1
  123. }
  124. return expBest / expSum
  125. }
  126. func top2(scores map[SignalClass]float64) (SignalClass, float64, SignalClass, float64) {
  127. var best, second SignalClass
  128. bestScore := 0.0
  129. secondScore := 0.0
  130. better := func(k SignalClass, v float64, cur SignalClass, curV float64) bool {
  131. if v > curV {
  132. return true
  133. }
  134. if v < curV {
  135. return false
  136. }
  137. if cur == "" {
  138. return true
  139. }
  140. return string(k) < string(cur)
  141. }
  142. for k, v := range scores {
  143. if better(k, v, best, bestScore) {
  144. second = best
  145. secondScore = bestScore
  146. best = k
  147. bestScore = v
  148. continue
  149. }
  150. if k != best && better(k, v, second, secondScore) {
  151. second = k
  152. secondScore = v
  153. }
  154. }
  155. return best, bestScore, second, secondScore
  156. }