Wideband autonomous SDR analysis engine forked from sdr-visual-suite
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

112 lines
2.7KB

  1. package gpudemod
  2. import (
  3. "fmt"
  4. "sdr-wideband-suite/internal/dsp"
  5. )
  6. type CPUOracleRunner struct {
  7. SampleRate int
  8. States map[int64]*CPUOracleState
  9. }
  10. func (r *CPUOracleRunner) ResetAllStates() {
  11. if r == nil {
  12. return
  13. }
  14. r.States = make(map[int64]*CPUOracleState)
  15. }
  16. func NewCPUOracleRunner(sampleRate int) *CPUOracleRunner {
  17. return &CPUOracleRunner{
  18. SampleRate: sampleRate,
  19. States: make(map[int64]*CPUOracleState),
  20. }
  21. }
  22. func (r *CPUOracleRunner) ResetSignalState(signalID int64) {
  23. if r == nil || r.States == nil {
  24. return
  25. }
  26. delete(r.States, signalID)
  27. }
  28. func (r *CPUOracleRunner) getOrInitState(job StreamingExtractJob) (*CPUOracleState, error) {
  29. if r == nil {
  30. return nil, fmt.Errorf("nil CPUOracleRunner")
  31. }
  32. if r.States == nil {
  33. r.States = make(map[int64]*CPUOracleState)
  34. }
  35. decim, err := ExactIntegerDecimation(r.SampleRate, job.OutRate)
  36. if err != nil {
  37. return nil, err
  38. }
  39. state := r.States[job.SignalID]
  40. if state == nil {
  41. state = &CPUOracleState{SignalID: job.SignalID}
  42. r.States[job.SignalID] = state
  43. }
  44. ResetCPUOracleStateIfConfigChanged(state, job.ConfigHash)
  45. state.Decim = decim
  46. state.NumTaps = job.NumTaps
  47. if state.NumTaps <= 0 {
  48. state.NumTaps = 101
  49. }
  50. cutoff := job.Bandwidth / 2
  51. if cutoff < 200 {
  52. cutoff = 200
  53. }
  54. base := dsp.LowpassFIR(cutoff, r.SampleRate, state.NumTaps)
  55. state.BaseTaps = make([]float32, len(base))
  56. for i, v := range base {
  57. state.BaseTaps[i] = float32(v)
  58. }
  59. state.PolyphaseTaps = BuildPolyphaseTapsPhaseMajor(state.BaseTaps, state.Decim)
  60. if state.ShiftedHistory == nil {
  61. state.ShiftedHistory = make([]complex64, 0, maxInt(0, state.NumTaps-1))
  62. }
  63. return state, nil
  64. }
  65. func (r *CPUOracleRunner) StreamingExtract(iqNew []complex64, jobs []StreamingExtractJob) ([]StreamingExtractResult, error) {
  66. results := make([]StreamingExtractResult, len(jobs))
  67. active := make(map[int64]struct{}, len(jobs))
  68. for i, job := range jobs {
  69. active[job.SignalID] = struct{}{}
  70. state, err := r.getOrInitState(job)
  71. if err != nil {
  72. return nil, err
  73. }
  74. out, phase, phaseCount, hist := runStreamingPolyphaseHostCore(
  75. iqNew,
  76. r.SampleRate,
  77. job.OffsetHz,
  78. state.NCOPhase,
  79. state.PhaseCount,
  80. state.NumTaps,
  81. state.Decim,
  82. state.ShiftedHistory,
  83. state.PolyphaseTaps,
  84. )
  85. state.NCOPhase = phase
  86. state.PhaseCount = phaseCount
  87. state.ShiftedHistory = append(state.ShiftedHistory[:0], hist...)
  88. results[i] = StreamingExtractResult{
  89. SignalID: job.SignalID,
  90. IQ: out,
  91. Rate: job.OutRate,
  92. NOut: len(out),
  93. PhaseCount: state.PhaseCount,
  94. HistoryLen: len(state.ShiftedHistory),
  95. }
  96. }
  97. for signalID := range r.States {
  98. if _, ok := active[signalID]; !ok {
  99. delete(r.States, signalID)
  100. }
  101. }
  102. return results, nil
  103. }