Wideband autonomous SDR analysis engine forked from sdr-visual-suite
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

90 lines
2.1KB

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