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

226 строки
5.7KB

  1. package pipeline
  2. import (
  3. "sort"
  4. "time"
  5. )
  6. type DecisionQueueStats struct {
  7. RecordQueued int `json:"record_queued"`
  8. DecodeQueued int `json:"decode_queued"`
  9. RecordSelected int `json:"record_selected"`
  10. DecodeSelected int `json:"decode_selected"`
  11. RecordActive int `json:"record_active"`
  12. DecodeActive int `json:"decode_active"`
  13. RecordOldestS float64 `json:"record_oldest_sec"`
  14. DecodeOldestS float64 `json:"decode_oldest_sec"`
  15. RecordBudget int `json:"record_budget"`
  16. DecodeBudget int `json:"decode_budget"`
  17. HoldMs int `json:"hold_ms"`
  18. RecordHoldMs int `json:"record_hold_ms"`
  19. DecodeHoldMs int `json:"decode_hold_ms"`
  20. RecordDropped int `json:"record_dropped"`
  21. DecodeDropped int `json:"decode_dropped"`
  22. }
  23. type queuedDecision struct {
  24. ID int64
  25. SNRDb float64
  26. Hint string
  27. Class string
  28. FirstSeen time.Time
  29. LastSeen time.Time
  30. }
  31. type decisionQueues struct {
  32. record map[int64]*queuedDecision
  33. decode map[int64]*queuedDecision
  34. recordHold map[int64]time.Time
  35. decodeHold map[int64]time.Time
  36. }
  37. func newDecisionQueues() *decisionQueues {
  38. return &decisionQueues{
  39. record: map[int64]*queuedDecision{},
  40. decode: map[int64]*queuedDecision{},
  41. recordHold: map[int64]time.Time{},
  42. decodeHold: map[int64]time.Time{},
  43. }
  44. }
  45. func (dq *decisionQueues) Apply(decisions []SignalDecision, budget BudgetModel, now time.Time, policy Policy) DecisionQueueStats {
  46. if dq == nil {
  47. return DecisionQueueStats{}
  48. }
  49. holdPolicy := HoldPolicyFromPolicy(policy)
  50. recordHold := time.Duration(holdPolicy.RecordMs) * time.Millisecond
  51. decodeHold := time.Duration(holdPolicy.DecodeMs) * time.Millisecond
  52. recSeen := map[int64]bool{}
  53. decSeen := map[int64]bool{}
  54. for i := range decisions {
  55. id := decisions[i].Candidate.ID
  56. if id == 0 {
  57. continue
  58. }
  59. if decisions[i].ShouldRecord {
  60. qd := dq.record[id]
  61. if qd == nil {
  62. qd = &queuedDecision{ID: id, FirstSeen: now}
  63. dq.record[id] = qd
  64. }
  65. qd.SNRDb = decisions[i].Candidate.SNRDb
  66. qd.Hint = decisions[i].Candidate.Hint
  67. qd.Class = decisions[i].Class
  68. qd.LastSeen = now
  69. recSeen[id] = true
  70. }
  71. if decisions[i].ShouldAutoDecode {
  72. qd := dq.decode[id]
  73. if qd == nil {
  74. qd = &queuedDecision{ID: id, FirstSeen: now}
  75. dq.decode[id] = qd
  76. }
  77. qd.SNRDb = decisions[i].Candidate.SNRDb
  78. qd.Hint = decisions[i].Candidate.Hint
  79. qd.Class = decisions[i].Class
  80. qd.LastSeen = now
  81. decSeen[id] = true
  82. }
  83. }
  84. for id := range dq.record {
  85. if !recSeen[id] {
  86. delete(dq.record, id)
  87. }
  88. }
  89. for id := range dq.decode {
  90. if !decSeen[id] {
  91. delete(dq.decode, id)
  92. }
  93. }
  94. purgeExpired(dq.recordHold, now)
  95. purgeExpired(dq.decodeHold, now)
  96. recSelected := selectQueued("record", dq.record, dq.recordHold, budget.Record.Max, recordHold, now, policy)
  97. decSelected := selectQueued("decode", dq.decode, dq.decodeHold, budget.Decode.Max, decodeHold, now, policy)
  98. stats := DecisionQueueStats{
  99. RecordQueued: len(dq.record),
  100. DecodeQueued: len(dq.decode),
  101. RecordSelected: len(recSelected),
  102. DecodeSelected: len(decSelected),
  103. RecordActive: len(dq.recordHold),
  104. DecodeActive: len(dq.decodeHold),
  105. RecordOldestS: oldestAge(dq.record, now),
  106. DecodeOldestS: oldestAge(dq.decode, now),
  107. RecordBudget: budget.Record.Max,
  108. DecodeBudget: budget.Decode.Max,
  109. HoldMs: budget.HoldMs,
  110. RecordHoldMs: holdPolicy.RecordMs,
  111. DecodeHoldMs: holdPolicy.DecodeMs,
  112. }
  113. for i := range decisions {
  114. id := decisions[i].Candidate.ID
  115. if decisions[i].ShouldRecord {
  116. if _, ok := recSelected[id]; !ok {
  117. decisions[i].ShouldRecord = false
  118. decisions[i].Reason = DecisionReasonQueueRecord
  119. stats.RecordDropped++
  120. }
  121. }
  122. if decisions[i].ShouldAutoDecode {
  123. if _, ok := decSelected[id]; !ok {
  124. decisions[i].ShouldAutoDecode = false
  125. if decisions[i].Reason == "" {
  126. decisions[i].Reason = DecisionReasonQueueDecode
  127. }
  128. stats.DecodeDropped++
  129. }
  130. }
  131. }
  132. return stats
  133. }
  134. func selectQueued(queueName string, queue map[int64]*queuedDecision, hold map[int64]time.Time, max int, holdDur time.Duration, now time.Time, policy Policy) map[int64]struct{} {
  135. selected := map[int64]struct{}{}
  136. if len(queue) == 0 {
  137. return selected
  138. }
  139. type scored struct {
  140. id int64
  141. score float64
  142. }
  143. scoredList := make([]scored, 0, len(queue))
  144. for id, qd := range queue {
  145. age := now.Sub(qd.FirstSeen).Seconds()
  146. boost := age / 2.0
  147. if boost > 5 {
  148. boost = 5
  149. }
  150. hint := qd.Hint
  151. if hint == "" {
  152. hint = qd.Class
  153. }
  154. policyBoost := DecisionPriorityBoost(policy, hint, qd.Class, queueName)
  155. scoredList = append(scoredList, scored{id: id, score: qd.SNRDb + boost + policyBoost})
  156. }
  157. sort.Slice(scoredList, func(i, j int) bool {
  158. return scoredList[i].score > scoredList[j].score
  159. })
  160. limit := max
  161. if limit <= 0 || limit > len(scoredList) {
  162. limit = len(scoredList)
  163. }
  164. if len(hold) > 0 && len(hold) > limit {
  165. limit = len(hold)
  166. if limit > len(scoredList) {
  167. limit = len(scoredList)
  168. }
  169. }
  170. for id := range hold {
  171. if _, ok := queue[id]; ok {
  172. selected[id] = struct{}{}
  173. }
  174. }
  175. for _, s := range scoredList {
  176. if len(selected) >= limit {
  177. break
  178. }
  179. if _, ok := selected[s.id]; ok {
  180. continue
  181. }
  182. selected[s.id] = struct{}{}
  183. }
  184. if holdDur > 0 {
  185. for id := range selected {
  186. hold[id] = now.Add(holdDur)
  187. }
  188. }
  189. return selected
  190. }
  191. func purgeExpired(hold map[int64]time.Time, now time.Time) {
  192. for id, until := range hold {
  193. if now.After(until) {
  194. delete(hold, id)
  195. }
  196. }
  197. }
  198. func oldestAge(queue map[int64]*queuedDecision, now time.Time) float64 {
  199. oldest := 0.0
  200. first := true
  201. for _, qd := range queue {
  202. age := now.Sub(qd.FirstSeen).Seconds()
  203. if first || age > oldest {
  204. oldest = age
  205. first = false
  206. }
  207. }
  208. if first {
  209. return 0
  210. }
  211. return oldest
  212. }