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

464 行
11KB

  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. "sync/atomic"
  62. "time"
  63. "unsafe"
  64. "sdr-visual-suite/internal/sdr"
  65. )
  66. type Source struct {
  67. mu sync.Mutex
  68. dev C.sdrplay_api_DeviceT
  69. params *C.sdrplay_api_DeviceParamsT
  70. devSelected bool
  71. ch chan []complex64
  72. handle cgo.Handle
  73. open bool
  74. sampleRate int
  75. centerHz float64
  76. gainDb float64
  77. agc bool
  78. buf []complex64
  79. capSamples int
  80. head int
  81. size int
  82. bwKHz int
  83. dropped uint64
  84. resets uint64
  85. lastSample time.Time
  86. stopping uint32
  87. cond *sync.Cond
  88. }
  89. func New(sampleRate int, centerHz float64, gainDb float64, bwKHz int) (sdr.Source, error) {
  90. s := &Source{
  91. ch: make(chan []complex64, 16),
  92. sampleRate: sampleRate,
  93. centerHz: centerHz,
  94. gainDb: gainDb,
  95. bwKHz: bwKHz,
  96. }
  97. s.cond = sync.NewCond(&s.mu)
  98. s.resizeBuffer(sampleRate, 0)
  99. s.handle = cgo.NewHandle(s)
  100. if err := s.configure(sampleRate, centerHz, gainDb, bwKHz); err != nil {
  101. _ = s.Stop()
  102. return nil, err
  103. }
  104. return s, nil
  105. }
  106. func (s *Source) configure(sampleRate int, centerHz float64, gainDb float64, bwKHz int) error {
  107. if err := cErr(C.sdrplay_api_Open()); err != nil {
  108. return fmt.Errorf("sdrplay_api_Open: %w", err)
  109. }
  110. s.open = true
  111. if err := cErr(C.sdrplay_api_LockDeviceApi()); err != nil {
  112. return fmt.Errorf("sdrplay_api_LockDeviceApi: %w", err)
  113. }
  114. var numDevs C.uint
  115. var devices [8]C.sdrplay_api_DeviceT
  116. if err := cErr(C.sdrplay_api_GetDevices(&devices[0], &numDevs, C.uint(len(devices)))); err != nil {
  117. _ = cErr(C.sdrplay_api_UnlockDeviceApi())
  118. return fmt.Errorf("sdrplay_api_GetDevices: %w", err)
  119. }
  120. if numDevs == 0 {
  121. _ = cErr(C.sdrplay_api_UnlockDeviceApi())
  122. return errors.New("no SDRplay devices found")
  123. }
  124. s.dev = devices[0]
  125. if err := cErr(C.sdrplay_api_SelectDevice(&s.dev)); err != nil {
  126. _ = cErr(C.sdrplay_api_UnlockDeviceApi())
  127. return fmt.Errorf("sdrplay_api_SelectDevice: %w", err)
  128. }
  129. s.devSelected = true
  130. _ = cErr(C.sdrplay_api_UnlockDeviceApi())
  131. var params *C.sdrplay_api_DeviceParamsT
  132. if err := cErr(C.sdrplay_api_GetDeviceParams(s.dev.dev, &params)); err != nil {
  133. return fmt.Errorf("sdrplay_api_GetDeviceParams: %w", err)
  134. }
  135. // Enable verbose debug from SDRplay service.
  136. _ = cErr(C.sdrplay_api_DebugEnable(s.dev.dev, C.sdrplay_api_DbgLvl_Verbose))
  137. s.params = params
  138. C.sdrplay_set_fs(s.params, C.double(sampleRate))
  139. C.sdrplay_set_rf(s.params, C.double(centerHz))
  140. C.sdrplay_set_gain(s.params, C.uint(gainDb))
  141. if bw := bwEnum(bwKHz); bw != 0 {
  142. C.sdrplay_set_bw(s.params, bw)
  143. if bwKHz > 0 {
  144. s.bwKHz = bwKHz
  145. }
  146. }
  147. C.sdrplay_set_if_zero(s.params)
  148. C.sdrplay_disable_agc(s.params)
  149. cb := C.sdrplay_get_callbacks()
  150. if err := cErr(C.sdrplay_api_Init(s.dev.dev, &cb, unsafe.Pointer(uintptr(s.handle)))); err != nil {
  151. return fmt.Errorf("sdrplay_api_Init: %w", err)
  152. }
  153. return nil
  154. }
  155. func (s *Source) Start() error { return nil }
  156. func (s *Source) UpdateConfig(sampleRate int, centerHz float64, gainDb float64, agc bool, bwKHz int) error {
  157. s.mu.Lock()
  158. defer s.mu.Unlock()
  159. if s.params == nil {
  160. return errors.New("sdrplay not initialized")
  161. }
  162. updateReasons := C.int(0)
  163. if sampleRate > 0 && sampleRate != s.sampleRate {
  164. C.sdrplay_set_fs(s.params, C.double(sampleRate))
  165. updateReasons |= C.int(C.sdrplay_api_Update_Dev_Fs)
  166. s.sampleRate = sampleRate
  167. s.resizeBuffer(sampleRate, 0)
  168. }
  169. if centerHz != 0 && centerHz != s.centerHz {
  170. C.sdrplay_set_rf(s.params, C.double(centerHz))
  171. updateReasons |= C.int(C.sdrplay_api_Update_Tuner_Frf)
  172. s.centerHz = centerHz
  173. }
  174. if gainDb != s.gainDb {
  175. C.sdrplay_set_gain(s.params, C.uint(gainDb))
  176. updateReasons |= C.int(C.sdrplay_api_Update_Tuner_Gr)
  177. s.gainDb = gainDb
  178. }
  179. if agc != s.agc {
  180. if agc {
  181. C.sdrplay_set_agc(s.params, 1)
  182. } else {
  183. C.sdrplay_set_agc(s.params, 0)
  184. }
  185. updateReasons |= C.int(C.sdrplay_api_Update_Ctrl_Agc)
  186. s.agc = agc
  187. }
  188. if bwKHz > 0 && bwKHz != s.bwKHz {
  189. if bw := bwEnum(bwKHz); bw != 0 {
  190. C.sdrplay_set_bw(s.params, bw)
  191. updateReasons |= C.int(C.sdrplay_api_Update_Tuner_BwType)
  192. s.bwKHz = bwKHz
  193. }
  194. }
  195. if updateReasons == 0 {
  196. return nil
  197. }
  198. if err := cErr(C.sdrplay_update(unsafe.Pointer(s.dev.dev), C.int(updateReasons))); err != nil {
  199. return err
  200. }
  201. return nil
  202. }
  203. func bwEnum(khz int) C.sdrplay_api_Bw_MHzT {
  204. switch khz {
  205. case 200:
  206. return C.sdrplay_api_BW_0_200
  207. case 300:
  208. return C.sdrplay_api_BW_0_300
  209. case 600:
  210. return C.sdrplay_api_BW_0_600
  211. case 1536:
  212. return C.sdrplay_api_BW_1_536
  213. case 5000:
  214. return C.sdrplay_api_BW_5_000
  215. case 6000:
  216. return C.sdrplay_api_BW_6_000
  217. case 7000:
  218. return C.sdrplay_api_BW_7_000
  219. case 8000:
  220. return C.sdrplay_api_BW_8_000
  221. default:
  222. return 0
  223. }
  224. }
  225. func (s *Source) resizeBuffer(sampleRate int, fftSize int) {
  226. capSamples := sampleRate
  227. if fftSize > 0 && fftSize*4 > capSamples {
  228. capSamples = fftSize * 4
  229. }
  230. if capSamples < 4096 {
  231. capSamples = 4096
  232. }
  233. if s.capSamples == capSamples && len(s.buf) == capSamples {
  234. return
  235. }
  236. newBuf := make([]complex64, capSamples)
  237. // copy existing data from ring
  238. toCopy := s.size
  239. if toCopy > capSamples {
  240. toCopy = capSamples
  241. }
  242. for i := 0; i < toCopy; i++ {
  243. newBuf[i] = s.buf[(s.head+i)%max(1, s.capSamples)]
  244. }
  245. s.buf = newBuf
  246. s.capSamples = capSamples
  247. s.head = 0
  248. s.size = toCopy
  249. }
  250. func (s *Source) appendRing(samples []complex64) {
  251. if len(samples) == 0 || s.capSamples == 0 {
  252. return
  253. }
  254. incoming := len(samples)
  255. over := s.size + incoming - s.capSamples
  256. if over > 0 {
  257. s.head = (s.head + over) % s.capSamples
  258. s.size -= over
  259. s.dropped += uint64(over)
  260. }
  261. start := (s.head + s.size) % s.capSamples
  262. first := min(incoming, s.capSamples-start)
  263. copy(s.buf[start:start+first], samples[:first])
  264. if first < incoming {
  265. copy(s.buf[0:incoming-first], samples[first:])
  266. }
  267. s.size += incoming
  268. if s.cond != nil {
  269. s.cond.Broadcast()
  270. }
  271. }
  272. func (s *Source) Stats() sdr.SourceStats {
  273. s.mu.Lock()
  274. defer s.mu.Unlock()
  275. ago := int64(-1)
  276. if !s.lastSample.IsZero() {
  277. ago = time.Since(s.lastSample).Milliseconds()
  278. }
  279. return sdr.SourceStats{BufferSamples: s.size, Dropped: s.dropped, Resets: s.resets, LastSampleAgoMs: ago}
  280. }
  281. func (s *Source) Flush() {
  282. s.mu.Lock()
  283. s.head = 0
  284. s.size = 0
  285. s.mu.Unlock()
  286. if s.cond != nil {
  287. s.cond.Broadcast()
  288. }
  289. }
  290. func min(a, b int) int {
  291. if a < b {
  292. return a
  293. }
  294. return b
  295. }
  296. func max(a, b int) int {
  297. if a > b {
  298. return a
  299. }
  300. return b
  301. }
  302. func (s *Source) Stop() error {
  303. atomic.StoreUint32(&s.stopping, 1)
  304. s.mu.Lock()
  305. params := s.params
  306. s.params = nil
  307. s.mu.Unlock()
  308. if params != nil {
  309. _ = cErr(C.sdrplay_api_Uninit(s.dev.dev))
  310. }
  311. s.mu.Lock()
  312. defer s.mu.Unlock()
  313. if s.open {
  314. if s.devSelected {
  315. _ = cErr(C.sdrplay_api_ReleaseDevice(&s.dev))
  316. s.devSelected = false
  317. }
  318. _ = cErr(C.sdrplay_api_Close())
  319. s.open = false
  320. }
  321. if s.handle != 0 {
  322. s.handle.Delete()
  323. s.handle = 0
  324. }
  325. return nil
  326. }
  327. func (s *Source) ReadIQ(n int) ([]complex64, error) {
  328. deadline := time.Now().Add(5 * time.Second)
  329. s.mu.Lock()
  330. defer s.mu.Unlock()
  331. for s.size < n {
  332. remaining := time.Until(deadline)
  333. if remaining <= 0 {
  334. return nil, errors.New("timeout waiting for IQ samples")
  335. }
  336. if s.cond != nil {
  337. timer := time.AfterFunc(remaining, func() {
  338. s.cond.Broadcast()
  339. })
  340. s.cond.Wait()
  341. timer.Stop()
  342. } else {
  343. s.mu.Unlock()
  344. time.Sleep(20 * time.Millisecond)
  345. s.mu.Lock()
  346. }
  347. }
  348. out := make([]complex64, n)
  349. for i := 0; i < n; i++ {
  350. out[i] = s.buf[(s.head+i)%s.capSamples]
  351. }
  352. s.head = (s.head + n) % s.capSamples
  353. s.size -= n
  354. return out, nil
  355. }
  356. //export goStreamCallback
  357. func goStreamCallback(xi *C.short, xq *C.short, numSamples C.uint, reset C.uint, ctx unsafe.Pointer) {
  358. h := cgo.Handle(uintptr(ctx))
  359. src, ok := h.Value().(*Source)
  360. if !ok || src == nil {
  361. return
  362. }
  363. if atomic.LoadUint32(&src.stopping) != 0 {
  364. return
  365. }
  366. n := int(numSamples)
  367. if n <= 0 {
  368. return
  369. }
  370. src.mu.Lock()
  371. defer src.mu.Unlock()
  372. if reset != 0 {
  373. src.head = 0
  374. src.size = 0
  375. src.resets++
  376. }
  377. xiSlice := unsafe.Slice((*int16)(unsafe.Pointer(xi)), n)
  378. xqSlice := unsafe.Slice((*int16)(unsafe.Pointer(xq)), n)
  379. const scale = 1.0 / 32768.0
  380. incoming := n
  381. if incoming > src.capSamples {
  382. offset := incoming - src.capSamples
  383. xiSlice = xiSlice[offset:]
  384. xqSlice = xqSlice[offset:]
  385. incoming = src.capSamples
  386. src.dropped += uint64(offset)
  387. }
  388. over := src.size + incoming - src.capSamples
  389. if over > 0 {
  390. src.head = (src.head + over) % src.capSamples
  391. src.size -= over
  392. src.dropped += uint64(over)
  393. }
  394. start := (src.head + src.size) % src.capSamples
  395. first := min(incoming, src.capSamples-start)
  396. for i := 0; i < first; i++ {
  397. re := float32(float64(xiSlice[i]) * scale)
  398. im := float32(float64(xqSlice[i]) * scale)
  399. src.buf[start+i] = complex(re, im)
  400. }
  401. if first < incoming {
  402. for i := first; i < incoming; i++ {
  403. re := float32(float64(xiSlice[i]) * scale)
  404. im := float32(float64(xqSlice[i]) * scale)
  405. src.buf[i-first] = complex(re, im)
  406. }
  407. }
  408. src.size += incoming
  409. src.lastSample = time.Now()
  410. if src.cond != nil {
  411. src.cond.Broadcast()
  412. }
  413. }
  414. func cErr(err C.sdrplay_api_ErrT) error {
  415. if err == C.sdrplay_api_Success {
  416. return nil
  417. }
  418. return errors.New(C.GoString(C.sdrplay_api_GetErrorString(err)))
  419. }