Wideband autonomous SDR analysis engine forked from sdr-visual-suite
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

95 řádky
2.6KB

  1. package main
  2. import (
  3. "math"
  4. "sdr-wideband-suite/internal/demod/gpudemod"
  5. "sdr-wideband-suite/internal/detector"
  6. "sdr-wideband-suite/internal/telemetry"
  7. )
  8. const useStreamingOraclePath = false // temporarily disable oracle during bring-up to isolate production-path runtime behavior
  9. const useStreamingProductionPath = true // route top-level extraction through the new production path during bring-up/validation
  10. var streamingOracleRunner *gpudemod.CPUOracleRunner
  11. func buildStreamingJobs(sampleRate int, centerHz float64, signals []detector.Signal, aqCfg extractionConfig) ([]gpudemod.StreamingExtractJob, error) {
  12. jobs := make([]gpudemod.StreamingExtractJob, len(signals))
  13. decimTarget := 200000
  14. bwMult := aqCfg.bwMult
  15. if bwMult <= 0 {
  16. bwMult = 1.0
  17. }
  18. firTaps := aqCfg.firTaps
  19. if firTaps <= 0 {
  20. firTaps = 101
  21. }
  22. for i, sig := range signals {
  23. bw := sig.BWHz * bwMult
  24. sigMHz := sig.CenterHz / 1e6
  25. isWFM := (sigMHz >= 87.5 && sigMHz <= 108.0) ||
  26. (sig.Class != nil && (sig.Class.ModType == "WFM" || sig.Class.ModType == "WFM_STEREO"))
  27. outRate := decimTarget
  28. if isWFM {
  29. outRate = wfmStreamOutRate
  30. if bw < wfmStreamMinBW {
  31. bw = wfmStreamMinBW
  32. }
  33. } else if bw < 20000 {
  34. bw = 20000
  35. }
  36. if _, err := gpudemod.ExactIntegerDecimation(sampleRate, outRate); err != nil {
  37. return nil, err
  38. }
  39. offset := sig.CenterHz - centerHz
  40. jobs[i] = gpudemod.StreamingExtractJob{
  41. SignalID: sig.ID,
  42. OffsetHz: offset,
  43. Bandwidth: bw,
  44. OutRate: outRate,
  45. NumTaps: firTaps,
  46. ConfigHash: gpudemod.StreamingConfigHash(sig.ID, offset, bw, outRate, firTaps, sampleRate),
  47. }
  48. }
  49. return jobs, nil
  50. }
  51. func resetStreamingOracleRunner() {
  52. if streamingOracleRunner != nil {
  53. streamingOracleRunner.ResetAllStates()
  54. }
  55. }
  56. func extractForStreamingOracle(
  57. allIQ []complex64,
  58. sampleRate int,
  59. centerHz float64,
  60. signals []detector.Signal,
  61. aqCfg extractionConfig,
  62. coll *telemetry.Collector,
  63. ) ([][]complex64, []int, error) {
  64. out := make([][]complex64, len(signals))
  65. rates := make([]int, len(signals))
  66. jobs, err := buildStreamingJobs(sampleRate, centerHz, signals, aqCfg)
  67. if err != nil {
  68. return nil, nil, err
  69. }
  70. if streamingOracleRunner == nil || streamingOracleRunner.SampleRate != sampleRate {
  71. streamingOracleRunner = gpudemod.NewCPUOracleRunner(sampleRate)
  72. }
  73. results, err := streamingOracleRunner.StreamingExtract(allIQ, jobs)
  74. if err != nil {
  75. return nil, nil, err
  76. }
  77. for i, res := range results {
  78. out[i] = res.IQ
  79. rates[i] = res.Rate
  80. observeStreamingResult(coll, "streaming.oracle", res)
  81. }
  82. return out, rates, nil
  83. }
  84. func phaseIncForOffset(sampleRate int, offsetHz float64) float64 {
  85. return -2.0 * math.Pi * offsetHz / float64(sampleRate)
  86. }