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.

263 wiersze
8.0KB

  1. package runtime
  2. import (
  3. "testing"
  4. "sdr-wideband-suite/internal/config"
  5. )
  6. func TestApplyConfigUpdate(t *testing.T) {
  7. cfg := config.Default()
  8. mgr := New(cfg)
  9. center := 7.2e6
  10. sampleRate := 1_024_000
  11. fftSize := 4096
  12. threshold := -35.0
  13. bw := 1536
  14. cfarMode := "OS"
  15. cfarWrap := true
  16. cfarGuard := 2
  17. cfarTrain := 12
  18. cfarRank := 18
  19. cfarScale := 5.5
  20. mode := "wideband-balanced"
  21. profile := "wideband-balanced"
  22. intent := "wideband-surveillance"
  23. monitorSpan := 500000.0
  24. monitorWindows := []config.MonitorWindow{
  25. {Label: "primary", StartHz: 100, EndHz: 200},
  26. {Label: "secondary", CenterHz: 400, SpanHz: 50},
  27. }
  28. signalPriorities := []string{"digital", "weak"}
  29. autoRecord := []string{"WFM"}
  30. autoDecode := []string{"FT8"}
  31. survFPS := 12
  32. displayBins := 1024
  33. displayFPS := 8
  34. maxRefJobs := 24
  35. minSpan := 4000.0
  36. maxSpan := 200000.0
  37. autoSpan := false
  38. detailFFT := 1024
  39. maxDecode := 12
  40. decisionHold := 1500
  41. updated, err := mgr.ApplyConfig(ConfigUpdate{
  42. CenterHz: &center,
  43. SampleRate: &sampleRate,
  44. FFTSize: &fftSize,
  45. TunerBwKHz: &bw,
  46. Pipeline: &PipelineUpdate{
  47. Mode: &mode,
  48. Profile: &profile,
  49. Intent: &intent,
  50. MonitorSpanHz: &monitorSpan,
  51. MonitorWindows: &monitorWindows,
  52. SignalPriorities: &signalPriorities,
  53. AutoRecordClasses: &autoRecord,
  54. AutoDecodeClasses: &autoDecode,
  55. },
  56. Surveillance: &SurveillanceUpdate{FrameRate: &survFPS, DisplayBins: &displayBins, DisplayFPS: &displayFPS},
  57. Refinement: &RefinementUpdate{MinSpanHz: &minSpan, MaxSpanHz: &maxSpan, AutoSpan: &autoSpan, DetailFFTSize: &detailFFT},
  58. Resources: &ResourcesUpdate{MaxRefinementJobs: &maxRefJobs, MaxDecodeJobs: &maxDecode, DecisionHoldMs: &decisionHold},
  59. Detector: &DetectorUpdate{
  60. ThresholdDb: &threshold,
  61. CFARMode: &cfarMode,
  62. CFARWrapAround: &cfarWrap,
  63. CFARGuardCells: &cfarGuard,
  64. CFARTrainCells: &cfarTrain,
  65. CFARRank: &cfarRank,
  66. CFARScaleDb: &cfarScale,
  67. },
  68. })
  69. if err != nil {
  70. t.Fatalf("apply: %v", err)
  71. }
  72. if updated.CenterHz != center {
  73. t.Fatalf("center hz: %v", updated.CenterHz)
  74. }
  75. if updated.SampleRate != sampleRate {
  76. t.Fatalf("sample rate: %v", updated.SampleRate)
  77. }
  78. if updated.FFTSize != fftSize {
  79. t.Fatalf("fft size: %v", updated.FFTSize)
  80. }
  81. if updated.Surveillance.AnalysisFFTSize != fftSize {
  82. t.Fatalf("analysis fft size: %v", updated.Surveillance.AnalysisFFTSize)
  83. }
  84. if updated.Detector.ThresholdDb != threshold {
  85. t.Fatalf("threshold: %v", updated.Detector.ThresholdDb)
  86. }
  87. if updated.Detector.CFARMode != cfarMode {
  88. t.Fatalf("cfar mode: %v", updated.Detector.CFARMode)
  89. }
  90. if updated.Detector.CFARWrapAround != cfarWrap {
  91. t.Fatalf("cfar wrap: %v", updated.Detector.CFARWrapAround)
  92. }
  93. if updated.Detector.CFARGuardCells != cfarGuard {
  94. t.Fatalf("cfar guard: %v", updated.Detector.CFARGuardCells)
  95. }
  96. if updated.Detector.CFARTrainCells != cfarTrain {
  97. t.Fatalf("cfar train: %v", updated.Detector.CFARTrainCells)
  98. }
  99. if updated.Detector.CFARRank != cfarRank {
  100. t.Fatalf("cfar rank: %v", updated.Detector.CFARRank)
  101. }
  102. if updated.Detector.CFARScaleDb != cfarScale {
  103. t.Fatalf("cfar scale: %v", updated.Detector.CFARScaleDb)
  104. }
  105. if updated.TunerBwKHz != bw {
  106. t.Fatalf("tuner bw: %v", updated.TunerBwKHz)
  107. }
  108. if updated.Pipeline.Mode != mode {
  109. t.Fatalf("pipeline mode: %v", updated.Pipeline.Mode)
  110. }
  111. if updated.Pipeline.Profile != profile {
  112. t.Fatalf("pipeline profile: %v", updated.Pipeline.Profile)
  113. }
  114. if updated.Pipeline.Goals.Intent != intent {
  115. t.Fatalf("pipeline intent: %v", updated.Pipeline.Goals.Intent)
  116. }
  117. if updated.Pipeline.Goals.MonitorSpanHz != monitorSpan {
  118. t.Fatalf("monitor span: %v", updated.Pipeline.Goals.MonitorSpanHz)
  119. }
  120. if len(updated.Pipeline.Goals.MonitorWindows) != len(monitorWindows) {
  121. t.Fatalf("monitor windows not applied")
  122. }
  123. if len(updated.Pipeline.Goals.SignalPriorities) != len(signalPriorities) {
  124. t.Fatalf("signal priorities not applied")
  125. }
  126. if len(updated.Pipeline.Goals.AutoRecordClasses) != len(autoRecord) {
  127. t.Fatalf("auto record classes not applied")
  128. }
  129. if len(updated.Pipeline.Goals.AutoDecodeClasses) != len(autoDecode) {
  130. t.Fatalf("auto decode classes not applied")
  131. }
  132. if updated.Surveillance.FrameRate != survFPS || updated.FrameRate != survFPS {
  133. t.Fatalf("surveillance frame rate: %v / %v", updated.Surveillance.FrameRate, updated.FrameRate)
  134. }
  135. if updated.Resources.MaxRefinementJobs != maxRefJobs {
  136. t.Fatalf("max refinement jobs: %v", updated.Resources.MaxRefinementJobs)
  137. }
  138. if updated.Resources.MaxDecodeJobs != maxDecode {
  139. t.Fatalf("max decode jobs: %v", updated.Resources.MaxDecodeJobs)
  140. }
  141. if updated.Resources.DecisionHoldMs != decisionHold {
  142. t.Fatalf("decision hold: %v", updated.Resources.DecisionHoldMs)
  143. }
  144. if updated.Surveillance.DisplayBins != displayBins || updated.Surveillance.DisplayFPS != displayFPS {
  145. t.Fatalf("display settings not applied: bins=%d fps=%d", updated.Surveillance.DisplayBins, updated.Surveillance.DisplayFPS)
  146. }
  147. if updated.Refinement.MinSpanHz != minSpan || updated.Refinement.MaxSpanHz != maxSpan {
  148. t.Fatalf("refinement span not applied: %v / %v", updated.Refinement.MinSpanHz, updated.Refinement.MaxSpanHz)
  149. }
  150. if updated.Refinement.DetailFFTSize != detailFFT {
  151. t.Fatalf("refinement detail fft not applied: %v", updated.Refinement.DetailFFTSize)
  152. }
  153. if updated.Refinement.AutoSpan == nil || *updated.Refinement.AutoSpan != autoSpan {
  154. t.Fatalf("refinement auto span not applied")
  155. }
  156. }
  157. func TestApplyConfigRejectsInvalid(t *testing.T) {
  158. cfg := config.Default()
  159. {
  160. mgr := New(cfg)
  161. bad := 0
  162. if _, err := mgr.ApplyConfig(ConfigUpdate{SampleRate: &bad}); err == nil {
  163. t.Fatalf("expected error")
  164. }
  165. snap := mgr.Snapshot()
  166. if snap.SampleRate != cfg.SampleRate {
  167. t.Fatalf("sample rate changed on error")
  168. }
  169. }
  170. {
  171. mgr := New(cfg)
  172. badAlpha := -0.5
  173. if _, err := mgr.ApplyConfig(ConfigUpdate{Detector: &DetectorUpdate{EmaAlpha: &badAlpha}}); err == nil {
  174. t.Fatalf("expected ema_alpha error")
  175. }
  176. if mgr.Snapshot().Detector.EmaAlpha != cfg.Detector.EmaAlpha {
  177. t.Fatalf("ema_alpha changed on error")
  178. }
  179. }
  180. {
  181. mgr := New(cfg)
  182. badAlpha := 1.5
  183. if _, err := mgr.ApplyConfig(ConfigUpdate{Detector: &DetectorUpdate{EmaAlpha: &badAlpha}}); err == nil {
  184. t.Fatalf("expected ema_alpha upper bound error")
  185. }
  186. if mgr.Snapshot().Detector.EmaAlpha != cfg.Detector.EmaAlpha {
  187. t.Fatalf("ema_alpha changed on error")
  188. }
  189. }
  190. {
  191. mgr := New(cfg)
  192. badHyst := -1.0
  193. if _, err := mgr.ApplyConfig(ConfigUpdate{Detector: &DetectorUpdate{HysteresisDb: &badHyst}}); err == nil {
  194. t.Fatalf("expected hysteresis_db error")
  195. }
  196. if mgr.Snapshot().Detector.HysteresisDb != cfg.Detector.HysteresisDb {
  197. t.Fatalf("hysteresis_db changed on error")
  198. }
  199. }
  200. {
  201. mgr := New(cfg)
  202. badStable := 0
  203. if _, err := mgr.ApplyConfig(ConfigUpdate{Detector: &DetectorUpdate{MinStableFrames: &badStable}}); err == nil {
  204. t.Fatalf("expected min_stable_frames error")
  205. }
  206. if mgr.Snapshot().Detector.MinStableFrames != cfg.Detector.MinStableFrames {
  207. t.Fatalf("min_stable_frames changed on error")
  208. }
  209. }
  210. {
  211. mgr := New(cfg)
  212. badGap := -10
  213. if _, err := mgr.ApplyConfig(ConfigUpdate{Detector: &DetectorUpdate{GapToleranceMs: &badGap}}); err == nil {
  214. t.Fatalf("expected gap_tolerance_ms error")
  215. }
  216. if mgr.Snapshot().Detector.GapToleranceMs != cfg.Detector.GapToleranceMs {
  217. t.Fatalf("gap_tolerance_ms changed on error")
  218. }
  219. }
  220. {
  221. mgr := New(cfg)
  222. badDetail := 123
  223. if _, err := mgr.ApplyConfig(ConfigUpdate{Refinement: &RefinementUpdate{DetailFFTSize: &badDetail}}); err == nil {
  224. t.Fatalf("expected refinement.detail_fft_size error")
  225. }
  226. if mgr.Snapshot().Refinement.DetailFFTSize != cfg.Refinement.DetailFFTSize {
  227. t.Fatalf("detail fft changed on error")
  228. }
  229. }
  230. }
  231. func TestApplySettings(t *testing.T) {
  232. cfg := config.Default()
  233. mgr := New(cfg)
  234. agc := true
  235. dc := true
  236. iq := true
  237. updated, err := mgr.ApplySettings(SettingsUpdate{
  238. AGC: &agc,
  239. DCBlock: &dc,
  240. IQBalance: &iq,
  241. })
  242. if err != nil {
  243. t.Fatalf("apply settings: %v", err)
  244. }
  245. if !updated.AGC || !updated.DCBlock || !updated.IQBalance {
  246. t.Fatalf("settings not applied: %+v", updated)
  247. }
  248. }