Wideband autonomous SDR analysis engine forked from sdr-visual-suite
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

105 строки
2.2KB

  1. //go:build cufft
  2. package gpudemod
  3. /*
  4. #cgo windows LDFLAGS: -lcufft64_12 -lcudart64_13
  5. #include <cuda_runtime.h>
  6. #include <cufft.h>
  7. */
  8. import "C"
  9. import (
  10. "errors"
  11. "math"
  12. "sdr-visual-suite/internal/demod"
  13. "sdr-visual-suite/internal/dsp"
  14. )
  15. type DemodType int
  16. const (
  17. DemodNFM DemodType = iota
  18. DemodWFM
  19. DemodAM
  20. DemodUSB
  21. DemodLSB
  22. DemodCW
  23. )
  24. type Engine struct {
  25. maxSamples int
  26. sampleRate int
  27. phase float64
  28. bfoPhase float64
  29. firTaps []float32
  30. }
  31. func Available() bool { return true }
  32. func New(maxSamples int, sampleRate int) (*Engine, error) {
  33. if maxSamples <= 0 {
  34. return nil, errors.New("invalid maxSamples")
  35. }
  36. if sampleRate <= 0 {
  37. return nil, errors.New("invalid sampleRate")
  38. }
  39. return &Engine{maxSamples: maxSamples, sampleRate: sampleRate}, nil
  40. }
  41. func (e *Engine) SetFIR(taps []float32) {
  42. if len(taps) == 0 {
  43. e.firTaps = nil
  44. return
  45. }
  46. e.firTaps = append(e.firTaps[:0], taps...)
  47. }
  48. func (e *Engine) Demod(iq []complex64, offsetHz float64, bw float64, mode DemodType) ([]float32, int, error) {
  49. if e == nil {
  50. return nil, 0, errors.New("nil CUDA demod engine")
  51. }
  52. if len(iq) == 0 {
  53. return nil, 0, nil
  54. }
  55. if len(iq) > e.maxSamples {
  56. return nil, 0, errors.New("sample count exceeds engine capacity")
  57. }
  58. if mode != DemodNFM {
  59. return nil, 0, errors.New("CUDA demod phase 1 currently supports NFM only")
  60. }
  61. // Phase 1 conservative scaffold:
  62. // Keep build/tag/CUDA-specific package boundaries now, but use the existing
  63. // CPU DSP implementation as the processing backend until the actual CUDA
  64. // kernels are introduced in later phases.
  65. shifted := dsp.FreqShift(iq, e.sampleRate, offsetHz)
  66. cutoff := bw / 2
  67. if cutoff < 200 {
  68. cutoff = 200
  69. }
  70. taps := e.firTaps
  71. if len(taps) == 0 {
  72. base := dsp.LowpassFIR(cutoff, e.sampleRate, 101)
  73. taps = append(make([]float32, 0, len(base)), base...)
  74. }
  75. filtered := dsp.ApplyFIR(shifted, taps)
  76. outRate := demod.NFM{}.OutputSampleRate()
  77. decim := int(math.Round(float64(e.sampleRate) / float64(outRate)))
  78. if decim < 1 {
  79. decim = 1
  80. }
  81. dec := dsp.Decimate(filtered, decim)
  82. inputRate := e.sampleRate / decim
  83. audio := demod.NFM{}.Demod(dec, inputRate)
  84. return audio, inputRate, nil
  85. }
  86. func (e *Engine) Close() {
  87. if e == nil {
  88. return
  89. }
  90. e.firTaps = nil
  91. }