79 lines
2.7 KiB
Go
79 lines
2.7 KiB
Go
package udp
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"hamlog/internal/applog"
|
|
)
|
|
|
|
// WSJT-X Free Text (message type 9) — "put this in the free-text box, and send".
|
|
//
|
|
// Free Text type 9
|
|
// id utf8 the target application's own id
|
|
// text utf8 the message
|
|
// send bool true = transmit it now, false = only fill the box
|
|
//
|
|
// What this is FOR, and what it is not:
|
|
//
|
|
// It is the one "transmit this now" primitive that behaves the same on WSJT-X,
|
|
// WSJT-Z, JTDX, JTDX-Improved and MSHV, because it needs no decode to match
|
|
// against — unlike Reply, which the receiving application will only action if it
|
|
// can find the exact decode being answered.
|
|
//
|
|
// It is NOT a way to call a station. FT8 free text is 13 characters, so a
|
|
// standard "<DX> <ME> <GRID>" does not fit, and more importantly the text goes
|
|
// out AS free text without setting the DX Call — so the sequencer never takes
|
|
// over, and no report, RR73 or logging prompt follows. Use it for a 73, a short
|
|
// note or a CQ; use Reply to start a QSO.
|
|
const wsjtMsgFreeText = 9
|
|
|
|
// EncodeFreeText builds the datagram. send=false fills the box without keying.
|
|
func EncodeFreeText(programID, text string, send bool) []byte {
|
|
var b bytes.Buffer
|
|
_ = binary.Write(&b, binary.BigEndian, uint32(wsjtMagic))
|
|
_ = binary.Write(&b, binary.BigEndian, uint32(2)) // schema 2 — what every current sender speaks
|
|
_ = binary.Write(&b, binary.BigEndian, uint32(wsjtMsgFreeText))
|
|
writeQString(&b, programID)
|
|
writeQString(&b, text)
|
|
var s uint8
|
|
if send {
|
|
s = 1
|
|
}
|
|
_ = binary.Write(&b, binary.BigEndian, s)
|
|
return b.Bytes()
|
|
}
|
|
|
|
// SendFreeText hands a free-text message to one decoding application.
|
|
//
|
|
// Routed by program id to the address that instance's packets arrive from, for
|
|
// the same reason as SendReply and SendHaltTx: two receivers can share one
|
|
// multicast group, and transmitting from the wrong one is worse than not
|
|
// transmitting at all.
|
|
func (m *Manager) SendFreeText(programID, text string, send bool) error {
|
|
if strings.TrimSpace(programID) == "" {
|
|
return fmt.Errorf("no application id — cannot tell which receiver to send with")
|
|
}
|
|
m.mu.Lock()
|
|
servers := make([]*Server, 0, len(m.inbound))
|
|
for _, s := range m.inbound {
|
|
servers = append(servers, s)
|
|
}
|
|
m.mu.Unlock()
|
|
|
|
for _, s := range servers {
|
|
conn, addr := s.replyTarget(programID)
|
|
if conn == nil || addr == nil {
|
|
continue
|
|
}
|
|
if _, err := conn.WriteToUDP(EncodeFreeText(programID, text, send), addr); err != nil {
|
|
return fmt.Errorf("send free text to %s at %s: %w", programID, addr, err)
|
|
}
|
|
applog.Printf("udp: free text sent to %s at %s — %q (send=%v)", programID, addr, text, send)
|
|
return nil
|
|
}
|
|
return fmt.Errorf("no packet has arrived from %q yet — nothing to send to", programID)
|
|
}
|