Kaynağa Gözat

feat: add refinement window span policy

master
Jan Svabenik 9 saat önce
ebeveyn
işleme
7b54340f61
3 değiştirilmiş dosya ile 122 ekleme ve 101 silme
  1. +8
    -1
      cmd/sdrd/pipeline_runtime.go
  2. +110
    -100
      internal/config/config.go
  3. +4
    -0
      internal/pipeline/policy.go

+ 8
- 1
cmd/sdrd/pipeline_runtime.go Dosyayı Görüntüle

@@ -238,9 +238,16 @@ func (rt *dspRuntime) buildRefinementInput(surv pipeline.SurveillanceResult) pip
}
windows := make([]pipeline.RefinementWindow, 0, len(scheduled))
for _, sc := range scheduled {
span := sc.Candidate.BandwidthHz
if policy.RefinementMinSpanHz > 0 && span < policy.RefinementMinSpanHz {
span = policy.RefinementMinSpanHz
}
if policy.RefinementMaxSpanHz > 0 && span > policy.RefinementMaxSpanHz {
span = policy.RefinementMaxSpanHz
}
windows = append(windows, pipeline.RefinementWindow{
CenterHz: sc.Candidate.CenterHz,
SpanHz: sc.Candidate.BandwidthHz,
SpanHz: span,
Source: "candidate",
})
}


+ 110
- 100
internal/config/config.go Dosyayı Görüntüle

@@ -15,25 +15,25 @@ type Band struct {
}

type DetectorConfig struct {
ThresholdDb float64 `yaml:"threshold_db" json:"threshold_db"`
MinDurationMs int `yaml:"min_duration_ms" json:"min_duration_ms"`
HoldMs int `yaml:"hold_ms" json:"hold_ms"`
EmaAlpha float64 `yaml:"ema_alpha" json:"ema_alpha"`
HysteresisDb float64 `yaml:"hysteresis_db" json:"hysteresis_db"`
MinStableFrames int `yaml:"min_stable_frames" json:"min_stable_frames"`
GapToleranceMs int `yaml:"gap_tolerance_ms" json:"gap_tolerance_ms"`
CFARMode string `yaml:"cfar_mode" json:"cfar_mode"`
CFARGuardHz float64 `yaml:"cfar_guard_hz" json:"cfar_guard_hz"`
CFARTrainHz float64 `yaml:"cfar_train_hz" json:"cfar_train_hz"`
CFARGuardCells int `yaml:"cfar_guard_cells,omitempty" json:"cfar_guard_cells,omitempty"`
CFARTrainCells int `yaml:"cfar_train_cells,omitempty" json:"cfar_train_cells,omitempty"`
CFARRank int `yaml:"cfar_rank" json:"cfar_rank"`
CFARScaleDb float64 `yaml:"cfar_scale_db" json:"cfar_scale_db"`
CFARWrapAround bool `yaml:"cfar_wrap_around" json:"cfar_wrap_around"`
EdgeMarginDb float64 `yaml:"edge_margin_db" json:"edge_margin_db"`
MaxSignalBwHz float64 `yaml:"max_signal_bw_hz" json:"max_signal_bw_hz"`
MergeGapHz float64 `yaml:"merge_gap_hz" json:"merge_gap_hz"`
ClassHistorySize int `yaml:"class_history_size" json:"class_history_size"`
ThresholdDb float64 `yaml:"threshold_db" json:"threshold_db"`
MinDurationMs int `yaml:"min_duration_ms" json:"min_duration_ms"`
HoldMs int `yaml:"hold_ms" json:"hold_ms"`
EmaAlpha float64 `yaml:"ema_alpha" json:"ema_alpha"`
HysteresisDb float64 `yaml:"hysteresis_db" json:"hysteresis_db"`
MinStableFrames int `yaml:"min_stable_frames" json:"min_stable_frames"`
GapToleranceMs int `yaml:"gap_tolerance_ms" json:"gap_tolerance_ms"`
CFARMode string `yaml:"cfar_mode" json:"cfar_mode"`
CFARGuardHz float64 `yaml:"cfar_guard_hz" json:"cfar_guard_hz"`
CFARTrainHz float64 `yaml:"cfar_train_hz" json:"cfar_train_hz"`
CFARGuardCells int `yaml:"cfar_guard_cells,omitempty" json:"cfar_guard_cells,omitempty"`
CFARTrainCells int `yaml:"cfar_train_cells,omitempty" json:"cfar_train_cells,omitempty"`
CFARRank int `yaml:"cfar_rank" json:"cfar_rank"`
CFARScaleDb float64 `yaml:"cfar_scale_db" json:"cfar_scale_db"`
CFARWrapAround bool `yaml:"cfar_wrap_around" json:"cfar_wrap_around"`
EdgeMarginDb float64 `yaml:"edge_margin_db" json:"edge_margin_db"`
MaxSignalBwHz float64 `yaml:"max_signal_bw_hz" json:"max_signal_bw_hz"`
MergeGapHz float64 `yaml:"merge_gap_hz" json:"merge_gap_hz"`
ClassHistorySize int `yaml:"class_history_size" json:"class_history_size"`
ClassSwitchRatio float64 `yaml:"class_switch_ratio" json:"class_switch_ratio"`

// Deprecated (backward compatibility)
@@ -56,7 +56,7 @@ type RecorderConfig struct {
RingSeconds int `yaml:"ring_seconds" json:"ring_seconds"`

// Audio quality settings (AQ-2, AQ-3, AQ-5)
DeemphasisUs float64 `yaml:"deemphasis_us" json:"deemphasis_us"` // De-emphasis time constant in µs. 50=Europe, 75=US/Japan, 0=disabled. Default: 50
DeemphasisUs float64 `yaml:"deemphasis_us" json:"deemphasis_us"` // De-emphasis time constant in µs. 50=Europe, 75=US/Japan, 0=disabled. Default: 50
ExtractionTaps int `yaml:"extraction_fir_taps" json:"extraction_fir_taps"` // FIR tap count for extraction filter. Default: 101, max 301
ExtractionBwMult float64 `yaml:"extraction_bw_mult" json:"extraction_bw_mult"` // BW multiplier for extraction. Default: 1.2 (20% wider than detected)
}
@@ -71,13 +71,13 @@ type DecoderConfig struct {
}

type PipelineGoalConfig struct {
Intent string `yaml:"intent" json:"intent"`
MonitorStartHz float64 `yaml:"monitor_start_hz" json:"monitor_start_hz"`
MonitorEndHz float64 `yaml:"monitor_end_hz" json:"monitor_end_hz"`
MonitorSpanHz float64 `yaml:"monitor_span_hz" json:"monitor_span_hz"`
SignalPriorities []string `yaml:"signal_priorities" json:"signal_priorities"`
AutoRecordClasses []string `yaml:"auto_record_classes" json:"auto_record_classes"`
AutoDecodeClasses []string `yaml:"auto_decode_classes" json:"auto_decode_classes"`
Intent string `yaml:"intent" json:"intent"`
MonitorStartHz float64 `yaml:"monitor_start_hz" json:"monitor_start_hz"`
MonitorEndHz float64 `yaml:"monitor_end_hz" json:"monitor_end_hz"`
MonitorSpanHz float64 `yaml:"monitor_span_hz" json:"monitor_span_hz"`
SignalPriorities []string `yaml:"signal_priorities" json:"signal_priorities"`
AutoRecordClasses []string `yaml:"auto_record_classes" json:"auto_record_classes"`
AutoDecodeClasses []string `yaml:"auto_decode_classes" json:"auto_decode_classes"`
}

type PipelineConfig struct {
@@ -94,51 +94,53 @@ type SurveillanceConfig struct {
}

type RefinementConfig struct {
Enabled bool `yaml:"enabled" json:"enabled"`
MaxConcurrent int `yaml:"max_concurrent" json:"max_concurrent"`
MinCandidateSNRDb float64 `yaml:"min_candidate_snr_db" json:"min_candidate_snr_db"`
Enabled bool `yaml:"enabled" json:"enabled"`
MaxConcurrent int `yaml:"max_concurrent" json:"max_concurrent"`
MinCandidateSNRDb float64 `yaml:"min_candidate_snr_db" json:"min_candidate_snr_db"`
MinSpanHz float64 `yaml:"min_span_hz" json:"min_span_hz"`
MaxSpanHz float64 `yaml:"max_span_hz" json:"max_span_hz"`
}

type ResourceConfig struct {
PreferGPU bool `yaml:"prefer_gpu" json:"prefer_gpu"`
MaxRefinementJobs int `yaml:"max_refinement_jobs" json:"max_refinement_jobs"`
MaxRecordingStreams int `yaml:"max_recording_streams" json:"max_recording_streams"`
PreferGPU bool `yaml:"prefer_gpu" json:"prefer_gpu"`
MaxRefinementJobs int `yaml:"max_refinement_jobs" json:"max_refinement_jobs"`
MaxRecordingStreams int `yaml:"max_recording_streams" json:"max_recording_streams"`
}

type ProfileConfig struct {
Name string `yaml:"name" json:"name"`
Description string `yaml:"description" json:"description"`
Pipeline *PipelineConfig `yaml:"pipeline,omitempty" json:"pipeline,omitempty"`
Name string `yaml:"name" json:"name"`
Description string `yaml:"description" json:"description"`
Pipeline *PipelineConfig `yaml:"pipeline,omitempty" json:"pipeline,omitempty"`
Surveillance *SurveillanceConfig `yaml:"surveillance,omitempty" json:"surveillance,omitempty"`
Refinement *RefinementConfig `yaml:"refinement,omitempty" json:"refinement,omitempty"`
Resources *ResourceConfig `yaml:"resources,omitempty" json:"resources,omitempty"`
Refinement *RefinementConfig `yaml:"refinement,omitempty" json:"refinement,omitempty"`
Resources *ResourceConfig `yaml:"resources,omitempty" json:"resources,omitempty"`
}

type Config struct {
Bands []Band `yaml:"bands" json:"bands"`
CenterHz float64 `yaml:"center_hz" json:"center_hz"`
SampleRate int `yaml:"sample_rate" json:"sample_rate"`
FFTSize int `yaml:"fft_size" json:"fft_size"`
GainDb float64 `yaml:"gain_db" json:"gain_db"`
TunerBwKHz int `yaml:"tuner_bw_khz" json:"tuner_bw_khz"`
UseGPUFFT bool `yaml:"use_gpu_fft" json:"use_gpu_fft"`
ClassifierMode string `yaml:"classifier_mode" json:"classifier_mode"`
AGC bool `yaml:"agc" json:"agc"`
DCBlock bool `yaml:"dc_block" json:"dc_block"`
IQBalance bool `yaml:"iq_balance" json:"iq_balance"`
Pipeline PipelineConfig `yaml:"pipeline" json:"pipeline"`
Surveillance SurveillanceConfig `yaml:"surveillance" json:"surveillance"`
Refinement RefinementConfig `yaml:"refinement" json:"refinement"`
Resources ResourceConfig `yaml:"resources" json:"resources"`
Profiles []ProfileConfig `yaml:"profiles" json:"profiles"`
Detector DetectorConfig `yaml:"detector" json:"detector"`
Recorder RecorderConfig `yaml:"recorder" json:"recorder"`
Decoder DecoderConfig `yaml:"decoder" json:"decoder"`
WebAddr string `yaml:"web_addr" json:"web_addr"`
EventPath string `yaml:"event_path" json:"event_path"`
FrameRate int `yaml:"frame_rate" json:"frame_rate"`
WaterfallLines int `yaml:"waterfall_lines" json:"waterfall_lines"`
WebRoot string `yaml:"web_root" json:"web_root"`
Bands []Band `yaml:"bands" json:"bands"`
CenterHz float64 `yaml:"center_hz" json:"center_hz"`
SampleRate int `yaml:"sample_rate" json:"sample_rate"`
FFTSize int `yaml:"fft_size" json:"fft_size"`
GainDb float64 `yaml:"gain_db" json:"gain_db"`
TunerBwKHz int `yaml:"tuner_bw_khz" json:"tuner_bw_khz"`
UseGPUFFT bool `yaml:"use_gpu_fft" json:"use_gpu_fft"`
ClassifierMode string `yaml:"classifier_mode" json:"classifier_mode"`
AGC bool `yaml:"agc" json:"agc"`
DCBlock bool `yaml:"dc_block" json:"dc_block"`
IQBalance bool `yaml:"iq_balance" json:"iq_balance"`
Pipeline PipelineConfig `yaml:"pipeline" json:"pipeline"`
Surveillance SurveillanceConfig `yaml:"surveillance" json:"surveillance"`
Refinement RefinementConfig `yaml:"refinement" json:"refinement"`
Resources ResourceConfig `yaml:"resources" json:"resources"`
Profiles []ProfileConfig `yaml:"profiles" json:"profiles"`
Detector DetectorConfig `yaml:"detector" json:"detector"`
Recorder RecorderConfig `yaml:"recorder" json:"recorder"`
Decoder DecoderConfig `yaml:"decoder" json:"decoder"`
WebAddr string `yaml:"web_addr" json:"web_addr"`
EventPath string `yaml:"event_path" json:"event_path"`
FrameRate int `yaml:"frame_rate" json:"frame_rate"`
WaterfallLines int `yaml:"waterfall_lines" json:"waterfall_lines"`
WebRoot string `yaml:"web_root" json:"web_root"`
}

func Default() Config {
@@ -146,16 +148,16 @@ func Default() Config {
Bands: []Band{
{Name: "example", StartHz: 99.5e6, EndHz: 100.5e6},
},
CenterHz: 100.0e6,
SampleRate: 2_048_000,
FFTSize: 2048,
GainDb: 30,
TunerBwKHz: 1536,
UseGPUFFT: false,
CenterHz: 100.0e6,
SampleRate: 2_048_000,
FFTSize: 2048,
GainDb: 30,
TunerBwKHz: 1536,
UseGPUFFT: false,
ClassifierMode: "combined",
AGC: false,
DCBlock: false,
IQBalance: false,
AGC: false,
DCBlock: false,
IQBalance: false,
Pipeline: PipelineConfig{
Mode: "legacy",
Goals: PipelineGoalConfig{
@@ -173,6 +175,8 @@ func Default() Config {
Enabled: true,
MaxConcurrent: 8,
MinCandidateSNRDb: 0,
MinSpanHz: 0,
MaxSpanHz: 0,
},
Resources: ResourceConfig{
PreferGPU: true,
@@ -186,39 +190,39 @@ func Default() Config {
{Name: "archive", Description: "Record-first monitoring profile", Pipeline: &PipelineConfig{Mode: "archive", Goals: PipelineGoalConfig{Intent: "archive-and-triage"}}},
},
Detector: DetectorConfig{
ThresholdDb: -20,
MinDurationMs: 250,
HoldMs: 500,
EmaAlpha: 0.2,
HysteresisDb: 3,
MinStableFrames: 3,
GapToleranceMs: 500,
CFARMode: "GOSCA",
CFARGuardHz: 500,
CFARTrainHz: 5000,
CFARGuardCells: 3,
CFARTrainCells: 24,
CFARRank: 36,
CFARScaleDb: 6,
CFARWrapAround: true,
EdgeMarginDb: 3.0,
MaxSignalBwHz: 150000,
MergeGapHz: 5000,
ThresholdDb: -20,
MinDurationMs: 250,
HoldMs: 500,
EmaAlpha: 0.2,
HysteresisDb: 3,
MinStableFrames: 3,
GapToleranceMs: 500,
CFARMode: "GOSCA",
CFARGuardHz: 500,
CFARTrainHz: 5000,
CFARGuardCells: 3,
CFARTrainCells: 24,
CFARRank: 36,
CFARScaleDb: 6,
CFARWrapAround: true,
EdgeMarginDb: 3.0,
MaxSignalBwHz: 150000,
MergeGapHz: 5000,
ClassHistorySize: 10,
ClassSwitchRatio: 0.6,
},
Recorder: RecorderConfig{
Enabled: false,
MinSNRDb: 10,
MinDuration: "1s",
MaxDuration: "300s",
PrerollMs: 500,
RecordIQ: true,
RecordAudio: false,
AutoDemod: true,
AutoDecode: false,
MaxDiskMB: 0,
OutputDir: "data/recordings",
Enabled: false,
MinSNRDb: 10,
MinDuration: "1s",
MaxDuration: "300s",
PrerollMs: 500,
RecordIQ: true,
RecordAudio: false,
AutoDemod: true,
AutoDecode: false,
MaxDiskMB: 0,
OutputDir: "data/recordings",
RingSeconds: 8,
DeemphasisUs: 50,
ExtractionTaps: 101,
@@ -349,6 +353,12 @@ func applyDefaults(cfg Config) Config {
if cfg.Refinement.MaxConcurrent <= 0 {
cfg.Refinement.MaxConcurrent = 8
}
if cfg.Refinement.MinSpanHz < 0 {
cfg.Refinement.MinSpanHz = 0
}
if cfg.Refinement.MaxSpanHz < 0 {
cfg.Refinement.MaxSpanHz = 0
}
if cfg.Resources.MaxRefinementJobs <= 0 {
cfg.Resources.MaxRefinementJobs = cfg.Refinement.MaxConcurrent
}


+ 4
- 0
internal/pipeline/policy.go Dosyayı Görüntüle

@@ -18,6 +18,8 @@ type Policy struct {
RefinementEnabled bool `json:"refinement_enabled"`
MaxRefinementJobs int `json:"max_refinement_jobs"`
MinCandidateSNRDb float64 `json:"min_candidate_snr_db"`
RefinementMinSpanHz float64 `json:"refinement_min_span_hz"`
RefinementMaxSpanHz float64 `json:"refinement_max_span_hz"`
PreferGPU bool `json:"prefer_gpu"`
}

@@ -38,6 +40,8 @@ func PolicyFromConfig(cfg config.Config) Policy {
RefinementEnabled: cfg.Refinement.Enabled,
MaxRefinementJobs: cfg.Resources.MaxRefinementJobs,
MinCandidateSNRDb: cfg.Refinement.MinCandidateSNRDb,
RefinementMinSpanHz: cfg.Refinement.MinSpanHz,
RefinementMaxSpanHz: cfg.Refinement.MaxSpanHz,
PreferGPU: cfg.Resources.PreferGPU,
}
}


Yükleniyor…
İptal
Kaydet