package cfar import "math" // cellAvg implements CA-CFAR with a sliding sum window. type cellAvg struct { guard int train int scaleDb float64 wrapAround bool } func newCA(cfg Config) CFAR { return &cellAvg{ guard: cfg.GuardCells, train: cfg.TrainCells, scaleDb: cfg.ScaleDb, wrapAround: cfg.WrapAround, } } func (c *cellAvg) Thresholds(spectrum []float64) []float64 { n := len(spectrum) if n == 0 { return nil } out := make([]float64, n) train := c.train guard := c.guard total := 2 * train if total == 0 { return out } at := func(i int) float64 { if c.wrapAround { return spectrum[((i%n)+n)%n] } if i < 0 || i >= n { return spectrum[clampInt(i, 0, n-1)] } return spectrum[i] } toLinear := func(db float64) float64 { return math.Pow(10, db/10.0) } var leftSum, rightSum float64 for k := 1; k <= train; k++ { leftSum += toLinear(at(0 - guard - k)) rightSum += toLinear(at(0 + guard + k)) } invN := 1.0 / float64(total) out[0] = 10*math.Log10((leftSum+rightSum)*invN) + c.scaleDb for i := 1; i < n; i++ { leftSum -= toLinear(at(i - 1 - guard - train)) leftSum += toLinear(at(i - guard - 1)) rightSum -= toLinear(at(i - 1 + guard + 1)) rightSum += toLinear(at(i + guard + train)) out[i] = 10*math.Log10((leftSum+rightSum)*invN) + c.scaleDb } return out } func clampInt(v, lo, hi int) int { if v < lo { return lo } if v > hi { return hi } return v }