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.

94 líneas
2.2KB

  1. package recorder
  2. import (
  3. "errors"
  4. "math"
  5. "path/filepath"
  6. "sdr-visual-suite/internal/classifier"
  7. "sdr-visual-suite/internal/demod"
  8. "sdr-visual-suite/internal/detector"
  9. "sdr-visual-suite/internal/dsp"
  10. )
  11. func (m *Manager) demodAndWrite(dir string, ev detector.Event, iq []complex64, files map[string]any) error {
  12. if ev.Class == nil {
  13. return nil
  14. }
  15. name := mapClassToDemod(ev.Class.ModType)
  16. if name == "" {
  17. return nil
  18. }
  19. d := demod.Get(name)
  20. if d == nil {
  21. return errors.New("demodulator not found")
  22. }
  23. // band-extract around signal
  24. bw := ev.Bandwidth
  25. offset := ev.CenterHz - m.centerHz
  26. shifted := dsp.FreqShift(iq, m.sampleRate, offset)
  27. cutoff := bw / 2
  28. if cutoff < 200 {
  29. cutoff = 200
  30. }
  31. taps := dsp.LowpassFIR(cutoff, m.sampleRate, 101)
  32. filtered := dsp.ApplyFIR(shifted, taps)
  33. decim := int(math.Round(float64(m.sampleRate) / float64(d.OutputSampleRate())))
  34. if decim < 1 {
  35. decim = 1
  36. }
  37. dec := dsp.Decimate(filtered, decim)
  38. inputRate := m.sampleRate / decim
  39. audio := d.Demod(dec, inputRate)
  40. wav := filepath.Join(dir, "audio.wav")
  41. if err := writeWAV(wav, audio, inputRate, d.Channels()); err != nil {
  42. return err
  43. }
  44. files["audio"] = "audio.wav"
  45. files["audio_sample_rate"] = inputRate
  46. files["audio_channels"] = d.Channels()
  47. files["audio_demod"] = name
  48. if name == "WFM_STEREO" {
  49. if rds := demod.RDSBaseband(iq, m.sampleRate); len(rds) > 0 {
  50. rdsPath := filepath.Join(dir, "rds.wav")
  51. _ = writeWAV(rdsPath, rds, 2400, 1)
  52. files["rds_baseband"] = "rds.wav"
  53. files["rds_sample_rate"] = 2400
  54. // naive decode
  55. dec := rdsdecoder{}
  56. res := dec.Decode(rds, 2400)
  57. if res.PI != 0 {
  58. files["rds_pi"] = res.PI
  59. }
  60. if res.PS != "" {
  61. files["rds_ps"] = res.PS
  62. }
  63. if res.RT != "" {
  64. files["rds_rt"] = res.RT
  65. }
  66. }
  67. }
  68. return nil
  69. }
  70. func mapClassToDemod(c classifier.SignalClass) string {
  71. switch c {
  72. case classifier.ClassAM:
  73. return "AM"
  74. case classifier.ClassNFM:
  75. return "NFM"
  76. case classifier.ClassWFM:
  77. return "WFM"
  78. case classifier.ClassSSBUSB:
  79. return "USB"
  80. case classifier.ClassSSBLSB:
  81. return "LSB"
  82. case classifier.ClassCW:
  83. return "CW"
  84. case classifier.ClassFT8, classifier.ClassWSPR, classifier.ClassFSK, classifier.ClassPSK:
  85. return "USB"
  86. default:
  87. return ""
  88. }
  89. }