"the keyer buffer stayed full for 4s" and nothing keyed, on a rig whose CAT is working. The buffer was not full: the check demanded the reply be exactly "KY0;" with the digit at byte 2, so anything the FTDX10 phrases differently — a space before the digit, another command's reply arriving first — read as "still full" on every poll until the deadline. The test is now asymmetric on purpose: only a clear "1" holds the send back. Anything unrecognised goes ahead. Refusing to transmit because a status line was phrased unexpectedly is the worse failure — the operator gets silence with no explanation, where at worst sending early truncates a long message, which the chunk loop then recovers from. The reply is also logged once per run, since we have no verified sample of it — that line is what turns the next surprise into a fact instead of a theory.
95 lines
2.9 KiB
Go
95 lines
2.9 KiB
Go
package cat
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// What reaches the rig's keyer.
|
|
//
|
|
// An unsupported byte does not produce an error on a Yaesu — it can abort the
|
|
// whole buffer, so the rest of the message is lost silently. Filtering is
|
|
// therefore part of correctness, not tidiness.
|
|
func TestFilterYaesuCW(t *testing.T) {
|
|
cases := []struct{ in, want string }{
|
|
{"cq cq de f4bpo", "CQ CQ DE F4BPO"},
|
|
{"F4BPO/P", "F4BPO/P"},
|
|
{"599 tu 73", "599 TU 73"},
|
|
// Accents and symbols the keyer has no code for.
|
|
{"héllo", "HLLO"},
|
|
{"a*b#c", "ABC"},
|
|
// Runs of whitespace collapse: the rig sends each space as a word gap, so
|
|
// three in a row is three gaps and the message crawls.
|
|
{"cq cq", "CQ CQ"},
|
|
{" padded ", "PADDED"},
|
|
{"\tcq\ncq\t", "CQ CQ"},
|
|
{"", ""},
|
|
{" ", ""},
|
|
}
|
|
for _, c := range cases {
|
|
if got := filterYaesuCW(c.in); got != c.want {
|
|
t.Errorf("filterYaesuCW(%q) = %q, want %q", c.in, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Long macros have to be split: one KY command carries 24 characters and the rig
|
|
// DROPS what does not fit rather than reporting an error, so a contest CQ would
|
|
// lose its tail with no sign anywhere.
|
|
func TestYaesuCWChunking(t *testing.T) {
|
|
msg := filterYaesuCW("cq cq cq de f4bpo f4bpo f4bpo pse k")
|
|
if len(msg) <= yaesuCWChunk {
|
|
t.Fatalf("test message is %d chars — too short to exercise chunking", len(msg))
|
|
}
|
|
var chunks []string
|
|
for rest := msg; len(rest) > 0; {
|
|
n := yaesuCWChunk
|
|
if len(rest) < n {
|
|
n = len(rest)
|
|
}
|
|
chunks = append(chunks, rest[:n])
|
|
rest = rest[n:]
|
|
}
|
|
for i, c := range chunks {
|
|
if len(c) > yaesuCWChunk {
|
|
t.Errorf("chunk %d is %d chars, over the %d limit", i, len(c), yaesuCWChunk)
|
|
}
|
|
}
|
|
// Nothing added, nothing lost: the message the operator typed is what the
|
|
// keyer receives.
|
|
if joined := strings.Join(chunks, ""); joined != msg {
|
|
t.Errorf("chunks rejoin to %q, want %q", joined, msg)
|
|
}
|
|
}
|
|
|
|
// Reading the KY status reply.
|
|
//
|
|
// The first version demanded exactly "KY0;" with the digit at byte 2, and on a
|
|
// real FTDX10 that refused to send at all: the reply was phrased differently,
|
|
// every poll read as "still full", and after four seconds the send gave up
|
|
// having keyed nothing. Hence the asymmetry — only a clear "1" blocks.
|
|
func TestYaesuCWBufferFull(t *testing.T) {
|
|
cases := []struct {
|
|
reply string
|
|
full bool
|
|
}{
|
|
{"KY0;", false},
|
|
{"KY1;", true},
|
|
{"KY 0;", false}, // a space before the digit
|
|
{"KY 1;", true},
|
|
{"ky1;", true}, // lower case
|
|
{" KY0; ", false},
|
|
// Shapes we have no sample of must NOT block: silence with no explanation
|
|
// is a worse failure than sending a moment early.
|
|
{"KY;", false},
|
|
{"", false},
|
|
{"FA014074000;", false}, // another command's reply arriving first
|
|
{"KYX;", false},
|
|
}
|
|
for _, c := range cases {
|
|
if got := yaesuCWBufferFull(c.reply); got != c.full {
|
|
t.Errorf("yaesuCWBufferFull(%q) = %v, want %v", c.reply, got, c.full)
|
|
}
|
|
}
|
|
}
|