Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

146 рядки
3.7KB

  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. // RDSComplexResult holds complex baseband samples for the Costas loop RDS decoder.
  87. type RDSComplexResult struct {
  88. Samples []complex64
  89. SampleRate int
  90. }
  91. // RDSBasebandComplex extracts the RDS subcarrier as complex samples.
  92. // The Costas loop in the RDS decoder needs both I and Q to lock.
  93. func RDSBasebandComplex(iq []complex64, sampleRate int) RDSComplexResult {
  94. base := wfmMonoBase(iq)
  95. if len(base) == 0 || sampleRate <= 0 {
  96. return RDSComplexResult{}
  97. }
  98. cplx := make([]complex64, len(base))
  99. for i, v := range base {
  100. cplx[i] = complex(v, 0)
  101. }
  102. cplx = dsp.FreqShift(cplx, sampleRate, -57000)
  103. lpTaps := dsp.LowpassFIR(7500, sampleRate, 101)
  104. cplx = dsp.ApplyFIR(cplx, lpTaps)
  105. targetRate := 19000
  106. decim := sampleRate / targetRate
  107. if decim < 1 {
  108. decim = 1
  109. }
  110. cplx = dsp.Decimate(cplx, decim)
  111. actualRate := sampleRate / decim
  112. return RDSComplexResult{Samples: cplx, SampleRate: actualRate}
  113. }
  114. // RDSBasebandDecimated returns float32 baseband for WAV writing / recorder.
  115. func RDSBasebandDecimated(iq []complex64, sampleRate int) RDSBasebandResult {
  116. res := RDSBasebandComplex(iq, sampleRate)
  117. out := make([]float32, len(res.Samples))
  118. for i, c := range res.Samples {
  119. out[i] = real(c)
  120. }
  121. return RDSBasebandResult{Samples: out, SampleRate: res.SampleRate}
  122. }
  123. func init() {
  124. Register(NFM{})
  125. Register(WFM{})
  126. Register(WFMStereo{})
  127. }