Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

55 lines
1.4KB

  1. package dsp
  2. import "math"
  3. // BiquadLPF is a second-order Butterworth lowpass filter (biquad, direct form II transposed).
  4. // Used after the audio limiter to remove intermodulation products and harmonics
  5. // that could fall into the 19kHz pilot, 38kHz stereo sub, or 57kHz RDS bands.
  6. //
  7. // At 228kHz with fc=15kHz:
  8. // 15kHz: -3 dB (corner)
  9. // 19kHz: -5 dB
  10. // 38kHz: -18 dB
  11. // 57kHz: -27 dB ← protects RDS band
  12. type BiquadLPF struct {
  13. b0, b1, b2 float64
  14. a1, a2 float64
  15. z1, z2 float64 // state (direct form II transposed)
  16. }
  17. // NewBiquadLPF creates a 2nd-order Butterworth lowpass at the given cutoff.
  18. func NewBiquadLPF(cutoffHz, sampleRate float64) *BiquadLPF {
  19. if cutoffHz <= 0 || sampleRate <= 0 || cutoffHz >= sampleRate/2 {
  20. // Passthrough: return unity filter
  21. return &BiquadLPF{b0: 1}
  22. }
  23. omega := 2 * math.Pi * cutoffHz / sampleRate
  24. cosW := math.Cos(omega)
  25. sinW := math.Sin(omega)
  26. alpha := sinW / (2 * math.Sqrt2) // Q = 1/√2 for Butterworth
  27. a0 := 1 + alpha
  28. return &BiquadLPF{
  29. b0: (1 - cosW) / 2 / a0,
  30. b1: (1 - cosW) / a0,
  31. b2: (1 - cosW) / 2 / a0,
  32. a1: (-2 * cosW) / a0,
  33. a2: (1 - alpha) / a0,
  34. }
  35. }
  36. // Process filters a single sample.
  37. func (f *BiquadLPF) Process(in float64) float64 {
  38. out := f.b0*in + f.z1
  39. f.z1 = f.b1*in - f.a1*out + f.z2
  40. f.z2 = f.b2*in - f.a2*out
  41. return out
  42. }
  43. // Reset clears the filter state.
  44. func (f *BiquadLPF) Reset() {
  45. f.z1 = 0
  46. f.z2 = 0
  47. }