Nothing was ever transmitted. SetMmttyPTT does not start a transmission: N1MM
calls it with 1 to stop once the buffer is empty, which is its XmitOff, and with
0 to stop now, which is its AbortXmit. A transmission starts by setting the
control's PTT property, in XmitOn.
So the bridge learns `key <0|1>` for that property, and MmttyEngine now keys
with it, ends a message with SetMmttyPTT(1) so the buffer still goes out, and
aborts with SetMmttyPTT(0). This is the digital {TX} in the entry window and the
digital window as well as the probe: none of them could key the engine before.
The probe checks that the engine keys before it measures anything, and stops
with a plain statement if it does not, rather than reporting numbers from an
engine sitting still. It also asks a new question: whether the engine holds a
word until the space after it, which is MMTTY's Way to send. Received characters
are marked as noise while the engine is not transmitting, since a machine with a
sound card decodes the band all the way through the run.
The first run on a real engine says MMTTY 1.70 connects, the control answers
TxBufLen and refuses NotAProperty with DISP_E_UNKNOWNNAME. What TxBufLen counts
is still open: read while nothing was transmitting it rose over time and rose by
four after four backspaces, which is not what characters-left would do.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RtspmWmS7f8kUvcyaHpRWZ
124 lines
4.3 KiB
C#
124 lines
4.3 KiB
C#
using Nonemm.Digital;
|
|
|
|
namespace Nonemm.EngineProbe;
|
|
|
|
/// Starts MMTTY through the bridge, asks it the questions in `EngineProbe`, and
|
|
/// writes what it answered to a file. It has to run where MMTTY runs: a sound
|
|
/// card and a Wine prefix with XMMT.ocx registered.
|
|
public static class Program
|
|
{
|
|
private const string Usage = """
|
|
usage: engine probe --engine <path to MMTTY.EXE> [options]
|
|
|
|
--engine <path> the engine to start, as a Linux path
|
|
--bridge <path> the bridge program (default bridge/nonemm-mmtty-bridge.exe)
|
|
--prefix <path> WINEPREFIX to run in (default: Wine's own)
|
|
--ptt <port> serial port to key (default: none, so no radio is keyed)
|
|
--out <file> where to write the report (default engine-probe.log)
|
|
|
|
The probe transmits: MMTTY makes tones on the sound card for about half a
|
|
minute. It keys no serial port unless --ptt says so, but a rig listening to
|
|
that sound card through VOX will still go on the air.
|
|
""";
|
|
|
|
public static async Task<int> Main(string[] arguments)
|
|
{
|
|
Dictionary<string, string> options;
|
|
try
|
|
{
|
|
options = Read(arguments);
|
|
}
|
|
catch (ArgumentException problem)
|
|
{
|
|
Console.Error.WriteLine(problem.Message);
|
|
Console.Error.WriteLine();
|
|
Console.Error.WriteLine(Usage);
|
|
return 1;
|
|
}
|
|
|
|
using CancellationTokenSource stopping = new();
|
|
Console.CancelKeyPress += (_, e) =>
|
|
{
|
|
e.Cancel = true;
|
|
stopping.Cancel();
|
|
};
|
|
|
|
using ProbeLog log = new(Option(options, "out", "engine-probe.log"));
|
|
MmttyEngine engine = new(
|
|
new WineBridgeChannel(
|
|
Option(options, "bridge", Path.Combine("bridge", "nonemm-mmtty-bridge.exe")),
|
|
options.GetValueOrDefault("prefix")),
|
|
new MmttyOptions
|
|
{
|
|
EnginePath = options["engine"],
|
|
PttPort = Option(options, "ptt", ""),
|
|
});
|
|
try
|
|
{
|
|
log.Write($"starting {options["engine"]}");
|
|
await engine.StartAsync(stopping.Token).ConfigureAwait(false);
|
|
log.Write($"MMTTY {engine.Version} is up");
|
|
await new EngineProbe(engine, log).RunAsync(stopping.Token).ConfigureAwait(false);
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
log.Write("stopped");
|
|
}
|
|
catch (Exception problem)
|
|
{
|
|
log.Write($"failed: {problem.Message}");
|
|
Console.Error.WriteLine(problem);
|
|
return 1;
|
|
}
|
|
finally
|
|
{
|
|
await Shutdown(engine, log).ConfigureAwait(false);
|
|
Console.WriteLine();
|
|
Console.WriteLine($"the report is in {log.Path}");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// The engine is left keyed if the probe stopped partway through, so the
|
|
/// transmission is aborted before the engine is shut down, whatever
|
|
/// happened.
|
|
private static async Task Shutdown(MmttyEngine engine, ProbeLog log)
|
|
{
|
|
try
|
|
{
|
|
await engine.AbortAsync().ConfigureAwait(false);
|
|
await engine.StopAsync().ConfigureAwait(false);
|
|
}
|
|
catch (Exception problem)
|
|
{
|
|
log.Write($"the engine did not shut down cleanly: {problem.Message}");
|
|
}
|
|
engine.Dispose();
|
|
}
|
|
|
|
private static Dictionary<string, string> Read(string[] arguments)
|
|
{
|
|
Dictionary<string, string> options = [];
|
|
for (int i = 0; i < arguments.Length; i += 2)
|
|
{
|
|
if (!arguments[i].StartsWith("--", StringComparison.Ordinal))
|
|
{
|
|
throw new ArgumentException($"expected an option, found {arguments[i]}");
|
|
}
|
|
if (i + 1 >= arguments.Length)
|
|
{
|
|
throw new ArgumentException($"{arguments[i]} needs a value");
|
|
}
|
|
options[arguments[i][2..]] = arguments[i + 1];
|
|
}
|
|
if (!options.ContainsKey("engine"))
|
|
{
|
|
throw new ArgumentException("--engine is required");
|
|
}
|
|
return options;
|
|
}
|
|
|
|
private static string Option(Dictionary<string, string> options, string name, string fallback) =>
|
|
options.TryGetValue(name, out string? given) ? given : fallback;
|
|
}
|