Wideband autonomous SDR analysis engine forked from sdr-visual-suite
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

106 Zeilen
2.1KB

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