Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8113557015 | ||
|
|
3def789742 | ||
|
|
92ea98bcfa |
@@ -1,4 +1,14 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "0.25.4",
|
||||||
|
"date": "",
|
||||||
|
"en": [
|
||||||
|
"WinKeyer: the opening probe goes out as one write, matching a capture of a client that talks to the same K3NG keyer, and the handshake bytes are always logged so a keyer that stays silent can be diagnosed."
|
||||||
|
],
|
||||||
|
"fr": [
|
||||||
|
"WinKeyer : la sonde d’ouverture part en un seul envoi, calquée sur la capture d’un client qui dialogue avec le même manipulateur K3NG, et les octets de la poignée de main sont toujours journalisés pour diagnostiquer un manipulateur muet."
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "0.25.3",
|
"version": "0.25.3",
|
||||||
"date": "",
|
"date": "",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Single source of truth for the app version shown in the UI (header + About).
|
// Single source of truth for the app version shown in the UI (header + About).
|
||||||
// Bump this on a release (the release script updates it alongside telemetry.go).
|
// Bump this on a release (the release script updates it alongside telemetry.go).
|
||||||
export const APP_VERSION = '0.25.3';
|
export const APP_VERSION = '0.25.4';
|
||||||
|
|
||||||
// Author / credits, shown in Help -> About.
|
// Author / credits, shown in Help -> About.
|
||||||
export const APP_AUTHOR = 'F4BPO';
|
export const APP_AUTHOR = 'F4BPO';
|
||||||
|
|||||||
@@ -95,18 +95,25 @@ func hostOpenOnce(p serial.Port, boot time.Duration) (int, error) {
|
|||||||
time.Sleep(boot)
|
time.Sleep(boot)
|
||||||
drain(p)
|
drain(p)
|
||||||
|
|
||||||
// Resync the command parser before asking it anything.
|
// The resync nulls and the echo probe go out as ONE write.
|
||||||
if _, err := p.Write([]byte{cmdNull, cmdNull, cmdNull}); err != nil {
|
//
|
||||||
return 0, fmt.Errorf("resync: %w", err)
|
// Copied byte for byte from a Logger32 capture against the K3NG keyer that
|
||||||
}
|
// would not answer OpsLog: "Sent: 13 13 13 00 04 55 / Rcvd: 55". Same keyer,
|
||||||
time.Sleep(50 * time.Millisecond)
|
// same port, same six bytes — the only difference was that we sent them as
|
||||||
drain(p)
|
// two writes with a pause and a buffer purge in between, and Logger32 sends
|
||||||
|
// them as one. On a keyer that reboots when the port opens, that pause is a
|
||||||
// Is anything actually there?
|
// window for it to come up mid-sequence and swallow half of it.
|
||||||
if _, err := p.Write([]byte{cmdAdmin, adminEcho, echoProbe}); err != nil {
|
//
|
||||||
|
// There is nothing to wait for between the two halves anyway: a null produces
|
||||||
|
// no reply, so the pause was only ever giving the keyer a chance to change
|
||||||
|
// its mind.
|
||||||
|
probe := []byte{cmdNull, cmdNull, cmdNull, cmdAdmin, adminEcho, echoProbe}
|
||||||
|
traceHandshake("TX", probe, 0, false)
|
||||||
|
if _, err := p.Write(probe); err != nil {
|
||||||
return 0, fmt.Errorf("echo test: %w", err)
|
return 0, fmt.Errorf("echo test: %w", err)
|
||||||
}
|
}
|
||||||
b, ok := readByte(p, echoTimeout)
|
b, ok := readByte(p, echoTimeout)
|
||||||
|
traceHandshake("RX", nil, b, ok)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, errNoKeyer
|
return 0, errNoKeyer
|
||||||
}
|
}
|
||||||
@@ -116,16 +123,37 @@ func hostOpenOnce(p serial.Port, boot time.Duration) (int, error) {
|
|||||||
return 0, fmt.Errorf("echo test: expected 0x%02X, got 0x%02X — is this the keyer's port?", echoProbe, b)
|
return 0, fmt.Errorf("echo test: expected 0x%02X, got 0x%02X — is this the keyer's port?", echoProbe, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := p.Write([]byte{cmdAdmin, adminOpen}); err != nil {
|
open := []byte{cmdAdmin, adminOpen}
|
||||||
|
traceHandshake("TX", open, 0, false)
|
||||||
|
if _, err := p.Write(open); err != nil {
|
||||||
return 0, fmt.Errorf("host open: %w", err)
|
return 0, fmt.Errorf("host open: %w", err)
|
||||||
}
|
}
|
||||||
ver, ok := readByte(p, openTimeout)
|
ver, ok := readByte(p, openTimeout)
|
||||||
|
traceHandshake("RX", nil, ver, ok)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, errors.New("host open: the keyer echoed but did not return its firmware version")
|
return 0, errors.New("host open: the keyer echoed but did not return its firmware version")
|
||||||
}
|
}
|
||||||
return int(ver), nil
|
return int(ver), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// traceHandshake puts the opening exchange in the log, ALWAYS — unlike the
|
||||||
|
// running trace beside it, which is behind the diagnostic option.
|
||||||
|
//
|
||||||
|
// A failure that says only "no WinKeyer answered" cannot be told apart from a
|
||||||
|
// wrong port, a wrong baud rate, a keyer still rebooting, or another program
|
||||||
|
// holding the line. The bytes can. It is four lines per connect, and only when
|
||||||
|
// the connect is attempted.
|
||||||
|
func traceHandshake(dir string, b []byte, got byte, ok bool) {
|
||||||
|
switch {
|
||||||
|
case dir == "TX":
|
||||||
|
applog.Printf("winkeyer: handshake TX % 02X", b)
|
||||||
|
case ok:
|
||||||
|
applog.Printf("winkeyer: handshake RX %02X", got)
|
||||||
|
default:
|
||||||
|
applog.Printf("winkeyer: handshake RX — nothing came back")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// readByte waits up to d for one byte. The serial read timeout is per-call and
|
// readByte waits up to d for one byte. The serial read timeout is per-call and
|
||||||
// can return 0 bytes without an error, so this loops until the deadline rather
|
// can return 0 bytes without an error, so this loops until the deadline rather
|
||||||
// than trusting a single Read.
|
// than trusting a single Read.
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ func TestHostOpenFollowsK1ELSequence(t *testing.T) {
|
|||||||
if ver != 23 {
|
if ver != 23 {
|
||||||
t.Errorf("version = %d, want 23", ver)
|
t.Errorf("version = %d, want 23", ver)
|
||||||
}
|
}
|
||||||
|
// One write for the six probe bytes, then Host Open — the order and the
|
||||||
|
// grouping of a Logger32 capture against a real K3NG.
|
||||||
want := []byte{
|
want := []byte{
|
||||||
cmdNull, cmdNull, cmdNull,
|
cmdNull, cmdNull, cmdNull,
|
||||||
cmdAdmin, adminEcho, echoProbe,
|
cmdAdmin, adminEcho, echoProbe,
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// appVersion is stamped on every heartbeat (and could feed the About box).
|
// appVersion is stamped on every heartbeat (and could feed the About box).
|
||||||
appVersion = "0.25.3"
|
appVersion = "0.25.4"
|
||||||
|
|
||||||
// posthogHost is the PostHog ingestion endpoint. EU cloud by default; change
|
// posthogHost is the PostHog ingestion endpoint. EU cloud by default; change
|
||||||
// to https://us.i.posthog.com for a US project.
|
// to https://us.i.posthog.com for a US project.
|
||||||
|
|||||||
Reference in New Issue
Block a user