sdrangel/sdrbase/util/baudot.cpp

373 lines
12 KiB
C++
Raw Normal View History

2023-03-03 11:14:09 -05:00
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE <jon@beniston.com> //
2023-03-03 11:14:09 -05:00
// //
// 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
2023-09-01 12:10:26 -04:00
const QStringList Baudot::m_ita2Letter = {
2023-03-03 11:14:09 -05:00
"\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", ">"
};
2023-09-01 12:10:26 -04:00
const QStringList Baudot::m_ita2Figure = {
2023-03-03 11:14:09 -05:00
"\0", "3", "\n", "-", " ", "\'", "8", "7",
"\r", "\x5", "4", "\a", ",", "!", ":", "(",
"5", "+", ")", "2", "£", "6", "0", "1",
"9", "?", "&", "<", ".", "/", "=", ">"
};
2023-09-01 12:10:26 -04:00
const QStringList Baudot::m_ukLetter = {
2023-03-03 11:14:09 -05:00
"\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"
};
2023-09-01 12:10:26 -04:00
const QStringList Baudot::m_ukFigure = {
2023-03-03 11:14:09 -05:00
"\0", "1", "2", "", "3", "4", "³⁄", "5",
" ", "6", "7", "¹", "8", "9", "⁵⁄", "0",
">", ".", "⁹⁄", ":", "⁷⁄", "²", "?", "\'",
"\b", "(", ")", "=", "-", "/", "£", "+"
};
2023-09-01 12:10:26 -04:00
const QStringList Baudot::m_europeanLetter = {
2023-03-03 11:14:09 -05:00
"\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"
};
2023-09-01 12:10:26 -04:00
const QStringList Baudot::m_europeanFigure = {
2023-03-03 11:14:09 -05:00
"\0", "1", "2", "&", "3", "4", "º", "5",
" ", "6", "7", "", "8", "9", "", "0",
">", ".", ",", ":", ";", "!", "?", "\'",
"\b", "(", ")", "=", "-", "/", "", "%"
};
2023-09-01 12:10:26 -04:00
const QStringList Baudot::m_usLetter = {
2023-03-03 11:14:09 -05:00
"\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", ">"
};
2023-09-01 12:10:26 -04:00
const QStringList Baudot::m_usFigure = {
2023-03-03 11:14:09 -05:00
"\0", "3", "\n", "-", " ", "\a", "8", "7",
"\r", "\x5", "4", "\'", ",", "!", ":", "(",
"5", "\"", ")", "2", "#", "6", "0", "1",
"9", "?", "&", "<", ".", "/", ";", ">"
};
2023-09-01 12:10:26 -04:00
const QStringList Baudot::m_russianLetter = {
2023-03-03 11:14:09 -05:00
"\0", "Е", "\n", "А", " ", "С", "И", "У",
2023-09-03 04:02:53 -04:00
"\r", "Д", "Р", "Й", "Ч", "Ф", "Ц", "К",
"Т", "З", "Л", "В", "Х", "Ы", "П", "Я",
2023-03-03 11:14:09 -05:00
"О", "Б", "Г", "<", "М", "Ь", "Ж", ">"
};
2023-09-01 12:10:26 -04:00
const QStringList Baudot::m_russianFigure = {
2023-03-03 11:14:09 -05:00
"\0", "3", "\n", "-", " ", "\'", "8", "7",
"\r", "Ч", "4", "Ю", ",", "Э", ":", "(",
"5", "+", ")", "2", "Щ", "6", "0", "1",
"9", "?", "Ш", "<", ".", "/", ";", ">"
};
2023-09-01 12:10:26 -04:00
const QStringList Baudot::m_murrayLetter = {
2023-03-03 11:14:09 -05:00
" ", "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"
};
2023-09-01 12:10:26 -04:00
const QStringList Baudot::m_murrayFigure = {
2023-03-03 11:14:09 -05:00
" ", "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)
{
2023-03-03 11:46:03 -05:00
QString c = m_figure ? m_figures[(int)bits] : m_letters[(int)bits];
2023-03-03 11:14:09 -05:00
if ((c == ">") || (m_unshiftOnSpace && (c == " ")))
2023-03-03 11:14:09 -05:00
{
// Switch to letters
m_figure = false;
if (m_characterSet == Baudot::RUSSIAN) {
m_letters = Baudot::m_ita2Letter;
}
}
if (c == "<")
2023-03-03 11:14:09 -05:00
{
// Switch to figures
m_figure = true;
}
if ((m_characterSet == Baudot::RUSSIAN) && (c == "\0"))
2023-03-03 11:14:09 -05:00
{
// Switch to Cyrillic
m_figure = false;
m_letters = Baudot::m_russianLetter;
c = "^";
2023-03-03 11:14:09 -05:00
}
return c;
}
2023-09-01 12:10:26 -04:00
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:
2023-09-03 04:02:53 -04:00
m_chars[LETTERS] = Baudot::m_ita2Letter;
m_chars[FIGURES] = Baudot::m_ita2Figure;
2023-09-01 12:10:26 -04:00
break;
case Baudot::UK:
2023-09-03 04:02:53 -04:00
m_chars[LETTERS] = Baudot::m_ukLetter;
m_chars[FIGURES] = Baudot::m_ukFigure;
2023-09-01 12:10:26 -04:00
break;
case Baudot::EUROPEAN:
2023-09-03 04:02:53 -04:00
m_chars[LETTERS] = Baudot::m_europeanLetter;
m_chars[FIGURES] = Baudot::m_europeanFigure;
2023-09-01 12:10:26 -04:00
break;
case Baudot::US:
2023-09-03 04:02:53 -04:00
m_chars[LETTERS] = Baudot::m_usLetter;
m_chars[FIGURES] = Baudot::m_usFigure;
2023-09-01 12:10:26 -04:00
break;
case Baudot::RUSSIAN:
2023-09-03 04:02:53 -04:00
m_chars[LETTERS] = Baudot::m_ita2Letter;
m_chars[FIGURES] = Baudot::m_russianFigure;
2023-09-01 12:10:26 -04:00
break;
case Baudot::MURRAY:
2023-09-03 04:02:53 -04:00
m_chars[LETTERS] = Baudot::m_murrayLetter;
m_chars[FIGURES] = Baudot::m_murrayFigure;
2023-09-01 12:10:26 -04:00
break;
default:
qDebug() << "BaudotEncoder::BaudotEncoder: Unsupported character set " << m_characterSet;
2023-09-03 04:02:53 -04:00
m_chars[LETTERS] = Baudot::m_ita2Letter;
m_chars[FIGURES] = Baudot::m_ita2Figure;
2023-09-01 12:10:26 -04:00
m_characterSet = Baudot::ITA2;
break;
}
2023-09-03 04:02:53 -04:00
m_chars[(int)CYRILLIC] = Baudot::m_russianLetter;
2023-09-01 12:10:26 -04:00
}
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()
{
2023-09-03 04:02:53 -04:00
m_page = LETTERS;
2023-09-01 12:10:26 -04:00
}
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);
2023-09-03 04:02:53 -04:00
if (s == '>')
2023-09-01 12:10:26 -04:00
{
2023-09-03 04:02:53 -04:00
addCode(bits, bitCount, m_chars[m_page].indexOf(s));
m_page = LETTERS;
2023-09-01 12:10:26 -04:00
return true;
}
2023-09-03 04:02:53 -04:00
else if (s == '<')
2023-09-01 12:10:26 -04:00
{
2023-09-03 04:02:53 -04:00
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;
2023-09-01 12:10:26 -04:00
}
else
{
2023-09-03 04:02:53 -04:00
// Look for character in other pages
const QString switchPage[] = { ">", "<", "\0" };
2023-09-04 08:25:25 -04:00
for (int page = m_page == LETTERS ? 1 : 0; page < ((m_characterSet == Baudot::RUSSIAN) ? 3 : 2); page++)
2023-09-03 04:02:53 -04:00
{
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;
}
}
2023-09-01 12:10:26 -04:00
}
2023-09-03 04:02:53 -04:00
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);
2023-09-01 12:10:26 -04:00
}
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));
}