Wideband autonomous SDR analysis engine forked from sdr-visual-suite
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

156 rindas
3.8KB

  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 wfmMonoBase(iq []complex64) []float32 {
  21. return fmDiscrim(iq)
  22. }
  23. func (NFM) Demod(iq []complex64, sampleRate int) []float32 {
  24. return fmDiscrim(iq)
  25. }
  26. func (WFM) Demod(iq []complex64, sampleRate int) []float32 {
  27. return wfmMonoBase(iq)
  28. }
  29. func (WFMStereo) Demod(iq []complex64, sampleRate int) []float32 {
  30. return wfmStereo(iq, sampleRate)
  31. }
  32. func fmDiscrim(iq []complex64) []float32 {
  33. if len(iq) < 2 {
  34. return nil
  35. }
  36. out := make([]float32, len(iq)-1)
  37. for i := 1; i < len(iq); i++ {
  38. p := iq[i-1]
  39. c := iq[i]
  40. num := float64(real(p))*float64(imag(c)) - float64(imag(p))*float64(real(c))
  41. den := float64(real(p))*float64(real(c)) + float64(imag(p))*float64(imag(c))
  42. out[i-1] = float32(math.Atan2(num, den))
  43. }
  44. return out
  45. }
  46. func wfmStereo(iq []complex64, sampleRate int) []float32 {
  47. base := fmDiscrim(iq)
  48. if len(base) == 0 {
  49. return nil
  50. }
  51. lp := dsp.LowpassFIR(15000, sampleRate, 101)
  52. lpr := dsp.ApplyFIRReal(base, lp)
  53. bpHi := dsp.LowpassFIR(53000, sampleRate, 101)
  54. bpLo := dsp.LowpassFIR(23000, sampleRate, 101)
  55. hi := dsp.ApplyFIRReal(base, bpHi)
  56. lo := dsp.ApplyFIRReal(base, bpLo)
  57. bpf := make([]float32, len(base))
  58. for i := range base {
  59. bpf[i] = hi[i] - lo[i]
  60. }
  61. lr := make([]float32, len(base))
  62. phase := 0.0
  63. inc := 2 * math.Pi * 38000 / float64(sampleRate)
  64. for i := range bpf {
  65. phase += inc
  66. lr[i] = bpf[i] * float32(2*math.Cos(phase))
  67. }
  68. lr = dsp.ApplyFIRReal(lr, lp)
  69. out := make([]float32, len(lpr)*2)
  70. for i := range lpr {
  71. l := 0.5 * (lpr[i] + lr[i])
  72. r := 0.5 * (lpr[i] - lr[i])
  73. out[i*2] = l
  74. out[i*2+1] = r
  75. }
  76. return out
  77. }
  78. type RDSBasebandResult struct {
  79. Samples []float32
  80. SampleRate int
  81. }
  82. // RDSBaseband returns a rough 57k baseband (not decoded).
  83. func RDSBaseband(iq []complex64, sampleRate int) []float32 {
  84. return RDSBasebandDecimated(iq, sampleRate).Samples
  85. }
  86. // RDSBasebandDecimated returns the 57 kHz RDS baseband mixed to near-DC and decimated.
  87. func RDSBasebandDecimated(iq []complex64, sampleRate int) RDSBasebandResult {
  88. base := wfmMonoBase(iq)
  89. if len(base) == 0 || sampleRate <= 0 {
  90. return RDSBasebandResult{}
  91. }
  92. bpHi := dsp.LowpassFIR(60000, sampleRate, 101)
  93. bpLo := dsp.LowpassFIR(54000, sampleRate, 101)
  94. hi := dsp.ApplyFIRReal(base, bpHi)
  95. lo := dsp.ApplyFIRReal(base, bpLo)
  96. bpf := make([]float32, len(base))
  97. for i := range base {
  98. bpf[i] = hi[i] - lo[i]
  99. }
  100. phase := 0.0
  101. inc := 2 * math.Pi * 57000 / float64(sampleRate)
  102. mixed := make([]float32, len(base))
  103. for i := range bpf {
  104. phase += inc
  105. mixed[i] = bpf[i] * float32(math.Cos(phase))
  106. }
  107. lp := dsp.LowpassFIR(2400, sampleRate, 101)
  108. filtered := dsp.ApplyFIRReal(mixed, lp)
  109. targetRate := 4800
  110. decim := sampleRate / targetRate
  111. if decim < 1 {
  112. decim = 1
  113. }
  114. actualRate := sampleRate / decim
  115. out := make([]float32, 0, len(filtered)/decim+1)
  116. for i := 0; i < len(filtered); i += decim {
  117. out = append(out, filtered[i])
  118. }
  119. return RDSBasebandResult{Samples: out, SampleRate: actualRate}
  120. }
  121. func deemphasis(x []float32, sampleRate int, tau float64) []float32 {
  122. if len(x) == 0 || sampleRate <= 0 {
  123. return x
  124. }
  125. alpha := math.Exp(-1.0 / (float64(sampleRate) * tau))
  126. out := make([]float32, len(x))
  127. var y float64
  128. for i, v := range x {
  129. y = alpha*y + (1-alpha)*float64(v)
  130. out[i] = float32(y)
  131. }
  132. return out
  133. }
  134. func init() {
  135. Register(NFM{})
  136. Register(WFM{})
  137. Register(WFMStereo{})
  138. }