chore: release v0.26.0

This commit is contained in:
2026-08-20 17:53:55 +02:00
parent 47992b5f03
commit 1e507225dd
44 changed files with 3197 additions and 240 deletions
+78
View File
@@ -0,0 +1,78 @@
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)
}