Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

93 rindas
2.5KB

  1. package dsp
  2. import "math"
  3. // PreEmphasis implements a first-order high-shelf filter for FM broadcast
  4. // pre-emphasis. Standard time constants are 50 µs (Europe/world) and
  5. // 75 µs (North America/South Korea).
  6. //
  7. // Transfer function: H(s) = 1 + s*τ
  8. // Bilinear transform to discrete: H(z) = (b0 + b1*z^-1) / (1 + a1*z^-1)
  9. type PreEmphasis struct {
  10. b0, b1 float64
  11. x1 float64 // state
  12. enabled bool
  13. }
  14. // NewPreEmphasis creates a pre-emphasis filter for the given time constant
  15. // and sample rate. tau is in microseconds (50 or 75).
  16. func NewPreEmphasis(tauMicroseconds, sampleRate float64) *PreEmphasis {
  17. if tauMicroseconds <= 0 || sampleRate <= 0 {
  18. return &PreEmphasis{enabled: false}
  19. }
  20. tau := tauMicroseconds * 1e-6
  21. // Bilinear transform of H(s) = 1 + s*tau
  22. // With pre-warping: let c = 2*fs
  23. // Numerator: (1 + tau*c) + (-1 + tau*c)*z^-1 => b0 = 1+tau*c, b1 = -1+tau*c
  24. // Denominator: (1 + tau*c) + (1 - tau*c)*z^-1 (but we normalize so a0=1)
  25. // Wait - cleaner approach: standard first-order shelf.
  26. // H(s) = (1 + s*tau) mapped with bilinear: s = c*(1 - z^-1)/(1 + z^-1), c = 2*fs
  27. // De-emphasis: H_de(z) = (1-alpha)/(1 - alpha*z^-1), alpha = exp(-1/(tau*fs))
  28. // Pre-emphasis: H_pre(z) = 1/H_de(z) = (1 - alpha*z^-1)/(1-alpha)
  29. // y[n] = (x[n] - alpha*x[n-1]) / (1 - alpha)
  30. alpha := math.Exp(-1.0 / (tau * sampleRate))
  31. gain := 1.0 / (1.0 - alpha)
  32. return &PreEmphasis{
  33. b0: gain,
  34. b1: -alpha * gain,
  35. enabled: true,
  36. }
  37. }
  38. // Process applies the pre-emphasis filter to a single sample.
  39. func (p *PreEmphasis) Process(in float64) float64 {
  40. if !p.enabled {
  41. return in
  42. }
  43. out := p.b0*in + p.b1*p.x1
  44. p.x1 = in
  45. return out
  46. }
  47. // Reset clears the filter state.
  48. func (p *PreEmphasis) Reset() {
  49. p.x1 = 0
  50. }
  51. // DeEmphasis implements the complementary de-emphasis filter.
  52. // H(z) = (1-alpha)/(1 - alpha*z^-1)
  53. type DeEmphasis struct {
  54. alpha float64
  55. gain float64
  56. prevOut float64
  57. enabled bool
  58. }
  59. // NewDeEmphasis creates a de-emphasis filter.
  60. func NewDeEmphasis(tauMicroseconds, sampleRate float64) *DeEmphasis {
  61. if tauMicroseconds <= 0 || sampleRate <= 0 {
  62. return &DeEmphasis{enabled: false}
  63. }
  64. tau := tauMicroseconds * 1e-6
  65. alpha := math.Exp(-1.0 / (tau * sampleRate))
  66. return &DeEmphasis{alpha: alpha, gain: 1.0 - alpha, enabled: true}
  67. }
  68. // Process applies the de-emphasis filter.
  69. func (d *DeEmphasis) Process(in float64) float64 {
  70. if !d.enabled {
  71. return in
  72. }
  73. out := d.gain*in + d.alpha*d.prevOut
  74. d.prevOut = out
  75. return out
  76. }
  77. // Reset clears the filter state.
  78. func (d *DeEmphasis) Reset() {
  79. d.prevOut = 0
  80. }