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.

256 Zeilen
6.5KB

  1. package pipeline
  2. import (
  3. "math"
  4. "strings"
  5. "time"
  6. )
  7. type HoldPolicy struct {
  8. BaseMs int `json:"base_ms"`
  9. RefinementMs int `json:"refinement_ms"`
  10. RecordMs int `json:"record_ms"`
  11. DecodeMs int `json:"decode_ms"`
  12. Profile string `json:"profile,omitempty"`
  13. Strategy string `json:"strategy,omitempty"`
  14. Reasons []string `json:"reasons,omitempty"`
  15. }
  16. type RefinementHold struct {
  17. Active map[int64]time.Time
  18. }
  19. type RefinementAdmission struct {
  20. Budget int `json:"budget"`
  21. BudgetSource string `json:"budget_source,omitempty"`
  22. HoldMs int `json:"hold_ms"`
  23. HoldSource string `json:"hold_source,omitempty"`
  24. Planned int `json:"planned"`
  25. Admitted int `json:"admitted"`
  26. Skipped int `json:"skipped"`
  27. Displaced int `json:"displaced"`
  28. PriorityCutoff float64 `json:"priority_cutoff,omitempty"`
  29. Reason string `json:"reason,omitempty"`
  30. }
  31. type RefinementAdmissionResult struct {
  32. Plan RefinementPlan
  33. WorkItems []RefinementWorkItem
  34. Admitted []ScheduledCandidate
  35. Admission RefinementAdmission
  36. }
  37. func HoldPolicyFromPolicy(policy Policy) HoldPolicy {
  38. base := policy.DecisionHoldMs
  39. if base < 0 {
  40. base = 0
  41. }
  42. refMult := 1.0
  43. recMult := 1.0
  44. decMult := 1.0
  45. reasons := make([]string, 0, 2)
  46. profile := strings.ToLower(strings.TrimSpace(policy.Profile))
  47. strategy := strings.ToLower(strings.TrimSpace(policy.RefinementStrategy))
  48. archiveProfile := profileContains(profile, "archive")
  49. archiveStrategy := strategyContains(strategy, "archive")
  50. if archiveProfile || archiveStrategy {
  51. recMult *= 1.5
  52. decMult *= 1.1
  53. refMult *= 1.2
  54. if archiveProfile {
  55. reasons = append(reasons, HoldReasonProfileArchive)
  56. }
  57. if archiveStrategy {
  58. reasons = append(reasons, HoldReasonStrategyArchive)
  59. }
  60. }
  61. digitalProfile := profileContains(profile, "digital")
  62. digitalStrategy := strategyContains(strategy, "digital")
  63. if digitalProfile || digitalStrategy {
  64. decMult *= 1.6
  65. recMult *= 0.85
  66. refMult *= 1.1
  67. if digitalProfile {
  68. reasons = append(reasons, HoldReasonProfileDigital)
  69. }
  70. if digitalStrategy {
  71. reasons = append(reasons, HoldReasonStrategyDigital)
  72. }
  73. }
  74. if profileContains(profile, "aggressive") {
  75. refMult *= 1.15
  76. reasons = append(reasons, HoldReasonProfileAggressive)
  77. }
  78. if strategyContains(strings.ToLower(strings.TrimSpace(policy.SurveillanceStrategy)), "multi") {
  79. refMult *= 1.1
  80. reasons = append(reasons, HoldReasonStrategyMultiRes)
  81. }
  82. return HoldPolicy{
  83. BaseMs: base,
  84. RefinementMs: scaleHold(base, refMult),
  85. RecordMs: scaleHold(base, recMult),
  86. DecodeMs: scaleHold(base, decMult),
  87. Profile: policy.Profile,
  88. Strategy: policy.RefinementStrategy,
  89. Reasons: reasons,
  90. }
  91. }
  92. func AdmitRefinementPlan(plan RefinementPlan, policy Policy, now time.Time, hold *RefinementHold) RefinementAdmissionResult {
  93. ranked := plan.Ranked
  94. if len(ranked) == 0 {
  95. ranked = plan.Selected
  96. }
  97. workItems := append([]RefinementWorkItem(nil), plan.WorkItems...)
  98. admission := RefinementAdmission{
  99. Budget: plan.Budget,
  100. BudgetSource: plan.BudgetSource,
  101. }
  102. if len(ranked) == 0 {
  103. admission.Reason = ReasonAdmissionNoCandidates
  104. return RefinementAdmissionResult{Plan: plan, WorkItems: workItems, Admission: admission}
  105. }
  106. holdPolicy := HoldPolicyFromPolicy(policy)
  107. admission.HoldMs = holdPolicy.RefinementMs
  108. admission.HoldSource = "resources.decision_hold_ms"
  109. if len(holdPolicy.Reasons) > 0 {
  110. admission.HoldSource += ":" + strings.Join(holdPolicy.Reasons, ",")
  111. }
  112. planned := len(ranked)
  113. admission.Planned = planned
  114. selected := map[int64]struct{}{}
  115. if hold != nil {
  116. purgeHold(hold.Active, now)
  117. for id := range hold.Active {
  118. if rankedContains(ranked, id) {
  119. selected[id] = struct{}{}
  120. }
  121. }
  122. }
  123. limit := plan.Budget
  124. if limit <= 0 || limit > planned {
  125. limit = planned
  126. }
  127. if len(selected) > limit {
  128. limit = len(selected)
  129. if limit > planned {
  130. limit = planned
  131. }
  132. }
  133. for _, cand := range ranked {
  134. if len(selected) >= limit {
  135. break
  136. }
  137. if _, ok := selected[cand.Candidate.ID]; ok {
  138. continue
  139. }
  140. selected[cand.Candidate.ID] = struct{}{}
  141. }
  142. if hold != nil && admission.HoldMs > 0 {
  143. until := now.Add(time.Duration(admission.HoldMs) * time.Millisecond)
  144. if hold.Active == nil {
  145. hold.Active = map[int64]time.Time{}
  146. }
  147. for id := range selected {
  148. hold.Active[id] = until
  149. }
  150. }
  151. admitted := make([]ScheduledCandidate, 0, len(selected))
  152. for _, cand := range ranked {
  153. if _, ok := selected[cand.Candidate.ID]; ok {
  154. admitted = append(admitted, cand)
  155. }
  156. }
  157. admission.Admitted = len(admitted)
  158. admission.Skipped = planned - admission.Admitted
  159. if admission.Skipped < 0 {
  160. admission.Skipped = 0
  161. }
  162. displaced := map[int64]struct{}{}
  163. if len(admitted) > 0 {
  164. admission.PriorityCutoff = admitted[len(admitted)-1].Priority
  165. for _, cand := range ranked {
  166. if _, ok := selected[cand.Candidate.ID]; ok {
  167. continue
  168. }
  169. if cand.Priority >= admission.PriorityCutoff {
  170. displaced[cand.Candidate.ID] = struct{}{}
  171. }
  172. }
  173. }
  174. admission.Displaced = len(displaced)
  175. plan.Selected = admitted
  176. plan.PriorityCutoff = admission.PriorityCutoff
  177. plan.DroppedByBudget = admission.Skipped
  178. for i := range workItems {
  179. item := &workItems[i]
  180. if item.Status != RefinementStatusPlanned {
  181. continue
  182. }
  183. id := item.Candidate.ID
  184. if _, ok := selected[id]; ok {
  185. item.Status = RefinementStatusAdmitted
  186. item.Reason = RefinementReasonAdmitted
  187. continue
  188. }
  189. if _, ok := displaced[id]; ok {
  190. item.Status = RefinementStatusDisplaced
  191. item.Reason = RefinementReasonDisplaced
  192. continue
  193. }
  194. item.Status = RefinementStatusSkipped
  195. item.Reason = RefinementReasonBudget
  196. }
  197. return RefinementAdmissionResult{
  198. Plan: plan,
  199. WorkItems: workItems,
  200. Admitted: admitted,
  201. Admission: admission,
  202. }
  203. }
  204. func purgeHold(active map[int64]time.Time, now time.Time) {
  205. for id, until := range active {
  206. if now.After(until) {
  207. delete(active, id)
  208. }
  209. }
  210. }
  211. func rankedContains(items []ScheduledCandidate, id int64) bool {
  212. for _, item := range items {
  213. if item.Candidate.ID == id {
  214. return true
  215. }
  216. }
  217. return false
  218. }
  219. func scaleHold(base int, mult float64) int {
  220. if base <= 0 {
  221. return 0
  222. }
  223. return int(math.Round(float64(base) * mult))
  224. }
  225. func profileContains(profile string, token string) bool {
  226. if profile == "" || token == "" {
  227. return false
  228. }
  229. return strings.Contains(profile, strings.ToLower(token))
  230. }
  231. func strategyContains(strategy string, token string) bool {
  232. if strategy == "" || token == "" {
  233. return false
  234. }
  235. return strings.Contains(strategy, strings.ToLower(token))
  236. }