sdrangel/sdrbase/util/baudot.cpp

373 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE <jon@beniston.com> //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include "baudot.h"
// https://en.wikipedia.org/wiki/Baudot_code
// We use < for FIGS and > for LTRS and ^ for Cyrillic
// Unicode used for source file encoding
const QStringList Baudot::m_ita2Letter = {
"\0", "E", "\n", "A", " ", "S", "I", "U",
"\r", "D", "R", "J", "N", "F", "C", "K",
"T", "Z", "L", "W", "H", "Y", "P", "Q",
"O", "B", "G", "<", "M", "X", "V", ">"
};
const QStringList Baudot::m_ita2Figure = {
"\0", "3", "\n", "-", " ", "\'", "8", "7",
"\r", "\x5", "4", "\a", ",", "!", ":", "(",
"5", "+", ")", "2", "£", "6", "0", "1",
"9", "?", "&", "<", ".", "/", "=", ">"
};
const QStringList Baudot::m_ukLetter = {
"\0", "A", "E", "/", "Y", "U", "I", "O",
"<", "J", "G", "H", "B", "C", "F", "D",
" ", "-", "X", "Z", "S", "T", "W", "V",
"\b", "K", "M", "L", "R", "Q", "N", "P"
};
const QStringList Baudot::m_ukFigure = {
"\0", "1", "2", "", "3", "4", "³⁄", "5",
" ", "6", "7", "¹", "8", "9", "⁵⁄", "0",
">", ".", "⁹⁄", ":", "⁷⁄", "²", "?", "\'",
"\b", "(", ")", "=", "-", "/", "£", "+"
};
const QStringList Baudot::m_europeanLetter = {
"\0", "A", "E", "É", "Y", "U", "I", "O",
"<", "J", "G", "H", "B", "C", "F", "D",
" ", "t", "X", "Z", "S", "T", "W", "V",
"\b", "K", "M", "L", "R", "Q", "N", "P"
};
const QStringList Baudot::m_europeanFigure = {
"\0", "1", "2", "&", "3", "4", "º", "5",
" ", "6", "7", "", "8", "9", "", "0",
">", ".", ",", ":", ";", "!", "?", "\'",
"\b", "(", ")", "=", "-", "/", "", "%"
};
const QStringList Baudot::m_usLetter = {
"\0", "E", "\n", "A", " ", "S", "I", "U",
"\r", "D", "R", "J", "N", "F", "C", "K",
"T", "Z", "L", "W", "H", "Y", "P", "Q",
"O", "B", "G", "<", "M", "X", "V", ">"
};
const QStringList Baudot::m_usFigure = {
"\0", "3", "\n", "-", " ", "\a", "8", "7",
"\r", "\x5", "4", "\'", ",", "!", ":", "(",
"5", "\"", ")", "2", "#", "6", "0", "1",
"9", "?", "&", "<", ".", "/", ";", ">"
};
const QStringList Baudot::m_russianLetter = {
"\0", "Е", "\n", "А", " ", "С", "И", "У",
"\r", "Д", "Р", "Й", "Ч", "Ф", "Ц", "К",
"Т", "З", "Л", "В", "Х", "Ы", "П", "Я",
"О", "Б", "Г", "<", "М", "Ь", "Ж", ">"
};
const QStringList Baudot::m_russianFigure = {
"\0", "3", "\n", "-", " ", "\'", "8", "7",
"\r", "Ч", "4", "Ю", ",", "Э", ":", "(",
"5", "+", ")", "2", "Щ", "6", "0", "1",
"9", "?", "Ш", "<", ".", "/", ";", ">"
};
const QStringList Baudot::m_murrayLetter = {
" ", "E", "?", "A", ">", "S", "I", "U",
"\n", "D", "R", "J", "N", "F", "C", "K",
"T", "Z", "L", "W", "H", "Y", "P", "Q",
"O", "B", "G", "<", "M", "X", "V", "\b"
};
const QStringList Baudot::m_murrayFigure = {
" ", "3", "?", " ", ">", "'", "8", "7",
"\n", "²", "4", "⁷⁄", "-", "", "(", "⁹⁄",
"5", ".", "/", "2", "⁵⁄", "6", "0", "1",
"9", "?", "³⁄", "<", ",", "£", ")", "\b"
};
BaudotDecoder::BaudotDecoder()
{
setCharacterSet(Baudot::ITA2);
setUnshiftOnSpace(false);
init();
}
void BaudotDecoder::setCharacterSet(Baudot::CharacterSet characterSet)
{
m_characterSet = characterSet;
switch (m_characterSet)
{
case Baudot::ITA2:
m_letters = Baudot::m_ita2Letter;
m_figures = Baudot::m_ita2Figure;
break;
case Baudot::UK:
m_letters = Baudot::m_ukLetter;
m_figures = Baudot::m_ukFigure;
break;
case Baudot::EUROPEAN:
m_letters = Baudot::m_europeanLetter;
m_figures = Baudot::m_europeanFigure;
break;
case Baudot::US:
m_letters = Baudot::m_usLetter;
m_figures = Baudot::m_usFigure;
break;
case Baudot::RUSSIAN:
m_letters = Baudot::m_russianLetter;
m_figures = Baudot::m_russianFigure;
break;
case Baudot::MURRAY:
m_letters = Baudot::m_murrayLetter;
m_figures = Baudot::m_murrayFigure;
break;
default:
qDebug() << "BaudotDecoder::BaudotDecoder: Unsupported character set " << m_characterSet;
m_letters = Baudot::m_ita2Letter;
m_figures = Baudot::m_ita2Figure;
m_characterSet = Baudot::ITA2;
break;
}
}
void BaudotDecoder::setUnshiftOnSpace(bool unshiftOnSpace)
{
m_unshiftOnSpace = unshiftOnSpace;
}
void BaudotDecoder::init()
{
m_figure = false;
}
QString BaudotDecoder::decode(char bits)
{
QString c = m_figure ? m_figures[(int)bits] : m_letters[(int)bits];
if ((c == ">") || (m_unshiftOnSpace && (c == " ")))
{
// Switch to letters
m_figure = false;
if (m_characterSet == Baudot::RUSSIAN) {
m_letters = Baudot::m_ita2Letter;
}
}
if (c == "<")
{
// Switch to figures
m_figure = true;
}
if ((m_characterSet == Baudot::RUSSIAN) && (c == "\0"))
{
// Switch to Cyrillic
m_figure = false;
m_letters = Baudot::m_russianLetter;
c = "^";
}
return c;
}
BaudotEncoder::BaudotEncoder()
{
setCharacterSet(Baudot::ITA2);
setUnshiftOnSpace(false);
setMsbFirst(false);
setStartBits(1);
setStopBits(1);
init();
}
void BaudotEncoder::setCharacterSet(Baudot::CharacterSet characterSet)
{
m_characterSet = characterSet;
switch (m_characterSet)
{
case Baudot::ITA2:
m_chars[LETTERS] = Baudot::m_ita2Letter;
m_chars[FIGURES] = Baudot::m_ita2Figure;
break;
case Baudot::UK:
m_chars[LETTERS] = Baudot::m_ukLetter;
m_chars[FIGURES] = Baudot::m_ukFigure;
break;
case Baudot::EUROPEAN:
m_chars[LETTERS] = Baudot::m_europeanLetter;
m_chars[FIGURES] = Baudot::m_europeanFigure;
break;
case Baudot::US:
m_chars[LETTERS] = Baudot::m_usLetter;
m_chars[FIGURES] = Baudot::m_usFigure;
break;
case Baudot::RUSSIAN:
m_chars[LETTERS] = Baudot::m_ita2Letter;
m_chars[FIGURES] = Baudot::m_russianFigure;
break;
case Baudot::MURRAY:
m_chars[LETTERS] = Baudot::m_murrayLetter;
m_chars[FIGURES] = Baudot::m_murrayFigure;
break;
default:
qDebug() << "BaudotEncoder::BaudotEncoder: Unsupported character set " << m_characterSet;
m_chars[LETTERS] = Baudot::m_ita2Letter;
m_chars[FIGURES] = Baudot::m_ita2Figure;
m_characterSet = Baudot::ITA2;
break;
}
m_chars[(int)CYRILLIC] = Baudot::m_russianLetter;
}
void BaudotEncoder::setUnshiftOnSpace(bool unshiftOnSpace)
{
m_unshiftOnSpace = unshiftOnSpace;
}
void BaudotEncoder::setMsbFirst(bool msbFirst)
{
m_msbFirst = msbFirst;
}
// startBits should be 0 or 1
void BaudotEncoder::setStartBits(int startBits)
{
m_startBits = startBits;
}
// stopBits should be 0, 1 or 2
void BaudotEncoder::setStopBits(int stopBits)
{
m_stopBits = stopBits;
}
void BaudotEncoder::init()
{
m_page = LETTERS;
}
bool BaudotEncoder::encode(QChar c, unsigned &bits, unsigned int &bitCount)
{
bits = 0;
bitCount = 0;
// Only upper case is supported
c = c.toUpper();
QString s(c);
if (s == '>')
{
addCode(bits, bitCount, m_chars[m_page].indexOf(s));
m_page = LETTERS;
return true;
}
else if (s == '<')
{
addCode(bits, bitCount, m_chars[m_page].indexOf(s));
m_page = FIGURES;
return true;
}
else if ((m_characterSet == Baudot::RUSSIAN) && (s == '\0'))
{
addCode(bits, bitCount, m_chars[m_page].indexOf(s));
m_page = CYRILLIC;
return true;
}
// We could create reverse look-up tables to speed this up, but it's only 200 baud...
// Is character in current page? If so, use that, as it avoids switching
if (m_chars[m_page].contains(s))
{
addCode(bits, bitCount, m_chars[m_page].indexOf(s));
return true;
}
else
{
// Look for character in other pages
const QString switchPage[] = { ">", "<", "\0" };
for (int page = m_page == LETTERS ? 1 : 0; page < ((m_characterSet == Baudot::RUSSIAN) ? 3 : 2); page++)
{
if (m_chars[page].contains(s))
{
// Switch to page
addCode(bits, bitCount, m_chars[m_page].indexOf(switchPage[page]));
m_page = (BaudotEncoder::Page)page;
addCode(bits, bitCount, m_chars[m_page].indexOf(s));
return true;
}
}
}
return false;
}
void BaudotEncoder::addCode(unsigned& bits, unsigned int& bitCount, unsigned int code) const
{
const unsigned int codeLen = 5;
addStartBits(bits, bitCount);
code = reverseBits(code, codeLen);
addBits(bits, bitCount, code, codeLen);
addStopBits(bits, bitCount);
}
void BaudotEncoder::addStartBits(unsigned& bits, unsigned int& bitCount) const
{
// Start bit is 0
addBits(bits, bitCount, 0, m_startBits);
}
void BaudotEncoder::addStopBits(unsigned& bits, unsigned int& bitCount) const
{
// Stop bit is 1
addBits(bits, bitCount, ((1 << m_stopBits)) - 1, m_stopBits);
}
void BaudotEncoder::addBits(unsigned& bits, unsigned int& bitCount, int data, int count) const
{
bits |= data << bitCount;
bitCount += count;
}
unsigned BaudotEncoder::reverseBits(unsigned bits, unsigned int count) const
{
if (m_msbFirst) {
return BaudotEncoder::reverse(bits) >> (sizeof(unsigned int) * 8 - count);
} else {
return bits;
}
}
unsigned int BaudotEncoder::reverse(unsigned int x)
{
x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
return((x >> 16) | (x << 16));
}