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

130 行
2.7KB

  1. package recorder
  2. import (
  3. "sync"
  4. "time"
  5. )
  6. type iqBlock struct {
  7. t0 time.Time
  8. samples []complex64
  9. }
  10. // Ring keeps recent IQ blocks for preroll capture.
  11. type Ring struct {
  12. mu sync.RWMutex
  13. blocks []iqBlock
  14. maxSamples int
  15. total int
  16. sampleRate int
  17. }
  18. func NewRing(sampleRate int, blockSize int, seconds int) *Ring {
  19. if seconds <= 0 {
  20. seconds = 5
  21. }
  22. if sampleRate <= 0 {
  23. sampleRate = 2_048_000
  24. }
  25. if blockSize <= 0 {
  26. blockSize = 2048
  27. }
  28. maxSamples := sampleRate * seconds
  29. minSamples := blockSize * 2
  30. if minSamples < blockSize {
  31. minSamples = blockSize
  32. }
  33. if maxSamples < minSamples {
  34. maxSamples = minSamples
  35. }
  36. return &Ring{maxSamples: maxSamples, sampleRate: sampleRate}
  37. }
  38. func (r *Ring) Reset(sampleRate int, blockSize int, seconds int) {
  39. *r = *NewRing(sampleRate, blockSize, seconds)
  40. }
  41. func (r *Ring) Push(t0 time.Time, samples []complex64) {
  42. if r == nil || len(samples) == 0 {
  43. return
  44. }
  45. r.mu.Lock()
  46. defer r.mu.Unlock()
  47. cp := append([]complex64(nil), samples...)
  48. r.blocks = append(r.blocks, iqBlock{t0: t0, samples: cp})
  49. r.total += len(cp)
  50. for r.total > r.maxSamples && len(r.blocks) > 0 {
  51. overflow := r.total - r.maxSamples
  52. head := r.blocks[0]
  53. if overflow >= len(head.samples) {
  54. r.total -= len(head.samples)
  55. r.blocks = r.blocks[1:]
  56. continue
  57. }
  58. trim := overflow
  59. advance := time.Duration(float64(trim) / float64(r.sampleRate) * float64(time.Second))
  60. head.t0 = head.t0.Add(advance)
  61. head.samples = head.samples[trim:]
  62. r.blocks[0] = head
  63. r.total -= trim
  64. }
  65. }
  66. func (r *Ring) MaxSamples() int {
  67. if r == nil {
  68. return 0
  69. }
  70. r.mu.RLock()
  71. defer r.mu.RUnlock()
  72. return r.maxSamples
  73. }
  74. // Slice returns IQ samples between [start,end] (best-effort).
  75. func (r *Ring) Slice(start, end time.Time) []complex64 {
  76. if r == nil || end.Before(start) {
  77. return nil
  78. }
  79. r.mu.RLock()
  80. defer r.mu.RUnlock()
  81. var out []complex64
  82. for _, b := range r.blocks {
  83. blockDur := time.Duration(float64(len(b.samples)) / float64(r.sampleRate) * float64(time.Second))
  84. bEnd := b.t0.Add(blockDur)
  85. if bEnd.Before(start) || b.t0.After(end) {
  86. continue
  87. }
  88. // compute overlap
  89. oStart := maxTime(start, b.t0)
  90. oEnd := minTime(end, bEnd)
  91. if oEnd.Before(oStart) {
  92. continue
  93. }
  94. startIdx := int(float64(oStart.Sub(b.t0)) / float64(time.Second) * float64(r.sampleRate))
  95. endIdx := int(float64(oEnd.Sub(b.t0)) / float64(time.Second) * float64(r.sampleRate))
  96. if startIdx < 0 {
  97. startIdx = 0
  98. }
  99. if endIdx > len(b.samples) {
  100. endIdx = len(b.samples)
  101. }
  102. if endIdx > startIdx {
  103. out = append(out, b.samples[startIdx:endIdx]...)
  104. }
  105. }
  106. return out
  107. }
  108. func minTime(a, b time.Time) time.Time {
  109. if a.Before(b) {
  110. return a
  111. }
  112. return b
  113. }
  114. func maxTime(a, b time.Time) time.Time {
  115. if a.After(b) {
  116. return a
  117. }
  118. return b
  119. }