Wideband autonomous SDR analysis engine forked from sdr-visual-suite
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

223 wiersze
4.7KB

  1. package detector
  2. import (
  3. "math"
  4. "sort"
  5. "time"
  6. )
  7. type Event struct {
  8. ID int64 `json:"id"`
  9. Start time.Time `json:"start"`
  10. End time.Time `json:"end"`
  11. CenterHz float64 `json:"center_hz"`
  12. Bandwidth float64 `json:"bandwidth_hz"`
  13. PeakDb float64 `json:"peak_db"`
  14. SNRDb float64 `json:"snr_db"`
  15. FirstBin int `json:"first_bin"`
  16. LastBin int `json:"last_bin"`
  17. }
  18. type Detector struct {
  19. ThresholdDb float64
  20. MinDuration time.Duration
  21. Hold time.Duration
  22. binWidth float64
  23. nbins int
  24. active map[int64]*activeEvent
  25. nextID int64
  26. }
  27. type activeEvent struct {
  28. id int64
  29. start time.Time
  30. lastSeen time.Time
  31. centerHz float64
  32. bwHz float64
  33. peakDb float64
  34. snrDb float64
  35. firstBin int
  36. lastBin int
  37. }
  38. type Signal struct {
  39. FirstBin int `json:"first_bin"`
  40. LastBin int `json:"last_bin"`
  41. CenterHz float64 `json:"center_hz"`
  42. BWHz float64 `json:"bw_hz"`
  43. PeakDb float64 `json:"peak_db"`
  44. SNRDb float64 `json:"snr_db"`
  45. }
  46. func New(thresholdDb float64, sampleRate int, fftSize int, minDur, hold time.Duration) *Detector {
  47. if minDur <= 0 {
  48. minDur = 250 * time.Millisecond
  49. }
  50. if hold <= 0 {
  51. hold = 500 * time.Millisecond
  52. }
  53. return &Detector{
  54. ThresholdDb: thresholdDb,
  55. MinDuration: minDur,
  56. Hold: hold,
  57. binWidth: float64(sampleRate) / float64(fftSize),
  58. nbins: fftSize,
  59. active: map[int64]*activeEvent{},
  60. nextID: 1,
  61. }
  62. }
  63. func (d *Detector) Process(now time.Time, spectrum []float64, centerHz float64) ([]Event, []Signal) {
  64. signals := d.detectSignals(spectrum, centerHz)
  65. finished := d.matchSignals(now, signals)
  66. return finished, signals
  67. }
  68. func (d *Detector) detectSignals(spectrum []float64, centerHz float64) []Signal {
  69. n := len(spectrum)
  70. if n == 0 {
  71. return nil
  72. }
  73. threshold := d.ThresholdDb
  74. noise := median(spectrum)
  75. var signals []Signal
  76. in := false
  77. start := 0
  78. peak := -1e9
  79. peakBin := 0
  80. for i := 0; i < n; i++ {
  81. v := spectrum[i]
  82. if v >= threshold {
  83. if !in {
  84. in = true
  85. start = i
  86. peak = v
  87. peakBin = i
  88. } else if v > peak {
  89. peak = v
  90. peakBin = i
  91. }
  92. } else if in {
  93. signals = append(signals, d.makeSignal(start, i-1, peak, peakBin, noise, centerHz))
  94. in = false
  95. }
  96. }
  97. if in {
  98. signals = append(signals, d.makeSignal(start, n-1, peak, peakBin, noise, centerHz))
  99. }
  100. return signals
  101. }
  102. func (d *Detector) makeSignal(first, last int, peak float64, peakBin int, noise float64, centerHz float64) Signal {
  103. centerBin := float64(first+last) / 2.0
  104. centerFreq := centerHz + (centerBin-float64(d.nbins)/2.0)*d.binWidth
  105. bw := float64(last-first+1) * d.binWidth
  106. snr := peak - noise
  107. return Signal{
  108. FirstBin: first,
  109. LastBin: last,
  110. CenterHz: centerFreq,
  111. BWHz: bw,
  112. PeakDb: peak,
  113. SNRDb: snr,
  114. }
  115. }
  116. func (d *Detector) matchSignals(now time.Time, signals []Signal) []Event {
  117. used := make(map[int64]bool, len(d.active))
  118. for _, s := range signals {
  119. var best *activeEvent
  120. for _, ev := range d.active {
  121. if overlapHz(s.CenterHz, s.BWHz, ev.centerHz, ev.bwHz) && math.Abs(s.CenterHz-ev.centerHz) < (s.BWHz+ev.bwHz)/2.0 {
  122. best = ev
  123. break
  124. }
  125. }
  126. if best == nil {
  127. id := d.nextID
  128. d.nextID++
  129. d.active[id] = &activeEvent{
  130. id: id,
  131. start: now,
  132. lastSeen: now,
  133. centerHz: s.CenterHz,
  134. bwHz: s.BWHz,
  135. peakDb: s.PeakDb,
  136. snrDb: s.SNRDb,
  137. firstBin: s.FirstBin,
  138. lastBin: s.LastBin,
  139. }
  140. continue
  141. }
  142. used[best.id] = true
  143. best.lastSeen = now
  144. best.centerHz = (best.centerHz + s.CenterHz) / 2.0
  145. if s.BWHz > best.bwHz {
  146. best.bwHz = s.BWHz
  147. }
  148. if s.PeakDb > best.peakDb {
  149. best.peakDb = s.PeakDb
  150. }
  151. if s.SNRDb > best.snrDb {
  152. best.snrDb = s.SNRDb
  153. }
  154. if s.FirstBin < best.firstBin {
  155. best.firstBin = s.FirstBin
  156. }
  157. if s.LastBin > best.lastBin {
  158. best.lastBin = s.LastBin
  159. }
  160. }
  161. var finished []Event
  162. for id, ev := range d.active {
  163. if used[id] {
  164. continue
  165. }
  166. if now.Sub(ev.lastSeen) < d.Hold {
  167. continue
  168. }
  169. duration := ev.lastSeen.Sub(ev.start)
  170. if duration < d.MinDuration {
  171. delete(d.active, id)
  172. continue
  173. }
  174. finished = append(finished, Event{
  175. ID: ev.id,
  176. Start: ev.start,
  177. End: ev.lastSeen,
  178. CenterHz: ev.centerHz,
  179. Bandwidth: ev.bwHz,
  180. PeakDb: ev.peakDb,
  181. SNRDb: ev.snrDb,
  182. FirstBin: ev.firstBin,
  183. LastBin: ev.lastBin,
  184. })
  185. delete(d.active, id)
  186. }
  187. return finished
  188. }
  189. func overlapHz(c1, b1, c2, b2 float64) bool {
  190. l1 := c1 - b1/2.0
  191. r1 := c1 + b1/2.0
  192. l2 := c2 - b2/2.0
  193. r2 := c2 + b2/2.0
  194. return l1 <= r2 && l2 <= r1
  195. }
  196. func median(vals []float64) float64 {
  197. if len(vals) == 0 {
  198. return 0
  199. }
  200. cpy := append([]float64(nil), vals...)
  201. sort.Float64s(cpy)
  202. mid := len(cpy) / 2
  203. if len(cpy)%2 == 0 {
  204. return (cpy[mid-1] + cpy[mid]) / 2.0
  205. }
  206. return cpy[mid]
  207. }