Wideband autonomous SDR analysis engine forked from sdr-visual-suite
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

100 lines
2.3KB

  1. package dsp
  2. import "math"
  3. // LowpassFIR returns windowed-sinc lowpass taps (Hann).
  4. func LowpassFIR(cutoffHz float64, sampleRate int, taps int) []float64 {
  5. if taps%2 == 0 {
  6. taps++
  7. }
  8. out := make([]float64, taps)
  9. fc := cutoffHz / float64(sampleRate)
  10. if fc <= 0 {
  11. return out
  12. }
  13. m := float64(taps-1) / 2.0
  14. for n := 0; n < taps; n++ {
  15. x := float64(n) - m
  16. var sinc float64
  17. if x == 0 {
  18. sinc = 2 * fc
  19. } else {
  20. sinc = math.Sin(2*math.Pi*fc*x) / (math.Pi * x)
  21. }
  22. w := 0.5 * (1 - math.Cos(2*math.Pi*float64(n)/float64(taps-1)))
  23. out[n] = float64(sinc) * w
  24. }
  25. return out
  26. }
  27. // ApplyFIR applies real FIR taps to complex IQ.
  28. func ApplyFIR(iq []complex64, taps []float64) []complex64 {
  29. if len(iq) == 0 || len(taps) == 0 {
  30. return nil
  31. }
  32. out := make([]complex64, len(iq))
  33. n := len(taps)
  34. for i := 0; i < len(iq); i++ {
  35. var accR, accI float64
  36. for k := 0; k < n; k++ {
  37. idx := i - k
  38. if idx < 0 {
  39. break
  40. }
  41. v := iq[idx]
  42. w := taps[k]
  43. accR += float64(real(v)) * w
  44. accI += float64(imag(v)) * w
  45. }
  46. out[i] = complex(float32(accR), float32(accI))
  47. }
  48. return out
  49. }
  50. // Decimate keeps every nth sample.
  51. func Decimate(iq []complex64, factor int) []complex64 {
  52. if factor <= 1 {
  53. out := make([]complex64, len(iq))
  54. copy(out, iq)
  55. return out
  56. }
  57. out := make([]complex64, 0, len(iq)/factor+1)
  58. for i := 0; i < len(iq); i += factor {
  59. out = append(out, iq[i])
  60. }
  61. return out
  62. }
  63. // DecimateStateful keeps every nth sample, preserving the decimation phase
  64. // across calls. *phase is the index offset into the next frame where the
  65. // first output sample should be taken. It is updated on return so that
  66. // consecutive calls produce a continuous, gap-free decimated stream.
  67. //
  68. // Initial value of *phase should be 0.
  69. func DecimateStateful(iq []complex64, factor int, phase *int) []complex64 {
  70. if factor <= 1 || phase == nil {
  71. out := make([]complex64, len(iq))
  72. copy(out, iq)
  73. return out
  74. }
  75. n := len(iq)
  76. p := *phase
  77. if p < 0 {
  78. p = 0
  79. }
  80. out := make([]complex64, 0, (n-p)/factor+1)
  81. for i := p; i < n; i += factor {
  82. out = append(out, iq[i])
  83. }
  84. // Compute phase for next frame: how many samples past the end of this
  85. // frame until the next decimation point.
  86. if n > 0 && p < n {
  87. lastTaken := p + ((n-1-p)/factor)*factor
  88. *phase = lastTaken + factor - n
  89. } else if p >= n {
  90. // Entire frame was skipped (very short snippet)
  91. *phase = p - n
  92. }
  93. return out
  94. }