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.

116 líneas
2.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. best := ClassUnknown
  9. second := ClassUnknown
  10. conf := 0.3
  11. switch {
  12. case bw >= 80e3:
  13. best = ClassWFM
  14. conf = 0.9
  15. case bw >= 25e3 && bw < 80e3:
  16. best = ClassWFM
  17. second = ClassNFM
  18. conf = 0.65
  19. case bw >= 6e3 && bw < 25e3:
  20. best = ClassNFM
  21. conf = 0.8
  22. if flat > 0.7 {
  23. second = ClassNoise
  24. }
  25. // digital voice rough guesses
  26. if feat.InstFreqStd < 0.5 && feat.EnvVariance < 0.3 {
  27. second = ClassDMR
  28. }
  29. case bw >= 3e3 && bw < 6e3:
  30. // wider SSB/AM
  31. if sym > 0.2 {
  32. best = ClassSSBUSB
  33. conf = 0.65
  34. } else if sym < -0.2 {
  35. best = ClassSSBLSB
  36. conf = 0.65
  37. } else if p2a > 2.5 && flat < 0.5 {
  38. best = ClassAM
  39. conf = 0.6
  40. }
  41. case bw >= 500 && bw < 3e3:
  42. // narrow SSB/AM + digital
  43. if feat.EnvVariance < 0.6 && feat.InstFreqStd < 0.7 && bw >= 2000 && bw < 3000 {
  44. best = ClassFT8
  45. conf = 0.55
  46. } else if sym > 0.2 {
  47. best = ClassSSBUSB
  48. conf = 0.7
  49. } else if sym < -0.2 {
  50. best = ClassSSBLSB
  51. conf = 0.7
  52. } else if p2a > 3 && flat < 0.4 {
  53. best = ClassAM
  54. conf = 0.6
  55. } else if feat.InstFreqStd > 0.8 {
  56. best = ClassFSK
  57. conf = 0.5
  58. } else if feat.InstFreqStd < 0.3 {
  59. best = ClassPSK
  60. conf = 0.5
  61. }
  62. case bw >= 150 && bw < 500:
  63. if feat.EnvVariance < 0.4 && feat.InstFreqStd < 0.5 {
  64. best = ClassWSPR
  65. conf = 0.55
  66. } else if feat.InstFreqStd > 0.9 {
  67. best = ClassFSK
  68. conf = 0.45
  69. } else if feat.InstFreqStd < 0.25 {
  70. best = ClassPSK
  71. conf = 0.45
  72. }
  73. case bw < 150:
  74. best = ClassCW
  75. conf = 0.7
  76. }
  77. // noise hint
  78. if best == ClassUnknown && flat > 0.85 && bw > 2e3 {
  79. best = ClassNoise
  80. conf = 0.6
  81. }
  82. // edge-case: if symmetry is strong, second best opposite side
  83. if (best == ClassSSBUSB || best == ClassSSBLSB) && second == ClassUnknown {
  84. if best == ClassSSBUSB {
  85. second = ClassSSBLSB
  86. } else {
  87. second = ClassSSBUSB
  88. }
  89. }
  90. // slightly scale confidence by feature strength
  91. if best == ClassNFM || best == ClassWFM {
  92. conf = conf * (0.8 + 0.2*clamp01(1-flat))
  93. }
  94. if best == ClassAM {
  95. conf = conf * (0.7 + 0.3*clamp01(p2a/6.0))
  96. }
  97. if math.IsNaN(conf) || conf <= 0 {
  98. conf = 0.3
  99. }
  100. return Classification{
  101. ModType: best,
  102. Confidence: conf,
  103. BW3dB: bw,
  104. Features: feat,
  105. SecondBest: second,
  106. }
  107. }