package stereo import ( "math" "math/cmplx" "testing" "github.com/jan/fm-rds-tx/internal/audio" ) func TestSSBSidebandSuppression(t *testing.T) { const sampleRate = 228000 const testFreq = 5000.0 // 5 kHz test tone const nSamples = 228000 // 1 second for _, mode := range []Mode{ModeDSB, ModeSSB, ModeVSB} { enc := NewStereoEncoder(sampleRate) enc.SetMode(mode, sampleRate) // Generate 1 second of stereo: L=tone, R=-tone → mono=0, diff=tone composite := make([]float64, nSamples) for i := 0; i < nSamples; i++ { phase := 2 * math.Pi * testFreq * float64(i) / sampleRate l := audio.Sample(0.5 * math.Sin(phase)) r := audio.Sample(-0.5 * math.Sin(phase)) c := enc.Encode(audio.NewFrame(l, r)) composite[i] = c.Mono + c.Stereo } // FFT to check spectrum n := len(composite) buf := make([]complex128, n) for i := range buf { buf[i] = complex(composite[i], 0) } // Simple DFT at key frequencies (not full FFT, just spot-check) freqBin := func(hz float64) float64 { bin := int(hz * float64(n) / sampleRate) if bin >= n/2 { return 0 } var sum complex128 for i := 0; i < n; i++ { angle := -2 * math.Pi * float64(bin) * float64(i) / float64(n) sum += complex(composite[i], 0) * cmplx.Rect(1, angle) } return cmplx.Abs(sum) / float64(n) } lsb := freqBin(38000 - testFreq) // 33 kHz — lower sideband usb := freqBin(38000 + testFreq) // 43 kHz — upper sideband suppression := 0.0 if usb > 0 && lsb > 0 { suppression = 20 * math.Log10(usb/lsb) } t.Logf("Mode %s: LSB(%.0fHz)=%.4f USB(%.0fHz)=%.4f suppression=%.1f dB", mode, 38000-testFreq, lsb, 38000+testFreq, usb, suppression) switch mode { case ModeDSB: // Both sidebands should be roughly equal if math.Abs(suppression) > 3 { t.Errorf("DSB: sidebands should be balanced, got %.1f dB", suppression) } case ModeSSB: // USB should be suppressed by >20 dB if suppression > -20 { t.Errorf("SSB: USB suppression only %.1f dB (want >20 dB)", suppression) } case ModeVSB: // 5 kHz tone is above 200 Hz crossover → should behave like SSB if suppression > -15 { t.Errorf("VSB: USB suppression only %.1f dB at %0.f Hz (want >15 dB)", suppression, testFreq) } } } }