package rds import ( "math" "strings" "testing" ) func TestCRC10KnownVector(t *testing.T) { c := crc10(0x1234) if c > 0x3FF { t.Fatalf("CRC exceeds 10 bits: %x", c) } } func TestEncodeBlockProduces26Bits(t *testing.T) { block := encodeBlock(0x1234, 'A') if block>>26 != 0 { t.Fatalf("block exceeds 26 bits: %x", block) } if uint16(block>>10) != 0x1234 { t.Fatalf("data mismatch") } } func TestBuildGroup0A(t *testing.T) { g := buildGroup0A(0x1234, 0, false, false, 0, "TESTFM") if g[0] != 0x1234 { t.Fatalf("block A not PI: %x", g[0]) } if byte(g[3]>>8) != 'T' || byte(g[3]&0xFF) != 'E' { t.Fatal("wrong PS chars") } } func TestBuildGroup2A(t *testing.T) { g := buildGroup2A(0x1234, 0, false, false, 0, "Hello World") if g[0] != 0x1234 { t.Fatal("block A not PI") } if (g[1]>>12)&0x0F != 2 { t.Fatal("wrong group type") } } func TestBuildGroupUsesConfiguredPI(t *testing.T) { if buildGroup0A(0xBEEF, 0, false, false, 0, "TEST")[0] != 0xBEEF { t.Fatal("PI mismatch 0A") } if buildGroup2A(0xCAFE, 0, false, false, 0, "Hello")[0] != 0xCAFE { t.Fatal("PI mismatch 2A") } } func TestEncoderGenerate(t *testing.T) { cfg := DefaultConfig(); cfg.SampleRate = 228000 enc, err := NewEncoder(cfg) if err != nil { t.Fatal(err) } samples := enc.Generate(1024) if len(samples) != 1024 { t.Fatal("wrong length") } var energy, maxAbs float64 for _, s := range samples { energy += s * s if math.Abs(s) > maxAbs { maxAbs = math.Abs(s) } } if energy == 0 { t.Fatal("zero energy") } // Unity output: peak should be close to 1.0 if maxAbs > 1.01 { t.Fatalf("exceeds unity: %.6f", maxAbs) } } func TestEncoderNextSample(t *testing.T) { cfg := DefaultConfig(); cfg.SampleRate = 228000 enc, _ := NewEncoder(cfg) s := enc.NextSample() // Should not panic and should produce a value if math.IsNaN(s) { t.Fatal("NaN") } } func TestEncoderReset(t *testing.T) { cfg := DefaultConfig(); cfg.SampleRate = 228000 enc, _ := NewEncoder(cfg) a := enc.NextSample() for i := 0; i < 100; i++ { enc.NextSample() } enc.Reset() b := enc.NextSample() if math.Abs(a-b) > 1e-9 { t.Fatalf("reset failed: %v vs %v", a, b) } } func TestGroupSchedulerCycles(t *testing.T) { cfg := DefaultConfig(); cfg.PS = "TESTPS"; cfg.RT = "short" gs := newGroupScheduler(cfg) for i := 0; i < 40; i++ { _ = gs.NextGroup() } } func TestNormalizePS(t *testing.T) { if normalizePS("radiox") != "RADIOX " { t.Fatal("wrong PS") } } func TestNormalizeRT(t *testing.T) { if len(normalizeRT(strings.Repeat("a", 80))) != 64 { t.Fatal("wrong RT length") } } func TestDifferentialEncoder(t *testing.T) { d := diffEncoder{} expected := []uint8{0, 1, 1, 0} input := []uint8{0, 1, 0, 1} for i, in := range input { if got := d.encode(in); got != expected[i] { t.Fatalf("step %d: got %d want %d", i, got, expected[i]) } } } func TestRTSegmentCount(t *testing.T) { if rtSegmentCount("Hi") != 1 { t.Fatal("expected 1") } if rtSegmentCount("Hello World!") != 3 { t.Fatal("expected 3") } if rtSegmentCount(strings.Repeat("x", 64)) != 16 { t.Fatal("expected 16") } }