package gpudemod import ( "math" "math/cmplx" "testing" ) func makeDeterministicIQ(n int) []complex64 { out := make([]complex64, n) for i := 0; i < n; i++ { a := 0.017 * float64(i) b := 0.031 * float64(i) out[i] = complex64(complex(math.Cos(a)+0.2*math.Cos(b), math.Sin(a)+0.15*math.Sin(b))) } return out } func makeLowpassTaps(n int) []float32 { out := make([]float32, n) for i := range out { out[i] = 1.0 / float32(n) } return out } func requireComplexSlicesClose(t *testing.T, a []complex64, b []complex64, tol float64) { t.Helper() if len(a) != len(b) { t.Fatalf("length mismatch: %d vs %d", len(a), len(b)) } for i := range a { if cmplx.Abs(complex128(a[i]-b[i])) > tol { t.Fatalf("slice mismatch at %d: %v vs %v (tol=%f)", i, a[i], b[i], tol) } } } func TestCPUOracleMonolithicVsChunked(t *testing.T) { iq := makeDeterministicIQ(200000) mk := func() *CPUOracleState { return &CPUOracleState{ SignalID: 1, ConfigHash: 123, NCOPhase: 0, Decim: 20, PhaseCount: 0, NumTaps: 65, ShiftedHistory: make([]complex64, 0, 64), BaseTaps: makeLowpassTaps(65), } } phaseInc := 0.017 monoState := mk() mono := CPUOracleExtract(iq, monoState, phaseInc) chunked := RunChunkedCPUOracle(iq, []int{4096, 5000, 8192, 27307}, mk, phaseInc) requireComplexSlicesClose(t, mono, chunked, 1e-5) } func TestExactIntegerDecimation(t *testing.T) { if d, err := ExactIntegerDecimation(4000000, 200000); err != nil || d != 20 { t.Fatalf("unexpected exact decim result: d=%d err=%v", d, err) } if _, err := ExactIntegerDecimation(4000000, 192000); err == nil { t.Fatalf("expected non-integer decimation error") } } func TestCPUOracleDirectVsPolyphase(t *testing.T) { iq := makeDeterministicIQ(50000) mk := func() *CPUOracleState { taps := makeLowpassTaps(65) return &CPUOracleState{ SignalID: 1, ConfigHash: 123, NCOPhase: 0, Decim: 20, PhaseCount: 0, NumTaps: 65, ShiftedHistory: make([]complex64, 0, 64), BaseTaps: taps, PolyphaseTaps: BuildPolyphaseTapsPhaseMajor(taps, 20), } } phaseInc := 0.017 direct := CPUOracleExtract(iq, mk(), phaseInc) poly := CPUOracleExtractPolyphase(iq, mk(), phaseInc) requireComplexSlicesClose(t, direct, poly, 1e-5) }