選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

399 行
9.7KB

  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. #include <string.h>
  9. extern void goStreamCallback(short *xi, short *xq, unsigned int numSamples, unsigned int reset, void *cbContext);
  10. static void StreamACallback(short *xi, short *xq, sdrplay_api_StreamCbParamsT *params, unsigned int numSamples, unsigned int reset, void *cbContext) {
  11. (void)params;
  12. goStreamCallback(xi, xq, numSamples, reset, 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 sdrplay_api_CallbackFnsT sdrplay_get_callbacks() {
  18. sdrplay_api_CallbackFnsT cb;
  19. memset(&cb, 0, sizeof(cb));
  20. cb.StreamACbFn = StreamACallback;
  21. cb.StreamBCbFn = NULL;
  22. cb.EventCbFn = EventCallback;
  23. return cb;
  24. }
  25. static void sdrplay_set_fs(sdrplay_api_DeviceParamsT *p, double fsHz) {
  26. if (p && p->devParams) p->devParams->fsFreq.fsHz = fsHz;
  27. }
  28. static void sdrplay_set_rf(sdrplay_api_DeviceParamsT *p, double rfHz) {
  29. if (p && p->rxChannelA) p->rxChannelA->tunerParams.rfFreq.rfHz = rfHz;
  30. }
  31. static void sdrplay_set_gain(sdrplay_api_DeviceParamsT *p, unsigned int grDb) {
  32. if (p && p->rxChannelA) p->rxChannelA->tunerParams.gain.gRdB = grDb;
  33. }
  34. static void sdrplay_set_bw(sdrplay_api_DeviceParamsT *p, sdrplay_api_Bw_MHzT bw) {
  35. if (p && p->rxChannelA) p->rxChannelA->tunerParams.bwType = bw;
  36. }
  37. static void sdrplay_set_if_zero(sdrplay_api_DeviceParamsT *p) {
  38. if (p && p->rxChannelA) p->rxChannelA->tunerParams.ifType = sdrplay_api_IF_Zero;
  39. }
  40. static void sdrplay_disable_agc(sdrplay_api_DeviceParamsT *p) {
  41. if (p && p->rxChannelA) p->rxChannelA->ctrlParams.agc.enable = sdrplay_api_AGC_DISABLE;
  42. }
  43. static void sdrplay_set_agc(sdrplay_api_DeviceParamsT *p, int enable) {
  44. if (!p || !p->rxChannelA) return;
  45. if (enable) {
  46. p->rxChannelA->ctrlParams.agc.enable = sdrplay_api_AGC_100HZ;
  47. } else {
  48. p->rxChannelA->ctrlParams.agc.enable = sdrplay_api_AGC_DISABLE;
  49. }
  50. }
  51. static sdrplay_api_ErrT sdrplay_update(void *dev, int reason) {
  52. return sdrplay_api_Update(dev, sdrplay_api_Tuner_A, (sdrplay_api_ReasonForUpdateT)reason, sdrplay_api_Update_Ext1_None);
  53. }
  54. */
  55. import "C"
  56. import (
  57. "errors"
  58. "fmt"
  59. "runtime/cgo"
  60. "sync"
  61. "time"
  62. "unsafe"
  63. "sdr-visual-suite/internal/sdr"
  64. )
  65. type Source struct {
  66. mu sync.Mutex
  67. dev C.sdrplay_api_DeviceT
  68. params *C.sdrplay_api_DeviceParamsT
  69. ch chan []complex64
  70. handle cgo.Handle
  71. open bool
  72. sampleRate int
  73. centerHz float64
  74. gainDb float64
  75. agc bool
  76. buf []complex64
  77. capSamples int
  78. head int
  79. size int
  80. bwKHz int
  81. dropped uint64
  82. resets uint64
  83. lastSample time.Time
  84. cond *sync.Cond
  85. }
  86. func New(sampleRate int, centerHz float64, gainDb float64, bwKHz int) (sdr.Source, error) {
  87. s := &Source{
  88. ch: make(chan []complex64, 16),
  89. sampleRate: sampleRate,
  90. centerHz: centerHz,
  91. gainDb: gainDb,
  92. bwKHz: bwKHz,
  93. }
  94. s.cond = sync.NewCond(&s.mu)
  95. s.resizeBuffer(sampleRate, 0)
  96. s.handle = cgo.NewHandle(s)
  97. return s, s.configure(sampleRate, centerHz, gainDb, bwKHz)
  98. }
  99. func (s *Source) configure(sampleRate int, centerHz float64, gainDb float64, bwKHz int) error {
  100. if err := cErr(C.sdrplay_api_Open()); err != nil {
  101. return fmt.Errorf("sdrplay_api_Open: %w", err)
  102. }
  103. s.open = true
  104. var numDevs C.uint
  105. var devices [8]C.sdrplay_api_DeviceT
  106. if err := cErr(C.sdrplay_api_GetDevices(&devices[0], &numDevs, C.uint(len(devices)))); err != nil {
  107. return fmt.Errorf("sdrplay_api_GetDevices: %w", err)
  108. }
  109. if numDevs == 0 {
  110. return errors.New("no SDRplay devices found")
  111. }
  112. s.dev = devices[0]
  113. if err := cErr(C.sdrplay_api_SelectDevice(&s.dev)); err != nil {
  114. return fmt.Errorf("sdrplay_api_SelectDevice: %w", err)
  115. }
  116. var params *C.sdrplay_api_DeviceParamsT
  117. if err := cErr(C.sdrplay_api_GetDeviceParams(s.dev.dev, &params)); err != nil {
  118. return fmt.Errorf("sdrplay_api_GetDeviceParams: %w", err)
  119. }
  120. s.params = params
  121. C.sdrplay_set_fs(s.params, C.double(sampleRate))
  122. C.sdrplay_set_rf(s.params, C.double(centerHz))
  123. C.sdrplay_set_gain(s.params, C.uint(gainDb))
  124. if bw := bwEnum(bwKHz); bw != 0 {
  125. C.sdrplay_set_bw(s.params, bw)
  126. if bwKHz > 0 {
  127. s.bwKHz = bwKHz
  128. }
  129. }
  130. C.sdrplay_set_if_zero(s.params)
  131. C.sdrplay_disable_agc(s.params)
  132. cb := C.sdrplay_get_callbacks()
  133. if err := cErr(C.sdrplay_api_Init(s.dev.dev, &cb, unsafe.Pointer(uintptr(s.handle)))); err != nil {
  134. return fmt.Errorf("sdrplay_api_Init: %w", err)
  135. }
  136. return nil
  137. }
  138. func (s *Source) Start() error { return nil }
  139. func (s *Source) UpdateConfig(sampleRate int, centerHz float64, gainDb float64, agc bool, bwKHz int) error {
  140. s.mu.Lock()
  141. defer s.mu.Unlock()
  142. if s.params == nil {
  143. return errors.New("sdrplay not initialized")
  144. }
  145. updateReasons := C.int(0)
  146. if sampleRate > 0 && sampleRate != s.sampleRate {
  147. C.sdrplay_set_fs(s.params, C.double(sampleRate))
  148. updateReasons |= C.int(C.sdrplay_api_Update_Dev_Fs)
  149. s.sampleRate = sampleRate
  150. s.resizeBuffer(sampleRate, 0)
  151. }
  152. if centerHz != 0 && centerHz != s.centerHz {
  153. C.sdrplay_set_rf(s.params, C.double(centerHz))
  154. updateReasons |= C.int(C.sdrplay_api_Update_Tuner_Frf)
  155. s.centerHz = centerHz
  156. }
  157. if gainDb != s.gainDb {
  158. C.sdrplay_set_gain(s.params, C.uint(gainDb))
  159. updateReasons |= C.int(C.sdrplay_api_Update_Tuner_Gr)
  160. s.gainDb = gainDb
  161. }
  162. if agc != s.agc {
  163. if agc {
  164. C.sdrplay_set_agc(s.params, 1)
  165. } else {
  166. C.sdrplay_set_agc(s.params, 0)
  167. }
  168. updateReasons |= C.int(C.sdrplay_api_Update_Ctrl_Agc)
  169. s.agc = agc
  170. }
  171. if bwKHz > 0 && bwKHz != s.bwKHz {
  172. if bw := bwEnum(bwKHz); bw != 0 {
  173. C.sdrplay_set_bw(s.params, bw)
  174. updateReasons |= C.int(C.sdrplay_api_Update_Tuner_BwType)
  175. s.bwKHz = bwKHz
  176. }
  177. }
  178. if updateReasons == 0 {
  179. return nil
  180. }
  181. if err := cErr(C.sdrplay_update(unsafe.Pointer(s.dev.dev), C.int(updateReasons))); err != nil {
  182. return err
  183. }
  184. return nil
  185. }
  186. func bwEnum(khz int) C.sdrplay_api_Bw_MHzT {
  187. switch khz {
  188. case 200:
  189. return C.sdrplay_api_BW_0_200
  190. case 300:
  191. return C.sdrplay_api_BW_0_300
  192. case 600:
  193. return C.sdrplay_api_BW_0_600
  194. case 1536:
  195. return C.sdrplay_api_BW_1_536
  196. case 5000:
  197. return C.sdrplay_api_BW_5_000
  198. case 6000:
  199. return C.sdrplay_api_BW_6_000
  200. case 7000:
  201. return C.sdrplay_api_BW_7_000
  202. case 8000:
  203. return C.sdrplay_api_BW_8_000
  204. default:
  205. return 0
  206. }
  207. }
  208. func (s *Source) resizeBuffer(sampleRate int, fftSize int) {
  209. capSamples := sampleRate
  210. if fftSize > 0 && fftSize*4 > capSamples {
  211. capSamples = fftSize * 4
  212. }
  213. if capSamples < 4096 {
  214. capSamples = 4096
  215. }
  216. if s.capSamples == capSamples && len(s.buf) == capSamples {
  217. return
  218. }
  219. newBuf := make([]complex64, capSamples)
  220. // copy existing data from ring
  221. toCopy := s.size
  222. if toCopy > capSamples {
  223. toCopy = capSamples
  224. }
  225. for i := 0; i < toCopy; i++ {
  226. newBuf[i] = s.buf[(s.head+i)%max(1, s.capSamples)]
  227. }
  228. s.buf = newBuf
  229. s.capSamples = capSamples
  230. s.head = 0
  231. s.size = toCopy
  232. }
  233. func (s *Source) appendRing(samples []complex64) {
  234. if len(samples) == 0 || s.capSamples == 0 {
  235. return
  236. }
  237. incoming := len(samples)
  238. over := s.size + incoming - s.capSamples
  239. if over > 0 {
  240. s.head = (s.head + over) % s.capSamples
  241. s.size -= over
  242. s.dropped += uint64(over)
  243. }
  244. start := (s.head + s.size) % s.capSamples
  245. first := min(incoming, s.capSamples-start)
  246. copy(s.buf[start:start+first], samples[:first])
  247. if first < incoming {
  248. copy(s.buf[0:incoming-first], samples[first:])
  249. }
  250. s.size += incoming
  251. if s.cond != nil {
  252. s.cond.Broadcast()
  253. }
  254. }
  255. func (s *Source) Stats() sdr.SourceStats {
  256. s.mu.Lock()
  257. defer s.mu.Unlock()
  258. ago := int64(-1)
  259. if !s.lastSample.IsZero() {
  260. ago = time.Since(s.lastSample).Milliseconds()
  261. }
  262. return sdr.SourceStats{BufferSamples: s.size, Dropped: s.dropped, Resets: s.resets, LastSampleAgoMs: ago}
  263. }
  264. func (s *Source) Flush() {
  265. s.mu.Lock()
  266. s.head = 0
  267. s.size = 0
  268. s.mu.Unlock()
  269. if s.cond != nil {
  270. s.cond.Broadcast()
  271. }
  272. }
  273. func min(a, b int) int {
  274. if a < b {
  275. return a
  276. }
  277. return b
  278. }
  279. func max(a, b int) int {
  280. if a > b {
  281. return a
  282. }
  283. return b
  284. }
  285. func (s *Source) Stop() error {
  286. s.mu.Lock()
  287. defer s.mu.Unlock()
  288. if s.params != nil {
  289. _ = cErr(C.sdrplay_api_Uninit(s.dev.dev))
  290. s.params = nil
  291. }
  292. if s.open {
  293. _ = cErr(C.sdrplay_api_ReleaseDevice(&s.dev))
  294. _ = cErr(C.sdrplay_api_Close())
  295. s.open = false
  296. }
  297. if s.handle != 0 {
  298. s.handle.Delete()
  299. s.handle = 0
  300. }
  301. return nil
  302. }
  303. func (s *Source) ReadIQ(n int) ([]complex64, error) {
  304. deadline := time.Now().Add(5 * time.Second)
  305. s.mu.Lock()
  306. defer s.mu.Unlock()
  307. for s.size < n {
  308. if time.Now().After(deadline) {
  309. return nil, errors.New("timeout waiting for IQ samples")
  310. }
  311. // Timed wait to avoid indefinite block if callbacks stop.
  312. s.mu.Unlock()
  313. time.Sleep(20 * time.Millisecond)
  314. s.mu.Lock()
  315. }
  316. out := make([]complex64, n)
  317. for i := 0; i < n; i++ {
  318. out[i] = s.buf[(s.head+i)%s.capSamples]
  319. }
  320. s.head = (s.head + n) % s.capSamples
  321. s.size -= n
  322. return out, nil
  323. }
  324. //export goStreamCallback
  325. func goStreamCallback(xi *C.short, xq *C.short, numSamples C.uint, reset C.uint, ctx unsafe.Pointer) {
  326. h := cgo.Handle(uintptr(ctx))
  327. src, ok := h.Value().(*Source)
  328. if !ok || src == nil {
  329. return
  330. }
  331. if reset != 0 {
  332. src.mu.Lock()
  333. src.head = 0
  334. src.size = 0
  335. src.resets++
  336. src.mu.Unlock()
  337. }
  338. n := int(numSamples)
  339. if n <= 0 {
  340. return
  341. }
  342. iq := make([]complex64, n)
  343. xiSlice := unsafe.Slice((*int16)(unsafe.Pointer(xi)), n)
  344. xqSlice := unsafe.Slice((*int16)(unsafe.Pointer(xq)), n)
  345. const scale = 1.0 / 32768.0
  346. for i := 0; i < n; i++ {
  347. re := float32(float64(xiSlice[i]) * scale)
  348. im := float32(float64(xqSlice[i]) * scale)
  349. iq[i] = complex(re, im)
  350. }
  351. src.mu.Lock()
  352. src.lastSample = time.Now()
  353. src.appendRing(iq)
  354. src.mu.Unlock()
  355. }
  356. func cErr(err C.sdrplay_api_ErrT) error {
  357. if err == C.sdrplay_api_Success {
  358. return nil
  359. }
  360. return errors.New(C.GoString(C.sdrplay_api_GetErrorString(err)))
  361. }