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.

97 líneas
2.3KB

  1. package recorder
  2. import (
  3. "bytes"
  4. "errors"
  5. "math"
  6. "time"
  7. "sdr-visual-suite/internal/demod"
  8. "sdr-visual-suite/internal/demod/gpudemod"
  9. "sdr-visual-suite/internal/dsp"
  10. )
  11. // DemodLive demodulates a recent window and returns WAV bytes.
  12. func (m *Manager) DemodLive(centerHz float64, bw float64, mode string, seconds int) ([]byte, int, error) {
  13. if m == nil || m.ring == nil {
  14. return nil, 0, errors.New("recorder not ready")
  15. }
  16. if seconds <= 0 {
  17. seconds = 2
  18. }
  19. end := time.Now()
  20. start := end.Add(-time.Duration(seconds) * time.Second)
  21. segment := m.ring.Slice(start, end)
  22. if len(segment) == 0 {
  23. return nil, 0, errors.New("no iq in ring")
  24. }
  25. name := mode
  26. if name == "" {
  27. name = "NFM"
  28. }
  29. switch name {
  30. case "AM", "NFM", "WFM", "WFM_STEREO", "USB", "LSB", "CW":
  31. default:
  32. name = "NFM"
  33. }
  34. d := demod.Get(name)
  35. if d == nil {
  36. return nil, 0, errors.New("demodulator not found")
  37. }
  38. offset := centerHz - m.centerHz
  39. if bw <= 0 {
  40. bw = 12000
  41. }
  42. var audio []float32
  43. var inputRate int
  44. if m.gpuDemod != nil {
  45. var gpuMode gpudemod.DemodType
  46. var useGPU bool
  47. switch name {
  48. case "NFM":
  49. gpuMode, useGPU = gpudemod.DemodNFM, true
  50. case "WFM":
  51. gpuMode, useGPU = gpudemod.DemodWFM, true
  52. case "AM":
  53. gpuMode, useGPU = gpudemod.DemodAM, true
  54. case "USB":
  55. gpuMode, useGPU = gpudemod.DemodUSB, true
  56. case "LSB":
  57. gpuMode, useGPU = gpudemod.DemodLSB, true
  58. case "CW":
  59. gpuMode, useGPU = gpudemod.DemodCW, true
  60. }
  61. if useGPU {
  62. if gpuAudio, gpuRate, err := m.gpuDemod.DemodFused(segment, offset, bw, gpuMode); err == nil {
  63. audio = gpuAudio
  64. inputRate = gpuRate
  65. } else if gpuAudio, gpuRate, err := m.gpuDemod.Demod(segment, offset, bw, gpuMode); err == nil {
  66. audio = gpuAudio
  67. inputRate = gpuRate
  68. }
  69. }
  70. }
  71. if audio == nil {
  72. shifted := dsp.FreqShift(segment, m.sampleRate, offset)
  73. cutoff := bw / 2
  74. if cutoff < 200 {
  75. cutoff = 200
  76. }
  77. taps := dsp.LowpassFIR(cutoff, m.sampleRate, 101)
  78. filtered := dsp.ApplyFIR(shifted, taps)
  79. decim := int(math.Round(float64(m.sampleRate) / float64(d.OutputSampleRate())))
  80. if decim < 1 {
  81. decim = 1
  82. }
  83. dec := dsp.Decimate(filtered, decim)
  84. inputRate = m.sampleRate / decim
  85. audio = d.Demod(dec, inputRate)
  86. }
  87. buf := &bytes.Buffer{}
  88. if err := writeWAVTo(buf, audio, inputRate, d.Channels()); err != nil {
  89. return nil, 0, err
  90. }
  91. return buf.Bytes(), inputRate, nil
  92. }