sdrangel/sdrbase/util/baudot.cpp

362 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 //
// //
// 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", "Д", "П", "Й", "Н", "Ф", "Ц", "К",
"Т", "З", "Л", "В", "Х", "Ы", "P", "Я",
"О", "Б", "Г", "<", "М", "Ь", "Ж", ">"
};
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_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() << "BaudotEncoder::BaudotEncoder: Unsupported character set " << m_characterSet;
m_letters = Baudot::m_ita2Letter;
m_figures = Baudot::m_ita2Figure;
m_characterSet = Baudot::ITA2;
break;
}
}
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_figure = false;
}
bool BaudotEncoder::encode(QChar c, unsigned &bits, unsigned int &bitCount)
{
unsigned int code;
const unsigned int codeLen = 5;
bits = 0;
bitCount = 0;
// Only upper case is supported
c = c.toUpper();
QString s(c);
// We could create reverse look-up tables to speed this up, but it's only 200 baud...
if (m_letters.contains(s))
{
if (m_figure)
{
// Switch to letters
addStartBits(bits, bitCount);
code = reverseBits(m_letters.indexOf(">"), codeLen);
addBits(bits, bitCount, code, codeLen);
addStopBits(bits, bitCount);
m_figure = false;
}
addStartBits(bits, bitCount);
code = reverseBits(m_letters.indexOf(s), codeLen);
addBits(bits, bitCount, code, codeLen);
addStopBits(bits, bitCount);
return true;
}
else if (m_figures.contains(s))
{
if (!m_figure)
{
// Switch to figures
addStartBits(bits, bitCount);
code = reverseBits(m_letters.indexOf("<"), codeLen);
addBits(bits, bitCount, code, codeLen);
addStopBits(bits, bitCount);
m_figure = true;
}
addStartBits(bits, bitCount);
code = reverseBits(m_figures.indexOf(s), codeLen);
addBits(bits, bitCount, code, codeLen);
addStopBits(bits, bitCount);
if ((s == " ") && m_unshiftOnSpace) {
m_figure = false;
}
}
else
{
qDebug() << "BaudotEncoder::encode: Can't encode" << c;
return false;
}
return true;
}
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));
}