This commit is contained in:
2026-06-13 16:17:58 +02:00
parent ff53831be4
commit 00cab6b204
4 changed files with 78 additions and 37 deletions
+39 -28
View File
@@ -13,6 +13,7 @@ package winkeyer
import (
"fmt"
"runtime/debug"
"strings"
"sync"
"time"
@@ -26,28 +27,28 @@ import (
type Mode string
const (
ModeIambicB Mode = "iambic_b"
ModeIambicA Mode = "iambic_a"
ModeUltimatic Mode = "ultimatic"
ModeBug Mode = "bug"
ModeIambicB Mode = "iambic_b"
ModeIambicA Mode = "iambic_a"
ModeUltimatic Mode = "ultimatic"
ModeBug Mode = "bug"
)
// Config is the keyer configuration the UI persists and applies on connect.
type Config struct {
Port string `json:"port"` // e.g. "COM6"
Baud int `json:"baud"` // 1200 for WK2, also fine for WK3
WPM int `json:"wpm"` // 5..99
Weight int `json:"weight"` // 10..90, 50 = normal
LeadInMs int `json:"lead_in_ms"` // PTT lead-in, 10 ms units sent to device
TailMs int `json:"tail_ms"` // PTT tail
Ratio int `json:"ratio"` // dah/dit ratio 33..66 (50 = 3:1)
Farnsworth int `json:"farnsworth"` // Farnsworth WPM (0 = off)
Sidetone int `json:"sidetone_hz"` // 0 = off; else target Hz (mapped to WK code)
Mode Mode `json:"mode"` // paddle mode
Swap bool `json:"swap"` // swap dit/dah paddles
AutoSpace bool `json:"autospace"` // auto letter-space
UsePTT bool `json:"use_ptt"` // key PTT (Key/PTT output)
SerialEcho bool `json:"serial_echo"` // device echoes sent chars back to host
Port string `json:"port"` // e.g. "COM6"
Baud int `json:"baud"` // 1200 for WK2, also fine for WK3
WPM int `json:"wpm"` // 5..99
Weight int `json:"weight"` // 10..90, 50 = normal
LeadInMs int `json:"lead_in_ms"` // PTT lead-in, 10 ms units sent to device
TailMs int `json:"tail_ms"` // PTT tail
Ratio int `json:"ratio"` // dah/dit ratio 33..66 (50 = 3:1)
Farnsworth int `json:"farnsworth"` // Farnsworth WPM (0 = off)
Sidetone int `json:"sidetone_hz"` // 0 = off; else target Hz (mapped to WK code)
Mode Mode `json:"mode"` // paddle mode
Swap bool `json:"swap"` // swap dit/dah paddles
AutoSpace bool `json:"autospace"` // auto letter-space
UsePTT bool `json:"use_ptt"` // key PTT (Key/PTT output)
SerialEcho bool `json:"serial_echo"` // device echoes sent chars back to host
}
func (c Config) normalised() Config {
@@ -77,9 +78,9 @@ func (c Config) normalised() Config {
// Status is pushed to the UI whenever the link state or keyer activity changes.
type Status struct {
Connected bool `json:"connected"`
Busy bool `json:"busy"` // device is currently sending CW
WPM int `json:"wpm"` // current speed (tracks the speed pot)
Version int `json:"version"` // host firmware version byte
Busy bool `json:"busy"` // device is currently sending CW
WPM int `json:"wpm"` // current speed (tracks the speed pot)
Version int `json:"version"` // host firmware version byte
Port string `json:"port"`
Error string `json:"error,omitempty"`
}
@@ -177,11 +178,11 @@ func (m *Manager) Connect(cfg Config) error {
// applyConfig pushes the keying parameters to the device.
func (m *Manager) applyConfig(c Config) error {
cmds := [][]byte{
{0x0E, modeRegister(c)}, // set mode register (paddle mode, swap, autospace…)
{0x02, byte(c.WPM)}, // set speed (WPM)
{0x03, byte(c.Weight)}, // set weighting
{0x0E, modeRegister(c)}, // set mode register (paddle mode, swap, autospace…)
{0x02, byte(c.WPM)}, // set speed (WPM)
{0x03, byte(c.Weight)}, // set weighting
{0x04, byte(c.LeadInMs / 10), byte(c.TailMs / 10)}, // PTT lead-in / tail (10 ms units)
{0x11, byte(c.Ratio)}, // set dit/dah ratio
{0x11, byte(c.Ratio)}, // set dit/dah ratio
}
// Sidetone: <0x01 n>. Bit6 enables, low nibble selects the pitch divisor.
cmds = append(cmds, []byte{0x01, sidetoneCode(c.Sidetone)})
@@ -197,9 +198,11 @@ func (m *Manager) applyConfig(c Config) error {
}
// modeRegister builds the WinKey mode-register byte (command 0x0E).
// bits 1..0 : paddle mode (00 Iambic-B, 01 Iambic-A, 10 Ultimatic, 11 Bug)
// bit 3 : paddle swap
// bit 0/... : (autospace is bit 0 of a separate group on some firmwares)
//
// bits 1..0 : paddle mode (00 Iambic-B, 01 Iambic-A, 10 Ultimatic, 11 Bug)
// bit 3 : paddle swap
// bit 0/... : (autospace is bit 0 of a separate group on some firmwares)
//
// We keep to the widely-compatible WK2 layout.
func modeRegister(c Config) byte {
var b byte
@@ -339,6 +342,14 @@ func (m *Manager) Disconnect() {
// the echo of characters being sent. We track busy/idle and the speed pot.
func (m *Manager) readLoop(p serial.Port, stop, done chan struct{}) {
defer close(done)
// A panic here (e.g. in a status/echo callback) would otherwise take down
// the whole app — which showed up as a crash when logging a CW QSO while
// the keyer was still streaming echo bytes. Recover, log, end the loop.
defer func() {
if r := recover(); r != nil {
applog.Printf("winkeyer: readLoop panic recovered: %v\n%s", r, debug.Stack())
}
}()
buf := make([]byte, 64)
for {
select {