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.

285 lines
7.6KB

  1. //go:build cufft && windows
  2. package gpudemod
  3. /*
  4. #cgo windows CFLAGS: -I"C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v13.2/include"
  5. #include <cuda_runtime.h>
  6. typedef struct { float x; float y; } gpud_float2;
  7. */
  8. import "C"
  9. import (
  10. "math"
  11. "unsafe"
  12. )
  13. func (r *BatchRunner) executeStreamingGPUNativePrepared(invocations []StreamingGPUInvocation) ([]StreamingGPUExecutionResult, error) {
  14. if r == nil || r.eng == nil {
  15. return nil, ErrUnavailable
  16. }
  17. if r.nativeState == nil {
  18. r.nativeState = make(map[int64]*nativeStreamingSignalState)
  19. }
  20. results := make([]StreamingGPUExecutionResult, len(invocations))
  21. for i, inv := range invocations {
  22. state, err := r.getOrInitNativeStreamingState(inv)
  23. if err != nil {
  24. return nil, err
  25. }
  26. if len(inv.IQNew) > 0 {
  27. if err := ensureNativeBuffer(&state.dInNew, &state.inNewCap, len(inv.IQNew), unsafe.Sizeof(C.gpud_float2{})); err != nil {
  28. return nil, err
  29. }
  30. if bridgeMemcpyH2D(state.dInNew, unsafe.Pointer(&inv.IQNew[0]), uintptr(len(inv.IQNew))*unsafe.Sizeof(complex64(0))) != 0 {
  31. return nil, ErrUnavailable
  32. }
  33. }
  34. outCap := len(inv.IQNew)/maxInt(1, inv.Decim) + 2
  35. if outCap > 0 {
  36. if err := ensureNativeBuffer(&state.dOut, &state.outCap, outCap, unsafe.Sizeof(C.gpud_float2{})); err != nil {
  37. return nil, err
  38. }
  39. }
  40. phaseInc := -2.0 * math.Pi * inv.OffsetHz / float64(inv.SampleRate)
  41. // The native export consumes phase carry as host scalars while sample/history
  42. // buffers remain device-resident, so keep these counters in nativeState.
  43. var nOut C.int
  44. historyLen := C.int(state.historyLen)
  45. phaseCount := C.int(state.phaseCount)
  46. phaseNCO := C.double(state.phaseNCO)
  47. res := bridgeLaunchStreamingPolyphaseStateful(
  48. (*C.gpud_float2)(state.dInNew),
  49. len(inv.IQNew),
  50. (*C.gpud_float2)(state.dShifted),
  51. (*C.float)(state.dTaps),
  52. state.tapsLen,
  53. state.decim,
  54. state.numTaps,
  55. (*C.gpud_float2)(state.dHistory),
  56. (*C.gpud_float2)(state.dHistoryScratch),
  57. state.historyCap,
  58. &historyLen,
  59. &phaseCount,
  60. &phaseNCO,
  61. phaseInc,
  62. (*C.gpud_float2)(state.dOut),
  63. outCap,
  64. &nOut,
  65. )
  66. if res != 0 {
  67. return nil, ErrUnavailable
  68. }
  69. state.historyLen = int(historyLen)
  70. state.phaseCount = int(phaseCount)
  71. state.phaseNCO = float64(phaseNCO)
  72. outHost := make([]complex64, int(nOut))
  73. if len(outHost) > 0 {
  74. if bridgeMemcpyD2H(unsafe.Pointer(&outHost[0]), state.dOut, uintptr(len(outHost))*unsafe.Sizeof(complex64(0))) != 0 {
  75. return nil, ErrUnavailable
  76. }
  77. }
  78. histHost := make([]complex64, state.historyLen)
  79. if state.historyLen > 0 {
  80. if bridgeMemcpyD2H(unsafe.Pointer(&histHost[0]), state.dHistory, uintptr(state.historyLen)*unsafe.Sizeof(complex64(0))) != 0 {
  81. return nil, ErrUnavailable
  82. }
  83. }
  84. results[i] = StreamingGPUExecutionResult{
  85. SignalID: inv.SignalID,
  86. Mode: StreamingGPUExecCUDA,
  87. IQ: outHost,
  88. Rate: inv.OutRate,
  89. NOut: len(outHost),
  90. PhaseCountOut: state.phaseCount,
  91. NCOPhaseOut: state.phaseNCO,
  92. HistoryOut: histHost,
  93. HistoryLenOut: len(histHost),
  94. }
  95. }
  96. return results, nil
  97. }
  98. func (r *BatchRunner) getOrInitNativeStreamingState(inv StreamingGPUInvocation) (*nativeStreamingSignalState, error) {
  99. state := r.nativeState[inv.SignalID]
  100. needReset := false
  101. historyCap := maxInt(0, inv.NumTaps-1)
  102. if state == nil {
  103. state = &nativeStreamingSignalState{signalID: inv.SignalID}
  104. r.nativeState[inv.SignalID] = state
  105. needReset = true
  106. }
  107. if state.configHash != inv.ConfigHash {
  108. needReset = true
  109. }
  110. if state.decim != inv.Decim || state.numTaps != inv.NumTaps || state.tapsLen != len(inv.PolyphaseTaps) {
  111. needReset = true
  112. }
  113. if state.historyCap != historyCap {
  114. needReset = true
  115. }
  116. if needReset {
  117. releaseNativeStreamingSignalState(state)
  118. }
  119. if len(inv.PolyphaseTaps) == 0 {
  120. return nil, ErrUnavailable
  121. }
  122. if state.dTaps == nil && len(inv.PolyphaseTaps) > 0 {
  123. if bridgeCudaMalloc(&state.dTaps, uintptr(len(inv.PolyphaseTaps))*unsafe.Sizeof(C.float(0))) != 0 {
  124. return nil, ErrUnavailable
  125. }
  126. if bridgeMemcpyH2D(state.dTaps, unsafe.Pointer(&inv.PolyphaseTaps[0]), uintptr(len(inv.PolyphaseTaps))*unsafe.Sizeof(float32(0))) != 0 {
  127. return nil, ErrUnavailable
  128. }
  129. state.tapsLen = len(inv.PolyphaseTaps)
  130. }
  131. if state.dShifted == nil {
  132. minCap := maxInt(1, len(inv.IQNew))
  133. if bridgeCudaMalloc(&state.dShifted, uintptr(minCap)*unsafe.Sizeof(C.gpud_float2{})) != 0 {
  134. return nil, ErrUnavailable
  135. }
  136. state.shiftedCap = minCap
  137. }
  138. if state.shiftedCap < len(inv.IQNew) {
  139. if bridgeCudaFree(state.dShifted) != 0 {
  140. return nil, ErrUnavailable
  141. }
  142. state.dShifted = nil
  143. state.shiftedCap = 0
  144. if bridgeCudaMalloc(&state.dShifted, uintptr(len(inv.IQNew))*unsafe.Sizeof(C.gpud_float2{})) != 0 {
  145. return nil, ErrUnavailable
  146. }
  147. state.shiftedCap = len(inv.IQNew)
  148. }
  149. if state.dHistory == nil && historyCap > 0 {
  150. if bridgeCudaMalloc(&state.dHistory, uintptr(historyCap)*unsafe.Sizeof(C.gpud_float2{})) != 0 {
  151. return nil, ErrUnavailable
  152. }
  153. }
  154. if state.dHistoryScratch == nil && historyCap > 0 {
  155. if bridgeCudaMalloc(&state.dHistoryScratch, uintptr(historyCap)*unsafe.Sizeof(C.gpud_float2{})) != 0 {
  156. return nil, ErrUnavailable
  157. }
  158. state.historyScratchCap = historyCap
  159. }
  160. if needReset {
  161. state.phaseCount = inv.PhaseCountIn
  162. state.phaseNCO = inv.NCOPhaseIn
  163. state.historyLen = minInt(len(inv.ShiftedHistory), historyCap)
  164. if state.historyLen > 0 {
  165. if bridgeMemcpyH2D(state.dHistory, unsafe.Pointer(&inv.ShiftedHistory[len(inv.ShiftedHistory)-state.historyLen]), uintptr(state.historyLen)*unsafe.Sizeof(complex64(0))) != 0 {
  166. return nil, ErrUnavailable
  167. }
  168. }
  169. }
  170. state.decim = inv.Decim
  171. state.numTaps = inv.NumTaps
  172. state.historyCap = historyCap
  173. state.historyScratchCap = historyCap
  174. state.configHash = inv.ConfigHash
  175. return state, nil
  176. }
  177. func ensureNativeBuffer(ptr *unsafe.Pointer, capRef *int, need int, elemSize uintptr) error {
  178. if need <= 0 {
  179. return nil
  180. }
  181. if *ptr != nil && *capRef >= need {
  182. return nil
  183. }
  184. if *ptr != nil {
  185. if bridgeCudaFree(*ptr) != 0 {
  186. return ErrUnavailable
  187. }
  188. *ptr = nil
  189. *capRef = 0
  190. }
  191. if bridgeCudaMalloc(ptr, uintptr(need)*elemSize) != 0 {
  192. return ErrUnavailable
  193. }
  194. *capRef = need
  195. return nil
  196. }
  197. func (r *BatchRunner) syncNativeStreamingStates(active map[int64]struct{}) {
  198. if r == nil || r.nativeState == nil {
  199. return
  200. }
  201. for id, state := range r.nativeState {
  202. if _, ok := active[id]; ok {
  203. continue
  204. }
  205. releaseNativeStreamingSignalState(state)
  206. delete(r.nativeState, id)
  207. }
  208. }
  209. func (r *BatchRunner) resetNativeStreamingState(signalID int64) {
  210. if r == nil || r.nativeState == nil {
  211. return
  212. }
  213. if state := r.nativeState[signalID]; state != nil {
  214. releaseNativeStreamingSignalState(state)
  215. }
  216. delete(r.nativeState, signalID)
  217. }
  218. func (r *BatchRunner) resetAllNativeStreamingStates() {
  219. if r == nil {
  220. return
  221. }
  222. r.freeAllNativeStreamingStates()
  223. r.nativeState = make(map[int64]*nativeStreamingSignalState)
  224. }
  225. func (r *BatchRunner) freeAllNativeStreamingStates() {
  226. if r == nil || r.nativeState == nil {
  227. return
  228. }
  229. for id, state := range r.nativeState {
  230. releaseNativeStreamingSignalState(state)
  231. delete(r.nativeState, id)
  232. }
  233. }
  234. func releaseNativeStreamingSignalState(state *nativeStreamingSignalState) {
  235. if state == nil {
  236. return
  237. }
  238. for _, ptr := range []*unsafe.Pointer{
  239. &state.dInNew,
  240. &state.dShifted,
  241. &state.dOut,
  242. &state.dTaps,
  243. &state.dHistory,
  244. &state.dHistoryScratch,
  245. } {
  246. if *ptr != nil {
  247. _ = bridgeCudaFree(*ptr)
  248. *ptr = nil
  249. }
  250. }
  251. state.inNewCap = 0
  252. state.shiftedCap = 0
  253. state.outCap = 0
  254. state.tapsLen = 0
  255. state.historyCap = 0
  256. state.historyLen = 0
  257. state.historyScratchCap = 0
  258. state.phaseCount = 0
  259. state.phaseNCO = 0
  260. state.decim = 0
  261. state.numTaps = 0
  262. state.configHash = 0
  263. }
  264. func minInt(a int, b int) int {
  265. if a < b {
  266. return a
  267. }
  268. return b
  269. }