Read a QTC series out on RTTY
N1MM's Send All: the whole series as one message — the heading, every
line with the operator's spacing between them, then the ending, with
`{TX}` and `{RX}` around it so it keys the transmitter and drops it
again. The shape is N1MM's, from `QTCWindow.cs:3609`. A line is the three
fields joined with hyphens, which is how N1MM writes them, and Snd n
sends one line again for a station that missed it.
One message rather than a button per line because a series is close to a
minute of transmission at 45 baud: the type-ahead buffer holds it and
paces the engine.
Three settings with N1MM's names and defaults — `WAESendAllHeadingText`,
`WAESendAllEndingText` and `WAESQTCSpacing` — on the QTC window's setup
dialog. `{ENTERLF}` is new to the expander and stands for a carriage
return and a line feed; `{QTC}` is filled in by the window, which is the
only place that knows which series is going out.
The messages are tested as text. Nothing has gone on the air.
Left out: N1MM's four SSB recordings, which need a voice keyer, and its
RTTY messages for the station taking traffic down, which are typed into
the digital window's transmit pane here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PdAYHcdRktqKry7nk414TU
This commit is contained in:
@@ -355,13 +355,29 @@ of an SO2R station cannot call CQ by voice. Alternating CQ therefore works on CW
|
||||
only, though nothing in it is CW-specific: a voice keyer that reports when the
|
||||
recording has finished would drive it as it stands.
|
||||
|
||||
**The QTC window transmits on CW only.** The header, the lines, the QRV, the TU
|
||||
and the again messages go out through the keyer, with N1MM's messages and
|
||||
N1MM's defaults. Nothing goes out on SSB or RTTY: N1MM plays four recordings on
|
||||
SSB — QRV, Agn, Cfm and TU — and sends RTTY from its digital window, and this
|
||||
program has neither a voice keyer nor a digital window. N1MM's Send All, which
|
||||
keys a whole series at once, belongs to that RTTY window and is not here
|
||||
either.
|
||||
**The QTC window sends on CW and RTTY, not on SSB.** On CW the header, the
|
||||
lines, the QRV, the TU and the again messages go out through the keyer, with
|
||||
N1MM's messages and N1MM's defaults.
|
||||
|
||||
On RTTY the station reading traffic out has Send All, which is N1MM's: the whole
|
||||
series as one message, the heading, then every line with the operator's spacing
|
||||
between them, then the ending, and `{TX}` and `{RX}` around the lot so it keys
|
||||
the transmitter and drops it again. A line is the three fields joined with
|
||||
hyphens, which is how N1MM writes them and it offers no setting for it. Snd n
|
||||
sends one line again for a station that missed it. The heading, the ending and
|
||||
the spacing are settings, N1MM's `WAESendAllHeadingText`,
|
||||
`WAESendAllEndingText` and `WAESQTCSpacing`, with its defaults; `{ENTERLF}` in
|
||||
them is new to the expander and stands for a carriage return and a line feed.
|
||||
|
||||
A whole series is the best part of a minute of transmission at 45 baud, which is
|
||||
why it is one message rather than a button per line: the type-ahead buffer holds
|
||||
it and paces the engine, and the operator presses one button. This has been
|
||||
tested as text — what the message comes out as — and not on the air.
|
||||
|
||||
What is not here: N1MM's four SSB recordings — QRV, Agn, Cfm and TU — which need
|
||||
a voice keyer, and its RTTY messages for the station taking traffic down, which
|
||||
are `WAERXReadyText`, `WAEAllAgnText`, `WAEAGNText` and `WAESaveQTCText`. Those
|
||||
four are typed into the digital window's transmit pane here.
|
||||
|
||||
**Cut numbers are not sent.** N1MM can key a serial number as letters — `N` for
|
||||
9, `T` for 0 — and offers several styles. Nothing here does, in a QTC line or in
|
||||
@@ -411,9 +427,11 @@ ones the station's log needs — and both of N1MM's names open a log with it. A
|
||||
sprint log would be scored by the wrong rules, and a log started here writes
|
||||
`BARTGRTTYS` into the contest name, which N1MM would read as the sprint.
|
||||
|
||||
**Digital modes.** A contact can be logged as RTTY or another digital mode, and
|
||||
the contest rules score it, but there is no digital window: no decoding, no
|
||||
transmitting, no interface to fldigi, MMTTY or similar.
|
||||
**Digital interfaces other than MMTTY.** A contact can be logged as RTTY or
|
||||
another digital mode and the contest rules score it, and the digital window
|
||||
decodes and transmits through MMTTY over the Wine bridge. What is not here:
|
||||
fldigi, MMVARI, 2Tone and the hardware TNCs, all of which N1MM drives.
|
||||
`DigitalEngine` is the place to add them.
|
||||
|
||||
**Contest coverage.** Twenty-five families are built in, plus whatever `.udc` files
|
||||
are in the user-defined folder. N1MM ships well over a hundred. Opening a log
|
||||
|
||||
@@ -169,6 +169,16 @@ public sealed record Settings
|
||||
|
||||
public string QtcCwTu { get; init; } = "";
|
||||
|
||||
/// The RTTY messages the QTC window sends, with N1MM's defaults. The
|
||||
/// spacing goes between the lines of a series rather than between the
|
||||
/// fields of one, and all three are macro templates: `{ENTER}` for a
|
||||
/// carriage return, `{QTC}` for the header of the series.
|
||||
public string QtcRttySpacing { get; init; } = QtcMessages.DefaultRttySpacing;
|
||||
|
||||
public string QtcRttySendAllHeading { get; init; } = QtcMessages.DefaultSendAllHeading;
|
||||
|
||||
public string QtcRttySendAllEnding { get; init; } = QtcMessages.DefaultSendAllEnding;
|
||||
|
||||
/// Sub-band boundaries the operator has changed. Empty means the defaults
|
||||
/// in `BandPlan.Default`; an entry replaces one band's boundaries.
|
||||
public IReadOnlyList<StoredSubBand> SubBands { get; init; } = [];
|
||||
|
||||
@@ -33,8 +33,19 @@
|
||||
</Grid>
|
||||
<TextBlock Text="The spacing goes between the fields of a QTC line, written N1MM's way with S for a space. The three again messages go out on shift and Enter in that box while taking traffic down. TU goes out when the window closes after reading a series out; empty sends nothing."
|
||||
FontSize="11" Opacity="0.7" TextWrapping="Wrap" Margin="0,-2,0,0" />
|
||||
<TextBlock Text="RTTY messages" FontWeight="Bold" Margin="0,10,0,0" />
|
||||
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto">
|
||||
<TextBlock Text="Line spacing" VerticalAlignment="Center" Margin="0,2,8,2" />
|
||||
<TextBox Grid.Column="1" Name="RttySpacingBox" Margin="0,2" />
|
||||
<TextBlock Grid.Row="1" Text="Send All heading" VerticalAlignment="Center" Margin="0,2,8,2" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Name="SendAllHeadingBox" Margin="0,2" />
|
||||
<TextBlock Grid.Row="2" Text="Send All ending" VerticalAlignment="Center" Margin="0,2,8,2" />
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Name="SendAllEndingBox" Margin="0,2" />
|
||||
</Grid>
|
||||
<TextBlock Text="These go out when the All button reads a whole series out on RTTY. The spacing goes between the lines, not between the fields of one — use {ENTER} or {ENTERLF} for a carriage return. {QTC} stands for the header of the series, and the other function key macros work as well."
|
||||
FontSize="11" Opacity="0.7" TextWrapping="Wrap" Margin="0,-2,0,0" />
|
||||
<TextBlock Name="MissingText" FontSize="11" Opacity="0.7" TextWrapping="Wrap" Margin="0,6,0,0"
|
||||
Text="N1MM's SSB recordings and RTTY message templates are left out: sending those needs a voice keyer and a digital window, and this program has neither." />
|
||||
Text="N1MM's SSB recordings are left out: playing those needs a voice keyer, and this program has none. So are its RTTY messages for the station taking traffic down — RX Ready, All Agn, Agn and Save — which are typed into the digital window's transmit pane here." />
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="6" Margin="0,8,0,0">
|
||||
<Button Content="Cancel" Click="OnCancel" />
|
||||
<Button Content="Save" Click="OnSave" IsDefault="True" />
|
||||
|
||||
@@ -24,6 +24,9 @@ public sealed partial class QtcSetupDialog : Window
|
||||
CallAgainBox.Text = settings.QtcCwCallAgain;
|
||||
NumberAgainBox.Text = settings.QtcCwNumberAgain;
|
||||
TuBox.Text = settings.QtcCwTu;
|
||||
RttySpacingBox.Text = settings.QtcRttySpacing;
|
||||
SendAllHeadingBox.Text = settings.QtcRttySendAllHeading;
|
||||
SendAllEndingBox.Text = settings.QtcRttySendAllEnding;
|
||||
}
|
||||
|
||||
private void OnSave(object? sender, RoutedEventArgs e) => Close(settings with
|
||||
@@ -40,6 +43,9 @@ public sealed partial class QtcSetupDialog : Window
|
||||
QtcCwCallAgain = CallAgainBox.Text ?? "",
|
||||
QtcCwNumberAgain = NumberAgainBox.Text ?? "",
|
||||
QtcCwTu = TuBox.Text ?? "",
|
||||
QtcRttySpacing = RttySpacingBox.Text ?? "",
|
||||
QtcRttySendAllHeading = SendAllHeadingBox.Text ?? "",
|
||||
QtcRttySendAllEnding = SendAllEndingBox.Text ?? "",
|
||||
});
|
||||
|
||||
private void OnCancel(object? sender, RoutedEventArgs e) => Close(null);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<Button Name="ReadyButton" Content="RX Ready" Click="OnReady" />
|
||||
<Button Name="HeaderAgainButton" Content="Hdr Agn" Click="OnHeaderAgain" />
|
||||
<Button Name="HeaderCfmButton" Content="Cfm" Click="OnHeaderConfirm" />
|
||||
<Button Name="SendAllButton" Content="Send All" Click="OnSendAll" />
|
||||
</StackPanel>
|
||||
<Grid Name="Lines" Margin="0,6,0,0" />
|
||||
<TextBlock Name="KeysText" FontSize="11" Opacity="0.7" Margin="0,6,0,0" />
|
||||
|
||||
@@ -19,8 +19,10 @@ namespace Nonemm.App.Windows;
|
||||
/// the log and cannot be edited — what is being reported is what was worked.
|
||||
///
|
||||
/// On CW it puts the traffic on the air through the entry window's keyer, the
|
||||
/// way N1MM's QTC window sends through its own entry window. On SSB and RTTY
|
||||
/// nothing is sent: one needs a voice keyer and the other a digital window.
|
||||
/// way N1MM's QTC window sends through its own entry window. On RTTY it sends
|
||||
/// through the digital engine the same way, but a line at a time rather than a
|
||||
/// field at a time, and Send All reads the whole series out in one message. On
|
||||
/// SSB nothing is sent: that needs a voice keyer, and there is none here.
|
||||
public sealed partial class QtcWindow : Window
|
||||
{
|
||||
private static IBrush Saved => Themes.Brush(Themes.Current.GoodBackground);
|
||||
@@ -83,7 +85,9 @@ public sealed partial class QtcWindow : Window
|
||||
if (!IsCw)
|
||||
{
|
||||
return isSending
|
||||
? $"{traffic.Remaining(station)} of the ten QTCs for {station.Text} are still free"
|
||||
? IsRtty
|
||||
? "Send All reads the whole series out — Snd n sends one QTC again"
|
||||
: $"{traffic.Remaining(station)} of the ten QTCs for {station.Text} are still free"
|
||||
: "Type the header, then a line per QTC: time, callsign, serial number.";
|
||||
}
|
||||
return isSending
|
||||
@@ -91,10 +95,17 @@ public sealed partial class QtcWindow : Window
|
||||
: "Shift 1 = ask time Shift 2 = ask call Shift 3 = ask serial";
|
||||
}
|
||||
|
||||
/// Traffic goes out on CW alone. The contest says which mode it is, not
|
||||
/// what the radio happens to be on, which is how N1MM decides too.
|
||||
/// Which mode the traffic goes out on. The contest says, not what the radio
|
||||
/// happens to be on, which is how N1MM decides too.
|
||||
private bool IsCw => position.Contest.Modes is [ModeCategory.Cw];
|
||||
|
||||
private bool IsRtty => position.Contest.Modes.Contains(ModeCategory.Digital);
|
||||
|
||||
/// The header of the series as it goes out in a message, which is N1MM's
|
||||
/// `QTC 3/10`. The box shows the station callsign after it on RTTY, and
|
||||
/// that is for the operator rather than for the air.
|
||||
private string SeriesHeader => ready.Count > 0 ? $"QTC {ready[0].SeriesText}" : "";
|
||||
|
||||
private void SetDirection(bool sending)
|
||||
{
|
||||
if (sending == isSending)
|
||||
@@ -118,6 +129,9 @@ public sealed partial class QtcWindow : Window
|
||||
HeaderAgainButton.Content = isSending && IsCw ? "Snd Hdr" : "Hdr Agn";
|
||||
HeaderAgainButton.IsVisible = !isSending || IsCw;
|
||||
HeaderCfmButton.IsVisible = !isSending;
|
||||
// N1MM's Send All, which reads a whole series out in one message. It
|
||||
// needs the engine to hold the text, so it is RTTY only
|
||||
SendAllButton.IsVisible = isSending && IsRtty;
|
||||
ClearButton.IsVisible = !isSending;
|
||||
CloseButton.Content = IsCw ? "Exit" : "Close";
|
||||
HeaderBox.IsReadOnly = isSending;
|
||||
@@ -144,7 +158,7 @@ public sealed partial class QtcWindow : Window
|
||||
TextBox number = Box(at, 2);
|
||||
Button again = new()
|
||||
{
|
||||
Content = isSending && IsCw ? $"Snd{at + 1}" : $"Agn{at + 1}",
|
||||
Content = isSending && (IsCw || IsRtty) ? $"Snd{at + 1}" : $"Agn{at + 1}",
|
||||
Margin = new Avalonia.Thickness(2, 1, 2, 1),
|
||||
};
|
||||
Button confirm = new() { Content = $"Cfm{at + 1}", Margin = new Avalonia.Thickness(2, 1, 2, 1) };
|
||||
@@ -195,13 +209,11 @@ public sealed partial class QtcWindow : Window
|
||||
return;
|
||||
}
|
||||
ready.AddRange(traffic.ToSend(station, session.Settings.QtcLinesPerSeries));
|
||||
// the contest is what makes it RTTY, not whatever the radio is on now
|
||||
bool rtty = position.Contest.Modes.Contains(ModeCategory.Digital);
|
||||
HeaderBox.Text = ready.Count == 0
|
||||
? ""
|
||||
: rtty
|
||||
? $"QTC {ready[0].SeriesText} - {station.Text}"
|
||||
: $"QTC {ready[0].SeriesText}";
|
||||
: IsRtty
|
||||
? $"{SeriesHeader} - {station.Text}"
|
||||
: SeriesHeader;
|
||||
for (int at = 0; at < rows.Count; at++)
|
||||
{
|
||||
bool has = at < ready.Count;
|
||||
@@ -353,7 +365,7 @@ public sealed partial class QtcWindow : Window
|
||||
{
|
||||
if (isSending)
|
||||
{
|
||||
if (IsCw)
|
||||
if (IsCw || IsRtty)
|
||||
{
|
||||
SendLine(at);
|
||||
return;
|
||||
@@ -375,11 +387,13 @@ public sealed partial class QtcWindow : Window
|
||||
private void SendLine(int at)
|
||||
{
|
||||
lastSent = at;
|
||||
_ = send(QtcMessages.Line(
|
||||
rows[at].Time.Text ?? "",
|
||||
rows[at].Call.Text ?? "",
|
||||
rows[at].Number.Text ?? "",
|
||||
session.Settings.QtcCwFieldSpacing));
|
||||
_ = send(IsRtty
|
||||
? QtcMessages.SendOne(session.Settings.QtcRttySpacing, RttyLine(at))
|
||||
: QtcMessages.Line(
|
||||
rows[at].Time.Text ?? "",
|
||||
rows[at].Call.Text ?? "",
|
||||
rows[at].Number.Text ?? "",
|
||||
session.Settings.QtcCwFieldSpacing));
|
||||
for (int next = at + 1; next < rows.Count; next++)
|
||||
{
|
||||
if (rows[next].Again.IsEffectivelyEnabled)
|
||||
@@ -391,6 +405,43 @@ public sealed partial class QtcWindow : Window
|
||||
CloseButton.Focus();
|
||||
}
|
||||
|
||||
private string RttyLine(int at) => QtcMessages.RttyLine(
|
||||
rows[at].Time.Text ?? "",
|
||||
rows[at].Call.Text ?? "",
|
||||
rows[at].Number.Text ?? "");
|
||||
|
||||
/// N1MM's Send All: the whole series in one message. RTTY runs at 45 baud,
|
||||
/// so a series is the best part of a minute of transmission, and sending it
|
||||
/// a line at a time means the operator presses a button between each one
|
||||
/// while the transmitter is up. The heading, the spacing between the lines
|
||||
/// and the ending are the operator's, and the message keys the transmitter
|
||||
/// and drops it again itself.
|
||||
private void OnSendAll(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
List<string> lines = [];
|
||||
for (int at = 0; at < rows.Count; at++)
|
||||
{
|
||||
if (!rows[at].IsEmpty)
|
||||
{
|
||||
lines.Add(RttyLine(at));
|
||||
}
|
||||
}
|
||||
string message = QtcMessages.SendAll(
|
||||
session.Settings.QtcRttySendAllHeading,
|
||||
session.Settings.QtcRttySendAllEnding,
|
||||
session.Settings.QtcRttySpacing,
|
||||
SeriesHeader,
|
||||
lines);
|
||||
if (message.Length == 0)
|
||||
{
|
||||
StatusText.Text = "nothing to send";
|
||||
return;
|
||||
}
|
||||
lastSent = lines.Count - 1;
|
||||
_ = send(message);
|
||||
CloseButton.Focus();
|
||||
}
|
||||
|
||||
/// 1, 2, 3 and 4 as N1MM numbers them: the time, the call, the serial
|
||||
/// number and the header.
|
||||
private static int FieldOf(Key key) =>
|
||||
|
||||
@@ -113,6 +113,9 @@ public static class MessageExpander
|
||||
// the digital window's carriage return, which starts a new line on
|
||||
// the other station's screen
|
||||
"ENTER" => "\r",
|
||||
// the same with a line feed behind it, which is what N1MM's own
|
||||
// RTTY message defaults are written with
|
||||
"ENTERLF" => "\r\n",
|
||||
_ => null,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Nonemm.Session;
|
||||
|
||||
/// What a station sends on CW while exchanging QTC traffic, and N1MM's
|
||||
/// defaults for the parts an operator can change.
|
||||
/// What a station sends while exchanging QTC traffic, and N1MM's defaults for
|
||||
/// the parts an operator can change.
|
||||
///
|
||||
/// CW only. N1MM sends recorded voice messages on SSB and its digital window's
|
||||
/// templates on RTTY, and this program has neither.
|
||||
/// CW and RTTY. The two are laid out differently: on CW a line is the three
|
||||
/// fields with the operator's spacing between them and each one is keyed by
|
||||
/// hand, and on RTTY the fields are joined with hyphens and the whole series
|
||||
/// goes out as one message. N1MM plays recordings on SSB, and there is no voice
|
||||
/// keyer here.
|
||||
public static class QtcMessages
|
||||
{
|
||||
/// The gap between the fields of a QTC line, written the way N1MM writes
|
||||
@@ -28,6 +33,59 @@ public static class QtcMessages
|
||||
/// What the station reading traffic out asks before it starts.
|
||||
public const string AreYouReady = "QRV?";
|
||||
|
||||
/// What goes between the QTC lines on RTTY. N1MM keeps it as a macro rather
|
||||
/// than as characters, so an operator can put a carriage return there,
|
||||
/// which is what its default does.
|
||||
public const string DefaultRttySpacing = "{ENTER}";
|
||||
|
||||
/// What stands in front of the series and what closes it. `{QTC}` stands
|
||||
/// for the header, so the default reads the series out twice for a station
|
||||
/// that missed it the first time.
|
||||
public const string DefaultSendAllHeading = "{ENTERLF}{QTC} {QTC}";
|
||||
|
||||
public const string DefaultSendAllEnding = "{ENTERLF}QSL?? BK DE {MYCALL} K";
|
||||
|
||||
/// One line of traffic on RTTY: the three fields joined with hyphens. N1MM
|
||||
/// writes them this way and offers no setting for it, so neither does this.
|
||||
public static string RttyLine(string time, string call, string number) =>
|
||||
$"{time.Trim()}-{call.Trim()}-{number.Trim()}";
|
||||
|
||||
/// A whole series as one message, which is N1MM's Send All: the heading,
|
||||
/// then every line, then the ending, with `spacing` between them. It keys
|
||||
/// the transmitter itself and drops it at the end, so the operator presses
|
||||
/// one button and the series goes out.
|
||||
///
|
||||
/// The text macros in it are left as they stand — `{ENTER}`, `{MYCALL}` and
|
||||
/// the rest are filled in by the message expander, the same as in a
|
||||
/// function key. `{QTC}` is filled in here, because only this window knows
|
||||
/// which series is going out.
|
||||
///
|
||||
/// A series with no lines in it sends nothing.
|
||||
public static string SendAll(
|
||||
string heading,
|
||||
string ending,
|
||||
string spacing,
|
||||
string header,
|
||||
IReadOnlyList<string> lines)
|
||||
{
|
||||
if (lines.Count == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
StringBuilder text = new();
|
||||
text.Append("{TX}").Append(WithHeader(heading, header)).Append(spacing);
|
||||
foreach (string line in lines)
|
||||
{
|
||||
text.Append(line).Append(' ').Append(spacing);
|
||||
}
|
||||
return text.Append(WithHeader(ending, header)).Append("{RX}").ToString();
|
||||
}
|
||||
|
||||
/// One line of a series on its own, for a station that asks for it again.
|
||||
/// It keys and drops the transmitter the same way Send All does.
|
||||
public static string SendOne(string spacing, string line) =>
|
||||
$"{{TX}}{spacing}{line}{spacing}{{RX}}";
|
||||
|
||||
public static string Spacing(string setting) =>
|
||||
setting.Replace("S", " ", StringComparison.Ordinal);
|
||||
|
||||
@@ -43,6 +101,11 @@ public static class QtcMessages
|
||||
/// The message that ends the exchange, sent whichever way the traffic went.
|
||||
/// `{QTC}` in it stands for the header of the series, so `TU {QTC} 73`
|
||||
/// goes out as `TU QTC 3/10 73`.
|
||||
public static string Tu(string template, string header) =>
|
||||
public static string Tu(string template, string header) => WithHeader(template, header);
|
||||
|
||||
/// `{QTC}` filled in with the header of the series. It is filled in here
|
||||
/// rather than by the message expander because only the QTC window knows
|
||||
/// which series is going out.
|
||||
private static string WithHeader(string template, string header) =>
|
||||
template.Replace("{QTC}", header.Trim().ToUpperInvariant(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
@@ -100,6 +100,14 @@ public class MessageExpanderTests
|
||||
public void WithOneRadioTheOtherMacrosStandForNothing() =>
|
||||
Assert.Equal("", MessageExpander.Expand("{OTHERFREQ}{OTHERMHZ}{OTHERBAND}", Session()));
|
||||
|
||||
/// The two carriage return macros a digital message is written with. N1MM's
|
||||
/// own RTTY message defaults use `{ENTERLF}`.
|
||||
[Fact]
|
||||
public void TheCarriageReturnMacrosAreTheCharactersTheyStandFor()
|
||||
{
|
||||
Assert.Equal("A\rB\r\nC", MessageExpander.Expand("A{ENTER}B{ENTERLF}C", Session()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TheNameComesFromTheCallHistoryWhenTheContestHasNoNameBox()
|
||||
{
|
||||
|
||||
@@ -37,6 +37,57 @@ public class QtcMessagesTests
|
||||
Assert.Equal("TU 73", QtcMessages.Tu("TU 73", "QTC 3/10"));
|
||||
}
|
||||
|
||||
/// N1MM joins the three fields with hyphens on RTTY and offers no setting
|
||||
/// for it.
|
||||
[Fact]
|
||||
public void ARttyLineIsTheThreeFieldsJoinedWithHyphens()
|
||||
{
|
||||
Assert.Equal("1234-DL1ABC-123", QtcMessages.RttyLine(" 1234 ", " DL1ABC ", " 123 "));
|
||||
}
|
||||
|
||||
/// The shape is N1MM's, from `QTCWindow.cs:3609`: the heading, the spacing,
|
||||
/// then every line with a space and the spacing behind it, then the ending.
|
||||
/// The macros are left for the expander; `{QTC}` is the one filled in here.
|
||||
[Fact]
|
||||
public void SendAllReadsTheWholeSeriesOutInOneMessage()
|
||||
{
|
||||
string message = QtcMessages.SendAll(
|
||||
QtcMessages.DefaultSendAllHeading,
|
||||
QtcMessages.DefaultSendAllEnding,
|
||||
QtcMessages.DefaultRttySpacing,
|
||||
"QTC 3/10",
|
||||
["1234-DL1ABC-123", "1240-G3XYZ-124"]);
|
||||
|
||||
Assert.Equal(
|
||||
"{TX}{ENTERLF}QTC 3/10 QTC 3/10{ENTER}"
|
||||
+ "1234-DL1ABC-123 {ENTER}"
|
||||
+ "1240-G3XYZ-124 {ENTER}"
|
||||
+ "{ENTERLF}QSL?? BK DE {MYCALL} K{RX}",
|
||||
message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SendAllWithNoLinesSendsNothing()
|
||||
{
|
||||
Assert.Equal(
|
||||
"",
|
||||
QtcMessages.SendAll(
|
||||
QtcMessages.DefaultSendAllHeading,
|
||||
QtcMessages.DefaultSendAllEnding,
|
||||
QtcMessages.DefaultRttySpacing,
|
||||
"QTC 3/10",
|
||||
[]));
|
||||
}
|
||||
|
||||
/// One line asked for again, which N1MM wraps in the spacing on both sides.
|
||||
[Fact]
|
||||
public void OneLineKeysAndDropsTheTransmitterOfItsOwn()
|
||||
{
|
||||
Assert.Equal(
|
||||
"{TX}{ENTER}1234-DL1ABC-123{ENTER}{RX}",
|
||||
QtcMessages.SendOne(QtcMessages.DefaultRttySpacing, "1234-DL1ABC-123"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WhatIsInTheBoxesIsTrimmed()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user