Wideband autonomous SDR analysis engine forked from sdr-visual-suite
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

131 строка
3.1KB

  1. package demod
  2. import (
  3. "math"
  4. "sdr-visual-suite/internal/dsp"
  5. )
  6. type NFM struct{}
  7. type WFM struct{}
  8. type WFMStereo struct{}
  9. func (NFM) Name() string { return "NFM" }
  10. func (WFM) Name() string { return "WFM" }
  11. func (WFMStereo) Name() string { return "WFM_STEREO" }
  12. func (NFM) OutputSampleRate() int { return 48000 }
  13. func (WFM) OutputSampleRate() int { return 192000 }
  14. func (WFMStereo) OutputSampleRate() int {
  15. return 192000
  16. }
  17. func (NFM) Channels() int { return 1 }
  18. func (WFM) Channels() int { return 1 }
  19. func (WFMStereo) Channels() int { return 2 }
  20. func (NFM) Demod(iq []complex64, sampleRate int) []float32 {
  21. return fmDiscrim(iq)
  22. }
  23. func (WFM) Demod(iq []complex64, sampleRate int) []float32 {
  24. return fmDiscrim(iq)
  25. }
  26. func (WFMStereo) Demod(iq []complex64, sampleRate int) []float32 {
  27. return wfmStereo(iq, sampleRate)
  28. }
  29. func fmDiscrim(iq []complex64) []float32 {
  30. if len(iq) < 2 {
  31. return nil
  32. }
  33. out := make([]float32, len(iq)-1)
  34. for i := 1; i < len(iq); i++ {
  35. p := iq[i-1]
  36. c := iq[i]
  37. num := float64(real(p))*float64(imag(c)) - float64(imag(p))*float64(real(c))
  38. den := float64(real(p))*float64(real(c)) + float64(imag(p))*float64(imag(c))
  39. out[i-1] = float32(math.Atan2(num, den))
  40. }
  41. return out
  42. }
  43. func wfmStereo(iq []complex64, sampleRate int) []float32 {
  44. base := fmDiscrim(iq)
  45. if len(base) == 0 {
  46. return nil
  47. }
  48. lp := dsp.LowpassFIR(15000, sampleRate, 101)
  49. lpr := dsp.ApplyFIRReal(base, lp)
  50. bpHi := dsp.LowpassFIR(53000, sampleRate, 101)
  51. bpLo := dsp.LowpassFIR(23000, sampleRate, 101)
  52. hi := dsp.ApplyFIRReal(base, bpHi)
  53. lo := dsp.ApplyFIRReal(base, bpLo)
  54. bpf := make([]float32, len(base))
  55. for i := range base {
  56. bpf[i] = hi[i] - lo[i]
  57. }
  58. lr := make([]float32, len(base))
  59. phase := 0.0
  60. inc := 2 * math.Pi * 38000 / float64(sampleRate)
  61. for i := range bpf {
  62. phase += inc
  63. lr[i] = bpf[i] * float32(2*math.Cos(phase))
  64. }
  65. lr = dsp.ApplyFIRReal(lr, lp)
  66. out := make([]float32, len(lpr)*2)
  67. for i := range lpr {
  68. l := 0.5 * (lpr[i] + lr[i])
  69. r := 0.5 * (lpr[i] - lr[i])
  70. out[i*2] = l
  71. out[i*2+1] = r
  72. }
  73. return out
  74. }
  75. // RDSBaseband returns a rough 57k baseband (not decoded).
  76. func RDSBaseband(iq []complex64, sampleRate int) []float32 {
  77. base := fmDiscrim(iq)
  78. if len(base) == 0 {
  79. return nil
  80. }
  81. bpHi := dsp.LowpassFIR(60000, sampleRate, 101)
  82. bpLo := dsp.LowpassFIR(54000, sampleRate, 101)
  83. hi := dsp.ApplyFIRReal(base, bpHi)
  84. lo := dsp.ApplyFIRReal(base, bpLo)
  85. bpf := make([]float32, len(base))
  86. for i := range base {
  87. bpf[i] = hi[i] - lo[i]
  88. }
  89. phase := 0.0
  90. inc := 2 * math.Pi * 57000 / float64(sampleRate)
  91. out := make([]float32, len(base))
  92. for i := range bpf {
  93. phase += inc
  94. out[i] = bpf[i] * float32(math.Cos(phase))
  95. }
  96. lp := dsp.LowpassFIR(2400, sampleRate, 101)
  97. return dsp.ApplyFIRReal(out, lp)
  98. }
  99. func deemphasis(x []float32, sampleRate int, tau float64) []float32 {
  100. if len(x) == 0 || sampleRate <= 0 {
  101. return x
  102. }
  103. alpha := math.Exp(-1.0 / (float64(sampleRate) * tau))
  104. out := make([]float32, len(x))
  105. var y float64
  106. for i, v := range x {
  107. y = alpha*y + (1-alpha)*float64(v)
  108. out[i] = float32(y)
  109. }
  110. return out
  111. }
  112. func init() {
  113. Register(NFM{})
  114. Register(WFM{})
  115. Register(WFMStereo{})
  116. }