Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

85 строки
2.9KB

  1. package offline
  2. import (
  3. "testing"
  4. "time"
  5. cfgpkg "github.com/jan/fm-rds-tx/internal/config"
  6. "github.com/jan/fm-rds-tx/internal/dsp"
  7. )
  8. func extractComposite(g *Generator, duration time.Duration) ([]float64, float64) {
  9. cfg := g.cfg
  10. cfg.FM.FMModulationEnabled = false
  11. gen := NewGenerator(cfg)
  12. frame := gen.GenerateFrame(duration)
  13. samples := make([]float64, len(frame.Samples))
  14. for i, s := range frame.Samples {
  15. samples[i] = float64(s.I)
  16. }
  17. return samples, frame.SampleRateHz
  18. }
  19. func TestCompositeHasPilotAt19kHz(t *testing.T) {
  20. cfg := cfgpkg.Default()
  21. cfg.FM.FMModulationEnabled = false
  22. cfg.FM.StereoEnabled = true
  23. samples, rate := extractComposite(NewGenerator(cfg), 200*time.Millisecond)
  24. pilotEnergy := dsp.BandEnergy(samples, rate, 19000, 500)
  25. noiseEnergy := dsp.BandEnergy(samples, rate, 12000, 500)
  26. if pilotEnergy < noiseEnergy*10 {
  27. t.Fatalf("missing 19 kHz pilot: pilot=%.6f noise=%.6f", pilotEnergy, noiseEnergy)
  28. }
  29. }
  30. func TestCompositeHasStereoAt38kHz(t *testing.T) {
  31. cfg := cfgpkg.Default()
  32. cfg.FM.FMModulationEnabled = false
  33. cfg.FM.StereoEnabled = true
  34. cfg.Audio.ToneLeftHz = 1000
  35. cfg.Audio.ToneRightHz = 1600 // different L/R -> stereo energy
  36. samples, rate := extractComposite(NewGenerator(cfg), 200*time.Millisecond)
  37. stereoEnergy := dsp.BandEnergy(samples, rate, 38000, 3000)
  38. noiseEnergy := dsp.BandEnergy(samples, rate, 80000, 500)
  39. if stereoEnergy < noiseEnergy*1 {
  40. t.Fatalf("missing 38 kHz stereo energy: stereo=%.6f noise=%.6f", stereoEnergy, noiseEnergy)
  41. }
  42. }
  43. func TestCompositeHasRDSAt57kHz(t *testing.T) {
  44. cfg := cfgpkg.Default()
  45. cfg.FM.FMModulationEnabled = false
  46. cfg.RDS.Enabled = true
  47. samples, rate := extractComposite(NewGenerator(cfg), 200*time.Millisecond)
  48. rdsEnergy := dsp.BandEnergy(samples, rate, 57000, 3000)
  49. noiseEnergy := dsp.BandEnergy(samples, rate, 45000, 500)
  50. if rdsEnergy < noiseEnergy*5 {
  51. t.Fatalf("missing 57 kHz RDS energy: rds=%.6f noise=%.6f", rdsEnergy, noiseEnergy)
  52. }
  53. }
  54. func TestCompositeNoStereoWhenDisabled(t *testing.T) {
  55. cfg := cfgpkg.Default()
  56. cfg.FM.FMModulationEnabled = false
  57. cfg.FM.StereoEnabled = false
  58. samples, rate := extractComposite(NewGenerator(cfg), 200*time.Millisecond)
  59. pilotEnergy := dsp.BandEnergy(samples, rate, 19000, 500)
  60. // Should be near-zero compared to mono energy
  61. monoEnergy := dsp.BandEnergy(samples, rate, 1000, 500)
  62. if monoEnergy > 0 && pilotEnergy > monoEnergy*0.01 {
  63. t.Fatalf("pilot should be suppressed: pilot=%.6f mono=%.6f", pilotEnergy, monoEnergy)
  64. }
  65. }
  66. func TestCompositeNoRDSWhenDisabled(t *testing.T) {
  67. cfg := cfgpkg.Default()
  68. cfg.FM.FMModulationEnabled = false
  69. cfg.RDS.Enabled = false
  70. samples, rate := extractComposite(NewGenerator(cfg), 200*time.Millisecond)
  71. rdsEnergy := dsp.BandEnergy(samples, rate, 57000, 3000)
  72. monoEnergy := dsp.BandEnergy(samples, rate, 1000, 500)
  73. if monoEnergy > 0 && rdsEnergy > monoEnergy*0.001 {
  74. t.Fatalf("RDS should be suppressed: rds=%.6f mono=%.6f", rdsEnergy, monoEnergy)
  75. }
  76. }