Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

170 Zeilen
3.9KB

  1. package rds
  2. import (
  3. "math"
  4. "strings"
  5. "testing"
  6. )
  7. func TestCRC10KnownVector(t *testing.T) {
  8. // Verify the CRC polynomial produces 10-bit outputs
  9. c := crc10(0x1234)
  10. if c > 0x3FF {
  11. t.Fatalf("CRC exceeds 10 bits: %x", c)
  12. }
  13. }
  14. func TestEncodeBlockProduces26Bits(t *testing.T) {
  15. block := encodeBlock(0x1234, 'A')
  16. // Must fit in 26 bits
  17. if block>>26 != 0 {
  18. t.Fatalf("block exceeds 26 bits: %x", block)
  19. }
  20. // Data portion should be the original word
  21. data := uint16(block >> 10)
  22. if data != 0x1234 {
  23. t.Fatalf("data mismatch: got %x want %x", data, 0x1234)
  24. }
  25. }
  26. func TestBuildGroup0ABlockCount(t *testing.T) {
  27. g := buildGroup0A(0x1234, 0, false, false, 0, "TESTFM")
  28. // Block A must be PI
  29. if g[0] != 0x1234 {
  30. t.Fatalf("block A not PI: %x", g[0])
  31. }
  32. // Block D should contain first two PS chars 'T','E'
  33. ch0 := byte(g[3] >> 8)
  34. ch1 := byte(g[3] & 0xFF)
  35. if ch0 != 'T' || ch1 != 'E' {
  36. t.Fatalf("unexpected PS chars: %c %c", ch0, ch1)
  37. }
  38. }
  39. func TestBuildGroup2ABlockCount(t *testing.T) {
  40. g := buildGroup2A(0x1234, 0, false, false, 0, "Hello World")
  41. if g[0] != 0x1234 {
  42. t.Fatalf("block A not PI: %x", g[0])
  43. }
  44. // Group type field in block B should have type 2 in bits 15..12
  45. groupType := (g[1] >> 12) & 0x0F
  46. if groupType != 2 {
  47. t.Fatalf("unexpected group type: %d", groupType)
  48. }
  49. }
  50. func TestBuildGroupUsesConfiguredPI(t *testing.T) {
  51. g0 := buildGroup0A(0xBEEF, 0, false, false, 0, "TESTFM")
  52. if g0[0] != 0xBEEF {
  53. t.Fatalf("group0A block A not configured PI: %x", g0[0])
  54. }
  55. g2 := buildGroup2A(0xCAFE, 0, false, false, 0, "Hello World")
  56. if g2[0] != 0xCAFE {
  57. t.Fatalf("group2A block A not configured PI: %x", g2[0])
  58. }
  59. }
  60. func TestEncoderGenerate(t *testing.T) {
  61. cfg := DefaultConfig()
  62. cfg.SampleRate = 228000
  63. enc, err := NewEncoder(cfg)
  64. if err != nil {
  65. t.Fatalf("unexpected error: %v", err)
  66. }
  67. samples := enc.Generate(1024)
  68. if len(samples) != 1024 {
  69. t.Fatalf("expected 1024 samples, got %d", len(samples))
  70. }
  71. var maxAbs float64
  72. var energy float64
  73. for _, s := range samples {
  74. a := math.Abs(s)
  75. energy += s * s
  76. if a > maxAbs {
  77. maxAbs = a
  78. }
  79. }
  80. if energy == 0 {
  81. t.Fatal("expected non-zero energy in RDS output")
  82. }
  83. if maxAbs > defaultAmplitude*1.01 {
  84. t.Fatalf("samples exceed configured amplitude: %.6f", maxAbs)
  85. }
  86. }
  87. func TestEncoderReset(t *testing.T) {
  88. cfg := DefaultConfig()
  89. cfg.SampleRate = 228000
  90. enc, err := NewEncoder(cfg)
  91. if err != nil {
  92. t.Fatalf("unexpected error: %v", err)
  93. }
  94. sampleA := enc.Generate(1)[0]
  95. enc.Generate(100)
  96. enc.Reset()
  97. sampleB := enc.Generate(1)[0]
  98. if math.Abs(sampleA-sampleB) > 1e-9 {
  99. t.Fatalf("expected reset to replay initial sample: %v vs %v", sampleA, sampleB)
  100. }
  101. }
  102. func TestGroupSchedulerCycles(t *testing.T) {
  103. cfg := DefaultConfig()
  104. cfg.PS = "TESTPS"
  105. cfg.RT = "short"
  106. gs := newGroupScheduler(cfg)
  107. // Should get 4 PS groups then RT groups then cycle
  108. for i := 0; i < 40; i++ {
  109. _ = gs.NextGroup()
  110. }
  111. // No panic = success
  112. }
  113. func TestNormalizePS(t *testing.T) {
  114. got := normalizePS("radiox")
  115. if got != "RADIOX " {
  116. t.Fatalf("unexpected PS: %q", got)
  117. }
  118. }
  119. func TestNormalizeRT(t *testing.T) {
  120. long := strings.Repeat("a", 80)
  121. got := normalizeRT(long)
  122. if len(got) != 64 {
  123. t.Fatalf("unexpected RT length: %d", len(got))
  124. }
  125. }
  126. func TestDifferentialEncoder(t *testing.T) {
  127. d := diffEncoder{}
  128. // Input: 0 -> out = 0^0 = 0, prev=0
  129. // Input: 1 -> out = 0^1 = 1, prev=1
  130. // Input: 0 -> out = 1^0 = 1, prev=1
  131. // Input: 1 -> out = 1^1 = 0, prev=0
  132. expected := []uint8{0, 1, 1, 0}
  133. input := []uint8{0, 1, 0, 1}
  134. for i, in := range input {
  135. got := d.encode(in)
  136. if got != expected[i] {
  137. t.Fatalf("step %d: input=%d expected=%d got=%d", i, in, expected[i], got)
  138. }
  139. }
  140. }
  141. func TestRTSegmentCount(t *testing.T) {
  142. if n := rtSegmentCount("Hi"); n != 1 {
  143. t.Fatalf("expected 1, got %d", n)
  144. }
  145. if n := rtSegmentCount("Hello World!"); n != 3 {
  146. t.Fatalf("expected 3, got %d", n)
  147. }
  148. if n := rtSegmentCount(strings.Repeat("x", 64)); n != 16 {
  149. t.Fatalf("expected 16, got %d", n)
  150. }
  151. }