You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

174 line
5.2KB

  1. package config
  2. import (
  3. "os"
  4. "time"
  5. "gopkg.in/yaml.v3"
  6. )
  7. type Band struct {
  8. Name string `yaml:"name" json:"name"`
  9. StartHz float64 `yaml:"start_hz" json:"start_hz"`
  10. EndHz float64 `yaml:"end_hz" json:"end_hz"`
  11. }
  12. type DetectorConfig struct {
  13. ThresholdDb float64 `yaml:"threshold_db" json:"threshold_db"`
  14. MinDurationMs int `yaml:"min_duration_ms" json:"min_duration_ms"`
  15. HoldMs int `yaml:"hold_ms" json:"hold_ms"`
  16. EmaAlpha float64 `yaml:"ema_alpha" json:"ema_alpha"`
  17. HysteresisDb float64 `yaml:"hysteresis_db" json:"hysteresis_db"`
  18. MinStableFrames int `yaml:"min_stable_frames" json:"min_stable_frames"`
  19. GapToleranceMs int `yaml:"gap_tolerance_ms" json:"gap_tolerance_ms"`
  20. }
  21. type RecorderConfig struct {
  22. Enabled bool `yaml:"enabled" json:"enabled"`
  23. MinSNRDb float64 `yaml:"min_snr_db" json:"min_snr_db"`
  24. MinDuration string `yaml:"min_duration" json:"min_duration"`
  25. MaxDuration string `yaml:"max_duration" json:"max_duration"`
  26. PrerollMs int `yaml:"preroll_ms" json:"preroll_ms"`
  27. RecordIQ bool `yaml:"record_iq" json:"record_iq"`
  28. RecordAudio bool `yaml:"record_audio" json:"record_audio"`
  29. AutoDemod bool `yaml:"auto_demod" json:"auto_demod"`
  30. AutoDecode bool `yaml:"auto_decode" json:"auto_decode"`
  31. MaxDiskMB int `yaml:"max_disk_mb" json:"max_disk_mb"`
  32. OutputDir string `yaml:"output_dir" json:"output_dir"`
  33. ClassFilter []string `yaml:"class_filter" json:"class_filter"`
  34. RingSeconds int `yaml:"ring_seconds" json:"ring_seconds"`
  35. }
  36. type DecoderConfig struct {
  37. FT8Cmd string `yaml:"ft8_cmd" json:"ft8_cmd"`
  38. WSPRCmd string `yaml:"wspr_cmd" json:"wspr_cmd"`
  39. DMRCmd string `yaml:"dmr_cmd" json:"dmr_cmd"`
  40. DStarCmd string `yaml:"dstar_cmd" json:"dstar_cmd"`
  41. FSKCmd string `yaml:"fsk_cmd" json:"fsk_cmd"`
  42. PSKCmd string `yaml:"psk_cmd" json:"psk_cmd"`
  43. }
  44. type Config struct {
  45. Bands []Band `yaml:"bands" json:"bands"`
  46. CenterHz float64 `yaml:"center_hz" json:"center_hz"`
  47. SampleRate int `yaml:"sample_rate" json:"sample_rate"`
  48. FFTSize int `yaml:"fft_size" json:"fft_size"`
  49. GainDb float64 `yaml:"gain_db" json:"gain_db"`
  50. TunerBwKHz int `yaml:"tuner_bw_khz" json:"tuner_bw_khz"`
  51. UseGPUFFT bool `yaml:"use_gpu_fft" json:"use_gpu_fft"`
  52. AGC bool `yaml:"agc" json:"agc"`
  53. DCBlock bool `yaml:"dc_block" json:"dc_block"`
  54. IQBalance bool `yaml:"iq_balance" json:"iq_balance"`
  55. Detector DetectorConfig `yaml:"detector" json:"detector"`
  56. Recorder RecorderConfig `yaml:"recorder" json:"recorder"`
  57. Decoder DecoderConfig `yaml:"decoder" json:"decoder"`
  58. WebAddr string `yaml:"web_addr" json:"web_addr"`
  59. EventPath string `yaml:"event_path" json:"event_path"`
  60. FrameRate int `yaml:"frame_rate" json:"frame_rate"`
  61. WaterfallLines int `yaml:"waterfall_lines" json:"waterfall_lines"`
  62. WebRoot string `yaml:"web_root" json:"web_root"`
  63. }
  64. func Default() Config {
  65. return Config{
  66. Bands: []Band{
  67. {Name: "example", StartHz: 99.5e6, EndHz: 100.5e6},
  68. },
  69. CenterHz: 100.0e6,
  70. SampleRate: 2_048_000,
  71. FFTSize: 2048,
  72. GainDb: 30,
  73. TunerBwKHz: 1536,
  74. UseGPUFFT: false,
  75. AGC: false,
  76. DCBlock: false,
  77. IQBalance: false,
  78. Detector: DetectorConfig{ThresholdDb: -20, MinDurationMs: 250, HoldMs: 500, EmaAlpha: 0.2, HysteresisDb: 3, MinStableFrames: 3, GapToleranceMs: 500},
  79. Recorder: RecorderConfig{
  80. Enabled: false,
  81. MinSNRDb: 10,
  82. MinDuration: "1s",
  83. MaxDuration: "300s",
  84. PrerollMs: 500,
  85. RecordIQ: true,
  86. RecordAudio: false,
  87. AutoDemod: true,
  88. AutoDecode: false,
  89. MaxDiskMB: 0,
  90. OutputDir: "data/recordings",
  91. RingSeconds: 8,
  92. },
  93. Decoder: DecoderConfig{},
  94. WebAddr: ":8080",
  95. EventPath: "data/events.jsonl",
  96. FrameRate: 15,
  97. WaterfallLines: 200,
  98. WebRoot: "web",
  99. }
  100. }
  101. func Load(path string) (Config, error) {
  102. cfg := Default()
  103. b, err := os.ReadFile(path)
  104. if err != nil {
  105. return cfg, err
  106. }
  107. if err := yaml.Unmarshal(b, &cfg); err != nil {
  108. return cfg, err
  109. }
  110. if cfg.Detector.MinDurationMs <= 0 {
  111. cfg.Detector.MinDurationMs = 250
  112. }
  113. if cfg.Detector.HoldMs <= 0 {
  114. cfg.Detector.HoldMs = 500
  115. }
  116. if cfg.Detector.MinStableFrames <= 0 {
  117. cfg.Detector.MinStableFrames = 3
  118. }
  119. if cfg.Detector.GapToleranceMs <= 0 {
  120. cfg.Detector.GapToleranceMs = cfg.Detector.HoldMs
  121. }
  122. if cfg.FrameRate <= 0 {
  123. cfg.FrameRate = 15
  124. }
  125. if cfg.WaterfallLines <= 0 {
  126. cfg.WaterfallLines = 200
  127. }
  128. if cfg.WebRoot == "" {
  129. cfg.WebRoot = "web"
  130. }
  131. if cfg.WebAddr == "" {
  132. cfg.WebAddr = ":8080"
  133. }
  134. if cfg.EventPath == "" {
  135. cfg.EventPath = "data/events.jsonl"
  136. }
  137. if cfg.SampleRate <= 0 {
  138. cfg.SampleRate = 2_048_000
  139. }
  140. if cfg.FFTSize <= 0 {
  141. cfg.FFTSize = 2048
  142. }
  143. if cfg.TunerBwKHz <= 0 {
  144. cfg.TunerBwKHz = 1536
  145. }
  146. if cfg.CenterHz == 0 {
  147. cfg.CenterHz = 100.0e6
  148. }
  149. if cfg.Recorder.OutputDir == "" {
  150. cfg.Recorder.OutputDir = "data/recordings"
  151. }
  152. if cfg.Recorder.RingSeconds <= 0 {
  153. cfg.Recorder.RingSeconds = 8
  154. }
  155. return cfg, nil
  156. }
  157. func (c Config) FrameInterval() time.Duration {
  158. fps := c.FrameRate
  159. if fps <= 0 {
  160. fps = 15
  161. }
  162. return time.Second / time.Duration(fps)
  163. }