| @@ -449,6 +449,7 @@ func runDSP(ctx context.Context, srcMgr *sourceManager, cfg config.Config, det * | |||||
| dcBlocker := dsp.NewDCBlocker(0.995) | dcBlocker := dsp.NewDCBlocker(0.995) | ||||
| dcEnabled := cfg.DCBlock | dcEnabled := cfg.DCBlock | ||||
| iqEnabled := cfg.IQBalance | iqEnabled := cfg.IQBalance | ||||
| plan := fftutil.NewCmplxPlan(cfg.FFTSize) | |||||
| useGPU := cfg.UseGPUFFT | useGPU := cfg.UseGPUFFT | ||||
| var gpuEngine *gpufft.Engine | var gpuEngine *gpufft.Engine | ||||
| if useGPU && gpuState != nil && gpuState.Available { | if useGPU && gpuState != nil && gpuState.Available { | ||||
| @@ -480,6 +481,7 @@ func runDSP(ctx context.Context, srcMgr *sourceManager, cfg config.Config, det * | |||||
| } | } | ||||
| if upd.window != nil { | if upd.window != nil { | ||||
| window = upd.window | window = upd.window | ||||
| plan = fftutil.NewCmplxPlan(cfg.FFTSize) | |||||
| } | } | ||||
| dcEnabled = upd.dcBlock | dcEnabled = upd.dcBlock | ||||
| iqEnabled = upd.iqBalance | iqEnabled = upd.iqBalance | ||||
| @@ -546,7 +548,7 @@ func runDSP(ctx context.Context, srcMgr *sourceManager, cfg config.Config, det * | |||||
| spectrum = fftutil.SpectrumFromFFT(out) | spectrum = fftutil.SpectrumFromFFT(out) | ||||
| } | } | ||||
| } else { | } else { | ||||
| spectrum = fftutil.Spectrum(iq, window) | |||||
| spectrum = fftutil.SpectrumWithPlan(iq, window, plan) | |||||
| } | } | ||||
| now := time.Now() | now := time.Now() | ||||
| finished, signals := det.Process(now, spectrum, cfg.CenterHz) | finished, signals := det.Process(now, spectrum, cfg.CenterHz) | ||||
| @@ -1,10 +1,6 @@ | |||||
| package fftutil | package fftutil | ||||
| import ( | |||||
| "math" | |||||
| "gonum.org/v1/gonum/dsp/fourier" | |||||
| ) | |||||
| import "math" | |||||
| func Hann(n int) []float64 { | func Hann(n int) []float64 { | ||||
| w := make([]float64, n) | w := make([]float64, n) | ||||
| @@ -21,6 +17,11 @@ func Hann(n int) []float64 { | |||||
| } | } | ||||
| func Spectrum(iq []complex64, window []float64) []float64 { | func Spectrum(iq []complex64, window []float64) []float64 { | ||||
| plan := NewCmplxPlan(len(iq)) | |||||
| return SpectrumWithPlan(iq, window, plan) | |||||
| } | |||||
| func SpectrumWithPlan(iq []complex64, window []float64, plan *CmplxPlan) []float64 { | |||||
| n := len(iq) | n := len(iq) | ||||
| if n == 0 { | if n == 0 { | ||||
| return nil | return nil | ||||
| @@ -34,9 +35,12 @@ func Spectrum(iq []complex64, window []float64) []float64 { | |||||
| } | } | ||||
| in[i] = complex(float64(real(v))*w, float64(imag(v))*w) | in[i] = complex(float64(real(v))*w, float64(imag(v))*w) | ||||
| } | } | ||||
| fft := fourier.NewCmplxFFT(n) | |||||
| out := make([]complex128, n) | out := make([]complex128, n) | ||||
| fft.Coefficients(out, in) | |||||
| if plan != nil && plan.N() == n { | |||||
| plan.FFT(out, in) | |||||
| } else { | |||||
| NewCmplxPlan(n).FFT(out, in) | |||||
| } | |||||
| power := make([]float64, n) | power := make([]float64, n) | ||||
| eps := 1e-12 | eps := 1e-12 | ||||
| @@ -0,0 +1,24 @@ | |||||
| package fftutil | |||||
| import "gonum.org/v1/gonum/dsp/fourier" | |||||
| type CmplxPlan struct { | |||||
| fft *fourier.CmplxFFT | |||||
| n int | |||||
| } | |||||
| func NewCmplxPlan(n int) *CmplxPlan { | |||||
| if n <= 0 { | |||||
| return &CmplxPlan{} | |||||
| } | |||||
| return &CmplxPlan{fft: fourier.NewCmplxFFT(n), n: n} | |||||
| } | |||||
| func (p *CmplxPlan) N() int { return p.n } | |||||
| func (p *CmplxPlan) FFT(out, in []complex128) { | |||||
| if p == nil || p.fft == nil { | |||||
| return | |||||
| } | |||||
| p.fft.Coefficients(out, in) | |||||
| } | |||||
| @@ -128,6 +128,7 @@ func (s *Source) configure(sampleRate int, centerHz float64, gainDb float64, bwK | |||||
| if err := cErr(C.sdrplay_api_LockDeviceApi()); err != nil { | if err := cErr(C.sdrplay_api_LockDeviceApi()); err != nil { | ||||
| return fmt.Errorf("sdrplay_api_LockDeviceApi: %w", err) | return fmt.Errorf("sdrplay_api_LockDeviceApi: %w", err) | ||||
| } | } | ||||
| defer func() { _ = cErr(C.sdrplay_api_UnlockDeviceApi()) }() | |||||
| var numDevs C.uint | var numDevs C.uint | ||||
| var devices [8]C.sdrplay_api_DeviceT | var devices [8]C.sdrplay_api_DeviceT | ||||
| @@ -139,10 +140,8 @@ func (s *Source) configure(sampleRate int, centerHz float64, gainDb float64, bwK | |||||
| } | } | ||||
| s.dev = devices[0] | s.dev = devices[0] | ||||
| if err := cErr(C.sdrplay_api_SelectDevice(&s.dev)); err != nil { | if err := cErr(C.sdrplay_api_SelectDevice(&s.dev)); err != nil { | ||||
| _ = cErr(C.sdrplay_api_UnlockDeviceApi()) | |||||
| return fmt.Errorf("sdrplay_api_SelectDevice: %w", err) | return fmt.Errorf("sdrplay_api_SelectDevice: %w", err) | ||||
| } | } | ||||
| _ = cErr(C.sdrplay_api_UnlockDeviceApi()) | |||||
| var params *C.sdrplay_api_DeviceParamsT | var params *C.sdrplay_api_DeviceParamsT | ||||
| if err := cErr(C.sdrplay_api_GetDeviceParams(s.dev.dev, ¶ms)); err != nil { | if err := cErr(C.sdrplay_api_GetDeviceParams(s.dev.dev, ¶ms)); err != nil { | ||||