package civ import ( "bytes" "testing" ) func TestFreqBCDRoundTrip(t *testing.T) { cases := []int64{0, 1, 7074000, 14250000, 28074000, 50313000, 144174000, 1296000000} for _, hz := range cases { b := FreqToBCD(hz) if len(b) != 5 { t.Fatalf("FreqToBCD(%d) len=%d, want 5", hz, len(b)) } if got := BCDToFreq(b); got != hz { t.Errorf("round trip %d → % X → %d", hz, b, got) } } } func TestFreqBCDKnownEncoding(t *testing.T) { // 14.250.000 Hz → little-endian BCD 00 00 25 14 00. want := []byte{0x00, 0x00, 0x25, 0x14, 0x00} if got := FreqToBCD(14250000); !bytes.Equal(got, want) { t.Errorf("FreqToBCD(14250000) = % X, want % X", got, want) } } func TestFrame(t *testing.T) { // Read-frequency request to a 7610 (0x98) from the controller (0xE0). got := Frame(0x98, AddrController, CmdReadFreq) want := []byte{0xFE, 0xFE, 0x98, 0xE0, 0x03, 0xFD} if !bytes.Equal(got, want) { t.Errorf("Frame = % X, want % X", got, want) } } func TestScanSingleFreqResponse(t *testing.T) { // Rig (0x98) → controller (0xE0): freq read response for 14.250 MHz. in := Frame(AddrController, 0x98, CmdReadFreq, 0x00, 0x00, 0x25, 0x14, 0x00) frames, consumed := Scan(in) if consumed != len(in) { t.Fatalf("consumed=%d, want %d", consumed, len(in)) } if len(frames) != 1 { t.Fatalf("got %d frames, want 1", len(frames)) } f := frames[0] if f.From != 0x98 || f.To != AddrController || f.Cmd != CmdReadFreq { t.Errorf("addrs/cmd wrong: %+v", f) } if hz := BCDToFreq(f.Data); hz != 14250000 { t.Errorf("decoded freq %d, want 14250000", hz) } } func TestScanSkipsEchoAndKeepsPartial(t *testing.T) { echo := Frame(0x98, AddrController, CmdReadFreq) // our outgoing (echoed back) resp := Frame(AddrController, 0x98, CmdReadMode, ModeCW, 0x01) // a real response buf := append(append([]byte{}, echo...), resp...) buf = append(buf, 0xFE, 0xFE, 0x98) // a partial third frame (no FD yet) frames, consumed := Scan(buf) if len(frames) != 2 { t.Fatalf("got %d frames, want 2", len(frames)) } // The partial frame must be left unconsumed so the next read can finish it. if consumed != len(echo)+len(resp) { t.Errorf("consumed=%d, want %d (partial frame retained)", consumed, len(echo)+len(resp)) } if frames[1].Cmd != CmdReadMode || len(frames[1].Data) < 1 || frames[1].Data[0] != ModeCW { t.Errorf("second frame wrong: %+v", frames[1]) } } func TestModeToADIF(t *testing.T) { cases := []struct { m byte data bool want string }{ {ModeUSB, false, "SSB"}, {ModeLSB, false, "SSB"}, {ModeUSB, true, "DATA"}, {ModeCW, false, "CW"}, {ModeCWR, false, "CW"}, {ModeRTTY, false, "RTTY"}, {ModeAM, false, "AM"}, {ModeFM, false, "FM"}, } for _, c := range cases { if got := ModeToADIF(c.m, c.data); got != c.want { t.Errorf("ModeToADIF(0x%02X, %v) = %q, want %q", c.m, c.data, got, c.want) } } } func TestLevelBCDRoundTrip(t *testing.T) { for _, v := range []int{0, 1, 50, 99, 100, 128, 200, 255} { b := LevelToBCD(v) if len(b) != 2 { t.Fatalf("LevelToBCD(%d) len=%d", v, len(b)) } if got := BCDToLevel(b); got != v { t.Errorf("level round trip %d → % X → %d", v, b, got) } } // Known encodings from the Icom CI-V reference. if got := LevelToBCD(128); !bytes.Equal(got, []byte{0x01, 0x28}) { t.Errorf("LevelToBCD(128) = % X, want 01 28", got) } if got := LevelToBCD(255); !bytes.Equal(got, []byte{0x02, 0x55}) { t.Errorf("LevelToBCD(255) = % X, want 02 55", got) } } func TestByteBCDRoundTrip(t *testing.T) { for _, v := range []int{0, 6, 12, 18, 21} { if got := BCDToByte(ByteToBCD(v)); got != v { t.Errorf("byte BCD round trip %d → %d", v, got) } } } func TestModelName(t *testing.T) { if got := ModelName(0x98); got != "IC-7610" { t.Errorf("ModelName(0x98) = %q, want IC-7610", got) } if got := ModelName(0x12); got != "Icom (0x12)" { t.Errorf("ModelName(0x12) = %q, want fallback", got) } }