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 " " 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) }