//go:build windows package cat import "testing" // feed pushes messages at the backend the way the radio would. func feed(t *TCI, msgs ...string) { for _, m := range msgs { t.handle(m) } } // "PTT via CAT does nothing on TCI." // // ExpertSDR announces transmit permission with TX_ENABLE — on connect, and // again whenever the band changes "in case transmitter permission was changed" // (§4.3 of the protocol document). When it is false the radio simply IGNORES // trx. OpsLog sent the documented command, the radio discarded it, and nothing // anywhere said why: the operator pressed a dead key. func TestPTTIsRefusedOutLoudWhenTheRadioForbidsTransmitting(t *testing.T) { tci := NewTCI("localhost", 40001, "FT8", false) feed(tci, "tx_enable:0,false") err := tci.SetPTT(true) if err == nil { t.Fatal("keying was accepted while the radio forbids transmitting — the operator gets no reason at all") } // The message has to name where to look; "PTT failed" sends nobody anywhere. for _, want := range []string{"transmit", "ExpertSDR"} { if !contains(err.Error(), want) { t.Errorf("the refusal reads %q, which does not mention %q", err.Error(), want) } } // Unkeying is never blocked. Whatever the radio thinks about permission, a // request to STOP transmitting must always reach it. if err := tci.SetPTT(false); err != nil && contains(err.Error(), "refusing") { t.Errorf("unkeying was refused: %v", err) } } // Permission comes back when the operator returns to a band they may use, and // PTT has to come back with it — not stay blocked until OpsLog is restarted. func TestPermissionGrantedAgainRestoresPTT(t *testing.T) { tci := NewTCI("localhost", 40001, "FT8", false) feed(tci, "tx_enable:0,false") if err := tci.SetPTT(true); err == nil { t.Fatal("keying was accepted while forbidden") } feed(tci, "tx_enable:0,true") // No connection here, so the send fails — but it must fail as a TRANSPORT // error, never as a refusal. if err := tci.SetPTT(true); err != nil && contains(err.Error(), "refusing") { t.Errorf("still refusing after permission was granted: %v", err) } } // A radio that never mentions TX_ENABLE — an older ExpertSDR, or one of the // other programs that speak TCI — must not be treated as refusing. Silence is // not a "no": we key, and let the radio decide. func TestSilenceAboutPermissionIsNotARefusal(t *testing.T) { tci := NewTCI("localhost", 40001, "FT8", false) if err := tci.SetPTT(true); err != nil && contains(err.Error(), "refusing") { t.Errorf("a radio that never sent TX_ENABLE was treated as forbidding transmit: %v", err) } } func contains(s, sub string) bool { return len(sub) == 0 || (len(s) >= len(sub) && indexOf(s, sub) >= 0) } func indexOf(s, sub string) int { for i := 0; i+len(sub) <= len(s); i++ { if s[i:i+len(sub)] == sub { return i } } return -1 }