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.

195 líneas
6.1KB

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