Wideband autonomous SDR analysis engine forked from sdr-visual-suite
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

169 líneas
4.2KB

  1. package recorder
  2. import (
  3. "math"
  4. "sdr-wideband-suite/internal/demod"
  5. "sdr-wideband-suite/internal/dsp"
  6. )
  7. func demodWFMStereoBatchAudio(iq []complex64, sampleRate int, offset float64, bw float64, deemphasisUs float64) ([]float32, int) {
  8. if len(iq) == 0 || sampleRate <= 0 {
  9. return nil, 0
  10. }
  11. shifted := dsp.FreqShift(iq, sampleRate, offset)
  12. cutoff := bw / 2
  13. if cutoff < 200 {
  14. cutoff = 200
  15. }
  16. d := demod.Get("WFM")
  17. if d == nil {
  18. return nil, 0
  19. }
  20. if cutoff < 75000 && d.OutputSampleRate() >= 192000 {
  21. cutoff = 75000
  22. }
  23. taps := dsp.LowpassFIR(cutoff, sampleRate, 101)
  24. filtered := dsp.ApplyFIR(shifted, taps)
  25. demodRate := d.OutputSampleRate()
  26. decim1 := int(math.Round(float64(sampleRate) / float64(demodRate)))
  27. if decim1 < 1 {
  28. decim1 = 1
  29. }
  30. dec := dsp.Decimate(filtered, decim1)
  31. actualDemodRate := sampleRate / decim1
  32. mono := d.Demod(dec, actualDemodRate)
  33. if len(mono) == 0 {
  34. return nil, 0
  35. }
  36. stereo, locked := wfmStereoDecodeBatch(mono, actualDemodRate)
  37. if !locked || len(stereo) == 0 {
  38. stereo = make([]float32, len(mono)*2)
  39. for i, s := range mono {
  40. stereo[i*2] = s
  41. stereo[i*2+1] = s
  42. }
  43. }
  44. outRate := actualDemodRate
  45. if actualDemodRate != streamAudioRate {
  46. resampler := dsp.NewStereoResampler(actualDemodRate, streamAudioRate, resamplerTaps)
  47. stereo = resampler.Process(stereo)
  48. outRate = streamAudioRate
  49. }
  50. if deemphasisUs > 0 && outRate > 0 {
  51. tau := deemphasisUs * 1e-6
  52. alpha := math.Exp(-1.0 / (float64(outRate) * tau))
  53. var yL, yR float64
  54. for i := 0; i+1 < len(stereo); i += 2 {
  55. yL = alpha*yL + (1-alpha)*float64(stereo[i])
  56. stereo[i] = float32(yL)
  57. yR = alpha*yR + (1-alpha)*float64(stereo[i+1])
  58. stereo[i+1] = float32(yR)
  59. }
  60. }
  61. for i := range stereo {
  62. stereo[i] *= 0.35
  63. }
  64. return stereo, outRate
  65. }
  66. func wfmStereoDecodeBatch(mono []float32, sampleRate int) ([]float32, bool) {
  67. if len(mono) == 0 || sampleRate <= 0 {
  68. return nil, false
  69. }
  70. lp := dsp.LowpassFIR(15000, sampleRate, 101)
  71. lpr := dsp.ApplyFIRReal(mono, lp)
  72. hi := dsp.ApplyFIRReal(mono, dsp.LowpassFIR(53000, sampleRate, 101))
  73. lo := dsp.ApplyFIRReal(mono, dsp.LowpassFIR(23000, sampleRate, 101))
  74. bpf := make([]float32, len(mono))
  75. for i := range bpf {
  76. bpf[i] = hi[i] - lo[i]
  77. }
  78. pilotHi := dsp.ApplyFIRReal(mono, dsp.LowpassFIR(21000, sampleRate, 101))
  79. pilotLo := dsp.ApplyFIRReal(mono, dsp.LowpassFIR(17000, sampleRate, 101))
  80. pilot := make([]float32, len(mono))
  81. for i := range pilot {
  82. pilot[i] = pilotHi[i] - pilotLo[i]
  83. }
  84. phase := 0.0
  85. freq := 2 * math.Pi * 19000 / float64(sampleRate)
  86. alpha, beta := pllCoefficientsBatch(50, 0.707, sampleRate)
  87. lpAlpha := 1 - math.Exp(-2*math.Pi*200/float64(sampleRate))
  88. iState := 0.0
  89. qState := 0.0
  90. minFreq := 2 * math.Pi * 17000 / float64(sampleRate)
  91. maxFreq := 2 * math.Pi * 21000 / float64(sampleRate)
  92. var pilotPower float64
  93. var totalPower float64
  94. var errSum float64
  95. lr := make([]float32, len(mono))
  96. for i := 0; i < len(mono); i++ {
  97. p := float64(pilot[i])
  98. sinP, cosP := math.Sincos(phase)
  99. iMix := p * cosP
  100. qMix := p * -sinP
  101. iState += lpAlpha * (iMix - iState)
  102. qState += lpAlpha * (qMix - qState)
  103. err := math.Atan2(qState, iState)
  104. freq += beta * err
  105. if freq < minFreq {
  106. freq = minFreq
  107. } else if freq > maxFreq {
  108. freq = maxFreq
  109. }
  110. phase += freq + alpha*err
  111. if phase > 2*math.Pi {
  112. phase -= 2 * math.Pi
  113. } else if phase < 0 {
  114. phase += 2 * math.Pi
  115. }
  116. totalPower += float64(mono[i]) * float64(mono[i])
  117. pilotPower += p * p
  118. errSum += math.Abs(err)
  119. lr[i] = bpf[i] * float32(2*math.Sin(2*phase))
  120. }
  121. lr = dsp.ApplyFIRReal(lr, lp)
  122. pilotRatio := 0.0
  123. if totalPower > 0 {
  124. pilotRatio = pilotPower / totalPower
  125. }
  126. freqHz := freq * float64(sampleRate) / (2 * math.Pi)
  127. blockErr := errSum / float64(len(mono))
  128. locked := pilotRatio > 0.003 && math.Abs(freqHz-19000) < 250 && blockErr < 0.35
  129. out := make([]float32, len(lpr)*2)
  130. for i := range lpr {
  131. out[i*2] = 0.5 * (lpr[i] + lr[i])
  132. out[i*2+1] = 0.5 * (lpr[i] - lr[i])
  133. }
  134. return out, locked
  135. }
  136. func pllCoefficientsBatch(loopBW, damping float64, sampleRate int) (float64, float64) {
  137. if sampleRate <= 0 || loopBW <= 0 {
  138. return 0, 0
  139. }
  140. bl := loopBW / float64(sampleRate)
  141. theta := bl / (damping + 0.25/damping)
  142. d := 1 + 2*damping*theta + theta*theta
  143. alpha := (4 * damping * theta) / d
  144. beta := (4 * theta * theta) / d
  145. return alpha, beta
  146. }