Read a word in the other Baudot case

RTTY sends one shift character to say whether the codes that follow are
letters or figures. When it is lost to noise the printer stays in the wrong
case for the rest of the line, so 599 prints as TOO, 73 as UE and CQ TEST as
:1 53'5.

Baudot.LettersFigs swaps the case of a word using the US teleprinter table,
which is the one N1MM uses in DigitalMMTYIo.Letters_Figs.

N1MM converts a letter to its figure but not a figure back to its letter, so
its box turns VE4AEO into ;3R-39 and cannot turn it back. The table here is
read both ways, which is what MMTTY's own right click does: a call printed in
the wrong case is the case an operator has to read.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
This commit is contained in:
2026-09-01 00:03:01 +00:00
parent 14209e01e8
commit f536b1b6a1
2 changed files with 95 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
namespace Nonemm.Session;
/// The two cases of the Baudot alphabet RTTY sends. One shift character tells
/// the printer whether the five-bit codes that follow are letters or figures,
/// and a shift lost to noise prints a whole word in the wrong case: 599 comes
/// out as TOO, and CQ TEST as :1 53'5.
///
/// `LettersFigs` prints a word in the case it was not printed in, which is what
/// the digital window's Letters/Figs box shows for the word under the pointer.
/// The table is the US teleprinter one N1MM uses in
/// `DigitalMMTYIo.Letters_Figs`: a letter and the digit or punctuation mark on
/// the same key are a pair. J has no pair and is left as it is, and so is
/// anything outside the table.
///
/// N1MM converts a letter to its figure but not a figure back to its letter,
/// so its box turns VE4AEO into ;3R-39 but not ;3R-39 back into VE4AEO. The
/// table here is read both ways, which is what MMTTY's own right click does and
/// what the box is for: a call printed in the wrong case is the case an
/// operator has to read.
public static class Baudot
{
/// The letters in the order of the figures they share a key with, so A and
/// - are a pair and R and 4 are a pair.
private const string Letters = "ABCDEFGHIKLMNOPQRSTUVWXYZ";
private const string Figures = "-?:$3!&#8().,9014'57;2/6\"";
public static string LettersFigs(string text)
{
char[] swapped = new char[text.Length];
for (int at = 0; at < text.Length; at++)
{
swapped[at] = Swap(text[at]);
}
return new string(swapped);
}
private static char Swap(char c)
{
int figure = Figures.IndexOf(c);
if (figure >= 0)
{
return Letters[figure];
}
int letter = Letters.IndexOf(char.ToUpperInvariant(c));
return letter >= 0 ? Figures[letter] : c;
}
}

View File

@@ -0,0 +1,47 @@
using Nonemm.Session;
namespace Nonemm.Session.Tests;
/// The Letters/Figs box: a word printed in the case it was not printed in.
/// The examples are the ones in N1MM's manual.
public class BaudotTests
{
[Fact]
public void ALostFiguresShiftPrints599AsToo()
{
Assert.Equal("TOO", Baudot.LettersFigs("599"));
Assert.Equal("599", Baudot.LettersFigs("TOO"));
}
[Fact]
public void SeventyThreePrintsAsUe()
{
Assert.Equal("UE", Baudot.LettersFigs("73"));
}
[Fact]
public void ACallsignGoesToFiguresAndBack()
{
Assert.Equal(";3R-39", Baudot.LettersFigs("VE4AEO"));
Assert.Equal("VE4AEO", Baudot.LettersFigs(";3R-39"));
}
[Fact]
public void PunctuationCarriesTheSpacesAcross()
{
Assert.Equal(":1 53'5", Baudot.LettersFigs("CQ TEST"));
}
[Fact]
public void ASerialNumberComesBackFromLetters()
{
Assert.Equal("012", Baudot.LettersFigs("PQW"));
}
[Fact]
public void WhatTheTableHasNoPairForIsLeftAlone()
{
Assert.Equal("J", Baudot.LettersFigs("J"));
Assert.Equal("", Baudot.LettersFigs(""));
}
}