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.

130 líneas
3.6KB

  1. package main
  2. import (
  3. "context"
  4. "flag"
  5. "log"
  6. "net/http"
  7. "os"
  8. "os/signal"
  9. "path/filepath"
  10. "sync"
  11. "syscall"
  12. "time"
  13. "sdr-visual-suite/internal/config"
  14. "sdr-visual-suite/internal/detector"
  15. fftutil "sdr-visual-suite/internal/fft"
  16. "sdr-visual-suite/internal/fft/gpufft"
  17. "sdr-visual-suite/internal/mock"
  18. "sdr-visual-suite/internal/recorder"
  19. "sdr-visual-suite/internal/runtime"
  20. "sdr-visual-suite/internal/sdr"
  21. "sdr-visual-suite/internal/sdrplay"
  22. )
  23. func main() {
  24. var cfgPath string
  25. var mockFlag bool
  26. flag.StringVar(&cfgPath, "config", "config.yaml", "path to config YAML")
  27. flag.BoolVar(&mockFlag, "mock", false, "use synthetic IQ source")
  28. flag.Parse()
  29. cfg, err := config.Load(cfgPath)
  30. if err != nil {
  31. log.Fatalf("load config: %v", err)
  32. }
  33. cfgManager := runtime.New(cfg)
  34. gpuState := &gpuStatus{Available: gpufft.Available()}
  35. newSource := func(cfg config.Config) (sdr.Source, error) {
  36. if mockFlag {
  37. src := mock.New(cfg.SampleRate)
  38. if updatable, ok := interface{}(src).(sdr.ConfigurableSource); ok {
  39. _ = updatable.UpdateConfig(cfg.SampleRate, cfg.CenterHz, cfg.GainDb, cfg.AGC, cfg.TunerBwKHz)
  40. }
  41. return src, nil
  42. }
  43. src, err := sdrplay.New(cfg.SampleRate, cfg.CenterHz, cfg.GainDb, cfg.TunerBwKHz)
  44. if err != nil {
  45. return nil, err
  46. }
  47. if updatable, ok := src.(sdr.ConfigurableSource); ok {
  48. _ = updatable.UpdateConfig(cfg.SampleRate, cfg.CenterHz, cfg.GainDb, cfg.AGC, cfg.TunerBwKHz)
  49. }
  50. return src, nil
  51. }
  52. src, err := newSource(cfg)
  53. if err != nil {
  54. log.Fatalf("sdrplay init failed: %v (try --mock or build with -tags sdrplay)", err)
  55. }
  56. srcMgr := newSourceManager(src, newSource)
  57. if err := srcMgr.Start(); err != nil {
  58. log.Fatalf("source start: %v", err)
  59. }
  60. defer srcMgr.Stop()
  61. if err := os.MkdirAll(filepath.Dir(cfg.EventPath), 0o755); err != nil {
  62. log.Fatalf("event path: %v", err)
  63. }
  64. eventFile, err := os.OpenFile(cfg.EventPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
  65. if err != nil {
  66. log.Fatalf("open events: %v", err)
  67. }
  68. defer eventFile.Close()
  69. eventMu := &sync.RWMutex{}
  70. det := detector.New(cfg.Detector, cfg.SampleRate, cfg.FFTSize)
  71. window := fftutil.Hann(cfg.FFTSize)
  72. h := newHub()
  73. dspUpdates := make(chan dspUpdate, 1)
  74. ctx, cancel := context.WithCancel(context.Background())
  75. defer cancel()
  76. decodeMap := buildDecoderMap(cfg)
  77. recMgr := recorder.New(cfg.SampleRate, cfg.FFTSize, recorder.Policy{
  78. Enabled: cfg.Recorder.Enabled,
  79. MinSNRDb: cfg.Recorder.MinSNRDb,
  80. MinDuration: mustParseDuration(cfg.Recorder.MinDuration, 1*time.Second),
  81. MaxDuration: mustParseDuration(cfg.Recorder.MaxDuration, 300*time.Second),
  82. PrerollMs: cfg.Recorder.PrerollMs,
  83. RecordIQ: cfg.Recorder.RecordIQ,
  84. RecordAudio: cfg.Recorder.RecordAudio,
  85. AutoDemod: cfg.Recorder.AutoDemod,
  86. AutoDecode: cfg.Recorder.AutoDecode,
  87. MaxDiskMB: cfg.Recorder.MaxDiskMB,
  88. OutputDir: cfg.Recorder.OutputDir,
  89. ClassFilter: cfg.Recorder.ClassFilter,
  90. RingSeconds: cfg.Recorder.RingSeconds,
  91. }, cfg.CenterHz, decodeMap)
  92. defer recMgr.Close()
  93. sigSnap := &signalSnapshot{}
  94. extractMgr := &extractionManager{}
  95. defer extractMgr.reset()
  96. go runDSP(ctx, srcMgr, cfg, det, window, h, eventFile, eventMu, dspUpdates, gpuState, recMgr, sigSnap, extractMgr)
  97. server := newHTTPServer(cfg.WebAddr, cfg.WebRoot, h, cfgPath, cfgManager, srcMgr, dspUpdates, gpuState, recMgr, sigSnap, eventMu)
  98. go func() {
  99. log.Printf("web listening on %s", cfg.WebAddr)
  100. if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
  101. log.Fatalf("server: %v", err)
  102. }
  103. }()
  104. stop := make(chan os.Signal, 1)
  105. signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
  106. <-stop
  107. shutdownServer(server)
  108. }