/////////////////////////////////////////////////////////////////////////////////// // 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 . // /////////////////////////////////////////////////////////////////////////////////// #include #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", "H̱", "8", "9", "F̱", "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)); }