Wideband autonomous SDR analysis engine forked from sdr-visual-suite
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

171 líneas
4.2KB

  1. package gpudemod
  2. import (
  3. "fmt"
  4. "math"
  5. )
  6. type CPUOracleState struct {
  7. SignalID int64
  8. ConfigHash uint64
  9. NCOPhase float64
  10. Decim int
  11. PhaseCount int
  12. NumTaps int
  13. ShiftedHistory []complex64
  14. BaseTaps []float32
  15. PolyphaseTaps []float32
  16. }
  17. func ResetCPUOracleStateIfConfigChanged(state *CPUOracleState, newHash uint64) {
  18. if state == nil {
  19. return
  20. }
  21. if state.ConfigHash != newHash {
  22. state.ConfigHash = newHash
  23. state.NCOPhase = 0
  24. state.PhaseCount = 0
  25. state.ShiftedHistory = state.ShiftedHistory[:0]
  26. }
  27. }
  28. func CPUOracleExtract(iqNew []complex64, state *CPUOracleState, phaseInc float64) []complex64 {
  29. if state == nil || state.NumTaps <= 0 || state.Decim <= 0 || len(state.BaseTaps) < state.NumTaps {
  30. return nil
  31. }
  32. out := make([]complex64, 0, len(iqNew)/maxInt(1, state.Decim)+2)
  33. phase := state.NCOPhase
  34. hist := append([]complex64(nil), state.ShiftedHistory...)
  35. for _, x := range iqNew {
  36. rot := complex64(complex(math.Cos(phase), math.Sin(phase)))
  37. s := x * rot
  38. hist = append(hist, s)
  39. state.PhaseCount++
  40. if state.PhaseCount == state.Decim {
  41. var y complex64
  42. for k := 0; k < state.NumTaps; k++ {
  43. idx := len(hist) - 1 - k
  44. var sample complex64
  45. if idx >= 0 {
  46. sample = hist[idx]
  47. }
  48. y += complex(state.BaseTaps[k], 0) * sample
  49. }
  50. out = append(out, y)
  51. state.PhaseCount = 0
  52. }
  53. if len(hist) > state.NumTaps-1 {
  54. hist = hist[len(hist)-(state.NumTaps-1):]
  55. }
  56. phase += phaseInc
  57. if phase >= math.Pi {
  58. phase -= 2 * math.Pi
  59. } else if phase < -math.Pi {
  60. phase += 2 * math.Pi
  61. }
  62. }
  63. state.NCOPhase = phase
  64. state.ShiftedHistory = append(state.ShiftedHistory[:0], hist...)
  65. return out
  66. }
  67. // CPUOracleExtractPolyphase keeps the same streaming state semantics as CPUOracleExtract,
  68. // but computes outputs using the explicit phase-major polyphase tap layout.
  69. func CPUOracleExtractPolyphase(iqNew []complex64, state *CPUOracleState, phaseInc float64) []complex64 {
  70. if state == nil || state.NumTaps <= 0 || state.Decim <= 0 || len(state.BaseTaps) < state.NumTaps {
  71. return nil
  72. }
  73. if len(state.PolyphaseTaps) == 0 {
  74. state.PolyphaseTaps = BuildPolyphaseTapsPhaseMajor(state.BaseTaps, state.Decim)
  75. }
  76. phaseLen := PolyphasePhaseLen(len(state.BaseTaps), state.Decim)
  77. out := make([]complex64, 0, len(iqNew)/maxInt(1, state.Decim)+2)
  78. phase := state.NCOPhase
  79. hist := append([]complex64(nil), state.ShiftedHistory...)
  80. for _, x := range iqNew {
  81. rot := complex64(complex(math.Cos(phase), math.Sin(phase)))
  82. s := x * rot
  83. hist = append(hist, s)
  84. state.PhaseCount++
  85. if state.PhaseCount == state.Decim {
  86. var y complex64
  87. for p := 0; p < state.Decim; p++ {
  88. for k := 0; k < phaseLen; k++ {
  89. tap := state.PolyphaseTaps[p*phaseLen+k]
  90. if tap == 0 {
  91. continue
  92. }
  93. srcBack := p + k*state.Decim
  94. idx := len(hist) - 1 - srcBack
  95. if idx < 0 {
  96. continue
  97. }
  98. y += complex(tap, 0) * hist[idx]
  99. }
  100. }
  101. out = append(out, y)
  102. state.PhaseCount = 0
  103. }
  104. if len(hist) > state.NumTaps-1 {
  105. hist = hist[len(hist)-(state.NumTaps-1):]
  106. }
  107. phase += phaseInc
  108. if phase >= math.Pi {
  109. phase -= 2 * math.Pi
  110. } else if phase < -math.Pi {
  111. phase += 2 * math.Pi
  112. }
  113. }
  114. state.NCOPhase = phase
  115. state.ShiftedHistory = append(state.ShiftedHistory[:0], hist...)
  116. return out
  117. }
  118. func RunChunkedCPUOracle(all []complex64, chunkSizes []int, mkState func() *CPUOracleState, phaseInc float64) []complex64 {
  119. state := mkState()
  120. out := make([]complex64, 0)
  121. pos := 0
  122. for _, n := range chunkSizes {
  123. if pos >= len(all) {
  124. break
  125. }
  126. end := pos + n
  127. if end > len(all) {
  128. end = len(all)
  129. }
  130. out = append(out, CPUOracleExtract(all[pos:end], state, phaseInc)...)
  131. pos = end
  132. }
  133. if pos < len(all) {
  134. out = append(out, CPUOracleExtract(all[pos:], state, phaseInc)...)
  135. }
  136. return out
  137. }
  138. func ExactIntegerDecimation(sampleRate int, outRate int) (int, error) {
  139. if sampleRate <= 0 || outRate <= 0 {
  140. return 0, fmt.Errorf("invalid sampleRate/outRate: %d/%d", sampleRate, outRate)
  141. }
  142. if sampleRate%outRate != 0 {
  143. return 0, fmt.Errorf("streaming polyphase extractor requires integer decimation: sampleRate=%d outRate=%d", sampleRate, outRate)
  144. }
  145. return sampleRate / outRate, nil
  146. }
  147. func maxInt(a int, b int) int {
  148. if a > b {
  149. return a
  150. }
  151. return b
  152. }