Keep the engine fed instead of feeding it a character at a time

The pump handed the engine one character and then waited a character time
before the next, on purpose, so it stayed behind the engine rather than ahead.
That leaves the engine with an empty buffer between every two characters of a
message, and an engine with an empty buffer transmits idle rather than waiting.
The idle is added to how long the message takes, which in a contest is time
paid for nothing.

The pump now keeps two characters in the engine: one being transmitted and one
behind it, so the engine never runs dry. The clock at the baud rate says when
the engine has room for the next one, and the engine reporting that it has
stopped transmitting sets the estimate back to an empty buffer.

The last two characters are now beyond reach rather than the last one. Everything
before them can still be rewritten, which is what the pane is for.

A message is finished when the buffer is empty and the engine is estimated to
have transmitted what it holds, so {END} and {RX} no longer run while the engine
still has the last characters of the message.

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 21:56:41 +00:00
parent 9d0af06686
commit da7f7fb3f5
4 changed files with 184 additions and 50 deletions

View File

@@ -7,9 +7,9 @@ namespace Nonemm.Digital;
/// CW message does.
///
/// A message is not handed to the engine whole. It goes into the type-ahead
/// buffer, which feeds the engine a character at a time, so the operator can
/// still rewrite the part that has not gone out. `Finished` is raised when the
/// buffer runs dry, which is when the message has really gone.
/// buffer, which keeps the engine a couple of characters ahead of the operator,
/// so the rest can still be rewritten. `Finished` is raised when the buffer has
/// run dry and the engine has transmitted what it was given.
public sealed class DigitalEngineSender : MessageSender
{
private readonly DigitalEngine engine;
@@ -29,7 +29,7 @@ public sealed class DigitalEngineSender : MessageSender
public bool IsReady => engine.IsConnected;
/// The buffer says when the last character has gone to the engine, and the
/// The buffer says when the last character has been transmitted, and the
/// engine says when it has stopped transmitting.
public bool ReportsCompletion => true;
@@ -63,13 +63,20 @@ public sealed class DigitalEngineSender : MessageSender
private void WhenDrained(object? sender, EventArgs e) => Finished?.Invoke(this, EventArgs.Empty);
/// The engine dropping the transmitter ends the message, but only when
/// there is nothing left to send: an engine that keys itself off what it
/// is given drops between two characters of a message the pump is still
/// feeding, and that is not the end of anything.
/// The engine dropping the transmitter empties its buffer, which the
/// type-ahead buffer is told so it stops waiting for characters that have
/// already gone. It ends the message only when there is nothing left to
/// send: an engine that keys itself off what it is given drops between two
/// characters of a message the pump is still feeding, and that is not the
/// end of anything.
private void WhenTransmitChanged(object? sender, bool transmitting)
{
if (!transmitting && !TypeAhead.IsSending)
if (transmitting)
{
return;
}
TypeAhead.EngineIdle();
if (!TypeAhead.IsSending)
{
Finished?.Invoke(this, EventArgs.Empty);
}