Let MMTTY hold the text waiting to go out, behind a setting

MMTTY has a type-ahead buffer of its own: characters go into it, a backspace
takes back one it has not transmitted, and TxBufLen says how many are left. Used
that way it paces itself, so there is no gap between characters to tune and no
baud rate to keep in step with the engine.

EngineTypeAhead does that, and Config > Digital picks between it and the pump
that is there now. Off is still the default: three things it rests on have never
been seen with a real engine.

tools/Nonemm.EngineProbe asks the engine those three questions and writes the
answers to a file. It starts MMTTY through the bridge, pushes a message, polls
TxBufLen while it goes out, backspaces over text that has and has not been
transmitted, and logs what came back on the receive side and when.

The bridge learns one verb for it: `buffer` reads TxBufLen and answers with the
count, or -1 when the control will not say. A property the control does not know
is a log line rather than an error, since it stops nothing.

TypeAhead and EngineTypeAhead share the TransmitBuffer interface, which is what
the digital window now works through, so the window does not know which one it
has.

docs/unfinished.md states what each buffer assumes and how to run the probe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RtspmWmS7f8kUvcyaHpRWZ
This commit is contained in:
2026-09-01 22:41:18 +00:00
parent da7f7fb3f5
commit 54b9181c06
25 changed files with 1311 additions and 68 deletions

View File

@@ -32,6 +32,7 @@ To the bridge:
| `send <text>` | `SendString` |
| `ptt <0\|1>` | `SetMmttyPTT` |
| `post <message> <parameter>` | `PostMmttyMessage`, for everything in `MmttyMessage` |
| `buffer [property]` | reads `TxBufLen`, or the property named instead, and answers `buffer` |
| `close` | shuts the engine down and leaves the bridge running |
| `quit` | shuts the engine down and exits |
@@ -46,9 +47,17 @@ From the bridge:
| `tx <0\|1>` | the engine started or stopped transmitting |
| `mark <hz>`, `space <hz>` | the tone pair moved |
| `switch <bits>`, `view <bits>` | AFC (4), net (8) and reverse (256), and the engine's view state |
| `buffer <n>` | how many characters the engine still has to transmit, or -1 when the control would not say |
| `log <text>`, `error <text>` | anything the bridge has to say |
Each of those comes from an event of the control's own: `OnCharRcvd`,
`buffer` is the one thing that is asked for rather than reported: the control
has no event for the length of the transmit buffer, so it is polled. A property
name in the request is only for finding out what the control answers to; a name
it does not know fails with `DISP_E_UNKNOWNNAME`, which comes back as a `log`
line and `buffer -1` rather than an `error`, because a property the control will
not answer stops nothing.
Each of the others comes from an event of the control's own: `OnCharRcvd`,
`OnPttEvent`, `OnFreqChanged` (mark and space in one event), `OnSwitchChanged`
and `OnViewChanged`. The control raises `OnTranslateMessage` only for the
MMTTY messages it has no event for — width, resolution, thread and the like —

View File

@@ -44,23 +44,58 @@ type-ahead transmit pane were checked. Nothing has been decoded from a real
signal and nothing has gone on the air: this machine has no sound card, and
MMTTY does not start on it.
**What paces the type-ahead pump.** The pump keeps two characters in the
engine and counts character times off the clock at the baud rate in the digital
settings to work out when the engine has room for the next one. Two characters
is what stops the engine running dry between characters, which would make it
transmit idle and add that idle to the time the message takes; the price is that
the last two characters cannot be taken back. The estimate is corrected when the
engine reports that it has stopped transmitting, which means its buffer is
empty.
**Who holds the text waiting to go out.** There are two answers in the code and
a setting to pick between them: Config ▸ Digital ▸ "Let the engine hold the text
waiting to go out". Off is what runs today.
The engine can be asked instead of estimated: `XMMT.ocx` has a `TxBufLen`
property, and a probe against the control shows it answers, though what it
answers while a message is going out could not be measured here because the
engine will not start. Reporting it from the bridge would make the red text
exact and would take out the one error the estimate can still make: a baud rate
that does not match what the engine transmits at leaves a small gap between
*Off — `TypeAhead`.* The text is held here and the engine is kept two characters
ahead: one being transmitted and one behind it, so it never runs dry and never
transmits idle in the middle of a message. When the engine has room is worked
out from the clock at the baud rate in the digital settings, corrected whenever
the engine reports that it has stopped transmitting. The last two characters
cannot be taken back. The one error left is the baud rate: if it does not match
what the engine transmits at, the drift shows up as a small gap between
characters near the end of a long message.
*On — `EngineTypeAhead`.* MMTTY has a type-ahead buffer of its own, which is what
its own keyboard drives, so everything is pushed straight into it and an edit is
expressed as backspaces followed by the new text. MMTTY paces itself, so no baud
rate is needed and there is no gap by construction. `TxBufLen` says how many
characters are left, which is what splits the pane into what has gone and what
can still be changed. One character more than the count says is held back
(`EngineTypeAhead.Guard`), because the count is a poll old and a backspace over a
transmitted character is refused, which would put the text after it on the air
twice.
Three things it rests on have never been seen with a real engine, and MMTTY does
not start on this machine:
| What is assumed | Where it comes from |
|---|---|
| `TxBufLen` counts the characters left to transmit | the name, and a probe showing the control answers to it |
| a backspace pushed in as a character deletes one the engine has not transmitted | MMTTY's help, which says the backspace key does it and works only until the letter is transmitted; and N1MM printing a received `\b` by deleting the last character, which can only come from MMTTY's transmit side |
| backspaces over transmitted characters are refused rather than doing something else | nothing — this is the guess with the most to lose |
`tools/Nonemm.EngineProbe` asks the engine all three and writes the answers to a
file. It has to run where MMTTY runs:
```sh
./build.sh run --project tools/Nonemm.EngineProbe -- \
--engine ~/mmtty/MMTTY.EXE --prefix ~/.wine-nonemm --out engine-probe.log
```
It transmits for about half a minute on the sound card and keys no serial port
unless `--ptt` names one. The report says, step by step, what the engine
answered; `EngineProbe` states what each answer means.
There is a fourth question in the report that is not needed for either buffer,
but decides whether `TxBufLen` can be dropped later: what comes back on the
receive side while transmitting, and when. With MMTTY's sound loopback off, its
help says the receive window is fed from the transmit window, which would be a
per-character report of what has gone out, with no polling. With loopback on it
is the demodulator hearing the transmission instead, which is late and can
contain decode errors, so the count is what the pane uses.
**Voice keying.** `MessageSender` was written to cover a voice keyer playing a
recording, and nothing implements it. Each operator's recordings folder is
stored with the rest of his settings and waits on the same thing. No DVK support either, so the second radio