|
- //go:build cufft
-
- package gpudemod
-
- /*
- #cgo windows LDFLAGS: -lcufft64_12 -lcudart64_13
- #include <cuda_runtime.h>
- #include <cufft.h>
- */
- import "C"
-
- import (
- "errors"
- "math"
-
- "sdr-visual-suite/internal/demod"
- "sdr-visual-suite/internal/dsp"
- )
-
- type DemodType int
-
- const (
- DemodNFM DemodType = iota
- DemodWFM
- DemodAM
- DemodUSB
- DemodLSB
- DemodCW
- )
-
- type Engine struct {
- maxSamples int
- sampleRate int
- phase float64
- bfoPhase float64
- firTaps []float32
- }
-
- func Available() bool { return true }
-
- func New(maxSamples int, sampleRate int) (*Engine, error) {
- if maxSamples <= 0 {
- return nil, errors.New("invalid maxSamples")
- }
- if sampleRate <= 0 {
- return nil, errors.New("invalid sampleRate")
- }
- return &Engine{maxSamples: maxSamples, sampleRate: sampleRate}, nil
- }
-
- func (e *Engine) SetFIR(taps []float32) {
- if len(taps) == 0 {
- e.firTaps = nil
- return
- }
- e.firTaps = append(e.firTaps[:0], taps...)
- }
-
- func (e *Engine) Demod(iq []complex64, offsetHz float64, bw float64, mode DemodType) ([]float32, int, error) {
- if e == nil {
- return nil, 0, errors.New("nil CUDA demod engine")
- }
- if len(iq) == 0 {
- return nil, 0, nil
- }
- if len(iq) > e.maxSamples {
- return nil, 0, errors.New("sample count exceeds engine capacity")
- }
- if mode != DemodNFM {
- return nil, 0, errors.New("CUDA demod phase 1 currently supports NFM only")
- }
-
- // Phase 1 conservative scaffold:
- // Keep build/tag/CUDA-specific package boundaries now, but use the existing
- // CPU DSP implementation as the processing backend until the actual CUDA
- // kernels are introduced in later phases.
- shifted := dsp.FreqShift(iq, e.sampleRate, offsetHz)
- cutoff := bw / 2
- if cutoff < 200 {
- cutoff = 200
- }
- taps := e.firTaps
- if len(taps) == 0 {
- base := dsp.LowpassFIR(cutoff, e.sampleRate, 101)
- taps = append(make([]float32, 0, len(base)), base...)
- }
- filtered := dsp.ApplyFIR(shifted, taps)
- outRate := demod.NFM{}.OutputSampleRate()
- decim := int(math.Round(float64(e.sampleRate) / float64(outRate)))
- if decim < 1 {
- decim = 1
- }
- dec := dsp.Decimate(filtered, decim)
- inputRate := e.sampleRate / decim
- audio := demod.NFM{}.Demod(dec, inputRate)
- return audio, inputRate, nil
- }
-
- func (e *Engine) Close() {
- if e == nil {
- return
- }
- e.firTaps = nil
- }
|