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.

175 line
4.6KB

  1. //go:build sdrplay
  2. package sdrplay
  3. /*
  4. #cgo windows LDFLAGS: -lsdrplay_api
  5. #cgo linux LDFLAGS: -lsdrplay_api
  6. #include "sdrplay_api.h"
  7. #include <stdlib.h>
  8. extern void goStreamCallback(short *xi, short *xq, unsigned int numSamples, void *cbContext);
  9. static void StreamACallback(short *xi, short *xq, sdrplay_api_StreamCbParamsT *params, unsigned int numSamples, unsigned int reset, void *cbContext) {
  10. (void)params;
  11. (void)reset;
  12. goStreamCallback(xi, xq, numSamples, cbContext);
  13. }
  14. static void EventCallback(sdrplay_api_EventT eventId, sdrplay_api_TunerSelectT tuner, sdrplay_api_EventParamsT *params, void *cbContext) {
  15. (void)eventId; (void)tuner; (void)params; (void)cbContext;
  16. }
  17. static void sdrplay_set_fs(sdrplay_api_DeviceParamsT *p, double fsHz) {
  18. if (p && p->devParams) p->devParams->fsFreq.fsHz = fsHz;
  19. }
  20. static void sdrplay_set_rf(sdrplay_api_DeviceParamsT *p, double rfHz) {
  21. if (p && p->rxChannelA) p->rxChannelA->tunerParams.rfFreq.rfHz = rfHz;
  22. }
  23. static void sdrplay_set_gain(sdrplay_api_DeviceParamsT *p, unsigned int grDb) {
  24. if (p && p->rxChannelA) p->rxChannelA->tunerParams.gain.gRdB = grDb;
  25. }
  26. static void sdrplay_set_if_zero(sdrplay_api_DeviceParamsT *p) {
  27. if (p && p->rxChannelA) p->rxChannelA->tunerParams.ifType = sdrplay_api_IF_Zero;
  28. }
  29. static void sdrplay_disable_agc(sdrplay_api_DeviceParamsT *p) {
  30. if (p && p->rxChannelA) p->rxChannelA->ctrlParams.agc.enable = sdrplay_api_AGC_DISABLE;
  31. }
  32. */
  33. import "C"
  34. import (
  35. "errors"
  36. "fmt"
  37. "runtime/cgo"
  38. "sync"
  39. "unsafe"
  40. "sdr-visual-suite/internal/sdr"
  41. )
  42. type Source struct {
  43. mu sync.Mutex
  44. dev C.sdrplay_api_DeviceT
  45. params *C.sdrplay_api_DeviceParamsT
  46. ch chan []complex64
  47. handle cgo.Handle
  48. open bool
  49. }
  50. func New(sampleRate int, centerHz float64, gainDb float64) (sdr.Source, error) {
  51. s := &Source{
  52. ch: make(chan []complex64, 16),
  53. }
  54. s.handle = cgo.NewHandle(s)
  55. return s, s.configure(sampleRate, centerHz, gainDb)
  56. }
  57. func (s *Source) configure(sampleRate int, centerHz float64, gainDb float64) error {
  58. if err := cErr(C.sdrplay_api_Open()); err != nil {
  59. return fmt.Errorf("sdrplay_api_Open: %w", err)
  60. }
  61. s.open = true
  62. var numDevs C.uint
  63. var devices [8]C.sdrplay_api_DeviceT
  64. if err := cErr(C.sdrplay_api_GetDevices(&devices[0], &numDevs, C.uint(len(devices)))); err != nil {
  65. return fmt.Errorf("sdrplay_api_GetDevices: %w", err)
  66. }
  67. if numDevs == 0 {
  68. return errors.New("no SDRplay devices found")
  69. }
  70. s.dev = devices[0]
  71. if err := cErr(C.sdrplay_api_SelectDevice(&s.dev)); err != nil {
  72. return fmt.Errorf("sdrplay_api_SelectDevice: %w", err)
  73. }
  74. var params *C.sdrplay_api_DeviceParamsT
  75. if err := cErr(C.sdrplay_api_GetDeviceParams(s.dev.dev, &params)); err != nil {
  76. return fmt.Errorf("sdrplay_api_GetDeviceParams: %w", err)
  77. }
  78. s.params = params
  79. C.sdrplay_set_fs(s.params, C.double(sampleRate))
  80. C.sdrplay_set_rf(s.params, C.double(centerHz))
  81. C.sdrplay_set_gain(s.params, C.uint(gainDb))
  82. C.sdrplay_set_if_zero(s.params)
  83. C.sdrplay_disable_agc(s.params)
  84. cb := C.sdrplay_api_CallbackFnsT{}
  85. cb.StreamACbFn = (C.sdrplay_api_StreamCallback_t)(unsafe.Pointer(C.StreamACallback))
  86. cb.StreamBCbFn = nil
  87. cb.EventCbFn = (C.sdrplay_api_EventCallback_t)(unsafe.Pointer(C.EventCallback))
  88. if err := cErr(C.sdrplay_api_Init(s.dev.dev, &cb, unsafe.Pointer(uintptr(s.handle)))); err != nil {
  89. return fmt.Errorf("sdrplay_api_Init: %w", err)
  90. }
  91. return nil
  92. }
  93. func (s *Source) Start() error { return nil }
  94. func (s *Source) Stop() error {
  95. s.mu.Lock()
  96. defer s.mu.Unlock()
  97. if s.params != nil {
  98. _ = cErr(C.sdrplay_api_Uninit(s.dev.dev))
  99. s.params = nil
  100. }
  101. if s.open {
  102. _ = cErr(C.sdrplay_api_ReleaseDevice(&s.dev))
  103. _ = cErr(C.sdrplay_api_Close())
  104. s.open = false
  105. }
  106. if s.handle != 0 {
  107. s.handle.Delete()
  108. s.handle = 0
  109. }
  110. return nil
  111. }
  112. func (s *Source) ReadIQ(n int) ([]complex64, error) {
  113. buf := <-s.ch
  114. if len(buf) >= n {
  115. return buf[:n], nil
  116. }
  117. return buf, nil
  118. }
  119. //export goStreamCallback
  120. func goStreamCallback(xi *C.short, xq *C.short, numSamples C.uint, ctx unsafe.Pointer) {
  121. h := cgo.Handle(uintptr(ctx))
  122. src, ok := h.Value().(*Source)
  123. if !ok || src == nil {
  124. return
  125. }
  126. n := int(numSamples)
  127. if n <= 0 {
  128. return
  129. }
  130. iq := make([]complex64, n)
  131. xiSlice := unsafe.Slice((*int16)(unsafe.Pointer(xi)), n)
  132. xqSlice := unsafe.Slice((*int16)(unsafe.Pointer(xq)), n)
  133. const scale = 1.0 / 32768.0
  134. for i := 0; i < n; i++ {
  135. re := float32(float64(xiSlice[i]) * scale)
  136. im := float32(float64(xqSlice[i]) * scale)
  137. iq[i] = complex(re, im)
  138. }
  139. select {
  140. case src.ch <- iq:
  141. default:
  142. // Drop if consumer is slow.
  143. }
  144. }
  145. func cErr(err C.sdrplay_api_ErrT) error {
  146. if err == C.sdrplay_api_Success {
  147. return nil
  148. }
  149. return errors.New(C.GoString(C.sdrplay_api_GetErrorString(err)))
  150. }