commit d34d85c04bb296da473973dbc4e332bf5306fbe6 Author: Jonathan Naylor Date: Thu Jan 14 18:45:04 2016 +0000 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378186f --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +Debug +Release +x64 +*.bak +*.obj +*~ +*.sdf +*.log +*.zip +*.exe +*.user +.vs diff --git a/BPTC19696.cpp b/BPTC19696.cpp new file mode 100644 index 0000000..7fbddef --- /dev/null +++ b/BPTC19696.cpp @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2012 by Ian Wraith + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "BPTC19696.h" + +#include "Hamming.h" +#include "Utils.h" + +#include +#include +#include + +CBPTC19696::CBPTC19696() : +m_rawData(NULL), +m_deInterData(NULL) +{ + m_rawData = new bool[196]; + m_deInterData = new bool[196]; +} + +CBPTC19696::~CBPTC19696() +{ + delete[] m_rawData; + delete[] m_deInterData; +} + +// The main decode function +void CBPTC19696::decode(const unsigned char* in, unsigned char* out) +{ + assert(in != NULL); + assert(out != NULL); + + // Get the raw binary + decodeExtractBinary(in); + + // Deinterleave + decodeDeInterleave(); + + // Error check + decodeErrorCheck(); + + // Extract Data + decodeExtractData(out); +} + +// The main encode function +void CBPTC19696::encode(const unsigned char* in, unsigned char* out) +{ + assert(in != NULL); + assert(out != NULL); + + // Extract Data + encodeExtractData(in); + + // Error check + encodeErrorCheck(); + + // Deinterleave + encodeInterleave(); + + // Get the raw binary + encodeExtractBinary(out); +} + +void CBPTC19696::decodeExtractBinary(const unsigned char* in) +{ + // First block + CUtils::byteToBitsBE(in[0U], m_rawData + 0U); + CUtils::byteToBitsBE(in[1U], m_rawData + 8U); + CUtils::byteToBitsBE(in[2U], m_rawData + 16U); + CUtils::byteToBitsBE(in[3U], m_rawData + 24U); + CUtils::byteToBitsBE(in[4U], m_rawData + 32U); + CUtils::byteToBitsBE(in[5U], m_rawData + 40U); + CUtils::byteToBitsBE(in[6U], m_rawData + 48U); + CUtils::byteToBitsBE(in[7U], m_rawData + 56U); + CUtils::byteToBitsBE(in[8U], m_rawData + 64U); + CUtils::byteToBitsBE(in[9U], m_rawData + 72U); + CUtils::byteToBitsBE(in[10U], m_rawData + 80U); + CUtils::byteToBitsBE(in[11U], m_rawData + 88U); + CUtils::byteToBitsBE(in[12U], m_rawData + 96U); + + // Handle the two bits + bool bits[8U]; + CUtils::byteToBitsBE(in[20U], bits); + m_rawData[98U] = bits[6U]; + m_rawData[99U] = bits[7U]; + + // Second block + CUtils::byteToBitsBE(in[21U], m_rawData + 100U); + CUtils::byteToBitsBE(in[22U], m_rawData + 108U); + CUtils::byteToBitsBE(in[23U], m_rawData + 116U); + CUtils::byteToBitsBE(in[24U], m_rawData + 124U); + CUtils::byteToBitsBE(in[25U], m_rawData + 132U); + CUtils::byteToBitsBE(in[26U], m_rawData + 140U); + CUtils::byteToBitsBE(in[27U], m_rawData + 148U); + CUtils::byteToBitsBE(in[28U], m_rawData + 156U); + CUtils::byteToBitsBE(in[29U], m_rawData + 164U); + CUtils::byteToBitsBE(in[30U], m_rawData + 172U); + CUtils::byteToBitsBE(in[31U], m_rawData + 180U); + CUtils::byteToBitsBE(in[32U], m_rawData + 188U); +} + +// Deinterleave the raw data +void CBPTC19696::decodeDeInterleave() +{ + for (unsigned int i = 0U; i < 196U; i++) + m_deInterData[i] = false; + + // The first bit is R(3) which is not used so can be ignored + for (unsigned int a = 0U; a < 196U; a++) { + // Calculate the interleave sequence + unsigned int interleaveSequence = (a * 181U) % 196U; + // Shuffle the data + m_deInterData[a] = m_rawData[interleaveSequence]; + } +} + +// Check each row with a Hamming (15,11,3) code and each column with a Hamming (13,9,3) code +void CBPTC19696::decodeErrorCheck() +{ + bool fixing; + unsigned int count = 0U; + do { + fixing = false; + + // Run through each of the 15 columns + bool col[13U]; + for (unsigned int c = 0U; c < 15U; c++) { + unsigned int pos = c + 1U; + for (unsigned int a = 0U; a < 13U; a++) { + col[a] = m_deInterData[pos]; + pos = pos + 15U; + } + + if (CHamming::decode1393(col)) { + unsigned int pos = c + 1U; + for (unsigned int a = 0U; a < 13U; a++) { + m_deInterData[pos] = col[a]; + pos = pos + 15U; + } + + fixing = true; + } + } + + // Run through each of the 9 rows containing data + for (unsigned int r = 0U; r < 9U; r++) { + unsigned int pos = (r * 15U) + 1U; + if (CHamming::decode15113(m_deInterData + pos)) + fixing = true; + } + + count++; + } while (fixing && count < 5U); +} + +// Extract the 96 bits of payload +void CBPTC19696::decodeExtractData(unsigned char* data) const +{ + bool bData[96U]; + unsigned int pos = 0U; + for (unsigned int a = 4U; a <= 11U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 16U; a <= 26U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 31U; a <= 41U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 46U; a <= 56U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 61U; a <= 71U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 76U; a <= 86U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 91U; a <= 101U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 106U; a <= 116U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 121U; a <= 131U; a++, pos++) + bData[pos] = m_deInterData[a]; + + CUtils::bitsToByteBE(bData + 0U, data[0U]); + CUtils::bitsToByteBE(bData + 8U, data[1U]); + CUtils::bitsToByteBE(bData + 16U, data[2U]); + CUtils::bitsToByteBE(bData + 24U, data[3U]); + CUtils::bitsToByteBE(bData + 32U, data[4U]); + CUtils::bitsToByteBE(bData + 40U, data[5U]); + CUtils::bitsToByteBE(bData + 48U, data[6U]); + CUtils::bitsToByteBE(bData + 56U, data[7U]); + CUtils::bitsToByteBE(bData + 64U, data[8U]); + CUtils::bitsToByteBE(bData + 72U, data[9U]); + CUtils::bitsToByteBE(bData + 80U, data[10U]); + CUtils::bitsToByteBE(bData + 88U, data[11U]); +} + +// Extract the 96 bits of payload +void CBPTC19696::encodeExtractData(const unsigned char* in) const +{ + bool bData[96U]; + CUtils::byteToBitsBE(in[0U], bData + 0U); + CUtils::byteToBitsBE(in[1U], bData + 8U); + CUtils::byteToBitsBE(in[2U], bData + 16U); + CUtils::byteToBitsBE(in[3U], bData + 24U); + CUtils::byteToBitsBE(in[4U], bData + 32U); + CUtils::byteToBitsBE(in[5U], bData + 40U); + CUtils::byteToBitsBE(in[6U], bData + 48U); + CUtils::byteToBitsBE(in[7U], bData + 56U); + CUtils::byteToBitsBE(in[8U], bData + 64U); + CUtils::byteToBitsBE(in[9U], bData + 72U); + CUtils::byteToBitsBE(in[10U], bData + 80U); + CUtils::byteToBitsBE(in[11U], bData + 88U); + + for (unsigned int i = 0U; i < 196U; i++) + m_deInterData[i] = false; + + unsigned int pos = 0U; + for (unsigned int a = 4U; a <= 11U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 16U; a <= 26U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 31U; a <= 41U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 46U; a <= 56U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 61U; a <= 71U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 76U; a <= 86U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 91U; a <= 101U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 106U; a <= 116U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 121U; a <= 131U; a++, pos++) + m_deInterData[a] = bData[pos]; +} + +// Check each row with a Hamming (15,11,3) code and each column with a Hamming (13,9,3) code +void CBPTC19696::encodeErrorCheck() +{ + // Run through each of the 15 columns + bool col[13U]; + for (unsigned int c = 0U; c < 15U; c++) { + unsigned int pos = c + 1U; + for (unsigned int a = 0U; a < 13U; a++) { + col[a] = m_deInterData[pos]; + pos = pos + 15U; + } + + CHamming::encode1393(col); + + pos = c + 1U; + for (unsigned int a = 0U; a < 13U; a++) { + m_deInterData[pos] = col[a]; + pos = pos + 15U; + } + } + + // Run through each of the 9 rows containing data + for (unsigned int r = 0U; r < 9U; r++) { + unsigned int pos = (r * 15U) + 1U; + CHamming::encode15113(m_deInterData + pos); + } +} + +// Interleave the raw data +void CBPTC19696::encodeInterleave() +{ + for (unsigned int i = 0U; i < 196U; i++) + m_rawData[i] = false; + + // The first bit is R(3) which is not used so can be ignored + for (unsigned int a = 0U; a < 196U; a++) { + // Calculate the interleave sequence + unsigned int interleaveSequence = (a * 181U) % 196U; + // Unshuffle the data + m_rawData[interleaveSequence] = m_deInterData[a]; + } +} + +void CBPTC19696::encodeExtractBinary(unsigned char* data) +{ + // First block + CUtils::bitsToByteBE(m_rawData + 0U, data[0U]); + CUtils::bitsToByteBE(m_rawData + 8U, data[1U]); + CUtils::bitsToByteBE(m_rawData + 16U, data[2U]); + CUtils::bitsToByteBE(m_rawData + 24U, data[3U]); + CUtils::bitsToByteBE(m_rawData + 32U, data[4U]); + CUtils::bitsToByteBE(m_rawData + 40U, data[5U]); + CUtils::bitsToByteBE(m_rawData + 48U, data[6U]); + CUtils::bitsToByteBE(m_rawData + 56U, data[7U]); + CUtils::bitsToByteBE(m_rawData + 64U, data[8U]); + CUtils::bitsToByteBE(m_rawData + 72U, data[9U]); + CUtils::bitsToByteBE(m_rawData + 80U, data[10U]); + CUtils::bitsToByteBE(m_rawData + 88U, data[11U]); + + // Handle the two bits + unsigned char byte; + CUtils::bitsToByteBE(m_rawData + 96U, byte); + data[12U] = (data[12U] & 0x3FU) | ((byte >> 0) & 0xC0U); + data[13U] = (data[13U] & 0xFCU) | ((byte >> 4) & 0x03U); + +// Second block + CUtils::bitsToByteBE(m_rawData + 100U, data[21U]); + CUtils::bitsToByteBE(m_rawData + 108U, data[22U]); + CUtils::bitsToByteBE(m_rawData + 116U, data[23U]); + CUtils::bitsToByteBE(m_rawData + 124U, data[24U]); + CUtils::bitsToByteBE(m_rawData + 132U, data[25U]); + CUtils::bitsToByteBE(m_rawData + 140U, data[26U]); + CUtils::bitsToByteBE(m_rawData + 148U, data[27U]); + CUtils::bitsToByteBE(m_rawData + 156U, data[28U]); + CUtils::bitsToByteBE(m_rawData + 164U, data[29U]); + CUtils::bitsToByteBE(m_rawData + 172U, data[30U]); + CUtils::bitsToByteBE(m_rawData + 180U, data[31U]); + CUtils::bitsToByteBE(m_rawData + 188U, data[32U]); +} diff --git a/BPTC19696.h b/BPTC19696.h new file mode 100644 index 0000000..0a40a78 --- /dev/null +++ b/BPTC19696.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(BPTC19696_H) +#define BPTC19696_H + +class CBPTC19696 +{ +public: + CBPTC19696(); + ~CBPTC19696(); + + void decode(const unsigned char* in, unsigned char* out); + + void encode(const unsigned char* in, unsigned char* out); + +private: + bool* m_rawData; + bool* m_deInterData; + + void decodeExtractBinary(const unsigned char* in); + void decodeErrorCheck(); + void decodeDeInterleave(); + void decodeExtractData(unsigned char* data) const; + + void encodeExtractData(const unsigned char* in) const; + void encodeInterleave(); + void encodeErrorCheck(); + void encodeExtractBinary(unsigned char* data); +}; + +#endif diff --git a/CRC.cpp b/CRC.cpp new file mode 100644 index 0000000..b118f20 --- /dev/null +++ b/CRC.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "CRC.h" + +#include "Utils.h" + +#include +#include +#include + +bool CCRC::checkFiveBit(bool* in, unsigned int tcrc) +{ + assert(in != NULL); + + unsigned int crc; + encodeFiveBit(in, crc); + + return crc == tcrc; +} + +void CCRC::encodeFiveBit(const bool* in, unsigned int& tcrc) +{ + assert(in != NULL); + + unsigned short total = 0U; + for (unsigned int i = 0U; i < 72U; i += 8U) { + unsigned char c; + CUtils::bitsToByteBE(in + i, c); + total += c; + } + + total %= 31U; + + tcrc = total; +} + +unsigned char CCRC::encodeEightBit(const unsigned char *in, unsigned int length) +{ + assert(in != NULL); + + unsigned char crc = 0x00U; + + for (unsigned int i = 0U; i < length; i++) { + crc ^= in[i]; + + for (unsigned int j = 0U; j < 8U; j++) { + if ((crc & 0x80U) == 0x80U) { + crc <<= 1; + crc ^= 0x07U; + } else { + crc <<= 1; + } + } + } + + return crc; +} + +bool CCRC::checkCSBK(const unsigned char *in) +{ + unsigned short crc16 = 0U; + + // Run through all 12 bits + for (unsigned int a = 0; a < 12U; a++) { + unsigned char val = in[a]; + + // Allow for the CSBK CRC mask + if (a > 9U) + val ^= 0xA5U; + + for (unsigned int i = 0U; i < 8U; i++) { + bool c15 = (crc16 >> 15 & 0x01U) == 0x01U; + bool bit = (val >> (7 - i) & 0x01U) == 0x01U; + crc16 <<= 1; + if (c15 ^ bit) + crc16 ^= 0x1021U; + } + } + + return crc16 == 0x1D0FU; +} + +unsigned char CCRC::crc8(const unsigned char *in, unsigned int length) +{ + unsigned int crc = 0U; + + for (unsigned int j = 0U; j < length; j++, in++) { + crc ^= (*in << 8); + + for (unsigned int i = 0U; i < 8U; i++) { + if (crc & 0x8000U) + crc ^= (0x1070U << 3); + crc <<= 1; + } + } + + return crc >> 8; +} diff --git a/CRC.h b/CRC.h new file mode 100644 index 0000000..bbbc5b5 --- /dev/null +++ b/CRC.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(CRC_H) +#define CRC_H + +class CCRC +{ +public: + static bool checkFiveBit(bool* in, unsigned int tcrc); + static void encodeFiveBit(const bool* in, unsigned int& tcrc); + + static bool checkCSBK(const unsigned char* in); + + static unsigned char encodeEightBit(const unsigned char* in, unsigned int length); + + static unsigned char crc8(const unsigned char* in, unsigned int length); +}; + +#endif diff --git a/CSBK.cpp b/CSBK.cpp new file mode 100644 index 0000000..e285af2 --- /dev/null +++ b/CSBK.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "CSBK.h" +#include "BPTC19696.h" +#include "CRC.h" +#include "Log.h" // XXXX + +#include "Utils.h" + +#include +#include + +CCSBK::CCSBK(const unsigned char* bytes) : +m_CSBKO(CSBKO_NONE), +m_FID(0x00U), +m_bsId(0U), +m_srcId(0U), +m_valid(false) +{ + assert(bytes != NULL); + + CBPTC19696 bptc; + + unsigned char data[12U]; + bptc.decode(bytes, data); + + m_valid = CCRC::checkCSBK(data); + + m_CSBKO = CSBKO(data[0U] & 0x3FU); + m_FID = data[1U]; + + if (m_CSBKO == CSBKO_BSDWNACT) { + m_bsId = data[4U] << 16 | data[5U] << 8 | data[6U]; + m_srcId = data[7U] << 16 | data[8U] << 8 | data[9U]; + CUtils::dump("Download activate CSBK", data, 12U); + } else { + CUtils::dump("Unhandled CSBK type", data, 12U); + } +} + +CCSBK::~CCSBK() +{ +} + +bool CCSBK::isValid() const +{ + return m_valid; +} + +CSBKO CCSBK::getCSBKO() const +{ + return m_CSBKO; +} + +unsigned char CCSBK::getFID() const +{ + return m_FID; +} + +unsigned int CCSBK::getBSId() const +{ + return m_bsId; +} + +unsigned int CCSBK::getSrcId() const +{ + return m_srcId; +} diff --git a/CSBK.h b/CSBK.h new file mode 100644 index 0000000..91b6973 --- /dev/null +++ b/CSBK.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(CSBK_H) +#define CSBK_H + +#include "DMRDefines.h" + +enum CSBKO { + CSBKO_NONE = 0x00, + CSBKO_UUVREQ = 0x04, + CSBKO_UUANSRSP = 0x05, + CSBKO_CTCSBK = 0x07, + CSBKO_NACKRSP = 0x26, + CSBKO_BSDWNACT = 0x38, + CSBKO_PRECCSBK = 0x3D +}; + +class CCSBK +{ +public: + CCSBK(const unsigned char* bytes); + ~CCSBK(); + + bool isValid() const; + + // Generic fields + CSBKO getCSBKO() const; + unsigned char getFID() const; + + // For BS Dwn Act + unsigned int getBSId() const; + unsigned int getSrcId() const; + +private: + CSBKO m_CSBKO; + unsigned char m_FID; + unsigned int m_bsId; + unsigned int m_srcId; + bool m_valid; +}; + +#endif diff --git a/Conf.cpp b/Conf.cpp new file mode 100644 index 0000000..9eb3112 --- /dev/null +++ b/Conf.cpp @@ -0,0 +1,494 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Conf.h" +#include "Log.h" + +#include +#include +#include + +const int BUFFER_SIZE = 500; + +enum SECTION { + SECTION_NONE, + SECTION_GENERAL, + SECTION_INFO, + SECTION_LOG, + SECTION_MODEM, + SECTION_DSTAR, + SECTION_DMR, + SECTION_FUSION, + SECTION_DSTAR_NETWORK, + SECTION_DMR_NETWORK, + SECTION_FUSION_NETWORK, + SECTION_TFTSERIAL +}; + +CConf::CConf(const std::string& file) : +m_file(file), +m_callsign(), +m_timeout(120U), +m_duplex(true), +m_modeHang(10U), +m_display(), +m_rxFrequency(0U), +m_txFrequency(0U), +m_power(0U), +m_latitude(0.0F), +m_longitude(0.0F), +m_height(0), +m_location(), +m_description(), +m_url(), +m_logLevel(0U), +m_logPath(), +m_logRoot(), +m_logDisplay(true), +m_modemPort(), +m_modemRXInvert(false), +m_modemTXInvert(false), +m_modemPTTInvert(false), +m_modemTXDelay(100U), +m_modemRXLevel(100U), +m_modemTXLevel(100U), +m_modemDebug(false), +m_dstarEnabled(true), +m_dstarModule("C"), +m_dmrEnabled(true), +m_dmrId(0U), +m_dmrColorCode(2U), +m_fusionEnabled(true), +m_dstarNetworkEnabled(true), +m_dstarGatewayAddress(), +m_dstarGatewayPort(0U), +m_dstarLocalPort(0U), +m_dstarNetworkDebug(false), +m_dmrNetworkEnabled(true), +m_dmrNetworkAddress(), +m_dmrNetworkPort(0U), +m_dmrNetworkPassword(), +m_dmrNetworkDebug(false), +m_fusionNetworkEnabled(false), +m_fusionNetworkAddress(), +m_fusionNetworkPort(0U), +m_fusionNetworkDebug(false), +m_tftSerialPort() +{ +} + +CConf::~CConf() +{ +} + +bool CConf::read() +{ + FILE* fp = ::fopen(m_file.c_str(), "rt"); + if (fp == NULL) { + ::fprintf(stderr, "Couldn't open the .ini file - %s\n", m_file.c_str()); + return false; + } + + SECTION section = SECTION_NONE; + + char buffer[BUFFER_SIZE]; + while (::fgets(buffer, BUFFER_SIZE, fp) != NULL) { + if (buffer[0U] == '#') + continue; + + if (buffer[0U] == '[') { + if (::strncmp(buffer, "[General]", 9U) == 0) + section = SECTION_GENERAL; + else if (::strncmp(buffer, "[Info]", 6U) == 0) + section = SECTION_INFO; + else if (::strncmp(buffer, "[Log]", 5U) == 0) + section = SECTION_LOG; + else if (::strncmp(buffer, "[Modem]", 7U) == 0) + section = SECTION_MODEM; + else if (::strncmp(buffer, "[D-Star]", 8U) == 0) + section = SECTION_DSTAR; + else if (::strncmp(buffer, "[DMR]", 5U) == 0) + section = SECTION_DMR; + else if (::strncmp(buffer, "[System Fusion]", 15U) == 0) + section = SECTION_FUSION; + else if (::strncmp(buffer, "[D-Star Network]", 16U) == 0) + section = SECTION_DSTAR_NETWORK; + else if (::strncmp(buffer, "[DMR Network]", 13U) == 0) + section = SECTION_DMR_NETWORK; + else if (::strncmp(buffer, "[System Fusion Network]", 23U) == 0) + section = SECTION_FUSION_NETWORK; + else if (::strncmp(buffer, "[TFT Serial]", 11U) == 0) + section = SECTION_TFTSERIAL; + else + section = SECTION_NONE; + + continue; + } + + char* key = ::strtok(buffer, " \t=\r\n"); + if (key == NULL) + continue; + + char* value = ::strtok(NULL, "\r\n"); + if (section == SECTION_GENERAL) { + if (::strcmp(key, "Callsign") == 0) + m_callsign = value; + else if (::strcmp(key, "Timeout") == 0) + m_timeout = (unsigned int)::atoi(value); + else if (::strcmp(key, "Duplex") == 0) + m_duplex = ::atoi(value) == 1; + else if (::strcmp(key, "ModeHang") == 0) + m_modeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Display") == 0) + m_display = value; + } else if (section == SECTION_INFO) { + if (::strcmp(key, "TXFrequency") == 0) + m_txFrequency = (unsigned int)::atoi(value); + else if (::strcmp(key, "RXFrequency") == 0) + m_rxFrequency = (unsigned int)::atoi(value); + else if (::strcmp(key, "Power") == 0) + m_power = (unsigned int)::atoi(value); + else if (::strcmp(key, "Latitude") == 0) + m_latitude = float(::atof(value)); + else if (::strcmp(key, "Longitude") == 0) + m_longitude = float(::atof(value)); + else if (::strcmp(key, "Height") == 0) + m_height = ::atoi(value); + else if (::strcmp(key, "Location") == 0) + m_location = value; + else if (::strcmp(key, "Description") == 0) + m_description = value; + else if (::strcmp(key, "URL") == 0) + m_url = value; + } else if (section == SECTION_LOG) { + if (::strcmp(key, "Path") == 0) + m_logPath = value; + else if (::strcmp(key, "Root") == 0) + m_logRoot = value; + else if (::strcmp(key, "Level") == 0) + m_logLevel = (unsigned int)::atoi(value); + else if (::strcmp(key, "Display") == 0) + m_logDisplay = ::atoi(value) == 1; + } else if (section == SECTION_MODEM) { + if (::strcmp(key, "Port") == 0) + m_modemPort = value; + else if (::strcmp(key, "RXInvert") == 0) + m_modemRXInvert = ::atoi(value) == 1; + else if (::strcmp(key, "TXInvert") == 0) + m_modemTXInvert = ::atoi(value) == 1; + else if (::strcmp(key, "PTTInvert") == 0) + m_modemPTTInvert = ::atoi(value) == 1; + else if (::strcmp(key, "TXDelay") == 0) + m_modemTXDelay = (unsigned int)::atoi(value); + else if (::strcmp(key, "RXLevel") == 0) + m_modemRXLevel = (unsigned int)::atoi(value); + else if (::strcmp(key, "TXLevel") == 0) + m_modemTXLevel = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_modemDebug = ::atoi(value) == 1; + } else if (section == SECTION_DSTAR) { + if (::strcmp(key, "Enabled") == 0) + m_dstarEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Module") == 0) + m_dstarModule = value; + } else if (section == SECTION_DMR) { + if (::strcmp(key, "Enabled") == 0) + m_dmrEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Id") == 0) + m_dmrId = (unsigned int)::atoi(value); + else if (::strcmp(key, "ColorCode") == 0) + m_dmrColorCode = (unsigned int)::atoi(value); + } else if (section == SECTION_FUSION) { + if (::strcmp(key, "Enabled") == 0) + m_fusionEnabled = ::atoi(value) == 1; + } else if (section == SECTION_DSTAR_NETWORK) { + if (::strcmp(key, "Enabled") == 0) + m_dstarNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "GatewayAddress") == 0) + m_dstarGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_dstarGatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "LocalPort") == 0) + m_dstarLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_dstarNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_DMR_NETWORK) { + if (::strcmp(key, "Enabled") == 0) + m_dmrNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Address") == 0) + m_dmrNetworkAddress = value; + else if (::strcmp(key, "Port") == 0) + m_dmrNetworkPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "Password") == 0) + m_dmrNetworkPassword = value; + else if (::strcmp(key, "Debug") == 0) + m_dmrNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_FUSION_NETWORK) { + if (::strcmp(key, "Enabled") == 0) + m_fusionNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Address") == 0) + m_fusionNetworkAddress = value; + else if (::strcmp(key, "Port") == 0) + m_fusionNetworkPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_fusionNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_TFTSERIAL) { + if (::strcmp(key, "Port") == 0) + m_tftSerialPort = value; + } + } + + ::fclose(fp); + + return true; +} + +std::string CConf::getCallsign() const +{ + return m_callsign; +} + +unsigned int CConf::getTimeout() const +{ + return m_timeout; +} + +bool CConf::getDuplex() const +{ + return m_duplex; +} + +unsigned int CConf::getModeHang() const +{ + return m_modeHang; +} + +std::string CConf::getDisplay() const +{ + return m_display; +} + +unsigned int CConf::getRxFrequency() const +{ + return m_rxFrequency; +} + +unsigned int CConf::getTxFrequency() const +{ + return m_txFrequency; +} + +unsigned int CConf::getPower() const +{ + return m_power; +} + +float CConf::getLatitude() const +{ + return m_latitude; +} + +float CConf::getLongitude() const +{ + return m_longitude; +} + +int CConf::getHeight() const +{ + return m_height; +} + +std::string CConf::getLocation() const +{ + return m_location; +} + +std::string CConf::getDescription() const +{ + return m_description; +} + +std::string CConf::getURL() const +{ + return m_url; +} + +unsigned int CConf::getLogLevel() const +{ + return m_logLevel; +} + +std::string CConf::getLogPath() const +{ + return m_logPath; +} + +std::string CConf::getLogRoot() const +{ + return m_logRoot; +} + +bool CConf::getLogDisplay() const +{ + return m_logDisplay; +} + +std::string CConf::getModemPort() const +{ + return m_modemPort; +} + +bool CConf::getModemRXInvert() const +{ + return m_modemRXInvert; +} + +bool CConf::getModemTXInvert() const +{ + return m_modemTXInvert; +} + +bool CConf::getModemPTTInvert() const +{ + return m_modemPTTInvert; +} + +unsigned int CConf::getModemTXDelay() const +{ + return m_modemTXDelay; +} + +unsigned int CConf::getModemRXLevel() const +{ + return m_modemRXLevel; +} + +unsigned int CConf::getModemTXLevel() const +{ + return m_modemTXLevel; +} + +bool CConf::getModemDebug() const +{ + return m_modemDebug; +} + +bool CConf::getDStarEnabled() const +{ + return m_dstarEnabled; +} + +std::string CConf::getDStarModule() const +{ + return m_dstarModule; +} + +bool CConf::getDMREnabled() const +{ + return m_dmrEnabled; +} + +unsigned int CConf::getDMRId() const +{ + return m_dmrId; +} + +unsigned int CConf::getDMRColorCode() const +{ + return m_dmrColorCode; +} + +bool CConf::getFusionEnabled() const +{ + return m_fusionEnabled; +} + +bool CConf::getDStarNetworkEnabled() const +{ + return m_dstarNetworkEnabled; +} + +std::string CConf::getDStarGatewayAddress() const +{ + return m_dstarGatewayAddress; +} + +unsigned int CConf::getDStarGatewayPort() const +{ + return m_dstarGatewayPort; +} + +unsigned int CConf::getDStarLocalPort() const +{ + return m_dstarLocalPort; +} + +bool CConf::getDStarNetworkDebug() const +{ + return m_dstarNetworkDebug; +} + +bool CConf::getDMRNetworkEnabled() const +{ + return m_dmrNetworkEnabled; +} + +std::string CConf::getDMRNetworkAddress() const +{ + return m_dmrNetworkAddress; +} + +unsigned int CConf::getDMRNetworkPort() const +{ + return m_dmrNetworkPort; +} + +std::string CConf::getDMRNetworkPassword() const +{ + return m_dmrNetworkPassword; +} + +bool CConf::getDMRNetworkDebug() const +{ + return m_dmrNetworkDebug; +} + +bool CConf::getFusionNetworkEnabled() const +{ + return m_fusionNetworkEnabled; +} + +std::string CConf::getFusionNetworkAddress() const +{ + return m_fusionNetworkAddress; +} + +unsigned int CConf::getFusionNetworkPort() const +{ + return m_fusionNetworkPort; +} + +bool CConf::getFusionNetworkDebug() const +{ + return m_fusionNetworkDebug; +} + +std::string CConf::getTFTSerialPort() const +{ + return m_tftSerialPort; +} diff --git a/Conf.h b/Conf.h new file mode 100644 index 0000000..d81f189 --- /dev/null +++ b/Conf.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(CONF_H) +#define CONF_H + +#include + +class CConf +{ +public: + CConf(const std::string& file); + ~CConf(); + + bool read(); + + // The General section + std::string getCallsign() const; + unsigned int getTimeout() const; + bool getDuplex() const; + unsigned int getModeHang() const; + std::string getDisplay() const; + + // The Info section + unsigned int getRxFrequency() const; + unsigned int getTxFrequency() const; + unsigned int getPower() const; + float getLatitude() const; + float getLongitude() const; + int getHeight() const; + std::string getLocation() const; + std::string getDescription() const; + std::string getURL() const; + + // The Log section + std::string getLogPath() const; + std::string getLogRoot() const; + unsigned int getLogLevel() const; + bool getLogDisplay() const; + + // The Modem section + std::string getModemPort() const; + bool getModemRXInvert() const; + bool getModemTXInvert() const; + bool getModemPTTInvert() const; + unsigned int getModemTXDelay() const; + unsigned int getModemRXLevel() const; + unsigned int getModemTXLevel() const; + bool getModemDebug() const; + + // The D-Star section + bool getDStarEnabled() const; + std::string getDStarModule() const; + + // The DMR section + bool getDMREnabled() const; + unsigned int getDMRId() const; + unsigned int getDMRColorCode() const; + + // The System Fusion section + bool getFusionEnabled() const; + + // The D-Star Network section + bool getDStarNetworkEnabled() const; + std::string getDStarGatewayAddress() const; + unsigned int getDStarGatewayPort() const; + unsigned int getDStarLocalPort() const; + bool getDStarNetworkDebug() const; + + // The DMR Network section + bool getDMRNetworkEnabled() const; + std::string getDMRNetworkAddress() const; + unsigned int getDMRNetworkPort() const; + std::string getDMRNetworkPassword() const; + bool getDMRNetworkDebug() const; + + // The System Fusion Network section + bool getFusionNetworkEnabled() const; + std::string getFusionNetworkAddress() const; + unsigned int getFusionNetworkPort() const; + bool getFusionNetworkDebug() const; + + // The TFTSERIAL section + std::string getTFTSerialPort() const; + +private: + std::string m_file; + std::string m_callsign; + unsigned int m_timeout; + bool m_duplex; + unsigned int m_modeHang; + std::string m_display; + + unsigned int m_rxFrequency; + unsigned int m_txFrequency; + unsigned int m_power; + float m_latitude; + float m_longitude; + int m_height; + std::string m_location; + std::string m_description; + std::string m_url; + + unsigned int m_logLevel; + std::string m_logPath; + std::string m_logRoot; + bool m_logDisplay; + + std::string m_modemPort; + bool m_modemRXInvert; + bool m_modemTXInvert; + bool m_modemPTTInvert; + unsigned int m_modemTXDelay; + unsigned int m_modemRXLevel; + unsigned int m_modemTXLevel; + bool m_modemDebug; + + bool m_dstarEnabled; + std::string m_dstarModule; + + bool m_dmrEnabled; + unsigned int m_dmrId; + unsigned int m_dmrColorCode; + + bool m_fusionEnabled; + + bool m_dstarNetworkEnabled; + std::string m_dstarGatewayAddress; + unsigned int m_dstarGatewayPort; + unsigned int m_dstarLocalPort; + bool m_dstarNetworkDebug; + + bool m_dmrNetworkEnabled; + std::string m_dmrNetworkAddress; + unsigned int m_dmrNetworkPort; + std::string m_dmrNetworkPassword; + bool m_dmrNetworkDebug; + + bool m_fusionNetworkEnabled; + unsigned int m_fusionNetworkPort; + std::string m_fusionNetworkAddress; + bool m_fusionNetworkDebug; + + std::string m_tftSerialPort; +}; + +#endif diff --git a/DMRControl.cpp b/DMRControl.cpp new file mode 100644 index 0000000..52e8058 --- /dev/null +++ b/DMRControl.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2015,2016 Jonathan Naylor, G4KLX + * + * 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; version 2 of the License. + * + * 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 for more details. + */ + +#include "DMRControl.h" +#include "Defines.h" +#include "CSBK.h" +#include "Log.h" + +#include + +CDMRControl::CDMRControl(unsigned int id, unsigned int colorCode, unsigned int timeout, CModem* modem, CHomebrewDMRIPSC* network, IDisplay* display) : +m_id(id), +m_colorCode(colorCode), +m_modem(modem), +m_network(network), +m_slot1(1U, timeout), +m_slot2(2U, timeout) +{ + assert(modem != NULL); + assert(display != NULL); + + CDMRSlot::init(colorCode, modem, network, display); +} + +CDMRControl::~CDMRControl() +{ +} + +bool CDMRControl::processWakeup(const unsigned char* data) +{ + // Wakeups always come in on slot 1 + if (data[0U] != TAG_DATA || data[1U] != (DMR_IDLE_RX | DMR_SYNC_DATA | DT_CSBK)) + return false; + + CCSBK csbk(data + 2U); + + CSBKO csbko = csbk.getCSBKO(); + if (csbko != CSBKO_BSDWNACT) + return false; + + unsigned int bsId = csbk.getBSId(); + if (bsId == 0xFFFFFFU) { + LogMessage("CSBK BS_Dwn_Act for any received from %u", csbk.getSrcId()); + return true; + } else if (bsId == m_id) { + LogMessage("CSBK BS_Dwn_Act for %u received from %u", bsId, csbk.getSrcId()); + return true; + } + + return false; +} + +void CDMRControl::writeModemSlot1(unsigned char *data) +{ + m_slot1.writeModem(data); +} + +void CDMRControl::writeModemSlot2(unsigned char *data) +{ + m_slot2.writeModem(data); +} + +unsigned int CDMRControl::readModemSlot1(unsigned char *data) +{ + return m_slot1.readModem(data); +} + +unsigned int CDMRControl::readModemSlot2(unsigned char *data) +{ + return m_slot2.readModem(data); +} + +void CDMRControl::clock(unsigned int ms) +{ + if (m_network != NULL) { + CDMRData data; + bool ret = m_network->read(data); + if (ret) { + unsigned int slotNo = data.getSlotNo(); + switch (slotNo) { + case 1U: m_slot1.writeNetwork(data); break; + case 2U: m_slot2.writeNetwork(data); break; + default: LogError("Invalid slot no %u", slotNo); break; + } + } + + m_network->clock(ms); + } + + m_slot1.clock(ms); + m_slot2.clock(ms); +} diff --git a/DMRControl.h b/DMRControl.h new file mode 100644 index 0000000..bc33e69 --- /dev/null +++ b/DMRControl.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(DMRControl_H) +#define DMRControl_H + +#include "HomebrewDMRIPSC.h" +#include "Display.h" +#include "DMRSlot.h" +#include "DMRData.h" +#include "Modem.h" + + +class CDMRControl { +public: + CDMRControl(unsigned int id, unsigned int colorCode, unsigned int timeout, CModem* modem, CHomebrewDMRIPSC* network, IDisplay* display); + ~CDMRControl(); + + bool processWakeup(const unsigned char* data); + + void writeModemSlot1(unsigned char* data); + void writeModemSlot2(unsigned char* data); + + unsigned int readModemSlot1(unsigned char* data); + unsigned int readModemSlot2(unsigned char* data); + + void clock(unsigned int ms); + +private: + unsigned int m_id; + unsigned int m_colorCode; + CModem* m_modem; + CHomebrewDMRIPSC* m_network; + CDMRSlot m_slot1; + CDMRSlot m_slot2; +}; + +#endif diff --git a/DMRData.cpp b/DMRData.cpp new file mode 100644 index 0000000..3952ce4 --- /dev/null +++ b/DMRData.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2015 Jonathan Naylor, G4KLX + * + * 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; version 2 of the License. + * + * 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 for more details. + */ + +#include "DMRData.h" +#include "DMRDefines.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + + +CDMRData::CDMRData(const CDMRData& data) : +m_slotNo(data.m_slotNo), +m_data(NULL), +m_srcId(data.m_srcId), +m_dstId(data.m_dstId), +m_flco(data.m_flco), +m_dataType(data.m_dataType), +m_seqNo(data.m_seqNo), +m_n(data.m_n) +{ + m_data = new unsigned char[2U * DMR_FRAME_LENGTH_BYTES]; + ::memcpy(m_data, data.m_data, 2U * DMR_FRAME_LENGTH_BYTES); +} + +CDMRData::CDMRData() : +m_slotNo(1U), +m_data(NULL), +m_srcId(0U), +m_dstId(0U), +m_flco(FLCO_GROUP), +m_dataType(0U), +m_seqNo(0U), +m_n(0U) +{ + m_data = new unsigned char[2U * DMR_FRAME_LENGTH_BYTES]; +} + +CDMRData::~CDMRData() +{ + delete[] m_data; +} + +CDMRData& CDMRData::operator=(const CDMRData& data) +{ + if (this != &data) { + ::memcpy(m_data, data.m_data, DMR_FRAME_LENGTH_BYTES); + + m_slotNo = data.m_slotNo; + m_srcId = data.m_srcId; + m_dstId = data.m_dstId; + m_flco = data.m_flco; + m_dataType = data.m_dataType; + m_seqNo = data.m_seqNo; + m_n = data.m_n; + } + + return *this; +} + +unsigned int CDMRData::getSlotNo() const +{ + return m_slotNo; +} + +void CDMRData::setSlotNo(unsigned int slotNo) +{ + assert(slotNo == 1U || slotNo == 2U); + + m_slotNo = slotNo; +} + +unsigned char CDMRData::getDataType() const +{ + return m_dataType; +} + +void CDMRData::setDataType(unsigned char dataType) +{ + m_dataType = dataType; +} + +unsigned int CDMRData::getSrcId() const +{ + return m_srcId; +} + +void CDMRData::setSrcId(unsigned int id) +{ + m_srcId = id; +} + +unsigned int CDMRData::getDstId() const +{ + return m_dstId; +} + +void CDMRData::setDstId(unsigned int id) +{ + m_dstId = id; +} + +FLCO CDMRData::getFLCO() const +{ + return m_flco; +} + +void CDMRData::setFLCO(FLCO flco) +{ + m_flco = flco; +} + +unsigned char CDMRData::getSeqNo() const +{ + return m_seqNo; +} + +void CDMRData::setSeqNo(unsigned char seqNo) +{ + m_seqNo = seqNo; +} + +unsigned char CDMRData::getN() const +{ + return m_n; +} + +void CDMRData::setN(unsigned char n) +{ + m_n = n; +} + +unsigned int CDMRData::getData(unsigned char* buffer) const +{ + assert(buffer != NULL); + + ::memcpy(buffer, m_data, DMR_FRAME_LENGTH_BYTES); + + return DMR_FRAME_LENGTH_BYTES; +} + +void CDMRData::setData(const unsigned char* buffer) +{ + assert(buffer != NULL); + + ::memcpy(m_data, buffer, DMR_FRAME_LENGTH_BYTES); +} diff --git a/DMRData.h b/DMRData.h new file mode 100644 index 0000000..7fcc0a3 --- /dev/null +++ b/DMRData.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor, G4KLX + * + * 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; version 2 of the License. + * + * 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 for more details. + */ + +#ifndef DMRData_H +#define DMRData_H + +#include "DMRDefines.h" + +class CDMRData { +public: + CDMRData(const CDMRData& data); + CDMRData(); + ~CDMRData(); + + CDMRData& operator=(const CDMRData& data); + + unsigned int getSlotNo() const; + void setSlotNo(unsigned int slotNo); + + unsigned int getSrcId() const; + void setSrcId(unsigned int id); + + unsigned int getDstId() const; + void setDstId(unsigned int id); + + FLCO getFLCO() const; + void setFLCO(FLCO flco); + + unsigned char getN() const; + void setN(unsigned char n); + + unsigned char getSeqNo() const; + void setSeqNo(unsigned char seqNo); + + unsigned char getDataType() const; + void setDataType(unsigned char dataType); + + void setData(const unsigned char* buffer); + unsigned int getData(unsigned char* buffer) const; + +private: + unsigned int m_slotNo; + unsigned char* m_data; + unsigned int m_srcId; + unsigned int m_dstId; + FLCO m_flco; + unsigned char m_dataType; + unsigned char m_seqNo; + unsigned char m_n; +}; + +#endif diff --git a/DMRDefines.h b/DMRDefines.h new file mode 100644 index 0000000..8e7cada --- /dev/null +++ b/DMRDefines.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(DMRDefines_H) +#define DMRDefines_H + +const unsigned int DMR_FRAME_LENGTH_BITS = 264U; +const unsigned int DMR_FRAME_LENGTH_BYTES = 33U; + +const unsigned int DMR_SYNC_LENGTH_BITS = 48U; +const unsigned int DMR_SYNC_LENGTH_BYTES = 6U; + +const unsigned int DMR_EMB_LENGTH_BITS = 8U; +const unsigned int DMR_EMB_LENGTH_BYTES = 1U; + +const unsigned int DMR_SLOT_TYPE_LENGTH_BITS = 8U; +const unsigned int DMR_SLOT_TYPE_LENGTH_BYTES = 1U; + +const unsigned int DMR_EMBEDDED_SIGNALLING_LENGTH_BITS = 32U; +const unsigned int DMR_EMBEDDED_SIGNALLING_LENGTH_BYTES = 4U; + +const unsigned int DMR_AMBE_LENGTH_BITS = 108U * 2U; +const unsigned int DMR_AMBE_LENGTH_BYTES = 27U; + +const unsigned char BS_SOURCED_AUDIO_SYNC[] = {0x07U, 0x55U, 0xFDU, 0x7DU, 0xF7U, 0x5FU, 0x70U}; +const unsigned char BS_SOURCED_DATA_SYNC[] = {0x0DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD0U}; + +const unsigned char MS_SOURCED_AUDIO_SYNC[] = {0x07U, 0xF7U, 0xD5U, 0xDDU, 0x57U, 0xDFU, 0xD0U}; +const unsigned char MS_SOURCED_DATA_SYNC[] = {0x0DU, 0x5DU, 0x7FU, 0x77U, 0xFDU, 0x75U, 0x70U}; + +const unsigned char DIRECT_SLOT1_AUDIO_SYNC[] = {0x05U, 0xD5U, 0x77U, 0xF7U, 0x75U, 0x7FU, 0xF0U}; +const unsigned char DIRECT_SLOT1_DATA_SYNC[] = {0x0FU, 0x7FU, 0xDDU, 0x5DU, 0xDFU, 0xD5U, 0x50U}; + +const unsigned char DIRECT_SLOT2_AUDIO_SYNC[] = {0x07U, 0xDFU, 0xFDU, 0x5FU, 0x55U, 0xD5U, 0xF0U}; +const unsigned char DIRECT_SLOT2_DATA_SYNC[] = {0x0DU, 0x75U, 0x57U, 0xF5U, 0xFFU, 0x7FU, 0x50U}; + +const unsigned char SYNC_MASK[] = {0x0FU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xF0U}; + +// The PR FILL and Data Sync pattern. +const unsigned char IDLE_DATA[] = + {0x53U, 0xC2U, 0x5EU, 0xABU, 0xA8U, 0x67U, 0x1DU, 0xC7U, 0x38U, 0x3BU, 0xD9U, + 0x36U, 0x00U, 0x0DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD0U, 0x03U, 0xF6U, + 0xE4U, 0x65U, 0x17U, 0x1BU, 0x48U, 0xCAU, 0x6DU, 0x4FU, 0xC6U, 0x10U, 0xB4U}; + +const unsigned char PAYLOAD_LEFT_MASK[] = {0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xF0U}; +const unsigned char PAYLOAD_RIGHT_MASK[] = {0x0FU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU}; + +const unsigned char VOICE_LC_HEADER_CRC_MASK[] = {0x96U, 0x96U, 0x96U}; +const unsigned char TERMINATOR_WITH_LC_CRC_MASK[] = {0x99U, 0x99U, 0x99U}; +const unsigned char CSBK_CRC_MASK[] = {0xA5U, 0xA5U}; + +const unsigned int DMR_SLOT_TIME = 60U; +const unsigned int AMBE_PER_SLOT = 3U; + +const unsigned char DT_MASK = 0x0FU; +const unsigned char DT_VOICE_PI_HEADER = 0x00U; +const unsigned char DT_VOICE_LC_HEADER = 0x01U; +const unsigned char DT_TERMINATOR_WITH_LC = 0x02U; +const unsigned char DT_CSBK = 0x03U; +const unsigned char DT_DATA_HEADER = 0x06U; +const unsigned char DT_IDLE = 0x09U; + +// Dummy values +const unsigned char DT_VOICE_SYNC = 0xF0U; +const unsigned char DT_VOICE = 0xF1U; + +const unsigned char DMR_IDLE_RX = 0x80U; +const unsigned char DMR_SYNC_DATA = 0x40U; +const unsigned char DMR_SYNC_AUDIO = 0x20U; + +const unsigned char DMR_SLOT1 = 0x00U; +const unsigned char DMR_SLOT2 = 0x80U; + +const unsigned char FID_ETSI = 0U; +const unsigned char FID_DMRA = 16U; + +enum FLCO { + FLCO_GROUP = 0, + FLCO_USER_USER = 3 +}; + +#endif diff --git a/DMRSlot.cpp b/DMRSlot.cpp new file mode 100644 index 0000000..314a320 --- /dev/null +++ b/DMRSlot.cpp @@ -0,0 +1,718 @@ +/* + * Copyright (C) 2015,2016 Jonathan Naylor, G4KLX + * + * 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; version 2 of the License. + * + * 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 for more details. + */ + +#include "SlotType.h" +#include "ShortLC.h" +#include "DMRSlot.h" +#include "Defines.h" +#include "DMRSync.h" +#include "FullLC.h" +#include "CSBK.h" +#include "Utils.h" +#include "EMB.h" +#include "CRC.h" +#include "Log.h" + +#include +#include + +unsigned int CDMRSlot::m_colorCode = 0U; +CModem* CDMRSlot::m_modem = NULL; +CHomebrewDMRIPSC* CDMRSlot::m_network = NULL; +IDisplay* CDMRSlot::m_display = NULL; + +unsigned char* CDMRSlot::m_idle = NULL; + +FLCO CDMRSlot::m_flco1; +unsigned char CDMRSlot::m_id1 = 0U; +FLCO CDMRSlot::m_flco2; +unsigned char CDMRSlot::m_id2 = 0U; + +// #define DUMP_DMR + +CDMRSlot::CDMRSlot(unsigned int slotNo, unsigned int timeout) : +m_slotNo(slotNo), +m_radioQueue(1000U), +m_networkQueue(100U), +m_state(SS_LISTENING), +m_embeddedLC(), +m_lc(NULL), +m_seqNo(0U), +m_n(0U), +m_playoutTimer(1000U, 0U, 500U), +m_networkWatchdog(1000U, 2U), +m_timeoutTimer(1000U, timeout), +m_fp(NULL) +{ +} + +CDMRSlot::~CDMRSlot() +{ +} + +void CDMRSlot::writeModem(unsigned char *data) +{ + if (data[0U] == TAG_LOST && m_state == SS_RELAYING_RF) { + LogMessage("DMR Slot %u, transmission lost", m_slotNo); + writeEndOfTransmission(); + return; + } + + if (data[0U] == TAG_LOST && m_state == SS_LATE_ENTRY) { + m_state = SS_LISTENING; + return; + } + + if (m_state == SS_RELAYING_NETWORK) + return; + + bool dataSync = (data[1U] & DMR_SYNC_DATA) == DMR_SYNC_DATA; + bool audioSync = (data[1U] & DMR_SYNC_AUDIO) == DMR_SYNC_AUDIO; + + if (dataSync) { + CSlotType slotType; + slotType.putData(data + 2U); + + unsigned char colorCode = slotType.getColorCode(); + unsigned char dataType = slotType.getDataType(); + + if (colorCode != m_colorCode) + return; + + if (dataType == DT_VOICE_LC_HEADER) { + if (m_state != SS_RELAYING_RF) { + CFullLC fullLC; + m_lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER); + if (m_lc == NULL) { + LogMessage("DMR Slot %u: unable to decode the LC", m_slotNo); + return; + } + + // Regenerate the LC + fullLC.encode(*m_lc, data + 2U, DT_VOICE_LC_HEADER); + + // Regenerate the Slot Type + slotType.getData(data + 2U); + + // Convert the Data Sync to be from the BS + CDMRSync sync; + sync.addSync(data + 2U, DST_BS_DATA); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + m_n = 0U; + + m_networkWatchdog.stop(); + m_timeoutTimer.start(); + + m_seqNo = 0U; + + for (unsigned i = 0U; i < 3U; i++) { + writeNetwork(data, DT_VOICE_LC_HEADER); + writeRadioQueue(data); + } + + m_state = SS_RELAYING_RF; + setShortLC(m_slotNo, m_lc->getDstId(), m_lc->getFLCO()); + + m_display->writeDMR(m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP, m_lc->getDstId()); + + LogMessage("DMR Slot %u, received RF header from %u to %s%u", m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP ? "TG " : "", m_lc->getDstId()); + } + } else if (dataType == DT_VOICE_PI_HEADER) { + if (m_state == SS_RELAYING_RF) { + // Regenerate the Slot Type + slotType.getData(data + 2U); + + // Convert the Data Sync to be from the BS + CDMRSync sync; + sync.addSync(data + 2U, DST_BS_DATA); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + m_n = 0U; + + writeNetwork(data, DT_VOICE_PI_HEADER); + writeRadioQueue(data); + + LogMessage("DMR Slot %u, received PI header", m_slotNo); + } else { + // Should save the PI header for after we have a valid LC + } + } else { + // Ignore wakeup CSBKs + if (dataType == DT_CSBK) { + CCSBK csbk(data + 2U); + CSBKO csbko = csbk.getCSBKO(); + if (csbko == CSBKO_BSDWNACT) + return; + } + + if (m_state == SS_RELAYING_RF) { + unsigned char end[DMR_FRAME_LENGTH_BYTES + 2U]; + + // Generate the LC + CFullLC fullLC; + fullLC.encode(*m_lc, end + 2U, DT_TERMINATOR_WITH_LC); + + // Generate the Slot Type + CSlotType slotType; + slotType.setColorCode(m_colorCode); + slotType.setDataType(DT_TERMINATOR_WITH_LC); + slotType.getData(end + 2U); + + // Set the Data Sync to be from the BS + CDMRSync sync; + sync.addSync(end + 2U, DST_BS_DATA); + + end[0U] = TAG_EOT; + end[1U] = 0x00U; + + writeNetwork(end, DT_TERMINATOR_WITH_LC); + writeRadioQueue(end); + + LogMessage("DMR Slot %u, received RF end of transmission", m_slotNo); + + // 480ms of idle to space things out + for (unsigned int i = 0U; i < 8U; i++) + writeRadioQueue(m_idle); + + writeEndOfTransmission(); + + if (dataType == DT_TERMINATOR_WITH_LC) + return; + } + + // Regenerate the Slot Type + slotType.getData(data + 2U); + + // Convert the Data Sync to be from the BS + CDMRSync sync; + sync.addSync(data + 2U, DST_BS_DATA); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetwork(data, dataType); + writeRadioQueue(data); + } + } else if (audioSync) { + if (m_state == SS_RELAYING_RF) { + // Convert the Audio Sync to be from the BS + CDMRSync sync; + sync.addSync(data + 2U, DST_BS_AUDIO); + + unsigned char fid = m_lc->getFID(); + if (fid == FID_ETSI || fid == FID_DMRA) + ; // AMBE FEC + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + m_n = 0U; + + writeRadioQueue(data); + writeNetwork(data, DT_VOICE_SYNC); + } else if (m_state == SS_LISTENING) { + m_state = SS_LATE_ENTRY; + } + } else { + if (m_state == SS_RELAYING_RF) { + // Regenerate the EMB + CEMB emb; + emb.putData(data + 2U); + emb.setColorCode(m_colorCode); + emb.getData(data + 2U); + + unsigned char fid = m_lc->getFID(); + if (fid == FID_ETSI || fid == FID_DMRA) + ; // AMBE FEC + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + m_n++; + + writeRadioQueue(data); + writeNetwork(data, DT_VOICE); + } else if (m_state == SS_LATE_ENTRY) { + // If we haven't received an LC yet, then be strict on the color code + CEMB emb; + emb.putData(data + 2U); + unsigned char colorCode = emb.getColorCode(); + if (colorCode != m_colorCode) + return; + + m_lc = m_embeddedLC.addData(data + 2U, emb.getLCSS()); + if (m_lc != NULL) { + // Create a dummy start frame to replace the received frame + unsigned char start[DMR_FRAME_LENGTH_BYTES + 2U]; + + CDMRSync sync; + sync.addSync(start + 2U, DST_BS_DATA); + + CFullLC fullLC; + fullLC.encode(*m_lc, start + 2U, DT_VOICE_LC_HEADER); + + CSlotType slotType; + slotType.setColorCode(m_colorCode); + slotType.setDataType(DT_VOICE_LC_HEADER); + slotType.getData(start + 2U); + + start[0U] = TAG_DATA; + start[1U] = 0x00U; + m_n = 0U; + + m_networkWatchdog.stop(); + m_timeoutTimer.start(); + + m_seqNo = 0U; + + for (unsigned int i = 0U; i < 3U; i++) { + writeNetwork(start, DT_VOICE_LC_HEADER); + writeRadioQueue(start); + } + + // Send the original audio frame out + unsigned char fid = m_lc->getFID(); + if (fid == FID_ETSI || fid == FID_DMRA) + ; // AMBE FEC + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + m_n++; + + writeRadioQueue(data); + writeNetwork(data, DT_VOICE); + + m_state = SS_RELAYING_RF; + + setShortLC(m_slotNo, m_lc->getDstId(), m_lc->getFLCO()); + + m_display->writeDMR(m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP, m_lc->getDstId()); + + LogMessage("DMR Slot %u, received RF late entry from %u to %s%u", m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP ? "TG " : "", m_lc->getDstId()); + } + } + } +} + +unsigned int CDMRSlot::readModem(unsigned char* data) +{ + if (m_radioQueue.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_radioQueue.getData(&len, 1U); + + m_radioQueue.getData(data, len); + + return len; +} + +void CDMRSlot::writeEndOfTransmission() +{ + m_state = SS_LISTENING; + + setShortLC(m_slotNo, 0U); + + m_display->clearDMR(m_slotNo); + + m_networkWatchdog.stop(); + m_timeoutTimer.stop(); + + delete m_lc; + m_lc = NULL; +} + +void CDMRSlot::writeNetwork(const CDMRData& dmrData) +{ + if (m_state == SS_RELAYING_RF || m_state == SS_LATE_ENTRY) + return; + + m_networkWatchdog.start(); + + unsigned char dataType = dmrData.getDataType(); + + unsigned char data[DMR_FRAME_LENGTH_BYTES + 2U]; + dmrData.getData(data + 2U); + + if (dataType == DT_VOICE_LC_HEADER) { + if (m_state != SS_RELAYING_NETWORK) { + CFullLC fullLC; + m_lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER); + if (m_lc == NULL) + m_lc = new CLC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId()); + + // Regenerate the LC + fullLC.encode(*m_lc, data + 2U, DT_VOICE_LC_HEADER); + + // Regenerate the Slot Type + CSlotType slotType; + slotType.setColorCode(m_colorCode); + slotType.setDataType(DT_VOICE_LC_HEADER); + slotType.getData(data + 2U); + + // Convert the Data Sync to be from the BS + CDMRSync sync; + sync.addSync(data + 2U, DST_BS_DATA); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + m_playoutTimer.start(); + m_timeoutTimer.start(); + + for (unsigned int i = 0U; i < 3U; i++) + writeNetworkQueue(data); + + m_state = SS_RELAYING_NETWORK; + + setShortLC(m_slotNo, m_lc->getDstId(), m_lc->getFLCO()); + + m_display->writeDMR(m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP, m_lc->getDstId()); + +#if defined(DUMP_DMR) + openFile(); + writeFile(data); +#endif + LogMessage("DMR Slot %u, received network header from %u to %s%u", m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP ? "TG " : "", m_lc->getDstId()); + } + } else if (dataType == DT_VOICE_PI_HEADER) { + if (m_state != SS_RELAYING_NETWORK) + return; + + // Regenerate the Slot Type + CSlotType slotType; + slotType.setColorCode(m_colorCode); + slotType.setDataType(DT_VOICE_PI_HEADER); + slotType.getData(data + 2U); + + // Convert the Data Sync to be from the BS + CDMRSync sync; + sync.addSync(data + 2U, DST_BS_DATA); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetworkQueue(data); + +#if defined(DUMP_DMR) + writeFile(data); +#endif + } else if (dataType == DT_TERMINATOR_WITH_LC) { + if (m_state != SS_RELAYING_NETWORK) + return; + + // Regenerate the LC + CFullLC fullLC; + fullLC.encode(*m_lc, data + 2U, DT_TERMINATOR_WITH_LC); + + // Regenerate the Slot Type + CSlotType slotType; + slotType.setColorCode(m_colorCode); + slotType.setDataType(DT_TERMINATOR_WITH_LC); + slotType.getData(data + 2U); + + // Convert the Data Sync to be from the BS + CDMRSync sync; + sync.addSync(data + 2U, DST_BS_DATA); + + data[0U] = TAG_EOT; + data[1U] = 0x00U; + + writeNetworkQueue(data); + writeEndOfTransmission(); + +#if defined(DUMP_DMR) + writeFile(data); + closeFile(); +#endif + LogMessage("DMR Slot %u, received network end of transmission", m_slotNo); + } else if (dataType == DT_VOICE_SYNC) { + if (m_state != SS_RELAYING_NETWORK) + return; + + // Convert the Audio Sync to be from the BS + CDMRSync sync; + sync.addSync(data + 2U, DST_BS_AUDIO); + + unsigned char fid = m_lc->getFID(); + if (fid == FID_ETSI || fid == FID_DMRA) + ; // AMBE FEC + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetworkQueue(data); + +#if defined(DUMP_DMR) + writeFile(data); +#endif + } else if (dataType == DT_VOICE) { + if (m_state != SS_RELAYING_NETWORK) + return; + + unsigned char fid = m_lc->getFID(); + if (fid == FID_ETSI || fid == FID_DMRA) + ; // AMBE FEC + + // Change the color code in the EMB + CEMB emb; + emb.putData(data + 2U); + emb.setColorCode(m_colorCode); + emb.getData(data + 2U); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetworkQueue(data); + +#if defined(DUMP_DMR) + writeFile(data); +#endif + } else { + if (m_state != SS_RELAYING_NETWORK) + return; + + // Change the Color Code of the Slot Type + CSlotType slotType; + slotType.putData(data + 2U); + slotType.setColorCode(m_colorCode); + slotType.getData(data + 2U); + + // Convert the Data Sync to be from the BS + CDMRSync sync; + sync.addSync(data + 2U, DST_BS_DATA); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetworkQueue(data); + +#if defined(DUMP_DMR) + writeFile(data); +#endif + } +} + +void CDMRSlot::clock(unsigned int ms) +{ + m_timeoutTimer.clock(ms); + + m_playoutTimer.clock(ms); + if (m_playoutTimer.isRunning() && m_playoutTimer.hasExpired()) { + while (!m_networkQueue.isEmpty()) { + unsigned char len = 0U; + m_networkQueue.getData(&len, 1U); + + unsigned char buffer[100U]; + m_networkQueue.getData(buffer, len); + + m_radioQueue.addData(&len, 1U); + m_radioQueue.addData(buffer, len); + } + } + + if (m_state == SS_RELAYING_NETWORK) { + m_networkWatchdog.clock(ms); + + if (m_networkWatchdog.hasExpired()) { + LogMessage("DMR Slot %u, network watchdog has expired", m_slotNo); + writeEndOfTransmission(); +#if defined(DUMP_DMR) + closeFile(); +#endif + } + } +} + +void CDMRSlot::writeRadioQueue(const unsigned char *data) +{ + unsigned char len = DMR_FRAME_LENGTH_BYTES + 2U; + m_radioQueue.addData(&len, 1U); + + // If the timeout has expired, replace the audio with idles to keep the slot busy + if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) + m_radioQueue.addData(m_idle, len); + else + m_radioQueue.addData(data, len); +} + +void CDMRSlot::writeNetworkQueue(const unsigned char *data) +{ + // If the timeout has expired, send nothing + if (!m_timeoutTimer.isRunning() || !m_timeoutTimer.hasExpired()) { + unsigned char len = DMR_FRAME_LENGTH_BYTES + 2U; + m_networkQueue.addData(&len, 1U); + + m_networkQueue.addData(data, len); + } +} + +void CDMRSlot::writeNetwork(const unsigned char* data, unsigned char dataType) +{ + assert(m_lc != NULL); + + if (m_network == NULL) + return; + + // Don't send to the network if the timeout has expired + if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) + return; + + CDMRData dmrData; + dmrData.setSlotNo(m_slotNo); + dmrData.setDataType(dataType); + dmrData.setSrcId(m_lc->getSrcId()); + dmrData.setDstId(m_lc->getDstId()); + dmrData.setFLCO(m_lc->getFLCO()); + dmrData.setN(m_n); + dmrData.setSeqNo(m_seqNo); + + m_seqNo++; + + dmrData.setData(data + 2U); + + m_network->write(dmrData); +} + +void CDMRSlot::init(unsigned int colorCode, CModem* modem, CHomebrewDMRIPSC* network, IDisplay* display) +{ + assert(modem != NULL); + assert(display != NULL); + + m_colorCode = colorCode; + m_modem = modem; + m_network = network; + m_display = display; + + m_idle = new unsigned char[DMR_FRAME_LENGTH_BYTES + 2U]; + + ::memcpy(m_idle + 2U, IDLE_DATA, DMR_FRAME_LENGTH_BYTES); + + // Generate the Slot Type + CSlotType slotType; + slotType.setColorCode(colorCode); + slotType.setDataType(DT_IDLE); + slotType.getData(m_idle + 2U); + + m_idle[0U] = TAG_DATA; + m_idle[1U] = 0x00U; +} + +void CDMRSlot::setShortLC(unsigned int slotNo, unsigned int id, FLCO flco) +{ + assert(m_modem != NULL); + + switch (slotNo) { + case 1U: + m_id1 = 0U; + m_flco1 = flco; + if (id != 0U) { + unsigned char buffer[3U]; + buffer[0U] = (id << 16) & 0xFFU; + buffer[1U] = (id << 8) & 0xFFU; + buffer[2U] = (id << 0) & 0xFFU; + m_id1 = CCRC::crc8(buffer, 3U); + } + break; + case 2U: + m_id2 = 0U; + m_flco2 = flco; + if (id != 0U) { + unsigned char buffer[3U]; + buffer[0U] = (id << 16) & 0xFFU; + buffer[1U] = (id << 8) & 0xFFU; + buffer[2U] = (id << 0) & 0xFFU; + m_id2 = CCRC::crc8(buffer, 3U); + } + break; + default: + LogError("Invalid slot number passed to setShortLC - %u", slotNo); + return; + } + + unsigned char lc[5U]; + lc[0U] = 0x01U; + lc[1U] = 0x00U; + lc[2U] = 0x00U; + lc[3U] = 0x00U; + + if (m_id1 != 0U) { + lc[2U] = m_id1; + if (m_flco1 == FLCO_GROUP) + lc[1U] |= 0x80U; + else + lc[1U] |= 0x90U; + } + + if (m_id2 != 0U) { + lc[3U] = m_id2; + if (m_flco2 == FLCO_GROUP) + lc[1U] |= 0x08U; + else + lc[1U] |= 0x09U; + } + + lc[4U] = CCRC::crc8(lc, 4U); + + unsigned char sLC[9U]; + + CUtils::dump(1U, "Input Short LC", lc, 5U); + + CShortLC shortLC; + shortLC.encode(lc, sLC); + + CUtils::dump(1U, "Output Short LC", sLC, 9U); + + m_modem->writeDMRShortLC(sLC); +} + +bool CDMRSlot::openFile() +{ + if (m_fp != NULL) + return true; + + time_t t; + ::time(&t); + + struct tm* tm = ::localtime(&t); + + char name[100U]; + ::sprintf(name, "DMR_%u_%04d%02d%02d_%02d%02d%02d.ambe", m_slotNo, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + m_fp = ::fopen(name, "wb"); + if (m_fp == NULL) + return false; + + ::fwrite("DMR", 1U, 3U, m_fp); + + return true; +} + +bool CDMRSlot::writeFile(const unsigned char* data) +{ + if (m_fp == NULL) + return false; + + ::fwrite(data, 1U, DMR_FRAME_LENGTH_BYTES + 2U, m_fp); + + return true; +} + +void CDMRSlot::closeFile() +{ + if (m_fp != NULL) { + ::fclose(m_fp); + m_fp = NULL; + } +} diff --git a/DMRSlot.h b/DMRSlot.h new file mode 100644 index 0000000..d3d9dc1 --- /dev/null +++ b/DMRSlot.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(DMRSlot_H) +#define DMRSlot_H + +#include "HomebrewDMRIPSC.h" +#include "EmbeddedLC.h" +#include "RingBuffer.h" +#include "DMRSlot.h" +#include "DMRData.h" +#include "Display.h" +#include "Timer.h" +#include "Modem.h" +#include "LC.h" + +enum SLOT_STATE { + SS_LISTENING, + SS_LATE_ENTRY, + SS_RELAYING_RF, + SS_RELAYING_NETWORK +}; + +class CDMRSlot { +public: + CDMRSlot(unsigned int slotNo, unsigned int timeout); + ~CDMRSlot(); + + void writeModem(unsigned char* data); + + unsigned int readModem(unsigned char* data); + + void writeNetwork(const CDMRData& data); + + void clock(unsigned int ms); + + static void init(unsigned int colorCode, CModem* modem, CHomebrewDMRIPSC* network, IDisplay* display); + +private: + unsigned int m_slotNo; + CRingBuffer m_radioQueue; + CRingBuffer m_networkQueue; + SLOT_STATE m_state; + CEmbeddedLC m_embeddedLC; + CLC* m_lc; + unsigned char m_seqNo; + unsigned char m_n; + CTimer m_playoutTimer; + CTimer m_networkWatchdog; + CTimer m_timeoutTimer; + FILE* m_fp; + + static unsigned int m_colorCode; + static CModem* m_modem; + static CHomebrewDMRIPSC* m_network; + static IDisplay* m_display; + + static unsigned char* m_idle; + + static FLCO m_flco1; + static unsigned char m_id1; + static FLCO m_flco2; + static unsigned char m_id2; + + void writeRadioQueue(const unsigned char* data); + void writeNetworkQueue(const unsigned char* data); + void writeNetwork(const unsigned char* data, unsigned char dataType); + + void writeEndOfTransmission(); + + bool openFile(); + bool writeFile(const unsigned char* data); + void closeFile(); + + static void setShortLC(unsigned int slotNo, unsigned int id, FLCO flco = FLCO_GROUP); +}; + +#endif diff --git a/DMRSync.cpp b/DMRSync.cpp new file mode 100644 index 0000000..b1bc078 --- /dev/null +++ b/DMRSync.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DMRSync.h" +#include "DMRDefines.h" + +#include +#include + +const unsigned int BITS_LOOKUP[] = {0U, 1U, 1U, 2U, 1U, 2U, 2U, 3U, 1U, 2U, 2U, 3U, 2U, 3U, 3U, 4U, + 1U, 2U, 2U, 3U, 2U, 3U, 3U, 4U, 2U, 3U, 3U, 4U, 3U, 4U, 4U, 5U, + 1U, 2U, 2U, 3U, 2U, 3U, 3U, 4U, 2U, 3U, 3U, 4U, 3U, 4U, 4U, 5U, + 2U, 3U, 3U, 4U, 3U, 4U, 4U, 5U, 3U, 4U, 4U, 5U, 4U, 5U, 5U, 6U, + 1U, 2U, 2U, 3U, 2U, 3U, 3U, 4U, 2U, 3U, 3U, 4U, 3U, 4U, 4U, 5U, + 2U, 3U, 3U, 4U, 3U, 4U, 4U, 5U, 3U, 4U, 4U, 5U, 4U, 5U, 5U, 6U, + 2U, 3U, 3U, 4U, 3U, 4U, 4U, 5U, 3U, 4U, 4U, 5U, 4U, 5U, 5U, 6U, + 3U, 4U, 4U, 5U, 4U, 5U, 5U, 6U, 4U, 5U, 5U, 6U, 5U, 6U, 6U, 7U, + 1U, 2U, 2U, 3U, 2U, 3U, 3U, 4U, 2U, 3U, 3U, 4U, 3U, 4U, 4U, 5U, + 2U, 3U, 3U, 4U, 3U, 4U, 4U, 5U, 3U, 4U, 4U, 5U, 4U, 5U, 5U, 6U, + 2U, 3U, 3U, 4U, 3U, 4U, 4U, 5U, 3U, 4U, 4U, 5U, 4U, 5U, 5U, 6U, + 3U, 4U, 4U, 5U, 4U, 5U, 5U, 6U, 4U, 5U, 5U, 6U, 5U, 6U, 6U, 7U, + 2U, 3U, 3U, 4U, 3U, 4U, 4U, 5U, 3U, 4U, 4U, 5U, 4U, 5U, 5U, 6U, + 3U, 4U, 4U, 5U, 4U, 5U, 5U, 6U, 4U, 5U, 5U, 6U, 5U, 6U, 6U, 7U, + 3U, 4U, 4U, 5U, 4U, 5U, 5U, 6U, 4U, 5U, 5U, 6U, 5U, 6U, 6U, 7U, + 4U, 5U, 5U, 6U, 5U, 6U, 6U, 7U, 5U, 6U, 6U, 7U, 6U, 7U, 7U, 8U}; + +const unsigned int THRESHOLD = 4U; + +CDMRSync::CDMRSync() +{ +} + +CDMRSync::~CDMRSync() +{ +} + +DMR_SYNC_TYPE CDMRSync::matchDirectSync(const unsigned char* bytes) const +{ + assert(bytes != NULL); + + unsigned int diffs = compareBytes(bytes + 13U, DIRECT_SLOT1_AUDIO_SYNC); + if (diffs < THRESHOLD) + return DST_DIRECT_SLOT1_AUDIO; + + diffs = compareBytes(bytes + 13U, DIRECT_SLOT2_AUDIO_SYNC); + if (diffs < THRESHOLD) + return DST_DIRECT_SLOT2_AUDIO; + + diffs = compareBytes(bytes + 13U, DIRECT_SLOT1_DATA_SYNC); + if (diffs < THRESHOLD) + return DST_DIRECT_SLOT1_DATA; + + diffs = compareBytes(bytes + 13U, DIRECT_SLOT2_DATA_SYNC); + if (diffs < THRESHOLD) + return DST_DIRECT_SLOT2_DATA; + + return DST_NONE; +} + +DMR_SYNC_TYPE CDMRSync::matchMSSync(const unsigned char* bytes) const +{ + assert(bytes != NULL); + + unsigned int diffs = compareBytes(bytes + 13U, MS_SOURCED_AUDIO_SYNC); + if (diffs < THRESHOLD) + return DST_MS_AUDIO; + + diffs = compareBytes(bytes + 13U, MS_SOURCED_DATA_SYNC); + if (diffs < THRESHOLD) + return DST_MS_DATA; + + return DST_NONE; +} + +DMR_SYNC_TYPE CDMRSync::matchBSSync(const unsigned char* bytes) const +{ + assert(bytes != NULL); + + unsigned int diffs = compareBytes(bytes + 13U, BS_SOURCED_AUDIO_SYNC); + if (diffs < THRESHOLD) + return DST_BS_AUDIO; + + diffs = compareBytes(bytes + 13U, BS_SOURCED_DATA_SYNC); + if (diffs < THRESHOLD) + return DST_BS_DATA; + + return DST_NONE; +} + +void CDMRSync::addSync(unsigned char* data, DMR_SYNC_TYPE type) const +{ + assert(data != NULL); + + const unsigned char* sync = NULL; + switch (type) { + case DST_BS_AUDIO: + sync = BS_SOURCED_AUDIO_SYNC; + break; + case DST_BS_DATA: + sync = BS_SOURCED_DATA_SYNC; + break; + case DST_MS_AUDIO: + sync = MS_SOURCED_AUDIO_SYNC; + break; + case DST_MS_DATA: + sync = MS_SOURCED_DATA_SYNC; + break; + case DST_DIRECT_SLOT1_AUDIO: + sync = DIRECT_SLOT1_AUDIO_SYNC; + break; + case DST_DIRECT_SLOT1_DATA: + sync = DIRECT_SLOT1_DATA_SYNC; + break; + case DST_DIRECT_SLOT2_AUDIO: + sync = DIRECT_SLOT2_AUDIO_SYNC; + break; + case DST_DIRECT_SLOT2_DATA: + sync = DIRECT_SLOT2_DATA_SYNC; + break; + default: + return; + } + + for (unsigned int i = 0U; i < 7U; i++) + data[i + 13U] = (data[i + 13U] & ~SYNC_MASK[i]) | sync[i]; +} + +unsigned int CDMRSync::compareBytes(const unsigned char *bytes1, const unsigned char* bytes2) const +{ + assert(bytes1 != NULL); + assert(bytes2 != NULL); + + unsigned int diffs = 0U; + for (unsigned int i = 0U; i < 7U; i++) { + unsigned char b = SYNC_MASK[i] & (bytes1[i] ^ bytes2[i]); + diffs += BITS_LOOKUP[b]; + } + + return diffs; +} diff --git a/DMRSync.h b/DMRSync.h new file mode 100644 index 0000000..8534543 --- /dev/null +++ b/DMRSync.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(DMRSYNC_H) +#define DMRSYNC_H + +enum DMR_SYNC_TYPE { + DST_BS_AUDIO, + DST_BS_DATA, + + DST_MS_AUDIO, + DST_MS_DATA, + + DST_DIRECT_SLOT1_AUDIO, + DST_DIRECT_SLOT1_DATA, + + DST_DIRECT_SLOT2_AUDIO, + DST_DIRECT_SLOT2_DATA, + + DST_NONE +}; + +class CDMRSync +{ +public: + CDMRSync(); + ~CDMRSync(); + + DMR_SYNC_TYPE matchDirectSync(const unsigned char* bytes) const; + + DMR_SYNC_TYPE matchMSSync(const unsigned char* bytes) const; + + DMR_SYNC_TYPE matchBSSync(const unsigned char* bytes) const; + + void addSync(unsigned char* data, DMR_SYNC_TYPE type) const; + +private: + unsigned int compareBytes(const unsigned char* bytes1, const unsigned char* bytes2) const; +}; + +#endif diff --git a/DStarDefines.h b/DStarDefines.h new file mode 100644 index 0000000..9c3fbea --- /dev/null +++ b/DStarDefines.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(DStarDefines_H) +#define DStarDefines_H + +const unsigned int DSTAR_HEADER_LENGTH_BYTES = 41U; +const unsigned int DSTAR_FRAME_LENGTH_BYTES = 12U; + +// Check this +const unsigned char DSTAR_SYNC_BYTES[] = {0x55U, 0x2DU, 0x16U}; + +#endif diff --git a/DStarEcho.cpp b/DStarEcho.cpp new file mode 100644 index 0000000..8398e20 --- /dev/null +++ b/DStarEcho.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarEcho.h" + +CDStarEcho::CDStarEcho(unsigned int delay, unsigned int space) : +m_buffer(space), +m_timer(1000U, delay) +{ +} + +CDStarEcho::~CDStarEcho() +{ +} + +unsigned int CDStarEcho::readData(unsigned char* data) +{ + if (!hasData()) + return 0U; + + unsigned char len; + m_buffer.getData(&len, 1U); + + m_buffer.getData(data, len); + + if (!hasData()) + m_timer.stop(); + + return len; +} + +bool CDStarEcho::writeData(const unsigned char* data, unsigned int length) +{ + bool ret = m_buffer.hasSpace(length + 1U); + if (!ret) + return false; + + unsigned char len = length; + m_buffer.addData(&len, 1U); + + m_buffer.addData(data, length); + + m_timer.start(); + + return true; +} + +bool CDStarEcho::hasData() +{ + if (m_timer.isRunning() && m_timer.hasExpired()) + return m_buffer.hasData(); + else + return false; +} + +void CDStarEcho::clock(unsigned int ms) +{ + m_timer.clock(ms); +} diff --git a/DStarEcho.h b/DStarEcho.h new file mode 100644 index 0000000..b667c92 --- /dev/null +++ b/DStarEcho.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(DSTARECHO_H) +#define DSTARECHO_H + +#include "RingBuffer.h" +#include "Timer.h" + +class CDStarEcho { +public: + CDStarEcho(unsigned int delay, unsigned int space); + ~CDStarEcho(); + + unsigned int readData(unsigned char* data); + + bool writeData(const unsigned char* data, unsigned int length); + + bool hasData(); + + void clock(unsigned int ms); + +private: + CRingBuffer m_buffer; + CTimer m_timer; +}; + +#endif diff --git a/Defines.h b/Defines.h new file mode 100644 index 0000000..4fd684a --- /dev/null +++ b/Defines.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(Defines_H) +#define Defines_H + +const unsigned char MODE_IDLE = 0U; +const unsigned char MODE_DSTAR = 1U; +const unsigned char MODE_DMR = 2U; +const unsigned char MODE_YSF = 3U; + +const unsigned char TAG_HEADER = 0x00U; +const unsigned char TAG_DATA = 0x01U; +const unsigned char TAG_LOST = 0x02U; +const unsigned char TAG_EOT = 0x03U; + +#endif diff --git a/Display.cpp b/Display.cpp new file mode 100644 index 0000000..54b514b --- /dev/null +++ b/Display.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Display.h" + +IDisplay::~IDisplay() +{ +} diff --git a/Display.h b/Display.h new file mode 100644 index 0000000..1318832 --- /dev/null +++ b/Display.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(DISPLAY_H) +#define DISPLAY_H + +#include + +class IDisplay +{ +public: + virtual ~IDisplay() = 0; + + virtual bool open() = 0; + + virtual void setIdle() = 0; + + virtual void setDStar() = 0; + virtual void writeDStar(const std::string& call1, const std::string& call2) = 0; + virtual void clearDStar() = 0; + + virtual void setDMR() = 0; + virtual void writeDMR(unsigned int slotNo, unsigned int srdId, bool group, unsigned int dstId) = 0; + virtual void clearDMR(unsigned int slotNo) = 0; + + virtual void setFusion() = 0; + virtual void writeFusion(const std::string& callsign) = 0; + virtual void clearFusion() = 0; + + virtual void close() = 0; + +private: +}; + +#endif diff --git a/EMB.cpp b/EMB.cpp new file mode 100644 index 0000000..c2053df --- /dev/null +++ b/EMB.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "EMB.h" + +#include "QR1676.h" + +#include +#include + +CEMB::CEMB() : +m_colorCode(0U), +m_PI(false), +m_LCSS(0U) +{ +} + +CEMB::~CEMB() +{ +} + +void CEMB::putData(const unsigned char* data) +{ + assert(data != NULL); + + unsigned char emb[2U]; + emb[0U] = (data[13U] << 4) & 0xF0U; + emb[0U] |= (data[14U] >> 4) & 0x0FU; + emb[1U] = (data[18U] << 4) & 0xF0U; + emb[1U] |= (data[19U] >> 4) & 0x0FU; + + CQR1676::decode(emb); + + m_colorCode = (emb[0U] >> 4) & 0x0FU; + m_PI = (emb[0U] & 0x08U) == 0x08U; + m_LCSS = (emb[0U] >> 1) & 0x03U; +} + +void CEMB::getData(unsigned char* data) const +{ + assert(data != NULL); + + unsigned char emb[2U]; + emb[0U] = (m_colorCode << 4) & 0xF0U; + emb[0U] |= m_PI ? 0x08U : 0x00U; + emb[0U] |= (m_LCSS << 1) & 0x06U; + emb[1U] = 0x00U; + + CQR1676::encode(emb); + + data[13U] = (data[13U] & 0xF0U) | ((emb[0U] >> 4U) & 0x0FU); + data[14U] = (data[14U] & 0x0FU) | ((emb[0U] << 4U) & 0xF0U); + data[18U] = (data[18U] & 0xF0U) | ((emb[1U] >> 4U) & 0x0FU); + data[19U] = (data[19U] & 0x0FU) | ((emb[1U] << 4U) & 0xF0U); +} + +unsigned char CEMB::getColorCode() const +{ + return m_colorCode; +} + +void CEMB::setColorCode(unsigned char code) +{ + m_colorCode = code; +} + +bool CEMB::getPI() const +{ + return m_PI; +} + +void CEMB::setPI(bool pi) +{ + m_PI = pi; +} + +unsigned char CEMB::getLCSS() const +{ + return m_LCSS; +} + +void CEMB::setLCSS(unsigned char lcss) +{ + m_LCSS = lcss; +} diff --git a/EMB.h b/EMB.h new file mode 100644 index 0000000..4283c6b --- /dev/null +++ b/EMB.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(EMB_H) +#define EMB_H + +class CEMB +{ +public: + CEMB(); + ~CEMB(); + + void putData(const unsigned char* data); + void getData(unsigned char* data) const; + + unsigned char getColorCode() const; + void setColorCode(unsigned char code); + + bool getPI() const; + void setPI(bool pi); + + unsigned char getLCSS() const; + void setLCSS(unsigned char lcss); + +private: + unsigned char m_colorCode; + bool m_PI; + unsigned char m_LCSS; +}; + +#endif + diff --git a/EmbeddedLC.cpp b/EmbeddedLC.cpp new file mode 100644 index 0000000..c70ac7d --- /dev/null +++ b/EmbeddedLC.cpp @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "EmbeddedLC.h" + +#include "Hamming.h" +#include "Utils.h" +#include "CRC.h" +#include "Log.h" + +#include +#include +#include + +CEmbeddedLC::CEmbeddedLC() : +m_rawLC(NULL), +m_state(LCS_NONE) +{ + m_rawLC = new bool[128U]; +} + +CEmbeddedLC::~CEmbeddedLC() +{ + delete[] m_rawLC; +} + +// Add LC data (which may consist of 4 blocks) to the data store +CLC* CEmbeddedLC::addData(const unsigned char* data, unsigned char lcss) +{ + assert(data != NULL); + + bool rawData[40U]; + CUtils::byteToBitsBE(data[14U], rawData + 0U); + CUtils::byteToBitsBE(data[15U], rawData + 8U); + CUtils::byteToBitsBE(data[16U], rawData + 16U); + CUtils::byteToBitsBE(data[17U], rawData + 24U); + CUtils::byteToBitsBE(data[18U], rawData + 32U); + + // Is this the first block of a 4 block embedded LC ? + if (lcss == 1U) { + for (unsigned int a = 0U; a < 32U; a++) + m_rawLC[a] = rawData[a + 4U]; + + // Show we are ready for the next LC block + m_state = LCS_FIRST; + return NULL; + } + + // Is this the 2nd block of a 4 block embedded LC ? + if (lcss == 3U && m_state == LCS_FIRST) { + for (unsigned int a = 0U; a < 32U; a++) + m_rawLC[a + 32U] = rawData[a + 4U]; + + // Show we are ready for the next LC block + m_state = LCS_SECOND; + return NULL; + } + + // Is this the 3rd block of a 4 block embedded LC ? + if (lcss == 3U && m_state == LCS_SECOND) { + for (unsigned int a = 0U; a < 32U; a++) + m_rawLC[a + 64U] = rawData[a + 4U]; + + // Show we are ready for the final LC block + m_state = LCS_THIRD; + return NULL; + } + + // Is this the final block of a 4 block embedded LC ? + if (lcss == 2U && m_state == LCS_THIRD) { + for (unsigned int a = 0U; a < 32U; a++) + m_rawLC[a + 96U] = rawData[a + 4U]; + + // Process the complete data block + return processMultiBlockEmbeddedLC(); + } + + // Is this a single block embedded LC + if (lcss == 0U) { + processSingleBlockEmbeddedLC(rawData + 4U); + return NULL; + } + + return NULL; +} + +void CEmbeddedLC::setData(const CLC& lc) +{ + bool lcData[72U]; + lc.getData(lcData); + + unsigned int crc; + CCRC::encodeFiveBit(lcData, crc); + + bool data[128U]; + ::memset(data, 0x00U, 128U * sizeof(bool)); + + data[106U] = (crc & 0x01U) == 0x01U; + data[90U] = (crc & 0x02U) == 0x02U; + data[74U] = (crc & 0x04U) == 0x04U; + data[58U] = (crc & 0x08U) == 0x08U; + data[42U] = (crc & 0x10U) == 0x10U; + + unsigned int b = 0U; + for (unsigned int a = 0U; a < 11U; a++, b++) + data[a] = lcData[b]; + for (unsigned int a = 16U; a < 27U; a++, b++) + data[a] = lcData[b]; + for (unsigned int a = 32U; a < 42U; a++, b++) + data[a] = lcData[b]; + for (unsigned int a = 48U; a < 58U; a++, b++) + data[a] = lcData[b]; + for (unsigned int a = 64U; a < 74U; a++, b++) + data[a] = lcData[b]; + for (unsigned int a = 80U; a < 90U; a++, b++) + data[a] = lcData[b]; + for (unsigned int a = 96U; a < 106U; a++, b++) + data[a] = lcData[b]; + + // Hamming (16,11,4) check each row except the last one + for (unsigned int a = 0U; a < 112U; a += 16U) + CHamming::encode16114(data + a); + + // Add the parity bits for each column + for (unsigned int a = 0U; a < 16U; a++) + data[a + 112U] = data[a + 0U] ^ data[a + 16U] ^ data[a + 32U] ^ data[a + 48U] ^ data[a + 64U] ^ data[a + 80U] ^ data[a + 96U]; + + // The data is packed downwards in columns + b = 0U; + for (unsigned int a = 0U; a < 128U; a++) { + m_rawLC[a] = data[b]; + b += 16U; + if (b > 127U) + b -= 127U; + } +} + +unsigned int CEmbeddedLC::getData(unsigned char* data, unsigned int n) const +{ + assert(data != NULL); + + if (n < 4U) { + bool bits[40U]; + ::memset(bits, 0x00U, 40U * sizeof(bool)); + ::memcpy(bits + 4U, m_rawLC + n * 32U, 32U * sizeof(bool)); + + unsigned char bytes[5U]; + CUtils::bitsToByteBE(bits + 0U, bytes[0U]); + CUtils::bitsToByteBE(bits + 8U, bytes[1U]); + CUtils::bitsToByteBE(bits + 16U, bytes[2U]); + CUtils::bitsToByteBE(bits + 24U, bytes[3U]); + CUtils::bitsToByteBE(bits + 32U, bytes[4U]); + + data[14U] = (data[14U] & 0xF0U) | (bytes[0U] & 0x0FU); + data[15U] = bytes[1U]; + data[16U] = bytes[2U]; + data[17U] = bytes[3U]; + data[18U] = (data[18U] & 0x0FU) | (bytes[4U] & 0xF0U); + } else { + data[14U] &= 0xF0U; + data[15U] = 0x00U; + data[16U] = 0x00U; + data[17U] = 0x00U; + data[18U] &= 0x0FU; + } + + switch (n) { + case 0U: + return 1U; + case 1U: + case 2U: + return 3U; + case 3U: + return 2U; + default: + return 0U; + } +} + +// Unpack and error check an embedded LC +CLC* CEmbeddedLC::processMultiBlockEmbeddedLC() +{ + // The data is unpacked downwards in columns + bool data[128U]; + ::memset(data, 0x00U, 128U * sizeof(bool)); + + unsigned int b = 0U; + for (unsigned int a = 0U; a < 128U; a++) { + data[b] = m_rawLC[a]; + b += 16U; + if (b > 127U) + b -= 127U; + } + + // Hamming (16,11,4) check each row except the last one + for (unsigned int a = 0U; a < 112U; a += 16U) { + if (!CHamming::decode16114(data + a)) { + ::LogDebug("Hamming decode of a row of the Embedded LC failed"); + return NULL; + } + } + + // Check the parity bits + for (unsigned int a = 0U; a < 16U; a++) { + bool parity = data[a + 0U] ^ data[a + 16U] ^ data[a + 32U] ^ data[a + 48U] ^ data[a + 64U] ^ data[a + 80U] ^ data[a + 96U] ^ data[a + 112U]; + if (parity) { + ::LogDebug("Parity check of a column of the Embedded LC failed"); + return NULL; + } + } + + // We have passed the Hamming check so extract the actual payload + bool lcData[72U]; + b = 0U; + for (unsigned int a = 0U; a < 11U; a++, b++) + lcData[b] = data[a]; + for (unsigned int a = 16U; a < 27U; a++, b++) + lcData[b] = data[a]; + for (unsigned int a = 32U; a < 42U; a++, b++) + lcData[b] = data[a]; + for (unsigned int a = 48U; a < 58U; a++, b++) + lcData[b] = data[a]; + for (unsigned int a = 64U; a < 74U; a++, b++) + lcData[b] = data[a]; + for (unsigned int a = 80U; a < 90U; a++, b++) + lcData[b] = data[a]; + for (unsigned int a = 96U; a < 106U; a++, b++) + lcData[b] = data[a]; + + // Extract the 5 bit CRC + unsigned int crc = 0U; + if (data[42]) crc += 16U; + if (data[58]) crc += 8U; + if (data[74]) crc += 4U; + if (data[90]) crc += 2U; + if (data[106]) crc += 1U; + + // Now CRC check this + if (!CCRC::checkFiveBit(lcData, crc)) { + ::LogDebug("Checksum of the Embedded LC failed"); + return NULL; + } + + return new CLC(lcData); +} + +// Deal with a single block embedded LC +void CEmbeddedLC::processSingleBlockEmbeddedLC(const bool* data) +{ + // Nothing interesting, or just NULL (I think) +} diff --git a/EmbeddedLC.h b/EmbeddedLC.h new file mode 100644 index 0000000..f7c762b --- /dev/null +++ b/EmbeddedLC.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef EmbeddedLC_H +#define EmbeddedLC_H + +#include "LC.h" + +enum LC_STATE { + LCS_NONE, + LCS_FIRST, + LCS_SECOND, + LCS_THIRD +}; + +class CEmbeddedLC +{ +public: + CEmbeddedLC(); + ~CEmbeddedLC(); + + CLC* addData(const unsigned char* data, unsigned char lcss); + + void setData(const CLC& lc); + unsigned int getData(unsigned char* data, unsigned int n) const; + +private: + bool* m_rawLC; + LC_STATE m_state; + + CLC* processMultiBlockEmbeddedLC(); + void processSingleBlockEmbeddedLC(const bool* data); +}; + +#endif + diff --git a/FullLC.cpp b/FullLC.cpp new file mode 100644 index 0000000..135678a --- /dev/null +++ b/FullLC.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2012 by Ian Wraith + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "FullLC.h" +#include "Log.h" +#include "DMRDefines.h" +#include "RS129.h" + +#include +#include + +CFullLC::CFullLC() : +m_bptc() +{ +} + +CFullLC::~CFullLC() +{ +} + +CLC* CFullLC::decode(const unsigned char* data, unsigned char type) +{ + assert(data != NULL); + + unsigned char lcData[12U]; + m_bptc.decode(data, lcData); + + switch (type) { + case DT_VOICE_LC_HEADER: + lcData[9U] ^= VOICE_LC_HEADER_CRC_MASK[0U]; + lcData[10U] ^= VOICE_LC_HEADER_CRC_MASK[1U]; + lcData[11U] ^= VOICE_LC_HEADER_CRC_MASK[2U]; + break; + + case DT_TERMINATOR_WITH_LC: + lcData[9U] ^= TERMINATOR_WITH_LC_CRC_MASK[0U]; + lcData[10U] ^= TERMINATOR_WITH_LC_CRC_MASK[1U]; + lcData[11U] ^= TERMINATOR_WITH_LC_CRC_MASK[2U]; + break; + + default: + ::LogError("Unsupported LC type - %d", int(type)); + return NULL; + } + + if (!CRS129::check(lcData)) { + ::LogDebug("Checksum failed for the LC"); + CLC lc(lcData); + LogDebug("Invalid LC, src = %u, dst = %s%u", lc.getSrcId(), lc.getFLCO() == FLCO_GROUP ? "TG " : "", lc.getDstId()); + return NULL; + } + + return new CLC(lcData); +} + +void CFullLC::encode(const CLC& lc, unsigned char* data, unsigned char type) +{ + assert(data != NULL); + + unsigned char lcData[12U]; + lc.getData(lcData); + + unsigned char parity[4U]; + CRS129::encode(lcData, 9U, parity); + + switch (type) { + case DT_VOICE_LC_HEADER: + lcData[9U] = parity[2U] ^ VOICE_LC_HEADER_CRC_MASK[0U]; + lcData[10U] = parity[1U] ^ VOICE_LC_HEADER_CRC_MASK[1U]; + lcData[11U] = parity[0U] ^ VOICE_LC_HEADER_CRC_MASK[2U]; + break; + + case DT_TERMINATOR_WITH_LC: + lcData[9U] = parity[2U] ^ TERMINATOR_WITH_LC_CRC_MASK[0U]; + lcData[10U] = parity[1U] ^ TERMINATOR_WITH_LC_CRC_MASK[1U]; + lcData[11U] = parity[0U] ^ TERMINATOR_WITH_LC_CRC_MASK[2U]; + break; + + default: + ::LogError("Unsupported LC type - %d", int(type)); + return; + } + + m_bptc.encode(lcData, data); +} diff --git a/FullLC.h b/FullLC.h new file mode 100644 index 0000000..28ba8cf --- /dev/null +++ b/FullLC.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef FullLC_H +#define FullLC_H + +#include "LC.h" +#include "SlotType.h" + +#include "BPTC19696.h" + +class CFullLC +{ +public: + CFullLC(); + ~CFullLC(); + + CLC* decode(const unsigned char* data, unsigned char type); + + void encode(const CLC& lc, unsigned char* data, unsigned char type); + +private: + CBPTC19696 m_bptc; +}; + +#endif + diff --git a/Golay2087.cpp b/Golay2087.cpp new file mode 100644 index 0000000..61942d8 --- /dev/null +++ b/Golay2087.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Golay2087.h" + +#include +#include + +const unsigned int ENCODING_TABLE_2087[] = + {0x0000U, 0xB08EU, 0xE093U, 0x501DU, 0x70A9U, 0xC027U, 0x903AU, 0x20B4U, 0x60DCU, 0xD052U, 0x804FU, 0x30C1U, + 0x1075U, 0xA0FBU, 0xF0E6U, 0x4068U, 0x7036U, 0xC0B8U, 0x90A5U, 0x202BU, 0x009FU, 0xB011U, 0xE00CU, 0x5082U, + 0x10EAU, 0xA064U, 0xF079U, 0x40F7U, 0x6043U, 0xD0CDU, 0x80D0U, 0x305EU, 0xD06CU, 0x60E2U, 0x30FFU, 0x8071U, + 0xA0C5U, 0x104BU, 0x4056U, 0xF0D8U, 0xB0B0U, 0x003EU, 0x5023U, 0xE0ADU, 0xC019U, 0x7097U, 0x208AU, 0x9004U, + 0xA05AU, 0x10D4U, 0x40C9U, 0xF047U, 0xD0F3U, 0x607DU, 0x3060U, 0x80EEU, 0xC086U, 0x7008U, 0x2015U, 0x909BU, + 0xB02FU, 0x00A1U, 0x50BCU, 0xE032U, 0x90D9U, 0x2057U, 0x704AU, 0xC0C4U, 0xE070U, 0x50FEU, 0x00E3U, 0xB06DU, + 0xF005U, 0x408BU, 0x1096U, 0xA018U, 0x80ACU, 0x3022U, 0x603FU, 0xD0B1U, 0xE0EFU, 0x5061U, 0x007CU, 0xB0F2U, + 0x9046U, 0x20C8U, 0x70D5U, 0xC05BU, 0x8033U, 0x30BDU, 0x60A0U, 0xD02EU, 0xF09AU, 0x4014U, 0x1009U, 0xA087U, + 0x40B5U, 0xF03BU, 0xA026U, 0x10A8U, 0x301CU, 0x8092U, 0xD08FU, 0x6001U, 0x2069U, 0x90E7U, 0xC0FAU, 0x7074U, + 0x50C0U, 0xE04EU, 0xB053U, 0x00DDU, 0x3083U, 0x800DU, 0xD010U, 0x609EU, 0x402AU, 0xF0A4U, 0xA0B9U, 0x1037U, + 0x505FU, 0xE0D1U, 0xB0CCU, 0x0042U, 0x20F6U, 0x9078U, 0xC065U, 0x70EBU, 0xA03DU, 0x10B3U, 0x40AEU, 0xF020U, + 0xD094U, 0x601AU, 0x3007U, 0x8089U, 0xC0E1U, 0x706FU, 0x2072U, 0x90FCU, 0xB048U, 0x00C6U, 0x50DBU, 0xE055U, + 0xD00BU, 0x6085U, 0x3098U, 0x8016U, 0xA0A2U, 0x102CU, 0x4031U, 0xF0BFU, 0xB0D7U, 0x0059U, 0x5044U, 0xE0CAU, + 0xC07EU, 0x70F0U, 0x20EDU, 0x9063U, 0x7051U, 0xC0DFU, 0x90C2U, 0x204CU, 0x00F8U, 0xB076U, 0xE06BU, 0x50E5U, + 0x108DU, 0xA003U, 0xF01EU, 0x4090U, 0x6024U, 0xD0AAU, 0x80B7U, 0x3039U, 0x0067U, 0xB0E9U, 0xE0F4U, 0x507AU, + 0x70CEU, 0xC040U, 0x905DU, 0x20D3U, 0x60BBU, 0xD035U, 0x8028U, 0x30A6U, 0x1012U, 0xA09CU, 0xF081U, 0x400FU, + 0x30E4U, 0x806AU, 0xD077U, 0x60F9U, 0x404DU, 0xF0C3U, 0xA0DEU, 0x1050U, 0x5038U, 0xE0B6U, 0xB0ABU, 0x0025U, + 0x2091U, 0x901FU, 0xC002U, 0x708CU, 0x40D2U, 0xF05CU, 0xA041U, 0x10CFU, 0x307BU, 0x80F5U, 0xD0E8U, 0x6066U, + 0x200EU, 0x9080U, 0xC09DU, 0x7013U, 0x50A7U, 0xE029U, 0xB034U, 0x00BAU, 0xE088U, 0x5006U, 0x001BU, 0xB095U, + 0x9021U, 0x20AFU, 0x70B2U, 0xC03CU, 0x8054U, 0x30DAU, 0x60C7U, 0xD049U, 0xF0FDU, 0x4073U, 0x106EU, 0xA0E0U, + 0x90BEU, 0x2030U, 0x702DU, 0xC0A3U, 0xE017U, 0x5099U, 0x0084U, 0xB00AU, 0xF062U, 0x40ECU, 0x10F1U, 0xA07FU, + 0x80CBU, 0x3045U, 0x6058U, 0xD0D6U}; + +const unsigned int DECODING_TABLE_1987[] = + {0x00000U, 0x00001U, 0x00002U, 0x00003U, 0x00004U, 0x00005U, 0x00006U, 0x00007U, 0x00008U, 0x00009U, 0x0000AU, 0x0000BU, 0x0000CU, + 0x0000DU, 0x0000EU, 0x24020U, 0x00010U, 0x00011U, 0x00012U, 0x00013U, 0x00014U, 0x00015U, 0x00016U, 0x00017U, 0x00018U, 0x00019U, + 0x0001AU, 0x0001BU, 0x0001CU, 0x0001DU, 0x48040U, 0x01480U, 0x00020U, 0x00021U, 0x00022U, 0x00023U, 0x00024U, 0x00025U, 0x00026U, + 0x24008U, 0x00028U, 0x00029U, 0x0002AU, 0x24004U, 0x0002CU, 0x24002U, 0x24001U, 0x24000U, 0x00030U, 0x00031U, 0x00032U, 0x08180U, + 0x00034U, 0x00C40U, 0x00036U, 0x00C42U, 0x00038U, 0x43000U, 0x0003AU, 0x43002U, 0x02902U, 0x24012U, 0x02900U, 0x24010U, 0x00040U, + 0x00041U, 0x00042U, 0x00043U, 0x00044U, 0x00045U, 0x00046U, 0x00047U, 0x00048U, 0x00049U, 0x0004AU, 0x02500U, 0x0004CU, 0x0004DU, + 0x48010U, 0x48011U, 0x00050U, 0x00051U, 0x00052U, 0x21200U, 0x00054U, 0x00C20U, 0x48008U, 0x48009U, 0x00058U, 0x00059U, 0x48004U, + 0x48005U, 0x48002U, 0x48003U, 0x48000U, 0x48001U, 0x00060U, 0x00061U, 0x00062U, 0x00063U, 0x00064U, 0x00C10U, 0x10300U, 0x0B000U, + 0x00068U, 0x00069U, 0x01880U, 0x01881U, 0x40181U, 0x40180U, 0x24041U, 0x24040U, 0x00070U, 0x00C04U, 0x00072U, 0x00C06U, 0x00C01U, + 0x00C00U, 0x00C03U, 0x00C02U, 0x05204U, 0x00C0CU, 0x48024U, 0x48025U, 0x05200U, 0x00C08U, 0x48020U, 0x48021U, 0x00080U, 0x00081U, + 0x00082U, 0x00083U, 0x00084U, 0x00085U, 0x00086U, 0x00087U, 0x00088U, 0x00089U, 0x0008AU, 0x50200U, 0x0008CU, 0x0A800U, 0x01411U, + 0x01410U, 0x00090U, 0x00091U, 0x00092U, 0x08120U, 0x00094U, 0x00095U, 0x04A00U, 0x01408U, 0x00098U, 0x00099U, 0x01405U, 0x01404U, + 0x01403U, 0x01402U, 0x01401U, 0x01400U, 0x000A0U, 0x000A1U, 0x000A2U, 0x08110U, 0x000A4U, 0x000A5U, 0x42400U, 0x42401U, 0x000A8U, + 0x000A9U, 0x01840U, 0x01841U, 0x40141U, 0x40140U, 0x24081U, 0x24080U, 0x000B0U, 0x08102U, 0x08101U, 0x08100U, 0x000B4U, 0x08106U, + 0x08105U, 0x08104U, 0x20A01U, 0x20A00U, 0x08109U, 0x08108U, 0x01423U, 0x01422U, 0x01421U, 0x01420U, 0x000C0U, 0x000C1U, 0x000C2U, + 0x000C3U, 0x000C4U, 0x000C5U, 0x000C6U, 0x000C7U, 0x000C8U, 0x000C9U, 0x01820U, 0x01821U, 0x20600U, 0x40120U, 0x16000U, 0x16001U, + 0x000D0U, 0x000D1U, 0x42801U, 0x42800U, 0x03100U, 0x18200U, 0x03102U, 0x18202U, 0x000D8U, 0x000D9U, 0x48084U, 0x01444U, 0x48082U, + 0x01442U, 0x48080U, 0x01440U, 0x000E0U, 0x32000U, 0x01808U, 0x04600U, 0x40109U, 0x40108U, 0x0180CU, 0x4010AU, 0x01802U, 0x40104U, + 0x01800U, 0x01801U, 0x40101U, 0x40100U, 0x01804U, 0x40102U, 0x0A408U, 0x08142U, 0x08141U, 0x08140U, 0x00C81U, 0x00C80U, 0x00C83U, + 0x00C82U, 0x0A400U, 0x0A401U, 0x01810U, 0x01811U, 0x40111U, 0x40110U, 0x01814U, 0x40112U, 0x00100U, 0x00101U, 0x00102U, 0x00103U, + 0x00104U, 0x00105U, 0x00106U, 0x41800U, 0x00108U, 0x00109U, 0x0010AU, 0x02440U, 0x0010CU, 0x0010DU, 0x0010EU, 0x02444U, 0x00110U, + 0x00111U, 0x00112U, 0x080A0U, 0x00114U, 0x00115U, 0x00116U, 0x080A4U, 0x00118U, 0x00119U, 0x15000U, 0x15001U, 0x02822U, 0x02823U, + 0x02820U, 0x02821U, 0x00120U, 0x00121U, 0x00122U, 0x08090U, 0x00124U, 0x00125U, 0x10240U, 0x10241U, 0x00128U, 0x00129U, 0x0012AU, + 0x24104U, 0x09400U, 0x400C0U, 0x02810U, 0x24100U, 0x00130U, 0x08082U, 0x08081U, 0x08080U, 0x31001U, 0x31000U, 0x02808U, 0x08084U, + 0x02806U, 0x0808AU, 0x02804U, 0x08088U, 0x02802U, 0x02803U, 0x02800U, 0x02801U, 0x00140U, 0x00141U, 0x00142U, 0x02408U, 0x00144U, + 0x00145U, 0x10220U, 0x10221U, 0x00148U, 0x02402U, 0x02401U, 0x02400U, 0x400A1U, 0x400A0U, 0x02405U, 0x02404U, 0x00150U, 0x00151U, + 0x00152U, 0x02418U, 0x03080U, 0x03081U, 0x03082U, 0x03083U, 0x09801U, 0x09800U, 0x02411U, 0x02410U, 0x48102U, 0x09804U, 0x48100U, + 0x48101U, 0x00160U, 0x00161U, 0x10204U, 0x10205U, 0x10202U, 0x40088U, 0x10200U, 0x10201U, 0x40085U, 0x40084U, 0x02421U, 0x02420U, + 0x40081U, 0x40080U, 0x10208U, 0x40082U, 0x41402U, 0x080C2U, 0x41400U, 0x080C0U, 0x00D01U, 0x00D00U, 0x10210U, 0x10211U, 0x40095U, + 0x40094U, 0x02844U, 0x080C8U, 0x40091U, 0x40090U, 0x02840U, 0x02841U, 0x00180U, 0x00181U, 0x00182U, 0x08030U, 0x00184U, 0x14400U, + 0x22201U, 0x22200U, 0x00188U, 0x00189U, 0x0018AU, 0x08038U, 0x40061U, 0x40060U, 0x40063U, 0x40062U, 0x00190U, 0x08022U, 0x08021U, + 0x08020U, 0x03040U, 0x03041U, 0x08025U, 0x08024U, 0x40C00U, 0x40C01U, 0x08029U, 0x08028U, 0x2C000U, 0x2C001U, 0x01501U, 0x01500U, + 0x001A0U, 0x08012U, 0x08011U, 0x08010U, 0x40049U, 0x40048U, 0x08015U, 0x08014U, 0x06200U, 0x40044U, 0x30400U, 0x08018U, 0x40041U, + 0x40040U, 0x40043U, 0x40042U, 0x08003U, 0x08002U, 0x08001U, 0x08000U, 0x08007U, 0x08006U, 0x08005U, 0x08004U, 0x0800BU, 0x0800AU, + 0x08009U, 0x08008U, 0x40051U, 0x40050U, 0x02880U, 0x0800CU, 0x001C0U, 0x001C1U, 0x64000U, 0x64001U, 0x03010U, 0x40028U, 0x08C00U, + 0x08C01U, 0x40025U, 0x40024U, 0x02481U, 0x02480U, 0x40021U, 0x40020U, 0x40023U, 0x40022U, 0x03004U, 0x03005U, 0x08061U, 0x08060U, + 0x03000U, 0x03001U, 0x03002U, 0x03003U, 0x0300CU, 0x40034U, 0x30805U, 0x30804U, 0x03008U, 0x40030U, 0x30801U, 0x30800U, 0x4000DU, + 0x4000CU, 0x08051U, 0x08050U, 0x40009U, 0x40008U, 0x10280U, 0x4000AU, 0x40005U, 0x40004U, 0x01900U, 0x40006U, 0x40001U, 0x40000U, + 0x40003U, 0x40002U, 0x14800U, 0x08042U, 0x08041U, 0x08040U, 0x03020U, 0x40018U, 0x08045U, 0x08044U, 0x40015U, 0x40014U, 0x08049U, + 0x08048U, 0x40011U, 0x40010U, 0x40013U, 0x40012U, 0x00200U, 0x00201U, 0x00202U, 0x00203U, 0x00204U, 0x00205U, 0x00206U, 0x00207U, + 0x00208U, 0x00209U, 0x0020AU, 0x50080U, 0x0020CU, 0x0020DU, 0x0020EU, 0x50084U, 0x00210U, 0x00211U, 0x00212U, 0x21040U, 0x00214U, + 0x00215U, 0x04880U, 0x04881U, 0x00218U, 0x00219U, 0x0E001U, 0x0E000U, 0x0021CU, 0x0021DU, 0x04888U, 0x0E004U, 0x00220U, 0x00221U, + 0x00222U, 0x00223U, 0x00224U, 0x00225U, 0x10140U, 0x10141U, 0x00228U, 0x00229U, 0x0022AU, 0x24204U, 0x12401U, 0x12400U, 0x24201U, + 0x24200U, 0x00230U, 0x00231U, 0x00232U, 0x21060U, 0x2A000U, 0x2A001U, 0x2A002U, 0x2A003U, 0x20881U, 0x20880U, 0x20883U, 0x20882U, + 0x05040U, 0x05041U, 0x05042U, 0x24210U, 0x00240U, 0x00241U, 0x00242U, 0x21010U, 0x00244U, 0x46000U, 0x10120U, 0x10121U, 0x00248U, + 0x00249U, 0x0024AU, 0x21018U, 0x20480U, 0x20481U, 0x20482U, 0x20483U, 0x00250U, 0x21002U, 0x21001U, 0x21000U, 0x18081U, 0x18080U, + 0x21005U, 0x21004U, 0x12800U, 0x12801U, 0x21009U, 0x21008U, 0x05020U, 0x05021U, 0x48200U, 0x48201U, 0x00260U, 0x00261U, 0x10104U, + 0x04480U, 0x10102U, 0x10103U, 0x10100U, 0x10101U, 0x62002U, 0x62003U, 0x62000U, 0x62001U, 0x05010U, 0x05011U, 0x10108U, 0x10109U, + 0x0500CU, 0x21022U, 0x21021U, 0x21020U, 0x05008U, 0x00E00U, 0x10110U, 0x10111U, 0x05004U, 0x05005U, 0x05006U, 0x21028U, 0x05000U, + 0x05001U, 0x05002U, 0x05003U, 0x00280U, 0x00281U, 0x00282U, 0x50008U, 0x00284U, 0x00285U, 0x04810U, 0x22100U, 0x00288U, 0x50002U, + 0x50001U, 0x50000U, 0x20440U, 0x20441U, 0x50005U, 0x50004U, 0x00290U, 0x00291U, 0x04804U, 0x04805U, 0x04802U, 0x18040U, 0x04800U, + 0x04801U, 0x20821U, 0x20820U, 0x50011U, 0x50010U, 0x0480AU, 0x01602U, 0x04808U, 0x01600U, 0x002A0U, 0x002A1U, 0x04441U, 0x04440U, + 0x002A4U, 0x002A5U, 0x04830U, 0x04444U, 0x06100U, 0x20810U, 0x50021U, 0x50020U, 0x06104U, 0x20814U, 0x50025U, 0x50024U, 0x20809U, + 0x20808U, 0x13000U, 0x08300U, 0x04822U, 0x2080CU, 0x04820U, 0x04821U, 0x20801U, 0x20800U, 0x20803U, 0x20802U, 0x20805U, 0x20804U, + 0x04828U, 0x20806U, 0x002C0U, 0x002C1U, 0x04421U, 0x04420U, 0x20408U, 0x18010U, 0x2040AU, 0x18012U, 0x20404U, 0x20405U, 0x50041U, + 0x50040U, 0x20400U, 0x20401U, 0x20402U, 0x20403U, 0x18005U, 0x18004U, 0x21081U, 0x21080U, 0x18001U, 0x18000U, 0x04840U, 0x18002U, + 0x20414U, 0x1800CU, 0x21089U, 0x21088U, 0x20410U, 0x18008U, 0x20412U, 0x1800AU, 0x04403U, 0x04402U, 0x04401U, 0x04400U, 0x10182U, + 0x04406U, 0x10180U, 0x04404U, 0x01A02U, 0x0440AU, 0x01A00U, 0x04408U, 0x20420U, 0x40300U, 0x20422U, 0x40302U, 0x04413U, 0x04412U, + 0x04411U, 0x04410U, 0x18021U, 0x18020U, 0x10190U, 0x18022U, 0x20841U, 0x20840U, 0x01A10U, 0x20842U, 0x05080U, 0x05081U, 0x05082U, + 0x05083U, 0x00300U, 0x00301U, 0x00302U, 0x00303U, 0x00304U, 0x00305U, 0x10060U, 0x22080U, 0x00308U, 0x00309U, 0x28800U, 0x28801U, + 0x44402U, 0x44403U, 0x44400U, 0x44401U, 0x00310U, 0x00311U, 0x10C01U, 0x10C00U, 0x00314U, 0x00315U, 0x10070U, 0x10C04U, 0x00318U, + 0x00319U, 0x28810U, 0x10C08U, 0x44412U, 0x00000U, 0x44410U, 0x44411U, 0x00320U, 0x60400U, 0x10044U, 0x10045U, 0x10042U, 0x0C800U, + 0x10040U, 0x10041U, 0x06080U, 0x06081U, 0x06082U, 0x06083U, 0x1004AU, 0x0C808U, 0x10048U, 0x10049U, 0x58008U, 0x08282U, 0x08281U, + 0x08280U, 0x10052U, 0x0C810U, 0x10050U, 0x10051U, 0x58000U, 0x58001U, 0x58002U, 0x08288U, 0x02A02U, 0x02A03U, 0x02A00U, 0x02A01U, + 0x00340U, 0x00341U, 0x10024U, 0x10025U, 0x10022U, 0x10023U, 0x10020U, 0x10021U, 0x34001U, 0x34000U, 0x02601U, 0x02600U, 0x1002AU, + 0x34004U, 0x10028U, 0x10029U, 0x0C400U, 0x0C401U, 0x21101U, 0x21100U, 0x60800U, 0x60801U, 0x10030U, 0x10031U, 0x0C408U, 0x34010U, + 0x21109U, 0x21108U, 0x60808U, 0x60809U, 0x10038U, 0x28420U, 0x10006U, 0x10007U, 0x10004U, 0x10005U, 0x10002U, 0x10003U, 0x10000U, + 0x10001U, 0x1000EU, 0x40284U, 0x1000CU, 0x1000DU, 0x1000AU, 0x40280U, 0x10008U, 0x10009U, 0x10016U, 0x10017U, 0x10014U, 0x10015U, + 0x10012U, 0x10013U, 0x10010U, 0x10011U, 0x05104U, 0x44802U, 0x44801U, 0x44800U, 0x05100U, 0x05101U, 0x10018U, 0x28400U, 0x00380U, + 0x00381U, 0x22005U, 0x22004U, 0x22003U, 0x22002U, 0x22001U, 0x22000U, 0x06020U, 0x06021U, 0x50101U, 0x50100U, 0x11800U, 0x11801U, + 0x22009U, 0x22008U, 0x45001U, 0x45000U, 0x08221U, 0x08220U, 0x04902U, 0x22012U, 0x04900U, 0x22010U, 0x06030U, 0x45008U, 0x08229U, + 0x08228U, 0x11810U, 0x11811U, 0x04908U, 0x22018U, 0x06008U, 0x06009U, 0x08211U, 0x08210U, 0x100C2U, 0x22022U, 0x100C0U, 0x22020U, + 0x06000U, 0x06001U, 0x06002U, 0x06003U, 0x06004U, 0x40240U, 0x06006U, 0x40242U, 0x08203U, 0x08202U, 0x08201U, 0x08200U, 0x08207U, + 0x08206U, 0x08205U, 0x08204U, 0x06010U, 0x20900U, 0x08209U, 0x08208U, 0x61002U, 0x20904U, 0x61000U, 0x61001U, 0x29020U, 0x29021U, + 0x100A4U, 0x22044U, 0x100A2U, 0x22042U, 0x100A0U, 0x22040U, 0x20504U, 0x40224U, 0x0D005U, 0x0D004U, 0x20500U, 0x40220U, 0x0D001U, + 0x0D000U, 0x03204U, 0x18104U, 0x08261U, 0x08260U, 0x03200U, 0x18100U, 0x03202U, 0x18102U, 0x11421U, 0x11420U, 0x00000U, 0x11422U, + 0x03208U, 0x18108U, 0x0D011U, 0x0D010U, 0x29000U, 0x29001U, 0x10084U, 0x04500U, 0x10082U, 0x40208U, 0x10080U, 0x10081U, 0x06040U, + 0x40204U, 0x06042U, 0x40206U, 0x40201U, 0x40200U, 0x10088U, 0x40202U, 0x29010U, 0x08242U, 0x08241U, 0x08240U, 0x10092U, 0x40218U, + 0x10090U, 0x10091U, 0x11401U, 0x11400U, 0x11403U, 0x11402U, 0x40211U, 0x40210U, 0x10098U, 0x40212U, 0x00400U, 0x00401U, 0x00402U, + 0x00403U, 0x00404U, 0x00405U, 0x00406U, 0x00407U, 0x00408U, 0x00409U, 0x0040AU, 0x02140U, 0x0040CU, 0x0040DU, 0x01091U, 0x01090U, + 0x00410U, 0x00411U, 0x00412U, 0x00413U, 0x00414U, 0x00860U, 0x01089U, 0x01088U, 0x00418U, 0x38000U, 0x01085U, 0x01084U, 0x01083U, + 0x01082U, 0x01081U, 0x01080U, 0x00420U, 0x00421U, 0x00422U, 0x00423U, 0x00424U, 0x00850U, 0x42080U, 0x42081U, 0x00428U, 0x00429U, + 0x48801U, 0x48800U, 0x09100U, 0x12200U, 0x24401U, 0x24400U, 0x00430U, 0x00844U, 0x00432U, 0x00846U, 0x00841U, 0x00840U, 0x1C000U, + 0x00842U, 0x00438U, 0x0084CU, 0x010A5U, 0x010A4U, 0x00849U, 0x00848U, 0x010A1U, 0x010A0U, 0x00440U, 0x00441U, 0x00442U, 0x02108U, + 0x00444U, 0x00830U, 0x70001U, 0x70000U, 0x00448U, 0x02102U, 0x02101U, 0x02100U, 0x20280U, 0x20281U, 0x02105U, 0x02104U, 0x00450U, + 0x00824U, 0x00452U, 0x00826U, 0x00821U, 0x00820U, 0x00823U, 0x00822U, 0x24802U, 0x02112U, 0x24800U, 0x02110U, 0x00829U, 0x00828U, + 0x48400U, 0x010C0U, 0x00460U, 0x00814U, 0x04281U, 0x04280U, 0x00811U, 0x00810U, 0x00813U, 0x00812U, 0x54000U, 0x54001U, 0x02121U, + 0x02120U, 0x00819U, 0x00818U, 0x0081BU, 0x0081AU, 0x00805U, 0x00804U, 0x41100U, 0x00806U, 0x00801U, 0x00800U, 0x00803U, 0x00802U, + 0x0A080U, 0x0080CU, 0x0A082U, 0x0080EU, 0x00809U, 0x00808U, 0x0080BU, 0x0080AU, 0x00480U, 0x00481U, 0x00482U, 0x00483U, 0x00484U, + 0x14100U, 0x42020U, 0x01018U, 0x00488U, 0x00489U, 0x01015U, 0x01014U, 0x20240U, 0x01012U, 0x01011U, 0x01010U, 0x00490U, 0x00491U, + 0x0100DU, 0x0100CU, 0x0100BU, 0x0100AU, 0x01009U, 0x01008U, 0x40900U, 0x01006U, 0x01005U, 0x01004U, 0x01003U, 0x01002U, 0x01001U, + 0x01000U, 0x004A0U, 0x004A1U, 0x42004U, 0x04240U, 0x42002U, 0x42003U, 0x42000U, 0x42001U, 0x30102U, 0x30103U, 0x30100U, 0x30101U, + 0x4200AU, 0x01032U, 0x42008U, 0x01030U, 0x25000U, 0x25001U, 0x08501U, 0x08500U, 0x008C1U, 0x008C0U, 0x42010U, 0x01028U, 0x0A040U, + 0x0A041U, 0x01025U, 0x01024U, 0x01023U, 0x01022U, 0x01021U, 0x01020U, 0x004C0U, 0x49000U, 0x04221U, 0x04220U, 0x20208U, 0x20209U, + 0x08900U, 0x08901U, 0x20204U, 0x20205U, 0x02181U, 0x02180U, 0x20200U, 0x20201U, 0x20202U, 0x01050U, 0x0A028U, 0x008A4U, 0x0104DU, + 0x0104CU, 0x008A1U, 0x008A0U, 0x01049U, 0x01048U, 0x0A020U, 0x0A021U, 0x01045U, 0x01044U, 0x20210U, 0x01042U, 0x01041U, 0x01040U, + 0x04203U, 0x04202U, 0x04201U, 0x04200U, 0x00891U, 0x00890U, 0x42040U, 0x04204U, 0x0A010U, 0x0A011U, 0x01C00U, 0x04208U, 0x20220U, + 0x40500U, 0x20222U, 0x40502U, 0x0A008U, 0x00884U, 0x04211U, 0x04210U, 0x00881U, 0x00880U, 0x00883U, 0x00882U, 0x0A000U, 0x0A001U, + 0x0A002U, 0x0A003U, 0x0A004U, 0x00888U, 0x01061U, 0x01060U, 0x00500U, 0x00501U, 0x00502U, 0x02048U, 0x00504U, 0x14080U, 0x00506U, + 0x14082U, 0x00508U, 0x02042U, 0x02041U, 0x02040U, 0x09020U, 0x09021U, 0x44200U, 0x02044U, 0x00510U, 0x00511U, 0x10A01U, 0x10A00U, + 0x4A001U, 0x4A000U, 0x4A003U, 0x4A002U, 0x40880U, 0x40881U, 0x02051U, 0x02050U, 0x40884U, 0x01182U, 0x01181U, 0x01180U, 0x00520U, + 0x60200U, 0x00522U, 0x60202U, 0x09008U, 0x09009U, 0x0900AU, 0x0900BU, 0x09004U, 0x09005U, 0x30080U, 0x02060U, 0x09000U, 0x09001U, + 0x09002U, 0x09003U, 0x41042U, 0x08482U, 0x41040U, 0x08480U, 0x00941U, 0x00940U, 0x41044U, 0x00942U, 0x09014U, 0x09015U, 0x02C04U, + 0x08488U, 0x09010U, 0x09011U, 0x02C00U, 0x02C01U, 0x00540U, 0x0200AU, 0x02009U, 0x02008U, 0x08882U, 0x0200EU, 0x08880U, 0x0200CU, + 0x02003U, 0x02002U, 0x02001U, 0x02000U, 0x02007U, 0x02006U, 0x02005U, 0x02004U, 0x0C200U, 0x0C201U, 0x41020U, 0x02018U, 0x00921U, + 0x00920U, 0x41024U, 0x00922U, 0x02013U, 0x02012U, 0x02011U, 0x02010U, 0x02017U, 0x02016U, 0x02015U, 0x02014U, 0x41012U, 0x0202AU, + 0x41010U, 0x02028U, 0x26000U, 0x00910U, 0x10600U, 0x10601U, 0x02023U, 0x02022U, 0x02021U, 0x02020U, 0x09040U, 0x40480U, 0x02025U, + 0x02024U, 0x41002U, 0x00904U, 0x41000U, 0x41001U, 0x00901U, 0x00900U, 0x41004U, 0x00902U, 0x4100AU, 0x02032U, 0x41008U, 0x02030U, + 0x00909U, 0x00908U, 0x28201U, 0x28200U, 0x00580U, 0x14004U, 0x00582U, 0x14006U, 0x14001U, 0x14000U, 0x08840U, 0x14002U, 0x40810U, + 0x40811U, 0x30020U, 0x020C0U, 0x14009U, 0x14008U, 0x01111U, 0x01110U, 0x40808U, 0x40809U, 0x08421U, 0x08420U, 0x14011U, 0x14010U, + 0x01109U, 0x01108U, 0x40800U, 0x40801U, 0x40802U, 0x01104U, 0x40804U, 0x01102U, 0x01101U, 0x01100U, 0x03801U, 0x03800U, 0x30008U, + 0x08410U, 0x14021U, 0x14020U, 0x42100U, 0x42101U, 0x30002U, 0x30003U, 0x30000U, 0x30001U, 0x09080U, 0x40440U, 0x30004U, 0x30005U, + 0x08403U, 0x08402U, 0x08401U, 0x08400U, 0x08407U, 0x08406U, 0x08405U, 0x08404U, 0x40820U, 0x40821U, 0x30010U, 0x08408U, 0x40824U, + 0x01122U, 0x01121U, 0x01120U, 0x08806U, 0x0208AU, 0x08804U, 0x02088U, 0x08802U, 0x14040U, 0x08800U, 0x08801U, 0x02083U, 0x02082U, + 0x02081U, 0x02080U, 0x20300U, 0x40420U, 0x08808U, 0x02084U, 0x03404U, 0x03405U, 0x08814U, 0x02098U, 0x03400U, 0x03401U, 0x08810U, + 0x08811U, 0x40840U, 0x40841U, 0x02091U, 0x02090U, 0x40844U, 0x01142U, 0x01141U, 0x01140U, 0x04303U, 0x04302U, 0x04301U, 0x04300U, + 0x40409U, 0x40408U, 0x08820U, 0x08821U, 0x40405U, 0x40404U, 0x30040U, 0x020A0U, 0x40401U, 0x40400U, 0x40403U, 0x40402U, 0x41082U, + 0x08442U, 0x41080U, 0x08440U, 0x00981U, 0x00980U, 0x41084U, 0x00982U, 0x0A100U, 0x11200U, 0x0A102U, 0x11202U, 0x40411U, 0x40410U, + 0x40413U, 0x40412U, 0x00600U, 0x00601U, 0x00602U, 0x00603U, 0x00604U, 0x00605U, 0x00606U, 0x00607U, 0x00608U, 0x05800U, 0x0060AU, + 0x05802U, 0x200C0U, 0x12020U, 0x44100U, 0x44101U, 0x00610U, 0x00611U, 0x10901U, 0x10900U, 0x51000U, 0x51001U, 0x51002U, 0x10904U, + 0x00618U, 0x05810U, 0x01285U, 0x01284U, 0x51008U, 0x01282U, 0x01281U, 0x01280U, 0x00620U, 0x60100U, 0x040C1U, 0x040C0U, 0x12009U, + 0x12008U, 0x21800U, 0x21801U, 0x12005U, 0x12004U, 0x12007U, 0x12006U, 0x12001U, 0x12000U, 0x12003U, 0x12002U, 0x00630U, 0x00A44U, + 0x040D1U, 0x040D0U, 0x00A41U, 0x00A40U, 0x21810U, 0x00A42U, 0x12015U, 0x12014U, 0x00000U, 0x12016U, 0x12011U, 0x12010U, 0x12013U, + 0x12012U, 0x00640U, 0x00641U, 0x040A1U, 0x040A0U, 0x20088U, 0x20089U, 0x2008AU, 0x040A4U, 0x20084U, 0x20085U, 0x19000U, 0x02300U, + 0x20080U, 0x20081U, 0x20082U, 0x20083U, 0x0C100U, 0x0C101U, 0x21401U, 0x21400U, 0x00A21U, 0x00A20U, 0x00A23U, 0x00A22U, 0x20094U, + 0x20095U, 0x19010U, 0x21408U, 0x20090U, 0x20091U, 0x20092U, 0x28120U, 0x04083U, 0x04082U, 0x04081U, 0x04080U, 0x00A11U, 0x00A10U, + 0x10500U, 0x04084U, 0x200A4U, 0x0408AU, 0x04089U, 0x04088U, 0x200A0U, 0x12040U, 0x200A2U, 0x12042U, 0x00A05U, 0x00A04U, 0x04091U, + 0x04090U, 0x00A01U, 0x00A00U, 0x00A03U, 0x00A02U, 0x05404U, 0x00A0CU, 0x28105U, 0x28104U, 0x05400U, 0x00A08U, 0x28101U, 0x28100U, + 0x00680U, 0x00681U, 0x04061U, 0x04060U, 0x20048U, 0x20049U, 0x2004AU, 0x04064U, 0x20044U, 0x20045U, 0x50401U, 0x50400U, 0x20040U, + 0x20041U, 0x20042U, 0x01210U, 0x68002U, 0x68003U, 0x68000U, 0x68001U, 0x04C02U, 0x0120AU, 0x04C00U, 0x01208U, 0x20054U, 0x01206U, + 0x01205U, 0x01204U, 0x20050U, 0x01202U, 0x01201U, 0x01200U, 0x18800U, 0x04042U, 0x04041U, 0x04040U, 0x42202U, 0x04046U, 0x42200U, + 0x04044U, 0x20064U, 0x0404AU, 0x04049U, 0x04048U, 0x20060U, 0x12080U, 0x20062U, 0x12082U, 0x18810U, 0x04052U, 0x04051U, 0x04050U, + 0x4C009U, 0x4C008U, 0x42210U, 0x04054U, 0x20C01U, 0x20C00U, 0x20C03U, 0x20C02U, 0x4C001U, 0x4C000U, 0x01221U, 0x01220U, 0x2000CU, + 0x04022U, 0x04021U, 0x04020U, 0x20008U, 0x20009U, 0x2000AU, 0x04024U, 0x20004U, 0x20005U, 0x20006U, 0x04028U, 0x20000U, 0x20001U, + 0x20002U, 0x20003U, 0x2001CU, 0x04032U, 0x04031U, 0x04030U, 0x20018U, 0x18400U, 0x2001AU, 0x18402U, 0x20014U, 0x20015U, 0x20016U, + 0x01244U, 0x20010U, 0x20011U, 0x20012U, 0x01240U, 0x04003U, 0x04002U, 0x04001U, 0x04000U, 0x20028U, 0x04006U, 0x04005U, 0x04004U, + 0x20024U, 0x0400AU, 0x04009U, 0x04008U, 0x20020U, 0x20021U, 0x20022U, 0x0400CU, 0x04013U, 0x04012U, 0x04011U, 0x04010U, 0x00A81U, + 0x00A80U, 0x04015U, 0x04014U, 0x0A200U, 0x11100U, 0x04019U, 0x04018U, 0x20030U, 0x20031U, 0x50800U, 0x50801U, 0x00700U, 0x60020U, + 0x10811U, 0x10810U, 0x4400AU, 0x60024U, 0x44008U, 0x44009U, 0x44006U, 0x02242U, 0x44004U, 0x02240U, 0x44002U, 0x44003U, 0x44000U, + 0x44001U, 0x0C040U, 0x10802U, 0x10801U, 0x10800U, 0x0C044U, 0x10806U, 0x10805U, 0x10804U, 0x23000U, 0x23001U, 0x10809U, 0x10808U, + 0x44012U, 0x44013U, 0x44010U, 0x44011U, 0x60001U, 0x60000U, 0x60003U, 0x60002U, 0x60005U, 0x60004U, 0x10440U, 0x10441U, 0x60009U, + 0x60008U, 0x44024U, 0x6000AU, 0x09200U, 0x12100U, 0x44020U, 0x44021U, 0x60011U, 0x60010U, 0x10821U, 0x10820U, 0x07003U, 0x07002U, + 0x07001U, 0x07000U, 0x23020U, 0x60018U, 0x28045U, 0x28044U, 0x09210U, 0x28042U, 0x28041U, 0x28040U, 0x0C010U, 0x0C011U, 0x02209U, + 0x02208U, 0x10422U, 0x10423U, 0x10420U, 0x10421U, 0x02203U, 0x02202U, 0x02201U, 0x02200U, 0x20180U, 0x20181U, 0x44040U, 0x02204U, + 0x0C000U, 0x0C001U, 0x0C002U, 0x10840U, 0x0C004U, 0x0C005U, 0x0C006U, 0x10844U, 0x0C008U, 0x0C009U, 0x02211U, 0x02210U, 0x0C00CU, + 0x28022U, 0x28021U, 0x28020U, 0x60041U, 0x60040U, 0x10404U, 0x04180U, 0x10402U, 0x10403U, 0x10400U, 0x10401U, 0x02223U, 0x02222U, + 0x02221U, 0x02220U, 0x1040AU, 0x28012U, 0x10408U, 0x28010U, 0x0C020U, 0x0C021U, 0x41200U, 0x41201U, 0x00B01U, 0x00B00U, 0x10410U, + 0x28008U, 0x11081U, 0x11080U, 0x28005U, 0x28004U, 0x28003U, 0x28002U, 0x28001U, 0x28000U, 0x52040U, 0x14204U, 0x22405U, 0x22404U, + 0x14201U, 0x14200U, 0x22401U, 0x22400U, 0x20144U, 0x20145U, 0x44084U, 0x022C0U, 0x20140U, 0x20141U, 0x44080U, 0x44081U, 0x40A08U, + 0x10882U, 0x10881U, 0x10880U, 0x14211U, 0x14210U, 0x1A008U, 0x10884U, 0x40A00U, 0x40A01U, 0x40A02U, 0x01304U, 0x1A002U, 0x01302U, + 0x1A000U, 0x01300U, 0x60081U, 0x60080U, 0x04141U, 0x04140U, 0x60085U, 0x60084U, 0x104C0U, 0x04144U, 0x06400U, 0x06401U, 0x30200U, + 0x30201U, 0x06404U, 0x40640U, 0x30204U, 0x30205U, 0x08603U, 0x08602U, 0x08601U, 0x08600U, 0x00000U, 0x08606U, 0x08605U, 0x08604U, + 0x11041U, 0x11040U, 0x30210U, 0x11042U, 0x11045U, 0x11044U, 0x1A020U, 0x01320U, 0x52000U, 0x52001U, 0x04121U, 0x04120U, 0x20108U, + 0x20109U, 0x08A00U, 0x08A01U, 0x20104U, 0x20105U, 0x02281U, 0x02280U, 0x20100U, 0x20101U, 0x20102U, 0x20103U, 0x0C080U, 0x0C081U, + 0x0C082U, 0x04130U, 0x0C084U, 0x06808U, 0x08A10U, 0x08A11U, 0x11021U, 0x11020U, 0x11023U, 0x11022U, 0x20110U, 0x06800U, 0x20112U, + 0x06802U, 0x04103U, 0x04102U, 0x04101U, 0x04100U, 0x10482U, 0x04106U, 0x10480U, 0x04104U, 0x11011U, 0x11010U, 0x04109U, 0x04108U, + 0x20120U, 0x40600U, 0x20122U, 0x40602U, 0x11009U, 0x11008U, 0x22800U, 0x04110U, 0x1100DU, 0x1100CU, 0x22804U, 0x04114U, 0x11001U, + 0x11000U, 0x11003U, 0x11002U, 0x11005U, 0x11004U, 0x28081U, 0x28080U}; + +#define X18 0x00040000 /* vector representation of X^{18} */ +#define X11 0x00000800 /* vector representation of X^{11} */ +#define MASK8 0xfffff800 /* auxiliary vector for testing */ +#define GENPOL 0x00000c75 /* generator polinomial, g(x) */ + +unsigned int CGolay2087::getSyndrome1987(unsigned int pattern) +/* + * Compute the syndrome corresponding to the given pattern, i.e., the + * remainder after dividing the pattern (when considering it as the vector + * representation of a polynomial) by the generator polynomial, GENPOL. + * In the program this pattern has several meanings: (1) pattern = infomation + * bits, when constructing the encoding table; (2) pattern = error pattern, + * when constructing the decoding table; and (3) pattern = received vector, to + * obtain its syndrome in decoding. + */ +{ + unsigned int aux = X18; + + if (pattern >= X11) { + while (pattern & MASK8) { + while (!(aux & pattern)) + aux = aux >> 1; + + pattern ^= (aux / X11) * GENPOL; + } + } + + return pattern; +} + +unsigned char CGolay2087::decode(const unsigned char* data) +{ + assert(data != NULL); + + unsigned int code = (data[0U] << 11) + (data[1U] << 3) + (data[2U] >> 5); + unsigned int syndrome = getSyndrome1987(code); + unsigned int error_pattern = DECODING_TABLE_1987[syndrome]; + + if (error_pattern != 0x00U) + code ^= error_pattern; + + return code >> 11; +} + +void CGolay2087::encode(unsigned char* data) +{ + assert(data != NULL); + + unsigned int value = data[0U]; + + unsigned int cksum = ENCODING_TABLE_2087[value]; + + data[1U] = cksum & 0xFFU; + data[2U] = cksum >> 8; +} diff --git a/Golay2087.h b/Golay2087.h new file mode 100644 index 0000000..d54daed --- /dev/null +++ b/Golay2087.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Golay2087_H +#define Golay2087_H + +class CGolay2087 { +public: + static void encode(unsigned char* data); + + static unsigned char decode(const unsigned char* data); + +private: + static unsigned int getSyndrome1987(unsigned int pattern); +}; + +#endif diff --git a/Golay24128.cpp b/Golay24128.cpp new file mode 100644 index 0000000..4bca5de --- /dev/null +++ b/Golay24128.cpp @@ -0,0 +1,1092 @@ +/* + * Copyright (C) 2010,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2002 by Robert H. Morelos-Zaragoza. All rights reserved. + */ + +#include "Golay24128.h" + +static const unsigned int ENCODING_TABLE_23127[] = { + 0x000000U, 0x0018EAU, 0x00293EU, 0x0031D4U, 0x004A96U, 0x00527CU, 0x0063A8U, 0x007B42U, 0x008DC6U, 0x00952CU, + 0x00A4F8U, 0x00BC12U, 0x00C750U, 0x00DFBAU, 0x00EE6EU, 0x00F684U, 0x010366U, 0x011B8CU, 0x012A58U, 0x0132B2U, + 0x0149F0U, 0x01511AU, 0x0160CEU, 0x017824U, 0x018EA0U, 0x01964AU, 0x01A79EU, 0x01BF74U, 0x01C436U, 0x01DCDCU, + 0x01ED08U, 0x01F5E2U, 0x0206CCU, 0x021E26U, 0x022FF2U, 0x023718U, 0x024C5AU, 0x0254B0U, 0x026564U, 0x027D8EU, + 0x028B0AU, 0x0293E0U, 0x02A234U, 0x02BADEU, 0x02C19CU, 0x02D976U, 0x02E8A2U, 0x02F048U, 0x0305AAU, 0x031D40U, + 0x032C94U, 0x03347EU, 0x034F3CU, 0x0357D6U, 0x036602U, 0x037EE8U, 0x03886CU, 0x039086U, 0x03A152U, 0x03B9B8U, + 0x03C2FAU, 0x03DA10U, 0x03EBC4U, 0x03F32EU, 0x040D98U, 0x041572U, 0x0424A6U, 0x043C4CU, 0x04470EU, 0x045FE4U, + 0x046E30U, 0x0476DAU, 0x04805EU, 0x0498B4U, 0x04A960U, 0x04B18AU, 0x04CAC8U, 0x04D222U, 0x04E3F6U, 0x04FB1CU, + 0x050EFEU, 0x051614U, 0x0527C0U, 0x053F2AU, 0x054468U, 0x055C82U, 0x056D56U, 0x0575BCU, 0x058338U, 0x059BD2U, + 0x05AA06U, 0x05B2ECU, 0x05C9AEU, 0x05D144U, 0x05E090U, 0x05F87AU, 0x060B54U, 0x0613BEU, 0x06226AU, 0x063A80U, + 0x0641C2U, 0x065928U, 0x0668FCU, 0x067016U, 0x068692U, 0x069E78U, 0x06AFACU, 0x06B746U, 0x06CC04U, 0x06D4EEU, + 0x06E53AU, 0x06FDD0U, 0x070832U, 0x0710D8U, 0x07210CU, 0x0739E6U, 0x0742A4U, 0x075A4EU, 0x076B9AU, 0x077370U, + 0x0785F4U, 0x079D1EU, 0x07ACCAU, 0x07B420U, 0x07CF62U, 0x07D788U, 0x07E65CU, 0x07FEB6U, 0x0803DAU, 0x081B30U, + 0x082AE4U, 0x08320EU, 0x08494CU, 0x0851A6U, 0x086072U, 0x087898U, 0x088E1CU, 0x0896F6U, 0x08A722U, 0x08BFC8U, + 0x08C48AU, 0x08DC60U, 0x08EDB4U, 0x08F55EU, 0x0900BCU, 0x091856U, 0x092982U, 0x093168U, 0x094A2AU, 0x0952C0U, + 0x096314U, 0x097BFEU, 0x098D7AU, 0x099590U, 0x09A444U, 0x09BCAEU, 0x09C7ECU, 0x09DF06U, 0x09EED2U, 0x09F638U, + 0x0A0516U, 0x0A1DFCU, 0x0A2C28U, 0x0A34C2U, 0x0A4F80U, 0x0A576AU, 0x0A66BEU, 0x0A7E54U, 0x0A88D0U, 0x0A903AU, + 0x0AA1EEU, 0x0AB904U, 0x0AC246U, 0x0ADAACU, 0x0AEB78U, 0x0AF392U, 0x0B0670U, 0x0B1E9AU, 0x0B2F4EU, 0x0B37A4U, + 0x0B4CE6U, 0x0B540CU, 0x0B65D8U, 0x0B7D32U, 0x0B8BB6U, 0x0B935CU, 0x0BA288U, 0x0BBA62U, 0x0BC120U, 0x0BD9CAU, + 0x0BE81EU, 0x0BF0F4U, 0x0C0E42U, 0x0C16A8U, 0x0C277CU, 0x0C3F96U, 0x0C44D4U, 0x0C5C3EU, 0x0C6DEAU, 0x0C7500U, + 0x0C8384U, 0x0C9B6EU, 0x0CAABAU, 0x0CB250U, 0x0CC912U, 0x0CD1F8U, 0x0CE02CU, 0x0CF8C6U, 0x0D0D24U, 0x0D15CEU, + 0x0D241AU, 0x0D3CF0U, 0x0D47B2U, 0x0D5F58U, 0x0D6E8CU, 0x0D7666U, 0x0D80E2U, 0x0D9808U, 0x0DA9DCU, 0x0DB136U, + 0x0DCA74U, 0x0DD29EU, 0x0DE34AU, 0x0DFBA0U, 0x0E088EU, 0x0E1064U, 0x0E21B0U, 0x0E395AU, 0x0E4218U, 0x0E5AF2U, + 0x0E6B26U, 0x0E73CCU, 0x0E8548U, 0x0E9DA2U, 0x0EAC76U, 0x0EB49CU, 0x0ECFDEU, 0x0ED734U, 0x0EE6E0U, 0x0EFE0AU, + 0x0F0BE8U, 0x0F1302U, 0x0F22D6U, 0x0F3A3CU, 0x0F417EU, 0x0F5994U, 0x0F6840U, 0x0F70AAU, 0x0F862EU, 0x0F9EC4U, + 0x0FAF10U, 0x0FB7FAU, 0x0FCCB8U, 0x0FD452U, 0x0FE586U, 0x0FFD6CU, 0x1007B4U, 0x101F5EU, 0x102E8AU, 0x103660U, + 0x104D22U, 0x1055C8U, 0x10641CU, 0x107CF6U, 0x108A72U, 0x109298U, 0x10A34CU, 0x10BBA6U, 0x10C0E4U, 0x10D80EU, + 0x10E9DAU, 0x10F130U, 0x1104D2U, 0x111C38U, 0x112DECU, 0x113506U, 0x114E44U, 0x1156AEU, 0x11677AU, 0x117F90U, + 0x118914U, 0x1191FEU, 0x11A02AU, 0x11B8C0U, 0x11C382U, 0x11DB68U, 0x11EABCU, 0x11F256U, 0x120178U, 0x121992U, + 0x122846U, 0x1230ACU, 0x124BEEU, 0x125304U, 0x1262D0U, 0x127A3AU, 0x128CBEU, 0x129454U, 0x12A580U, 0x12BD6AU, + 0x12C628U, 0x12DEC2U, 0x12EF16U, 0x12F7FCU, 0x13021EU, 0x131AF4U, 0x132B20U, 0x1333CAU, 0x134888U, 0x135062U, + 0x1361B6U, 0x13795CU, 0x138FD8U, 0x139732U, 0x13A6E6U, 0x13BE0CU, 0x13C54EU, 0x13DDA4U, 0x13EC70U, 0x13F49AU, + 0x140A2CU, 0x1412C6U, 0x142312U, 0x143BF8U, 0x1440BAU, 0x145850U, 0x146984U, 0x14716EU, 0x1487EAU, 0x149F00U, + 0x14AED4U, 0x14B63EU, 0x14CD7CU, 0x14D596U, 0x14E442U, 0x14FCA8U, 0x15094AU, 0x1511A0U, 0x152074U, 0x15389EU, + 0x1543DCU, 0x155B36U, 0x156AE2U, 0x157208U, 0x15848CU, 0x159C66U, 0x15ADB2U, 0x15B558U, 0x15CE1AU, 0x15D6F0U, + 0x15E724U, 0x15FFCEU, 0x160CE0U, 0x16140AU, 0x1625DEU, 0x163D34U, 0x164676U, 0x165E9CU, 0x166F48U, 0x1677A2U, + 0x168126U, 0x1699CCU, 0x16A818U, 0x16B0F2U, 0x16CBB0U, 0x16D35AU, 0x16E28EU, 0x16FA64U, 0x170F86U, 0x17176CU, + 0x1726B8U, 0x173E52U, 0x174510U, 0x175DFAU, 0x176C2EU, 0x1774C4U, 0x178240U, 0x179AAAU, 0x17AB7EU, 0x17B394U, + 0x17C8D6U, 0x17D03CU, 0x17E1E8U, 0x17F902U, 0x18046EU, 0x181C84U, 0x182D50U, 0x1835BAU, 0x184EF8U, 0x185612U, + 0x1867C6U, 0x187F2CU, 0x1889A8U, 0x189142U, 0x18A096U, 0x18B87CU, 0x18C33EU, 0x18DBD4U, 0x18EA00U, 0x18F2EAU, + 0x190708U, 0x191FE2U, 0x192E36U, 0x1936DCU, 0x194D9EU, 0x195574U, 0x1964A0U, 0x197C4AU, 0x198ACEU, 0x199224U, + 0x19A3F0U, 0x19BB1AU, 0x19C058U, 0x19D8B2U, 0x19E966U, 0x19F18CU, 0x1A02A2U, 0x1A1A48U, 0x1A2B9CU, 0x1A3376U, + 0x1A4834U, 0x1A50DEU, 0x1A610AU, 0x1A79E0U, 0x1A8F64U, 0x1A978EU, 0x1AA65AU, 0x1ABEB0U, 0x1AC5F2U, 0x1ADD18U, + 0x1AECCCU, 0x1AF426U, 0x1B01C4U, 0x1B192EU, 0x1B28FAU, 0x1B3010U, 0x1B4B52U, 0x1B53B8U, 0x1B626CU, 0x1B7A86U, + 0x1B8C02U, 0x1B94E8U, 0x1BA53CU, 0x1BBDD6U, 0x1BC694U, 0x1BDE7EU, 0x1BEFAAU, 0x1BF740U, 0x1C09F6U, 0x1C111CU, + 0x1C20C8U, 0x1C3822U, 0x1C4360U, 0x1C5B8AU, 0x1C6A5EU, 0x1C72B4U, 0x1C8430U, 0x1C9CDAU, 0x1CAD0EU, 0x1CB5E4U, + 0x1CCEA6U, 0x1CD64CU, 0x1CE798U, 0x1CFF72U, 0x1D0A90U, 0x1D127AU, 0x1D23AEU, 0x1D3B44U, 0x1D4006U, 0x1D58ECU, + 0x1D6938U, 0x1D71D2U, 0x1D8756U, 0x1D9FBCU, 0x1DAE68U, 0x1DB682U, 0x1DCDC0U, 0x1DD52AU, 0x1DE4FEU, 0x1DFC14U, + 0x1E0F3AU, 0x1E17D0U, 0x1E2604U, 0x1E3EEEU, 0x1E45ACU, 0x1E5D46U, 0x1E6C92U, 0x1E7478U, 0x1E82FCU, 0x1E9A16U, + 0x1EABC2U, 0x1EB328U, 0x1EC86AU, 0x1ED080U, 0x1EE154U, 0x1EF9BEU, 0x1F0C5CU, 0x1F14B6U, 0x1F2562U, 0x1F3D88U, + 0x1F46CAU, 0x1F5E20U, 0x1F6FF4U, 0x1F771EU, 0x1F819AU, 0x1F9970U, 0x1FA8A4U, 0x1FB04EU, 0x1FCB0CU, 0x1FD3E6U, + 0x1FE232U, 0x1FFAD8U, 0x200F68U, 0x201782U, 0x202656U, 0x203EBCU, 0x2045FEU, 0x205D14U, 0x206CC0U, 0x20742AU, + 0x2082AEU, 0x209A44U, 0x20AB90U, 0x20B37AU, 0x20C838U, 0x20D0D2U, 0x20E106U, 0x20F9ECU, 0x210C0EU, 0x2114E4U, + 0x212530U, 0x213DDAU, 0x214698U, 0x215E72U, 0x216FA6U, 0x21774CU, 0x2181C8U, 0x219922U, 0x21A8F6U, 0x21B01CU, + 0x21CB5EU, 0x21D3B4U, 0x21E260U, 0x21FA8AU, 0x2209A4U, 0x22114EU, 0x22209AU, 0x223870U, 0x224332U, 0x225BD8U, + 0x226A0CU, 0x2272E6U, 0x228462U, 0x229C88U, 0x22AD5CU, 0x22B5B6U, 0x22CEF4U, 0x22D61EU, 0x22E7CAU, 0x22FF20U, + 0x230AC2U, 0x231228U, 0x2323FCU, 0x233B16U, 0x234054U, 0x2358BEU, 0x23696AU, 0x237180U, 0x238704U, 0x239FEEU, + 0x23AE3AU, 0x23B6D0U, 0x23CD92U, 0x23D578U, 0x23E4ACU, 0x23FC46U, 0x2402F0U, 0x241A1AU, 0x242BCEU, 0x243324U, + 0x244866U, 0x24508CU, 0x246158U, 0x2479B2U, 0x248F36U, 0x2497DCU, 0x24A608U, 0x24BEE2U, 0x24C5A0U, 0x24DD4AU, + 0x24EC9EU, 0x24F474U, 0x250196U, 0x25197CU, 0x2528A8U, 0x253042U, 0x254B00U, 0x2553EAU, 0x25623EU, 0x257AD4U, + 0x258C50U, 0x2594BAU, 0x25A56EU, 0x25BD84U, 0x25C6C6U, 0x25DE2CU, 0x25EFF8U, 0x25F712U, 0x26043CU, 0x261CD6U, + 0x262D02U, 0x2635E8U, 0x264EAAU, 0x265640U, 0x266794U, 0x267F7EU, 0x2689FAU, 0x269110U, 0x26A0C4U, 0x26B82EU, + 0x26C36CU, 0x26DB86U, 0x26EA52U, 0x26F2B8U, 0x27075AU, 0x271FB0U, 0x272E64U, 0x27368EU, 0x274DCCU, 0x275526U, + 0x2764F2U, 0x277C18U, 0x278A9CU, 0x279276U, 0x27A3A2U, 0x27BB48U, 0x27C00AU, 0x27D8E0U, 0x27E934U, 0x27F1DEU, + 0x280CB2U, 0x281458U, 0x28258CU, 0x283D66U, 0x284624U, 0x285ECEU, 0x286F1AU, 0x2877F0U, 0x288174U, 0x28999EU, + 0x28A84AU, 0x28B0A0U, 0x28CBE2U, 0x28D308U, 0x28E2DCU, 0x28FA36U, 0x290FD4U, 0x29173EU, 0x2926EAU, 0x293E00U, + 0x294542U, 0x295DA8U, 0x296C7CU, 0x297496U, 0x298212U, 0x299AF8U, 0x29AB2CU, 0x29B3C6U, 0x29C884U, 0x29D06EU, + 0x29E1BAU, 0x29F950U, 0x2A0A7EU, 0x2A1294U, 0x2A2340U, 0x2A3BAAU, 0x2A40E8U, 0x2A5802U, 0x2A69D6U, 0x2A713CU, + 0x2A87B8U, 0x2A9F52U, 0x2AAE86U, 0x2AB66CU, 0x2ACD2EU, 0x2AD5C4U, 0x2AE410U, 0x2AFCFAU, 0x2B0918U, 0x2B11F2U, + 0x2B2026U, 0x2B38CCU, 0x2B438EU, 0x2B5B64U, 0x2B6AB0U, 0x2B725AU, 0x2B84DEU, 0x2B9C34U, 0x2BADE0U, 0x2BB50AU, + 0x2BCE48U, 0x2BD6A2U, 0x2BE776U, 0x2BFF9CU, 0x2C012AU, 0x2C19C0U, 0x2C2814U, 0x2C30FEU, 0x2C4BBCU, 0x2C5356U, + 0x2C6282U, 0x2C7A68U, 0x2C8CECU, 0x2C9406U, 0x2CA5D2U, 0x2CBD38U, 0x2CC67AU, 0x2CDE90U, 0x2CEF44U, 0x2CF7AEU, + 0x2D024CU, 0x2D1AA6U, 0x2D2B72U, 0x2D3398U, 0x2D48DAU, 0x2D5030U, 0x2D61E4U, 0x2D790EU, 0x2D8F8AU, 0x2D9760U, + 0x2DA6B4U, 0x2DBE5EU, 0x2DC51CU, 0x2DDDF6U, 0x2DEC22U, 0x2DF4C8U, 0x2E07E6U, 0x2E1F0CU, 0x2E2ED8U, 0x2E3632U, + 0x2E4D70U, 0x2E559AU, 0x2E644EU, 0x2E7CA4U, 0x2E8A20U, 0x2E92CAU, 0x2EA31EU, 0x2EBBF4U, 0x2EC0B6U, 0x2ED85CU, + 0x2EE988U, 0x2EF162U, 0x2F0480U, 0x2F1C6AU, 0x2F2DBEU, 0x2F3554U, 0x2F4E16U, 0x2F56FCU, 0x2F6728U, 0x2F7FC2U, + 0x2F8946U, 0x2F91ACU, 0x2FA078U, 0x2FB892U, 0x2FC3D0U, 0x2FDB3AU, 0x2FEAEEU, 0x2FF204U, 0x3008DCU, 0x301036U, + 0x3021E2U, 0x303908U, 0x30424AU, 0x305AA0U, 0x306B74U, 0x30739EU, 0x30851AU, 0x309DF0U, 0x30AC24U, 0x30B4CEU, + 0x30CF8CU, 0x30D766U, 0x30E6B2U, 0x30FE58U, 0x310BBAU, 0x311350U, 0x312284U, 0x313A6EU, 0x31412CU, 0x3159C6U, + 0x316812U, 0x3170F8U, 0x31867CU, 0x319E96U, 0x31AF42U, 0x31B7A8U, 0x31CCEAU, 0x31D400U, 0x31E5D4U, 0x31FD3EU, + 0x320E10U, 0x3216FAU, 0x32272EU, 0x323FC4U, 0x324486U, 0x325C6CU, 0x326DB8U, 0x327552U, 0x3283D6U, 0x329B3CU, + 0x32AAE8U, 0x32B202U, 0x32C940U, 0x32D1AAU, 0x32E07EU, 0x32F894U, 0x330D76U, 0x33159CU, 0x332448U, 0x333CA2U, + 0x3347E0U, 0x335F0AU, 0x336EDEU, 0x337634U, 0x3380B0U, 0x33985AU, 0x33A98EU, 0x33B164U, 0x33CA26U, 0x33D2CCU, + 0x33E318U, 0x33FBF2U, 0x340544U, 0x341DAEU, 0x342C7AU, 0x343490U, 0x344FD2U, 0x345738U, 0x3466ECU, 0x347E06U, + 0x348882U, 0x349068U, 0x34A1BCU, 0x34B956U, 0x34C214U, 0x34DAFEU, 0x34EB2AU, 0x34F3C0U, 0x350622U, 0x351EC8U, + 0x352F1CU, 0x3537F6U, 0x354CB4U, 0x35545EU, 0x35658AU, 0x357D60U, 0x358BE4U, 0x35930EU, 0x35A2DAU, 0x35BA30U, + 0x35C172U, 0x35D998U, 0x35E84CU, 0x35F0A6U, 0x360388U, 0x361B62U, 0x362AB6U, 0x36325CU, 0x36491EU, 0x3651F4U, + 0x366020U, 0x3678CAU, 0x368E4EU, 0x3696A4U, 0x36A770U, 0x36BF9AU, 0x36C4D8U, 0x36DC32U, 0x36EDE6U, 0x36F50CU, + 0x3700EEU, 0x371804U, 0x3729D0U, 0x37313AU, 0x374A78U, 0x375292U, 0x376346U, 0x377BACU, 0x378D28U, 0x3795C2U, + 0x37A416U, 0x37BCFCU, 0x37C7BEU, 0x37DF54U, 0x37EE80U, 0x37F66AU, 0x380B06U, 0x3813ECU, 0x382238U, 0x383AD2U, + 0x384190U, 0x38597AU, 0x3868AEU, 0x387044U, 0x3886C0U, 0x389E2AU, 0x38AFFEU, 0x38B714U, 0x38CC56U, 0x38D4BCU, + 0x38E568U, 0x38FD82U, 0x390860U, 0x39108AU, 0x39215EU, 0x3939B4U, 0x3942F6U, 0x395A1CU, 0x396BC8U, 0x397322U, + 0x3985A6U, 0x399D4CU, 0x39AC98U, 0x39B472U, 0x39CF30U, 0x39D7DAU, 0x39E60EU, 0x39FEE4U, 0x3A0DCAU, 0x3A1520U, + 0x3A24F4U, 0x3A3C1EU, 0x3A475CU, 0x3A5FB6U, 0x3A6E62U, 0x3A7688U, 0x3A800CU, 0x3A98E6U, 0x3AA932U, 0x3AB1D8U, + 0x3ACA9AU, 0x3AD270U, 0x3AE3A4U, 0x3AFB4EU, 0x3B0EACU, 0x3B1646U, 0x3B2792U, 0x3B3F78U, 0x3B443AU, 0x3B5CD0U, + 0x3B6D04U, 0x3B75EEU, 0x3B836AU, 0x3B9B80U, 0x3BAA54U, 0x3BB2BEU, 0x3BC9FCU, 0x3BD116U, 0x3BE0C2U, 0x3BF828U, + 0x3C069EU, 0x3C1E74U, 0x3C2FA0U, 0x3C374AU, 0x3C4C08U, 0x3C54E2U, 0x3C6536U, 0x3C7DDCU, 0x3C8B58U, 0x3C93B2U, + 0x3CA266U, 0x3CBA8CU, 0x3CC1CEU, 0x3CD924U, 0x3CE8F0U, 0x3CF01AU, 0x3D05F8U, 0x3D1D12U, 0x3D2CC6U, 0x3D342CU, + 0x3D4F6EU, 0x3D5784U, 0x3D6650U, 0x3D7EBAU, 0x3D883EU, 0x3D90D4U, 0x3DA100U, 0x3DB9EAU, 0x3DC2A8U, 0x3DDA42U, + 0x3DEB96U, 0x3DF37CU, 0x3E0052U, 0x3E18B8U, 0x3E296CU, 0x3E3186U, 0x3E4AC4U, 0x3E522EU, 0x3E63FAU, 0x3E7B10U, + 0x3E8D94U, 0x3E957EU, 0x3EA4AAU, 0x3EBC40U, 0x3EC702U, 0x3EDFE8U, 0x3EEE3CU, 0x3EF6D6U, 0x3F0334U, 0x3F1BDEU, + 0x3F2A0AU, 0x3F32E0U, 0x3F49A2U, 0x3F5148U, 0x3F609CU, 0x3F7876U, 0x3F8EF2U, 0x3F9618U, 0x3FA7CCU, 0x3FBF26U, + 0x3FC464U, 0x3FDC8EU, 0x3FED5AU, 0x3FF5B0U, 0x40063AU, 0x401ED0U, 0x402F04U, 0x4037EEU, 0x404CACU, 0x405446U, + 0x406592U, 0x407D78U, 0x408BFCU, 0x409316U, 0x40A2C2U, 0x40BA28U, 0x40C16AU, 0x40D980U, 0x40E854U, 0x40F0BEU, + 0x41055CU, 0x411DB6U, 0x412C62U, 0x413488U, 0x414FCAU, 0x415720U, 0x4166F4U, 0x417E1EU, 0x41889AU, 0x419070U, + 0x41A1A4U, 0x41B94EU, 0x41C20CU, 0x41DAE6U, 0x41EB32U, 0x41F3D8U, 0x4200F6U, 0x42181CU, 0x4229C8U, 0x423122U, + 0x424A60U, 0x42528AU, 0x42635EU, 0x427BB4U, 0x428D30U, 0x4295DAU, 0x42A40EU, 0x42BCE4U, 0x42C7A6U, 0x42DF4CU, + 0x42EE98U, 0x42F672U, 0x430390U, 0x431B7AU, 0x432AAEU, 0x433244U, 0x434906U, 0x4351ECU, 0x436038U, 0x4378D2U, + 0x438E56U, 0x4396BCU, 0x43A768U, 0x43BF82U, 0x43C4C0U, 0x43DC2AU, 0x43EDFEU, 0x43F514U, 0x440BA2U, 0x441348U, + 0x44229CU, 0x443A76U, 0x444134U, 0x4459DEU, 0x44680AU, 0x4470E0U, 0x448664U, 0x449E8EU, 0x44AF5AU, 0x44B7B0U, + 0x44CCF2U, 0x44D418U, 0x44E5CCU, 0x44FD26U, 0x4508C4U, 0x45102EU, 0x4521FAU, 0x453910U, 0x454252U, 0x455AB8U, + 0x456B6CU, 0x457386U, 0x458502U, 0x459DE8U, 0x45AC3CU, 0x45B4D6U, 0x45CF94U, 0x45D77EU, 0x45E6AAU, 0x45FE40U, + 0x460D6EU, 0x461584U, 0x462450U, 0x463CBAU, 0x4647F8U, 0x465F12U, 0x466EC6U, 0x46762CU, 0x4680A8U, 0x469842U, + 0x46A996U, 0x46B17CU, 0x46CA3EU, 0x46D2D4U, 0x46E300U, 0x46FBEAU, 0x470E08U, 0x4716E2U, 0x472736U, 0x473FDCU, + 0x47449EU, 0x475C74U, 0x476DA0U, 0x47754AU, 0x4783CEU, 0x479B24U, 0x47AAF0U, 0x47B21AU, 0x47C958U, 0x47D1B2U, + 0x47E066U, 0x47F88CU, 0x4805E0U, 0x481D0AU, 0x482CDEU, 0x483434U, 0x484F76U, 0x48579CU, 0x486648U, 0x487EA2U, + 0x488826U, 0x4890CCU, 0x48A118U, 0x48B9F2U, 0x48C2B0U, 0x48DA5AU, 0x48EB8EU, 0x48F364U, 0x490686U, 0x491E6CU, + 0x492FB8U, 0x493752U, 0x494C10U, 0x4954FAU, 0x49652EU, 0x497DC4U, 0x498B40U, 0x4993AAU, 0x49A27EU, 0x49BA94U, + 0x49C1D6U, 0x49D93CU, 0x49E8E8U, 0x49F002U, 0x4A032CU, 0x4A1BC6U, 0x4A2A12U, 0x4A32F8U, 0x4A49BAU, 0x4A5150U, + 0x4A6084U, 0x4A786EU, 0x4A8EEAU, 0x4A9600U, 0x4AA7D4U, 0x4ABF3EU, 0x4AC47CU, 0x4ADC96U, 0x4AED42U, 0x4AF5A8U, + 0x4B004AU, 0x4B18A0U, 0x4B2974U, 0x4B319EU, 0x4B4ADCU, 0x4B5236U, 0x4B63E2U, 0x4B7B08U, 0x4B8D8CU, 0x4B9566U, + 0x4BA4B2U, 0x4BBC58U, 0x4BC71AU, 0x4BDFF0U, 0x4BEE24U, 0x4BF6CEU, 0x4C0878U, 0x4C1092U, 0x4C2146U, 0x4C39ACU, + 0x4C42EEU, 0x4C5A04U, 0x4C6BD0U, 0x4C733AU, 0x4C85BEU, 0x4C9D54U, 0x4CAC80U, 0x4CB46AU, 0x4CCF28U, 0x4CD7C2U, + 0x4CE616U, 0x4CFEFCU, 0x4D0B1EU, 0x4D13F4U, 0x4D2220U, 0x4D3ACAU, 0x4D4188U, 0x4D5962U, 0x4D68B6U, 0x4D705CU, + 0x4D86D8U, 0x4D9E32U, 0x4DAFE6U, 0x4DB70CU, 0x4DCC4EU, 0x4DD4A4U, 0x4DE570U, 0x4DFD9AU, 0x4E0EB4U, 0x4E165EU, + 0x4E278AU, 0x4E3F60U, 0x4E4422U, 0x4E5CC8U, 0x4E6D1CU, 0x4E75F6U, 0x4E8372U, 0x4E9B98U, 0x4EAA4CU, 0x4EB2A6U, + 0x4EC9E4U, 0x4ED10EU, 0x4EE0DAU, 0x4EF830U, 0x4F0DD2U, 0x4F1538U, 0x4F24ECU, 0x4F3C06U, 0x4F4744U, 0x4F5FAEU, + 0x4F6E7AU, 0x4F7690U, 0x4F8014U, 0x4F98FEU, 0x4FA92AU, 0x4FB1C0U, 0x4FCA82U, 0x4FD268U, 0x4FE3BCU, 0x4FFB56U, + 0x50018EU, 0x501964U, 0x5028B0U, 0x50305AU, 0x504B18U, 0x5053F2U, 0x506226U, 0x507ACCU, 0x508C48U, 0x5094A2U, + 0x50A576U, 0x50BD9CU, 0x50C6DEU, 0x50DE34U, 0x50EFE0U, 0x50F70AU, 0x5102E8U, 0x511A02U, 0x512BD6U, 0x51333CU, + 0x51487EU, 0x515094U, 0x516140U, 0x5179AAU, 0x518F2EU, 0x5197C4U, 0x51A610U, 0x51BEFAU, 0x51C5B8U, 0x51DD52U, + 0x51EC86U, 0x51F46CU, 0x520742U, 0x521FA8U, 0x522E7CU, 0x523696U, 0x524DD4U, 0x52553EU, 0x5264EAU, 0x527C00U, + 0x528A84U, 0x52926EU, 0x52A3BAU, 0x52BB50U, 0x52C012U, 0x52D8F8U, 0x52E92CU, 0x52F1C6U, 0x530424U, 0x531CCEU, + 0x532D1AU, 0x5335F0U, 0x534EB2U, 0x535658U, 0x53678CU, 0x537F66U, 0x5389E2U, 0x539108U, 0x53A0DCU, 0x53B836U, + 0x53C374U, 0x53DB9EU, 0x53EA4AU, 0x53F2A0U, 0x540C16U, 0x5414FCU, 0x542528U, 0x543DC2U, 0x544680U, 0x545E6AU, + 0x546FBEU, 0x547754U, 0x5481D0U, 0x54993AU, 0x54A8EEU, 0x54B004U, 0x54CB46U, 0x54D3ACU, 0x54E278U, 0x54FA92U, + 0x550F70U, 0x55179AU, 0x55264EU, 0x553EA4U, 0x5545E6U, 0x555D0CU, 0x556CD8U, 0x557432U, 0x5582B6U, 0x559A5CU, + 0x55AB88U, 0x55B362U, 0x55C820U, 0x55D0CAU, 0x55E11EU, 0x55F9F4U, 0x560ADAU, 0x561230U, 0x5623E4U, 0x563B0EU, + 0x56404CU, 0x5658A6U, 0x566972U, 0x567198U, 0x56871CU, 0x569FF6U, 0x56AE22U, 0x56B6C8U, 0x56CD8AU, 0x56D560U, + 0x56E4B4U, 0x56FC5EU, 0x5709BCU, 0x571156U, 0x572082U, 0x573868U, 0x57432AU, 0x575BC0U, 0x576A14U, 0x5772FEU, + 0x57847AU, 0x579C90U, 0x57AD44U, 0x57B5AEU, 0x57CEECU, 0x57D606U, 0x57E7D2U, 0x57FF38U, 0x580254U, 0x581ABEU, + 0x582B6AU, 0x583380U, 0x5848C2U, 0x585028U, 0x5861FCU, 0x587916U, 0x588F92U, 0x589778U, 0x58A6ACU, 0x58BE46U, + 0x58C504U, 0x58DDEEU, 0x58EC3AU, 0x58F4D0U, 0x590132U, 0x5919D8U, 0x59280CU, 0x5930E6U, 0x594BA4U, 0x59534EU, + 0x59629AU, 0x597A70U, 0x598CF4U, 0x59941EU, 0x59A5CAU, 0x59BD20U, 0x59C662U, 0x59DE88U, 0x59EF5CU, 0x59F7B6U, + 0x5A0498U, 0x5A1C72U, 0x5A2DA6U, 0x5A354CU, 0x5A4E0EU, 0x5A56E4U, 0x5A6730U, 0x5A7FDAU, 0x5A895EU, 0x5A91B4U, + 0x5AA060U, 0x5AB88AU, 0x5AC3C8U, 0x5ADB22U, 0x5AEAF6U, 0x5AF21CU, 0x5B07FEU, 0x5B1F14U, 0x5B2EC0U, 0x5B362AU, + 0x5B4D68U, 0x5B5582U, 0x5B6456U, 0x5B7CBCU, 0x5B8A38U, 0x5B92D2U, 0x5BA306U, 0x5BBBECU, 0x5BC0AEU, 0x5BD844U, + 0x5BE990U, 0x5BF17AU, 0x5C0FCCU, 0x5C1726U, 0x5C26F2U, 0x5C3E18U, 0x5C455AU, 0x5C5DB0U, 0x5C6C64U, 0x5C748EU, + 0x5C820AU, 0x5C9AE0U, 0x5CAB34U, 0x5CB3DEU, 0x5CC89CU, 0x5CD076U, 0x5CE1A2U, 0x5CF948U, 0x5D0CAAU, 0x5D1440U, + 0x5D2594U, 0x5D3D7EU, 0x5D463CU, 0x5D5ED6U, 0x5D6F02U, 0x5D77E8U, 0x5D816CU, 0x5D9986U, 0x5DA852U, 0x5DB0B8U, + 0x5DCBFAU, 0x5DD310U, 0x5DE2C4U, 0x5DFA2EU, 0x5E0900U, 0x5E11EAU, 0x5E203EU, 0x5E38D4U, 0x5E4396U, 0x5E5B7CU, + 0x5E6AA8U, 0x5E7242U, 0x5E84C6U, 0x5E9C2CU, 0x5EADF8U, 0x5EB512U, 0x5ECE50U, 0x5ED6BAU, 0x5EE76EU, 0x5EFF84U, + 0x5F0A66U, 0x5F128CU, 0x5F2358U, 0x5F3BB2U, 0x5F40F0U, 0x5F581AU, 0x5F69CEU, 0x5F7124U, 0x5F87A0U, 0x5F9F4AU, + 0x5FAE9EU, 0x5FB674U, 0x5FCD36U, 0x5FD5DCU, 0x5FE408U, 0x5FFCE2U, 0x600952U, 0x6011B8U, 0x60206CU, 0x603886U, + 0x6043C4U, 0x605B2EU, 0x606AFAU, 0x607210U, 0x608494U, 0x609C7EU, 0x60ADAAU, 0x60B540U, 0x60CE02U, 0x60D6E8U, + 0x60E73CU, 0x60FFD6U, 0x610A34U, 0x6112DEU, 0x61230AU, 0x613BE0U, 0x6140A2U, 0x615848U, 0x61699CU, 0x617176U, + 0x6187F2U, 0x619F18U, 0x61AECCU, 0x61B626U, 0x61CD64U, 0x61D58EU, 0x61E45AU, 0x61FCB0U, 0x620F9EU, 0x621774U, + 0x6226A0U, 0x623E4AU, 0x624508U, 0x625DE2U, 0x626C36U, 0x6274DCU, 0x628258U, 0x629AB2U, 0x62AB66U, 0x62B38CU, + 0x62C8CEU, 0x62D024U, 0x62E1F0U, 0x62F91AU, 0x630CF8U, 0x631412U, 0x6325C6U, 0x633D2CU, 0x63466EU, 0x635E84U, + 0x636F50U, 0x6377BAU, 0x63813EU, 0x6399D4U, 0x63A800U, 0x63B0EAU, 0x63CBA8U, 0x63D342U, 0x63E296U, 0x63FA7CU, + 0x6404CAU, 0x641C20U, 0x642DF4U, 0x64351EU, 0x644E5CU, 0x6456B6U, 0x646762U, 0x647F88U, 0x64890CU, 0x6491E6U, + 0x64A032U, 0x64B8D8U, 0x64C39AU, 0x64DB70U, 0x64EAA4U, 0x64F24EU, 0x6507ACU, 0x651F46U, 0x652E92U, 0x653678U, + 0x654D3AU, 0x6555D0U, 0x656404U, 0x657CEEU, 0x658A6AU, 0x659280U, 0x65A354U, 0x65BBBEU, 0x65C0FCU, 0x65D816U, + 0x65E9C2U, 0x65F128U, 0x660206U, 0x661AECU, 0x662B38U, 0x6633D2U, 0x664890U, 0x66507AU, 0x6661AEU, 0x667944U, + 0x668FC0U, 0x66972AU, 0x66A6FEU, 0x66BE14U, 0x66C556U, 0x66DDBCU, 0x66EC68U, 0x66F482U, 0x670160U, 0x67198AU, + 0x67285EU, 0x6730B4U, 0x674BF6U, 0x67531CU, 0x6762C8U, 0x677A22U, 0x678CA6U, 0x67944CU, 0x67A598U, 0x67BD72U, + 0x67C630U, 0x67DEDAU, 0x67EF0EU, 0x67F7E4U, 0x680A88U, 0x681262U, 0x6823B6U, 0x683B5CU, 0x68401EU, 0x6858F4U, + 0x686920U, 0x6871CAU, 0x68874EU, 0x689FA4U, 0x68AE70U, 0x68B69AU, 0x68CDD8U, 0x68D532U, 0x68E4E6U, 0x68FC0CU, + 0x6909EEU, 0x691104U, 0x6920D0U, 0x69383AU, 0x694378U, 0x695B92U, 0x696A46U, 0x6972ACU, 0x698428U, 0x699CC2U, + 0x69AD16U, 0x69B5FCU, 0x69CEBEU, 0x69D654U, 0x69E780U, 0x69FF6AU, 0x6A0C44U, 0x6A14AEU, 0x6A257AU, 0x6A3D90U, + 0x6A46D2U, 0x6A5E38U, 0x6A6FECU, 0x6A7706U, 0x6A8182U, 0x6A9968U, 0x6AA8BCU, 0x6AB056U, 0x6ACB14U, 0x6AD3FEU, + 0x6AE22AU, 0x6AFAC0U, 0x6B0F22U, 0x6B17C8U, 0x6B261CU, 0x6B3EF6U, 0x6B45B4U, 0x6B5D5EU, 0x6B6C8AU, 0x6B7460U, + 0x6B82E4U, 0x6B9A0EU, 0x6BABDAU, 0x6BB330U, 0x6BC872U, 0x6BD098U, 0x6BE14CU, 0x6BF9A6U, 0x6C0710U, 0x6C1FFAU, + 0x6C2E2EU, 0x6C36C4U, 0x6C4D86U, 0x6C556CU, 0x6C64B8U, 0x6C7C52U, 0x6C8AD6U, 0x6C923CU, 0x6CA3E8U, 0x6CBB02U, + 0x6CC040U, 0x6CD8AAU, 0x6CE97EU, 0x6CF194U, 0x6D0476U, 0x6D1C9CU, 0x6D2D48U, 0x6D35A2U, 0x6D4EE0U, 0x6D560AU, + 0x6D67DEU, 0x6D7F34U, 0x6D89B0U, 0x6D915AU, 0x6DA08EU, 0x6DB864U, 0x6DC326U, 0x6DDBCCU, 0x6DEA18U, 0x6DF2F2U, + 0x6E01DCU, 0x6E1936U, 0x6E28E2U, 0x6E3008U, 0x6E4B4AU, 0x6E53A0U, 0x6E6274U, 0x6E7A9EU, 0x6E8C1AU, 0x6E94F0U, + 0x6EA524U, 0x6EBDCEU, 0x6EC68CU, 0x6EDE66U, 0x6EEFB2U, 0x6EF758U, 0x6F02BAU, 0x6F1A50U, 0x6F2B84U, 0x6F336EU, + 0x6F482CU, 0x6F50C6U, 0x6F6112U, 0x6F79F8U, 0x6F8F7CU, 0x6F9796U, 0x6FA642U, 0x6FBEA8U, 0x6FC5EAU, 0x6FDD00U, + 0x6FECD4U, 0x6FF43EU, 0x700EE6U, 0x70160CU, 0x7027D8U, 0x703F32U, 0x704470U, 0x705C9AU, 0x706D4EU, 0x7075A4U, + 0x708320U, 0x709BCAU, 0x70AA1EU, 0x70B2F4U, 0x70C9B6U, 0x70D15CU, 0x70E088U, 0x70F862U, 0x710D80U, 0x71156AU, + 0x7124BEU, 0x713C54U, 0x714716U, 0x715FFCU, 0x716E28U, 0x7176C2U, 0x718046U, 0x7198ACU, 0x71A978U, 0x71B192U, + 0x71CAD0U, 0x71D23AU, 0x71E3EEU, 0x71FB04U, 0x72082AU, 0x7210C0U, 0x722114U, 0x7239FEU, 0x7242BCU, 0x725A56U, + 0x726B82U, 0x727368U, 0x7285ECU, 0x729D06U, 0x72ACD2U, 0x72B438U, 0x72CF7AU, 0x72D790U, 0x72E644U, 0x72FEAEU, + 0x730B4CU, 0x7313A6U, 0x732272U, 0x733A98U, 0x7341DAU, 0x735930U, 0x7368E4U, 0x73700EU, 0x73868AU, 0x739E60U, + 0x73AFB4U, 0x73B75EU, 0x73CC1CU, 0x73D4F6U, 0x73E522U, 0x73FDC8U, 0x74037EU, 0x741B94U, 0x742A40U, 0x7432AAU, + 0x7449E8U, 0x745102U, 0x7460D6U, 0x74783CU, 0x748EB8U, 0x749652U, 0x74A786U, 0x74BF6CU, 0x74C42EU, 0x74DCC4U, + 0x74ED10U, 0x74F5FAU, 0x750018U, 0x7518F2U, 0x752926U, 0x7531CCU, 0x754A8EU, 0x755264U, 0x7563B0U, 0x757B5AU, + 0x758DDEU, 0x759534U, 0x75A4E0U, 0x75BC0AU, 0x75C748U, 0x75DFA2U, 0x75EE76U, 0x75F69CU, 0x7605B2U, 0x761D58U, + 0x762C8CU, 0x763466U, 0x764F24U, 0x7657CEU, 0x76661AU, 0x767EF0U, 0x768874U, 0x76909EU, 0x76A14AU, 0x76B9A0U, + 0x76C2E2U, 0x76DA08U, 0x76EBDCU, 0x76F336U, 0x7706D4U, 0x771E3EU, 0x772FEAU, 0x773700U, 0x774C42U, 0x7754A8U, + 0x77657CU, 0x777D96U, 0x778B12U, 0x7793F8U, 0x77A22CU, 0x77BAC6U, 0x77C184U, 0x77D96EU, 0x77E8BAU, 0x77F050U, + 0x780D3CU, 0x7815D6U, 0x782402U, 0x783CE8U, 0x7847AAU, 0x785F40U, 0x786E94U, 0x78767EU, 0x7880FAU, 0x789810U, + 0x78A9C4U, 0x78B12EU, 0x78CA6CU, 0x78D286U, 0x78E352U, 0x78FBB8U, 0x790E5AU, 0x7916B0U, 0x792764U, 0x793F8EU, + 0x7944CCU, 0x795C26U, 0x796DF2U, 0x797518U, 0x79839CU, 0x799B76U, 0x79AAA2U, 0x79B248U, 0x79C90AU, 0x79D1E0U, + 0x79E034U, 0x79F8DEU, 0x7A0BF0U, 0x7A131AU, 0x7A22CEU, 0x7A3A24U, 0x7A4166U, 0x7A598CU, 0x7A6858U, 0x7A70B2U, + 0x7A8636U, 0x7A9EDCU, 0x7AAF08U, 0x7AB7E2U, 0x7ACCA0U, 0x7AD44AU, 0x7AE59EU, 0x7AFD74U, 0x7B0896U, 0x7B107CU, + 0x7B21A8U, 0x7B3942U, 0x7B4200U, 0x7B5AEAU, 0x7B6B3EU, 0x7B73D4U, 0x7B8550U, 0x7B9DBAU, 0x7BAC6EU, 0x7BB484U, + 0x7BCFC6U, 0x7BD72CU, 0x7BE6F8U, 0x7BFE12U, 0x7C00A4U, 0x7C184EU, 0x7C299AU, 0x7C3170U, 0x7C4A32U, 0x7C52D8U, + 0x7C630CU, 0x7C7BE6U, 0x7C8D62U, 0x7C9588U, 0x7CA45CU, 0x7CBCB6U, 0x7CC7F4U, 0x7CDF1EU, 0x7CEECAU, 0x7CF620U, + 0x7D03C2U, 0x7D1B28U, 0x7D2AFCU, 0x7D3216U, 0x7D4954U, 0x7D51BEU, 0x7D606AU, 0x7D7880U, 0x7D8E04U, 0x7D96EEU, + 0x7DA73AU, 0x7DBFD0U, 0x7DC492U, 0x7DDC78U, 0x7DEDACU, 0x7DF546U, 0x7E0668U, 0x7E1E82U, 0x7E2F56U, 0x7E37BCU, + 0x7E4CFEU, 0x7E5414U, 0x7E65C0U, 0x7E7D2AU, 0x7E8BAEU, 0x7E9344U, 0x7EA290U, 0x7EBA7AU, 0x7EC138U, 0x7ED9D2U, + 0x7EE806U, 0x7EF0ECU, 0x7F050EU, 0x7F1DE4U, 0x7F2C30U, 0x7F34DAU, 0x7F4F98U, 0x7F5772U, 0x7F66A6U, 0x7F7E4CU, + 0x7F88C8U, 0x7F9022U, 0x7FA1F6U, 0x7FB91CU, 0x7FC25EU, 0x7FDAB4U, 0x7FEB60U, 0x7FF38AU, 0x800C74U, 0x80149EU, + 0x80254AU, 0x803DA0U, 0x8046E2U, 0x805E08U, 0x806FDCU, 0x807736U, 0x8081B2U, 0x809958U, 0x80A88CU, 0x80B066U, + 0x80CB24U, 0x80D3CEU, 0x80E21AU, 0x80FAF0U, 0x810F12U, 0x8117F8U, 0x81262CU, 0x813EC6U, 0x814584U, 0x815D6EU, + 0x816CBAU, 0x817450U, 0x8182D4U, 0x819A3EU, 0x81ABEAU, 0x81B300U, 0x81C842U, 0x81D0A8U, 0x81E17CU, 0x81F996U, + 0x820AB8U, 0x821252U, 0x822386U, 0x823B6CU, 0x82402EU, 0x8258C4U, 0x826910U, 0x8271FAU, 0x82877EU, 0x829F94U, + 0x82AE40U, 0x82B6AAU, 0x82CDE8U, 0x82D502U, 0x82E4D6U, 0x82FC3CU, 0x8309DEU, 0x831134U, 0x8320E0U, 0x83380AU, + 0x834348U, 0x835BA2U, 0x836A76U, 0x83729CU, 0x838418U, 0x839CF2U, 0x83AD26U, 0x83B5CCU, 0x83CE8EU, 0x83D664U, + 0x83E7B0U, 0x83FF5AU, 0x8401ECU, 0x841906U, 0x8428D2U, 0x843038U, 0x844B7AU, 0x845390U, 0x846244U, 0x847AAEU, + 0x848C2AU, 0x8494C0U, 0x84A514U, 0x84BDFEU, 0x84C6BCU, 0x84DE56U, 0x84EF82U, 0x84F768U, 0x85028AU, 0x851A60U, + 0x852BB4U, 0x85335EU, 0x85481CU, 0x8550F6U, 0x856122U, 0x8579C8U, 0x858F4CU, 0x8597A6U, 0x85A672U, 0x85BE98U, + 0x85C5DAU, 0x85DD30U, 0x85ECE4U, 0x85F40EU, 0x860720U, 0x861FCAU, 0x862E1EU, 0x8636F4U, 0x864DB6U, 0x86555CU, + 0x866488U, 0x867C62U, 0x868AE6U, 0x86920CU, 0x86A3D8U, 0x86BB32U, 0x86C070U, 0x86D89AU, 0x86E94EU, 0x86F1A4U, + 0x870446U, 0x871CACU, 0x872D78U, 0x873592U, 0x874ED0U, 0x87563AU, 0x8767EEU, 0x877F04U, 0x878980U, 0x87916AU, + 0x87A0BEU, 0x87B854U, 0x87C316U, 0x87DBFCU, 0x87EA28U, 0x87F2C2U, 0x880FAEU, 0x881744U, 0x882690U, 0x883E7AU, + 0x884538U, 0x885DD2U, 0x886C06U, 0x8874ECU, 0x888268U, 0x889A82U, 0x88AB56U, 0x88B3BCU, 0x88C8FEU, 0x88D014U, + 0x88E1C0U, 0x88F92AU, 0x890CC8U, 0x891422U, 0x8925F6U, 0x893D1CU, 0x89465EU, 0x895EB4U, 0x896F60U, 0x89778AU, + 0x89810EU, 0x8999E4U, 0x89A830U, 0x89B0DAU, 0x89CB98U, 0x89D372U, 0x89E2A6U, 0x89FA4CU, 0x8A0962U, 0x8A1188U, + 0x8A205CU, 0x8A38B6U, 0x8A43F4U, 0x8A5B1EU, 0x8A6ACAU, 0x8A7220U, 0x8A84A4U, 0x8A9C4EU, 0x8AAD9AU, 0x8AB570U, + 0x8ACE32U, 0x8AD6D8U, 0x8AE70CU, 0x8AFFE6U, 0x8B0A04U, 0x8B12EEU, 0x8B233AU, 0x8B3BD0U, 0x8B4092U, 0x8B5878U, + 0x8B69ACU, 0x8B7146U, 0x8B87C2U, 0x8B9F28U, 0x8BAEFCU, 0x8BB616U, 0x8BCD54U, 0x8BD5BEU, 0x8BE46AU, 0x8BFC80U, + 0x8C0236U, 0x8C1ADCU, 0x8C2B08U, 0x8C33E2U, 0x8C48A0U, 0x8C504AU, 0x8C619EU, 0x8C7974U, 0x8C8FF0U, 0x8C971AU, + 0x8CA6CEU, 0x8CBE24U, 0x8CC566U, 0x8CDD8CU, 0x8CEC58U, 0x8CF4B2U, 0x8D0150U, 0x8D19BAU, 0x8D286EU, 0x8D3084U, + 0x8D4BC6U, 0x8D532CU, 0x8D62F8U, 0x8D7A12U, 0x8D8C96U, 0x8D947CU, 0x8DA5A8U, 0x8DBD42U, 0x8DC600U, 0x8DDEEAU, + 0x8DEF3EU, 0x8DF7D4U, 0x8E04FAU, 0x8E1C10U, 0x8E2DC4U, 0x8E352EU, 0x8E4E6CU, 0x8E5686U, 0x8E6752U, 0x8E7FB8U, + 0x8E893CU, 0x8E91D6U, 0x8EA002U, 0x8EB8E8U, 0x8EC3AAU, 0x8EDB40U, 0x8EEA94U, 0x8EF27EU, 0x8F079CU, 0x8F1F76U, + 0x8F2EA2U, 0x8F3648U, 0x8F4D0AU, 0x8F55E0U, 0x8F6434U, 0x8F7CDEU, 0x8F8A5AU, 0x8F92B0U, 0x8FA364U, 0x8FBB8EU, + 0x8FC0CCU, 0x8FD826U, 0x8FE9F2U, 0x8FF118U, 0x900BC0U, 0x90132AU, 0x9022FEU, 0x903A14U, 0x904156U, 0x9059BCU, + 0x906868U, 0x907082U, 0x908606U, 0x909EECU, 0x90AF38U, 0x90B7D2U, 0x90CC90U, 0x90D47AU, 0x90E5AEU, 0x90FD44U, + 0x9108A6U, 0x91104CU, 0x912198U, 0x913972U, 0x914230U, 0x915ADAU, 0x916B0EU, 0x9173E4U, 0x918560U, 0x919D8AU, + 0x91AC5EU, 0x91B4B4U, 0x91CFF6U, 0x91D71CU, 0x91E6C8U, 0x91FE22U, 0x920D0CU, 0x9215E6U, 0x922432U, 0x923CD8U, + 0x92479AU, 0x925F70U, 0x926EA4U, 0x92764EU, 0x9280CAU, 0x929820U, 0x92A9F4U, 0x92B11EU, 0x92CA5CU, 0x92D2B6U, + 0x92E362U, 0x92FB88U, 0x930E6AU, 0x931680U, 0x932754U, 0x933FBEU, 0x9344FCU, 0x935C16U, 0x936DC2U, 0x937528U, + 0x9383ACU, 0x939B46U, 0x93AA92U, 0x93B278U, 0x93C93AU, 0x93D1D0U, 0x93E004U, 0x93F8EEU, 0x940658U, 0x941EB2U, + 0x942F66U, 0x94378CU, 0x944CCEU, 0x945424U, 0x9465F0U, 0x947D1AU, 0x948B9EU, 0x949374U, 0x94A2A0U, 0x94BA4AU, + 0x94C108U, 0x94D9E2U, 0x94E836U, 0x94F0DCU, 0x95053EU, 0x951DD4U, 0x952C00U, 0x9534EAU, 0x954FA8U, 0x955742U, + 0x956696U, 0x957E7CU, 0x9588F8U, 0x959012U, 0x95A1C6U, 0x95B92CU, 0x95C26EU, 0x95DA84U, 0x95EB50U, 0x95F3BAU, + 0x960094U, 0x96187EU, 0x9629AAU, 0x963140U, 0x964A02U, 0x9652E8U, 0x96633CU, 0x967BD6U, 0x968D52U, 0x9695B8U, + 0x96A46CU, 0x96BC86U, 0x96C7C4U, 0x96DF2EU, 0x96EEFAU, 0x96F610U, 0x9703F2U, 0x971B18U, 0x972ACCU, 0x973226U, + 0x974964U, 0x97518EU, 0x97605AU, 0x9778B0U, 0x978E34U, 0x9796DEU, 0x97A70AU, 0x97BFE0U, 0x97C4A2U, 0x97DC48U, + 0x97ED9CU, 0x97F576U, 0x98081AU, 0x9810F0U, 0x982124U, 0x9839CEU, 0x98428CU, 0x985A66U, 0x986BB2U, 0x987358U, + 0x9885DCU, 0x989D36U, 0x98ACE2U, 0x98B408U, 0x98CF4AU, 0x98D7A0U, 0x98E674U, 0x98FE9EU, 0x990B7CU, 0x991396U, + 0x992242U, 0x993AA8U, 0x9941EAU, 0x995900U, 0x9968D4U, 0x99703EU, 0x9986BAU, 0x999E50U, 0x99AF84U, 0x99B76EU, + 0x99CC2CU, 0x99D4C6U, 0x99E512U, 0x99FDF8U, 0x9A0ED6U, 0x9A163CU, 0x9A27E8U, 0x9A3F02U, 0x9A4440U, 0x9A5CAAU, + 0x9A6D7EU, 0x9A7594U, 0x9A8310U, 0x9A9BFAU, 0x9AAA2EU, 0x9AB2C4U, 0x9AC986U, 0x9AD16CU, 0x9AE0B8U, 0x9AF852U, + 0x9B0DB0U, 0x9B155AU, 0x9B248EU, 0x9B3C64U, 0x9B4726U, 0x9B5FCCU, 0x9B6E18U, 0x9B76F2U, 0x9B8076U, 0x9B989CU, + 0x9BA948U, 0x9BB1A2U, 0x9BCAE0U, 0x9BD20AU, 0x9BE3DEU, 0x9BFB34U, 0x9C0582U, 0x9C1D68U, 0x9C2CBCU, 0x9C3456U, + 0x9C4F14U, 0x9C57FEU, 0x9C662AU, 0x9C7EC0U, 0x9C8844U, 0x9C90AEU, 0x9CA17AU, 0x9CB990U, 0x9CC2D2U, 0x9CDA38U, + 0x9CEBECU, 0x9CF306U, 0x9D06E4U, 0x9D1E0EU, 0x9D2FDAU, 0x9D3730U, 0x9D4C72U, 0x9D5498U, 0x9D654CU, 0x9D7DA6U, + 0x9D8B22U, 0x9D93C8U, 0x9DA21CU, 0x9DBAF6U, 0x9DC1B4U, 0x9DD95EU, 0x9DE88AU, 0x9DF060U, 0x9E034EU, 0x9E1BA4U, + 0x9E2A70U, 0x9E329AU, 0x9E49D8U, 0x9E5132U, 0x9E60E6U, 0x9E780CU, 0x9E8E88U, 0x9E9662U, 0x9EA7B6U, 0x9EBF5CU, + 0x9EC41EU, 0x9EDCF4U, 0x9EED20U, 0x9EF5CAU, 0x9F0028U, 0x9F18C2U, 0x9F2916U, 0x9F31FCU, 0x9F4ABEU, 0x9F5254U, + 0x9F6380U, 0x9F7B6AU, 0x9F8DEEU, 0x9F9504U, 0x9FA4D0U, 0x9FBC3AU, 0x9FC778U, 0x9FDF92U, 0x9FEE46U, 0x9FF6ACU, + 0xA0031CU, 0xA01BF6U, 0xA02A22U, 0xA032C8U, 0xA0498AU, 0xA05160U, 0xA060B4U, 0xA0785EU, 0xA08EDAU, 0xA09630U, + 0xA0A7E4U, 0xA0BF0EU, 0xA0C44CU, 0xA0DCA6U, 0xA0ED72U, 0xA0F598U, 0xA1007AU, 0xA11890U, 0xA12944U, 0xA131AEU, + 0xA14AECU, 0xA15206U, 0xA163D2U, 0xA17B38U, 0xA18DBCU, 0xA19556U, 0xA1A482U, 0xA1BC68U, 0xA1C72AU, 0xA1DFC0U, + 0xA1EE14U, 0xA1F6FEU, 0xA205D0U, 0xA21D3AU, 0xA22CEEU, 0xA23404U, 0xA24F46U, 0xA257ACU, 0xA26678U, 0xA27E92U, + 0xA28816U, 0xA290FCU, 0xA2A128U, 0xA2B9C2U, 0xA2C280U, 0xA2DA6AU, 0xA2EBBEU, 0xA2F354U, 0xA306B6U, 0xA31E5CU, + 0xA32F88U, 0xA33762U, 0xA34C20U, 0xA354CAU, 0xA3651EU, 0xA37DF4U, 0xA38B70U, 0xA3939AU, 0xA3A24EU, 0xA3BAA4U, + 0xA3C1E6U, 0xA3D90CU, 0xA3E8D8U, 0xA3F032U, 0xA40E84U, 0xA4166EU, 0xA427BAU, 0xA43F50U, 0xA44412U, 0xA45CF8U, + 0xA46D2CU, 0xA475C6U, 0xA48342U, 0xA49BA8U, 0xA4AA7CU, 0xA4B296U, 0xA4C9D4U, 0xA4D13EU, 0xA4E0EAU, 0xA4F800U, + 0xA50DE2U, 0xA51508U, 0xA524DCU, 0xA53C36U, 0xA54774U, 0xA55F9EU, 0xA56E4AU, 0xA576A0U, 0xA58024U, 0xA598CEU, + 0xA5A91AU, 0xA5B1F0U, 0xA5CAB2U, 0xA5D258U, 0xA5E38CU, 0xA5FB66U, 0xA60848U, 0xA610A2U, 0xA62176U, 0xA6399CU, + 0xA642DEU, 0xA65A34U, 0xA66BE0U, 0xA6730AU, 0xA6858EU, 0xA69D64U, 0xA6ACB0U, 0xA6B45AU, 0xA6CF18U, 0xA6D7F2U, + 0xA6E626U, 0xA6FECCU, 0xA70B2EU, 0xA713C4U, 0xA72210U, 0xA73AFAU, 0xA741B8U, 0xA75952U, 0xA76886U, 0xA7706CU, + 0xA786E8U, 0xA79E02U, 0xA7AFD6U, 0xA7B73CU, 0xA7CC7EU, 0xA7D494U, 0xA7E540U, 0xA7FDAAU, 0xA800C6U, 0xA8182CU, + 0xA829F8U, 0xA83112U, 0xA84A50U, 0xA852BAU, 0xA8636EU, 0xA87B84U, 0xA88D00U, 0xA895EAU, 0xA8A43EU, 0xA8BCD4U, + 0xA8C796U, 0xA8DF7CU, 0xA8EEA8U, 0xA8F642U, 0xA903A0U, 0xA91B4AU, 0xA92A9EU, 0xA93274U, 0xA94936U, 0xA951DCU, + 0xA96008U, 0xA978E2U, 0xA98E66U, 0xA9968CU, 0xA9A758U, 0xA9BFB2U, 0xA9C4F0U, 0xA9DC1AU, 0xA9EDCEU, 0xA9F524U, + 0xAA060AU, 0xAA1EE0U, 0xAA2F34U, 0xAA37DEU, 0xAA4C9CU, 0xAA5476U, 0xAA65A2U, 0xAA7D48U, 0xAA8BCCU, 0xAA9326U, + 0xAAA2F2U, 0xAABA18U, 0xAAC15AU, 0xAAD9B0U, 0xAAE864U, 0xAAF08EU, 0xAB056CU, 0xAB1D86U, 0xAB2C52U, 0xAB34B8U, + 0xAB4FFAU, 0xAB5710U, 0xAB66C4U, 0xAB7E2EU, 0xAB88AAU, 0xAB9040U, 0xABA194U, 0xABB97EU, 0xABC23CU, 0xABDAD6U, + 0xABEB02U, 0xABF3E8U, 0xAC0D5EU, 0xAC15B4U, 0xAC2460U, 0xAC3C8AU, 0xAC47C8U, 0xAC5F22U, 0xAC6EF6U, 0xAC761CU, + 0xAC8098U, 0xAC9872U, 0xACA9A6U, 0xACB14CU, 0xACCA0EU, 0xACD2E4U, 0xACE330U, 0xACFBDAU, 0xAD0E38U, 0xAD16D2U, + 0xAD2706U, 0xAD3FECU, 0xAD44AEU, 0xAD5C44U, 0xAD6D90U, 0xAD757AU, 0xAD83FEU, 0xAD9B14U, 0xADAAC0U, 0xADB22AU, + 0xADC968U, 0xADD182U, 0xADE056U, 0xADF8BCU, 0xAE0B92U, 0xAE1378U, 0xAE22ACU, 0xAE3A46U, 0xAE4104U, 0xAE59EEU, + 0xAE683AU, 0xAE70D0U, 0xAE8654U, 0xAE9EBEU, 0xAEAF6AU, 0xAEB780U, 0xAECCC2U, 0xAED428U, 0xAEE5FCU, 0xAEFD16U, + 0xAF08F4U, 0xAF101EU, 0xAF21CAU, 0xAF3920U, 0xAF4262U, 0xAF5A88U, 0xAF6B5CU, 0xAF73B6U, 0xAF8532U, 0xAF9DD8U, + 0xAFAC0CU, 0xAFB4E6U, 0xAFCFA4U, 0xAFD74EU, 0xAFE69AU, 0xAFFE70U, 0xB004A8U, 0xB01C42U, 0xB02D96U, 0xB0357CU, + 0xB04E3EU, 0xB056D4U, 0xB06700U, 0xB07FEAU, 0xB0896EU, 0xB09184U, 0xB0A050U, 0xB0B8BAU, 0xB0C3F8U, 0xB0DB12U, + 0xB0EAC6U, 0xB0F22CU, 0xB107CEU, 0xB11F24U, 0xB12EF0U, 0xB1361AU, 0xB14D58U, 0xB155B2U, 0xB16466U, 0xB17C8CU, + 0xB18A08U, 0xB192E2U, 0xB1A336U, 0xB1BBDCU, 0xB1C09EU, 0xB1D874U, 0xB1E9A0U, 0xB1F14AU, 0xB20264U, 0xB21A8EU, + 0xB22B5AU, 0xB233B0U, 0xB248F2U, 0xB25018U, 0xB261CCU, 0xB27926U, 0xB28FA2U, 0xB29748U, 0xB2A69CU, 0xB2BE76U, + 0xB2C534U, 0xB2DDDEU, 0xB2EC0AU, 0xB2F4E0U, 0xB30102U, 0xB319E8U, 0xB3283CU, 0xB330D6U, 0xB34B94U, 0xB3537EU, + 0xB362AAU, 0xB37A40U, 0xB38CC4U, 0xB3942EU, 0xB3A5FAU, 0xB3BD10U, 0xB3C652U, 0xB3DEB8U, 0xB3EF6CU, 0xB3F786U, + 0xB40930U, 0xB411DAU, 0xB4200EU, 0xB438E4U, 0xB443A6U, 0xB45B4CU, 0xB46A98U, 0xB47272U, 0xB484F6U, 0xB49C1CU, + 0xB4ADC8U, 0xB4B522U, 0xB4CE60U, 0xB4D68AU, 0xB4E75EU, 0xB4FFB4U, 0xB50A56U, 0xB512BCU, 0xB52368U, 0xB53B82U, + 0xB540C0U, 0xB5582AU, 0xB569FEU, 0xB57114U, 0xB58790U, 0xB59F7AU, 0xB5AEAEU, 0xB5B644U, 0xB5CD06U, 0xB5D5ECU, + 0xB5E438U, 0xB5FCD2U, 0xB60FFCU, 0xB61716U, 0xB626C2U, 0xB63E28U, 0xB6456AU, 0xB65D80U, 0xB66C54U, 0xB674BEU, + 0xB6823AU, 0xB69AD0U, 0xB6AB04U, 0xB6B3EEU, 0xB6C8ACU, 0xB6D046U, 0xB6E192U, 0xB6F978U, 0xB70C9AU, 0xB71470U, + 0xB725A4U, 0xB73D4EU, 0xB7460CU, 0xB75EE6U, 0xB76F32U, 0xB777D8U, 0xB7815CU, 0xB799B6U, 0xB7A862U, 0xB7B088U, + 0xB7CBCAU, 0xB7D320U, 0xB7E2F4U, 0xB7FA1EU, 0xB80772U, 0xB81F98U, 0xB82E4CU, 0xB836A6U, 0xB84DE4U, 0xB8550EU, + 0xB864DAU, 0xB87C30U, 0xB88AB4U, 0xB8925EU, 0xB8A38AU, 0xB8BB60U, 0xB8C022U, 0xB8D8C8U, 0xB8E91CU, 0xB8F1F6U, + 0xB90414U, 0xB91CFEU, 0xB92D2AU, 0xB935C0U, 0xB94E82U, 0xB95668U, 0xB967BCU, 0xB97F56U, 0xB989D2U, 0xB99138U, + 0xB9A0ECU, 0xB9B806U, 0xB9C344U, 0xB9DBAEU, 0xB9EA7AU, 0xB9F290U, 0xBA01BEU, 0xBA1954U, 0xBA2880U, 0xBA306AU, + 0xBA4B28U, 0xBA53C2U, 0xBA6216U, 0xBA7AFCU, 0xBA8C78U, 0xBA9492U, 0xBAA546U, 0xBABDACU, 0xBAC6EEU, 0xBADE04U, + 0xBAEFD0U, 0xBAF73AU, 0xBB02D8U, 0xBB1A32U, 0xBB2BE6U, 0xBB330CU, 0xBB484EU, 0xBB50A4U, 0xBB6170U, 0xBB799AU, + 0xBB8F1EU, 0xBB97F4U, 0xBBA620U, 0xBBBECAU, 0xBBC588U, 0xBBDD62U, 0xBBECB6U, 0xBBF45CU, 0xBC0AEAU, 0xBC1200U, + 0xBC23D4U, 0xBC3B3EU, 0xBC407CU, 0xBC5896U, 0xBC6942U, 0xBC71A8U, 0xBC872CU, 0xBC9FC6U, 0xBCAE12U, 0xBCB6F8U, + 0xBCCDBAU, 0xBCD550U, 0xBCE484U, 0xBCFC6EU, 0xBD098CU, 0xBD1166U, 0xBD20B2U, 0xBD3858U, 0xBD431AU, 0xBD5BF0U, + 0xBD6A24U, 0xBD72CEU, 0xBD844AU, 0xBD9CA0U, 0xBDAD74U, 0xBDB59EU, 0xBDCEDCU, 0xBDD636U, 0xBDE7E2U, 0xBDFF08U, + 0xBE0C26U, 0xBE14CCU, 0xBE2518U, 0xBE3DF2U, 0xBE46B0U, 0xBE5E5AU, 0xBE6F8EU, 0xBE7764U, 0xBE81E0U, 0xBE990AU, + 0xBEA8DEU, 0xBEB034U, 0xBECB76U, 0xBED39CU, 0xBEE248U, 0xBEFAA2U, 0xBF0F40U, 0xBF17AAU, 0xBF267EU, 0xBF3E94U, + 0xBF45D6U, 0xBF5D3CU, 0xBF6CE8U, 0xBF7402U, 0xBF8286U, 0xBF9A6CU, 0xBFABB8U, 0xBFB352U, 0xBFC810U, 0xBFD0FAU, + 0xBFE12EU, 0xBFF9C4U, 0xC00A4EU, 0xC012A4U, 0xC02370U, 0xC03B9AU, 0xC040D8U, 0xC05832U, 0xC069E6U, 0xC0710CU, + 0xC08788U, 0xC09F62U, 0xC0AEB6U, 0xC0B65CU, 0xC0CD1EU, 0xC0D5F4U, 0xC0E420U, 0xC0FCCAU, 0xC10928U, 0xC111C2U, + 0xC12016U, 0xC138FCU, 0xC143BEU, 0xC15B54U, 0xC16A80U, 0xC1726AU, 0xC184EEU, 0xC19C04U, 0xC1ADD0U, 0xC1B53AU, + 0xC1CE78U, 0xC1D692U, 0xC1E746U, 0xC1FFACU, 0xC20C82U, 0xC21468U, 0xC225BCU, 0xC23D56U, 0xC24614U, 0xC25EFEU, + 0xC26F2AU, 0xC277C0U, 0xC28144U, 0xC299AEU, 0xC2A87AU, 0xC2B090U, 0xC2CBD2U, 0xC2D338U, 0xC2E2ECU, 0xC2FA06U, + 0xC30FE4U, 0xC3170EU, 0xC326DAU, 0xC33E30U, 0xC34572U, 0xC35D98U, 0xC36C4CU, 0xC374A6U, 0xC38222U, 0xC39AC8U, + 0xC3AB1CU, 0xC3B3F6U, 0xC3C8B4U, 0xC3D05EU, 0xC3E18AU, 0xC3F960U, 0xC407D6U, 0xC41F3CU, 0xC42EE8U, 0xC43602U, + 0xC44D40U, 0xC455AAU, 0xC4647EU, 0xC47C94U, 0xC48A10U, 0xC492FAU, 0xC4A32EU, 0xC4BBC4U, 0xC4C086U, 0xC4D86CU, + 0xC4E9B8U, 0xC4F152U, 0xC504B0U, 0xC51C5AU, 0xC52D8EU, 0xC53564U, 0xC54E26U, 0xC556CCU, 0xC56718U, 0xC57FF2U, + 0xC58976U, 0xC5919CU, 0xC5A048U, 0xC5B8A2U, 0xC5C3E0U, 0xC5DB0AU, 0xC5EADEU, 0xC5F234U, 0xC6011AU, 0xC619F0U, + 0xC62824U, 0xC630CEU, 0xC64B8CU, 0xC65366U, 0xC662B2U, 0xC67A58U, 0xC68CDCU, 0xC69436U, 0xC6A5E2U, 0xC6BD08U, + 0xC6C64AU, 0xC6DEA0U, 0xC6EF74U, 0xC6F79EU, 0xC7027CU, 0xC71A96U, 0xC72B42U, 0xC733A8U, 0xC748EAU, 0xC75000U, + 0xC761D4U, 0xC7793EU, 0xC78FBAU, 0xC79750U, 0xC7A684U, 0xC7BE6EU, 0xC7C52CU, 0xC7DDC6U, 0xC7EC12U, 0xC7F4F8U, + 0xC80994U, 0xC8117EU, 0xC820AAU, 0xC83840U, 0xC84302U, 0xC85BE8U, 0xC86A3CU, 0xC872D6U, 0xC88452U, 0xC89CB8U, + 0xC8AD6CU, 0xC8B586U, 0xC8CEC4U, 0xC8D62EU, 0xC8E7FAU, 0xC8FF10U, 0xC90AF2U, 0xC91218U, 0xC923CCU, 0xC93B26U, + 0xC94064U, 0xC9588EU, 0xC9695AU, 0xC971B0U, 0xC98734U, 0xC99FDEU, 0xC9AE0AU, 0xC9B6E0U, 0xC9CDA2U, 0xC9D548U, + 0xC9E49CU, 0xC9FC76U, 0xCA0F58U, 0xCA17B2U, 0xCA2666U, 0xCA3E8CU, 0xCA45CEU, 0xCA5D24U, 0xCA6CF0U, 0xCA741AU, + 0xCA829EU, 0xCA9A74U, 0xCAABA0U, 0xCAB34AU, 0xCAC808U, 0xCAD0E2U, 0xCAE136U, 0xCAF9DCU, 0xCB0C3EU, 0xCB14D4U, + 0xCB2500U, 0xCB3DEAU, 0xCB46A8U, 0xCB5E42U, 0xCB6F96U, 0xCB777CU, 0xCB81F8U, 0xCB9912U, 0xCBA8C6U, 0xCBB02CU, + 0xCBCB6EU, 0xCBD384U, 0xCBE250U, 0xCBFABAU, 0xCC040CU, 0xCC1CE6U, 0xCC2D32U, 0xCC35D8U, 0xCC4E9AU, 0xCC5670U, + 0xCC67A4U, 0xCC7F4EU, 0xCC89CAU, 0xCC9120U, 0xCCA0F4U, 0xCCB81EU, 0xCCC35CU, 0xCCDBB6U, 0xCCEA62U, 0xCCF288U, + 0xCD076AU, 0xCD1F80U, 0xCD2E54U, 0xCD36BEU, 0xCD4DFCU, 0xCD5516U, 0xCD64C2U, 0xCD7C28U, 0xCD8AACU, 0xCD9246U, + 0xCDA392U, 0xCDBB78U, 0xCDC03AU, 0xCDD8D0U, 0xCDE904U, 0xCDF1EEU, 0xCE02C0U, 0xCE1A2AU, 0xCE2BFEU, 0xCE3314U, + 0xCE4856U, 0xCE50BCU, 0xCE6168U, 0xCE7982U, 0xCE8F06U, 0xCE97ECU, 0xCEA638U, 0xCEBED2U, 0xCEC590U, 0xCEDD7AU, + 0xCEECAEU, 0xCEF444U, 0xCF01A6U, 0xCF194CU, 0xCF2898U, 0xCF3072U, 0xCF4B30U, 0xCF53DAU, 0xCF620EU, 0xCF7AE4U, + 0xCF8C60U, 0xCF948AU, 0xCFA55EU, 0xCFBDB4U, 0xCFC6F6U, 0xCFDE1CU, 0xCFEFC8U, 0xCFF722U, 0xD00DFAU, 0xD01510U, + 0xD024C4U, 0xD03C2EU, 0xD0476CU, 0xD05F86U, 0xD06E52U, 0xD076B8U, 0xD0803CU, 0xD098D6U, 0xD0A902U, 0xD0B1E8U, + 0xD0CAAAU, 0xD0D240U, 0xD0E394U, 0xD0FB7EU, 0xD10E9CU, 0xD11676U, 0xD127A2U, 0xD13F48U, 0xD1440AU, 0xD15CE0U, + 0xD16D34U, 0xD175DEU, 0xD1835AU, 0xD19BB0U, 0xD1AA64U, 0xD1B28EU, 0xD1C9CCU, 0xD1D126U, 0xD1E0F2U, 0xD1F818U, + 0xD20B36U, 0xD213DCU, 0xD22208U, 0xD23AE2U, 0xD241A0U, 0xD2594AU, 0xD2689EU, 0xD27074U, 0xD286F0U, 0xD29E1AU, + 0xD2AFCEU, 0xD2B724U, 0xD2CC66U, 0xD2D48CU, 0xD2E558U, 0xD2FDB2U, 0xD30850U, 0xD310BAU, 0xD3216EU, 0xD33984U, + 0xD342C6U, 0xD35A2CU, 0xD36BF8U, 0xD37312U, 0xD38596U, 0xD39D7CU, 0xD3ACA8U, 0xD3B442U, 0xD3CF00U, 0xD3D7EAU, + 0xD3E63EU, 0xD3FED4U, 0xD40062U, 0xD41888U, 0xD4295CU, 0xD431B6U, 0xD44AF4U, 0xD4521EU, 0xD463CAU, 0xD47B20U, + 0xD48DA4U, 0xD4954EU, 0xD4A49AU, 0xD4BC70U, 0xD4C732U, 0xD4DFD8U, 0xD4EE0CU, 0xD4F6E6U, 0xD50304U, 0xD51BEEU, + 0xD52A3AU, 0xD532D0U, 0xD54992U, 0xD55178U, 0xD560ACU, 0xD57846U, 0xD58EC2U, 0xD59628U, 0xD5A7FCU, 0xD5BF16U, + 0xD5C454U, 0xD5DCBEU, 0xD5ED6AU, 0xD5F580U, 0xD606AEU, 0xD61E44U, 0xD62F90U, 0xD6377AU, 0xD64C38U, 0xD654D2U, + 0xD66506U, 0xD67DECU, 0xD68B68U, 0xD69382U, 0xD6A256U, 0xD6BABCU, 0xD6C1FEU, 0xD6D914U, 0xD6E8C0U, 0xD6F02AU, + 0xD705C8U, 0xD71D22U, 0xD72CF6U, 0xD7341CU, 0xD74F5EU, 0xD757B4U, 0xD76660U, 0xD77E8AU, 0xD7880EU, 0xD790E4U, + 0xD7A130U, 0xD7B9DAU, 0xD7C298U, 0xD7DA72U, 0xD7EBA6U, 0xD7F34CU, 0xD80E20U, 0xD816CAU, 0xD8271EU, 0xD83FF4U, + 0xD844B6U, 0xD85C5CU, 0xD86D88U, 0xD87562U, 0xD883E6U, 0xD89B0CU, 0xD8AAD8U, 0xD8B232U, 0xD8C970U, 0xD8D19AU, + 0xD8E04EU, 0xD8F8A4U, 0xD90D46U, 0xD915ACU, 0xD92478U, 0xD93C92U, 0xD947D0U, 0xD95F3AU, 0xD96EEEU, 0xD97604U, + 0xD98080U, 0xD9986AU, 0xD9A9BEU, 0xD9B154U, 0xD9CA16U, 0xD9D2FCU, 0xD9E328U, 0xD9FBC2U, 0xDA08ECU, 0xDA1006U, + 0xDA21D2U, 0xDA3938U, 0xDA427AU, 0xDA5A90U, 0xDA6B44U, 0xDA73AEU, 0xDA852AU, 0xDA9DC0U, 0xDAAC14U, 0xDAB4FEU, + 0xDACFBCU, 0xDAD756U, 0xDAE682U, 0xDAFE68U, 0xDB0B8AU, 0xDB1360U, 0xDB22B4U, 0xDB3A5EU, 0xDB411CU, 0xDB59F6U, + 0xDB6822U, 0xDB70C8U, 0xDB864CU, 0xDB9EA6U, 0xDBAF72U, 0xDBB798U, 0xDBCCDAU, 0xDBD430U, 0xDBE5E4U, 0xDBFD0EU, + 0xDC03B8U, 0xDC1B52U, 0xDC2A86U, 0xDC326CU, 0xDC492EU, 0xDC51C4U, 0xDC6010U, 0xDC78FAU, 0xDC8E7EU, 0xDC9694U, + 0xDCA740U, 0xDCBFAAU, 0xDCC4E8U, 0xDCDC02U, 0xDCEDD6U, 0xDCF53CU, 0xDD00DEU, 0xDD1834U, 0xDD29E0U, 0xDD310AU, + 0xDD4A48U, 0xDD52A2U, 0xDD6376U, 0xDD7B9CU, 0xDD8D18U, 0xDD95F2U, 0xDDA426U, 0xDDBCCCU, 0xDDC78EU, 0xDDDF64U, + 0xDDEEB0U, 0xDDF65AU, 0xDE0574U, 0xDE1D9EU, 0xDE2C4AU, 0xDE34A0U, 0xDE4FE2U, 0xDE5708U, 0xDE66DCU, 0xDE7E36U, + 0xDE88B2U, 0xDE9058U, 0xDEA18CU, 0xDEB966U, 0xDEC224U, 0xDEDACEU, 0xDEEB1AU, 0xDEF3F0U, 0xDF0612U, 0xDF1EF8U, + 0xDF2F2CU, 0xDF37C6U, 0xDF4C84U, 0xDF546EU, 0xDF65BAU, 0xDF7D50U, 0xDF8BD4U, 0xDF933EU, 0xDFA2EAU, 0xDFBA00U, + 0xDFC142U, 0xDFD9A8U, 0xDFE87CU, 0xDFF096U, 0xE00526U, 0xE01DCCU, 0xE02C18U, 0xE034F2U, 0xE04FB0U, 0xE0575AU, + 0xE0668EU, 0xE07E64U, 0xE088E0U, 0xE0900AU, 0xE0A1DEU, 0xE0B934U, 0xE0C276U, 0xE0DA9CU, 0xE0EB48U, 0xE0F3A2U, + 0xE10640U, 0xE11EAAU, 0xE12F7EU, 0xE13794U, 0xE14CD6U, 0xE1543CU, 0xE165E8U, 0xE17D02U, 0xE18B86U, 0xE1936CU, + 0xE1A2B8U, 0xE1BA52U, 0xE1C110U, 0xE1D9FAU, 0xE1E82EU, 0xE1F0C4U, 0xE203EAU, 0xE21B00U, 0xE22AD4U, 0xE2323EU, + 0xE2497CU, 0xE25196U, 0xE26042U, 0xE278A8U, 0xE28E2CU, 0xE296C6U, 0xE2A712U, 0xE2BFF8U, 0xE2C4BAU, 0xE2DC50U, + 0xE2ED84U, 0xE2F56EU, 0xE3008CU, 0xE31866U, 0xE329B2U, 0xE33158U, 0xE34A1AU, 0xE352F0U, 0xE36324U, 0xE37BCEU, + 0xE38D4AU, 0xE395A0U, 0xE3A474U, 0xE3BC9EU, 0xE3C7DCU, 0xE3DF36U, 0xE3EEE2U, 0xE3F608U, 0xE408BEU, 0xE41054U, + 0xE42180U, 0xE4396AU, 0xE44228U, 0xE45AC2U, 0xE46B16U, 0xE473FCU, 0xE48578U, 0xE49D92U, 0xE4AC46U, 0xE4B4ACU, + 0xE4CFEEU, 0xE4D704U, 0xE4E6D0U, 0xE4FE3AU, 0xE50BD8U, 0xE51332U, 0xE522E6U, 0xE53A0CU, 0xE5414EU, 0xE559A4U, + 0xE56870U, 0xE5709AU, 0xE5861EU, 0xE59EF4U, 0xE5AF20U, 0xE5B7CAU, 0xE5CC88U, 0xE5D462U, 0xE5E5B6U, 0xE5FD5CU, + 0xE60E72U, 0xE61698U, 0xE6274CU, 0xE63FA6U, 0xE644E4U, 0xE65C0EU, 0xE66DDAU, 0xE67530U, 0xE683B4U, 0xE69B5EU, + 0xE6AA8AU, 0xE6B260U, 0xE6C922U, 0xE6D1C8U, 0xE6E01CU, 0xE6F8F6U, 0xE70D14U, 0xE715FEU, 0xE7242AU, 0xE73CC0U, + 0xE74782U, 0xE75F68U, 0xE76EBCU, 0xE77656U, 0xE780D2U, 0xE79838U, 0xE7A9ECU, 0xE7B106U, 0xE7CA44U, 0xE7D2AEU, + 0xE7E37AU, 0xE7FB90U, 0xE806FCU, 0xE81E16U, 0xE82FC2U, 0xE83728U, 0xE84C6AU, 0xE85480U, 0xE86554U, 0xE87DBEU, + 0xE88B3AU, 0xE893D0U, 0xE8A204U, 0xE8BAEEU, 0xE8C1ACU, 0xE8D946U, 0xE8E892U, 0xE8F078U, 0xE9059AU, 0xE91D70U, + 0xE92CA4U, 0xE9344EU, 0xE94F0CU, 0xE957E6U, 0xE96632U, 0xE97ED8U, 0xE9885CU, 0xE990B6U, 0xE9A162U, 0xE9B988U, + 0xE9C2CAU, 0xE9DA20U, 0xE9EBF4U, 0xE9F31EU, 0xEA0030U, 0xEA18DAU, 0xEA290EU, 0xEA31E4U, 0xEA4AA6U, 0xEA524CU, + 0xEA6398U, 0xEA7B72U, 0xEA8DF6U, 0xEA951CU, 0xEAA4C8U, 0xEABC22U, 0xEAC760U, 0xEADF8AU, 0xEAEE5EU, 0xEAF6B4U, + 0xEB0356U, 0xEB1BBCU, 0xEB2A68U, 0xEB3282U, 0xEB49C0U, 0xEB512AU, 0xEB60FEU, 0xEB7814U, 0xEB8E90U, 0xEB967AU, + 0xEBA7AEU, 0xEBBF44U, 0xEBC406U, 0xEBDCECU, 0xEBED38U, 0xEBF5D2U, 0xEC0B64U, 0xEC138EU, 0xEC225AU, 0xEC3AB0U, + 0xEC41F2U, 0xEC5918U, 0xEC68CCU, 0xEC7026U, 0xEC86A2U, 0xEC9E48U, 0xECAF9CU, 0xECB776U, 0xECCC34U, 0xECD4DEU, + 0xECE50AU, 0xECFDE0U, 0xED0802U, 0xED10E8U, 0xED213CU, 0xED39D6U, 0xED4294U, 0xED5A7EU, 0xED6BAAU, 0xED7340U, + 0xED85C4U, 0xED9D2EU, 0xEDACFAU, 0xEDB410U, 0xEDCF52U, 0xEDD7B8U, 0xEDE66CU, 0xEDFE86U, 0xEE0DA8U, 0xEE1542U, + 0xEE2496U, 0xEE3C7CU, 0xEE473EU, 0xEE5FD4U, 0xEE6E00U, 0xEE76EAU, 0xEE806EU, 0xEE9884U, 0xEEA950U, 0xEEB1BAU, + 0xEECAF8U, 0xEED212U, 0xEEE3C6U, 0xEEFB2CU, 0xEF0ECEU, 0xEF1624U, 0xEF27F0U, 0xEF3F1AU, 0xEF4458U, 0xEF5CB2U, + 0xEF6D66U, 0xEF758CU, 0xEF8308U, 0xEF9BE2U, 0xEFAA36U, 0xEFB2DCU, 0xEFC99EU, 0xEFD174U, 0xEFE0A0U, 0xEFF84AU, + 0xF00292U, 0xF01A78U, 0xF02BACU, 0xF03346U, 0xF04804U, 0xF050EEU, 0xF0613AU, 0xF079D0U, 0xF08F54U, 0xF097BEU, + 0xF0A66AU, 0xF0BE80U, 0xF0C5C2U, 0xF0DD28U, 0xF0ECFCU, 0xF0F416U, 0xF101F4U, 0xF1191EU, 0xF128CAU, 0xF13020U, + 0xF14B62U, 0xF15388U, 0xF1625CU, 0xF17AB6U, 0xF18C32U, 0xF194D8U, 0xF1A50CU, 0xF1BDE6U, 0xF1C6A4U, 0xF1DE4EU, + 0xF1EF9AU, 0xF1F770U, 0xF2045EU, 0xF21CB4U, 0xF22D60U, 0xF2358AU, 0xF24EC8U, 0xF25622U, 0xF267F6U, 0xF27F1CU, + 0xF28998U, 0xF29172U, 0xF2A0A6U, 0xF2B84CU, 0xF2C30EU, 0xF2DBE4U, 0xF2EA30U, 0xF2F2DAU, 0xF30738U, 0xF31FD2U, + 0xF32E06U, 0xF336ECU, 0xF34DAEU, 0xF35544U, 0xF36490U, 0xF37C7AU, 0xF38AFEU, 0xF39214U, 0xF3A3C0U, 0xF3BB2AU, + 0xF3C068U, 0xF3D882U, 0xF3E956U, 0xF3F1BCU, 0xF40F0AU, 0xF417E0U, 0xF42634U, 0xF43EDEU, 0xF4459CU, 0xF45D76U, + 0xF46CA2U, 0xF47448U, 0xF482CCU, 0xF49A26U, 0xF4ABF2U, 0xF4B318U, 0xF4C85AU, 0xF4D0B0U, 0xF4E164U, 0xF4F98EU, + 0xF50C6CU, 0xF51486U, 0xF52552U, 0xF53DB8U, 0xF546FAU, 0xF55E10U, 0xF56FC4U, 0xF5772EU, 0xF581AAU, 0xF59940U, + 0xF5A894U, 0xF5B07EU, 0xF5CB3CU, 0xF5D3D6U, 0xF5E202U, 0xF5FAE8U, 0xF609C6U, 0xF6112CU, 0xF620F8U, 0xF63812U, + 0xF64350U, 0xF65BBAU, 0xF66A6EU, 0xF67284U, 0xF68400U, 0xF69CEAU, 0xF6AD3EU, 0xF6B5D4U, 0xF6CE96U, 0xF6D67CU, + 0xF6E7A8U, 0xF6FF42U, 0xF70AA0U, 0xF7124AU, 0xF7239EU, 0xF73B74U, 0xF74036U, 0xF758DCU, 0xF76908U, 0xF771E2U, + 0xF78766U, 0xF79F8CU, 0xF7AE58U, 0xF7B6B2U, 0xF7CDF0U, 0xF7D51AU, 0xF7E4CEU, 0xF7FC24U, 0xF80148U, 0xF819A2U, + 0xF82876U, 0xF8309CU, 0xF84BDEU, 0xF85334U, 0xF862E0U, 0xF87A0AU, 0xF88C8EU, 0xF89464U, 0xF8A5B0U, 0xF8BD5AU, + 0xF8C618U, 0xF8DEF2U, 0xF8EF26U, 0xF8F7CCU, 0xF9022EU, 0xF91AC4U, 0xF92B10U, 0xF933FAU, 0xF948B8U, 0xF95052U, + 0xF96186U, 0xF9796CU, 0xF98FE8U, 0xF99702U, 0xF9A6D6U, 0xF9BE3CU, 0xF9C57EU, 0xF9DD94U, 0xF9EC40U, 0xF9F4AAU, + 0xFA0784U, 0xFA1F6EU, 0xFA2EBAU, 0xFA3650U, 0xFA4D12U, 0xFA55F8U, 0xFA642CU, 0xFA7CC6U, 0xFA8A42U, 0xFA92A8U, + 0xFAA37CU, 0xFABB96U, 0xFAC0D4U, 0xFAD83EU, 0xFAE9EAU, 0xFAF100U, 0xFB04E2U, 0xFB1C08U, 0xFB2DDCU, 0xFB3536U, + 0xFB4E74U, 0xFB569EU, 0xFB674AU, 0xFB7FA0U, 0xFB8924U, 0xFB91CEU, 0xFBA01AU, 0xFBB8F0U, 0xFBC3B2U, 0xFBDB58U, + 0xFBEA8CU, 0xFBF266U, 0xFC0CD0U, 0xFC143AU, 0xFC25EEU, 0xFC3D04U, 0xFC4646U, 0xFC5EACU, 0xFC6F78U, 0xFC7792U, + 0xFC8116U, 0xFC99FCU, 0xFCA828U, 0xFCB0C2U, 0xFCCB80U, 0xFCD36AU, 0xFCE2BEU, 0xFCFA54U, 0xFD0FB6U, 0xFD175CU, + 0xFD2688U, 0xFD3E62U, 0xFD4520U, 0xFD5DCAU, 0xFD6C1EU, 0xFD74F4U, 0xFD8270U, 0xFD9A9AU, 0xFDAB4EU, 0xFDB3A4U, + 0xFDC8E6U, 0xFDD00CU, 0xFDE1D8U, 0xFDF932U, 0xFE0A1CU, 0xFE12F6U, 0xFE2322U, 0xFE3BC8U, 0xFE408AU, 0xFE5860U, + 0xFE69B4U, 0xFE715EU, 0xFE87DAU, 0xFE9F30U, 0xFEAEE4U, 0xFEB60EU, 0xFECD4CU, 0xFED5A6U, 0xFEE472U, 0xFEFC98U, + 0xFF097AU, 0xFF1190U, 0xFF2044U, 0xFF38AEU, 0xFF43ECU, 0xFF5B06U, 0xFF6AD2U, 0xFF7238U, 0xFF84BCU, 0xFF9C56U, + 0xFFAD82U, 0xFFB568U, 0xFFCE2AU, 0xFFD6C0U, 0xFFE714U, 0xFFFFFEU}; + +static const unsigned int ENCODING_TABLE_24128[] = { + 0x000000U, 0x0018EBU, 0x00293EU, 0x0031D5U, 0x004A97U, 0x00527CU, 0x0063A9U, 0x007B42U, 0x008DC6U, 0x00952DU, + 0x00A4F8U, 0x00BC13U, 0x00C751U, 0x00DFBAU, 0x00EE6FU, 0x00F684U, 0x010367U, 0x011B8CU, 0x012A59U, 0x0132B2U, + 0x0149F0U, 0x01511BU, 0x0160CEU, 0x017825U, 0x018EA1U, 0x01964AU, 0x01A79FU, 0x01BF74U, 0x01C436U, 0x01DCDDU, + 0x01ED08U, 0x01F5E3U, 0x0206CDU, 0x021E26U, 0x022FF3U, 0x023718U, 0x024C5AU, 0x0254B1U, 0x026564U, 0x027D8FU, + 0x028B0BU, 0x0293E0U, 0x02A235U, 0x02BADEU, 0x02C19CU, 0x02D977U, 0x02E8A2U, 0x02F049U, 0x0305AAU, 0x031D41U, + 0x032C94U, 0x03347FU, 0x034F3DU, 0x0357D6U, 0x036603U, 0x037EE8U, 0x03886CU, 0x039087U, 0x03A152U, 0x03B9B9U, + 0x03C2FBU, 0x03DA10U, 0x03EBC5U, 0x03F32EU, 0x040D99U, 0x041572U, 0x0424A7U, 0x043C4CU, 0x04470EU, 0x045FE5U, + 0x046E30U, 0x0476DBU, 0x04805FU, 0x0498B4U, 0x04A961U, 0x04B18AU, 0x04CAC8U, 0x04D223U, 0x04E3F6U, 0x04FB1DU, + 0x050EFEU, 0x051615U, 0x0527C0U, 0x053F2BU, 0x054469U, 0x055C82U, 0x056D57U, 0x0575BCU, 0x058338U, 0x059BD3U, + 0x05AA06U, 0x05B2EDU, 0x05C9AFU, 0x05D144U, 0x05E091U, 0x05F87AU, 0x060B54U, 0x0613BFU, 0x06226AU, 0x063A81U, + 0x0641C3U, 0x065928U, 0x0668FDU, 0x067016U, 0x068692U, 0x069E79U, 0x06AFACU, 0x06B747U, 0x06CC05U, 0x06D4EEU, + 0x06E53BU, 0x06FDD0U, 0x070833U, 0x0710D8U, 0x07210DU, 0x0739E6U, 0x0742A4U, 0x075A4FU, 0x076B9AU, 0x077371U, + 0x0785F5U, 0x079D1EU, 0x07ACCBU, 0x07B420U, 0x07CF62U, 0x07D789U, 0x07E65CU, 0x07FEB7U, 0x0803DAU, 0x081B31U, + 0x082AE4U, 0x08320FU, 0x08494DU, 0x0851A6U, 0x086073U, 0x087898U, 0x088E1CU, 0x0896F7U, 0x08A722U, 0x08BFC9U, + 0x08C48BU, 0x08DC60U, 0x08EDB5U, 0x08F55EU, 0x0900BDU, 0x091856U, 0x092983U, 0x093168U, 0x094A2AU, 0x0952C1U, + 0x096314U, 0x097BFFU, 0x098D7BU, 0x099590U, 0x09A445U, 0x09BCAEU, 0x09C7ECU, 0x09DF07U, 0x09EED2U, 0x09F639U, + 0x0A0517U, 0x0A1DFCU, 0x0A2C29U, 0x0A34C2U, 0x0A4F80U, 0x0A576BU, 0x0A66BEU, 0x0A7E55U, 0x0A88D1U, 0x0A903AU, + 0x0AA1EFU, 0x0AB904U, 0x0AC246U, 0x0ADAADU, 0x0AEB78U, 0x0AF393U, 0x0B0670U, 0x0B1E9BU, 0x0B2F4EU, 0x0B37A5U, + 0x0B4CE7U, 0x0B540CU, 0x0B65D9U, 0x0B7D32U, 0x0B8BB6U, 0x0B935DU, 0x0BA288U, 0x0BBA63U, 0x0BC121U, 0x0BD9CAU, + 0x0BE81FU, 0x0BF0F4U, 0x0C0E43U, 0x0C16A8U, 0x0C277DU, 0x0C3F96U, 0x0C44D4U, 0x0C5C3FU, 0x0C6DEAU, 0x0C7501U, + 0x0C8385U, 0x0C9B6EU, 0x0CAABBU, 0x0CB250U, 0x0CC912U, 0x0CD1F9U, 0x0CE02CU, 0x0CF8C7U, 0x0D0D24U, 0x0D15CFU, + 0x0D241AU, 0x0D3CF1U, 0x0D47B3U, 0x0D5F58U, 0x0D6E8DU, 0x0D7666U, 0x0D80E2U, 0x0D9809U, 0x0DA9DCU, 0x0DB137U, + 0x0DCA75U, 0x0DD29EU, 0x0DE34BU, 0x0DFBA0U, 0x0E088EU, 0x0E1065U, 0x0E21B0U, 0x0E395BU, 0x0E4219U, 0x0E5AF2U, + 0x0E6B27U, 0x0E73CCU, 0x0E8548U, 0x0E9DA3U, 0x0EAC76U, 0x0EB49DU, 0x0ECFDFU, 0x0ED734U, 0x0EE6E1U, 0x0EFE0AU, + 0x0F0BE9U, 0x0F1302U, 0x0F22D7U, 0x0F3A3CU, 0x0F417EU, 0x0F5995U, 0x0F6840U, 0x0F70ABU, 0x0F862FU, 0x0F9EC4U, + 0x0FAF11U, 0x0FB7FAU, 0x0FCCB8U, 0x0FD453U, 0x0FE586U, 0x0FFD6DU, 0x1007B4U, 0x101F5FU, 0x102E8AU, 0x103661U, + 0x104D23U, 0x1055C8U, 0x10641DU, 0x107CF6U, 0x108A72U, 0x109299U, 0x10A34CU, 0x10BBA7U, 0x10C0E5U, 0x10D80EU, + 0x10E9DBU, 0x10F130U, 0x1104D3U, 0x111C38U, 0x112DEDU, 0x113506U, 0x114E44U, 0x1156AFU, 0x11677AU, 0x117F91U, + 0x118915U, 0x1191FEU, 0x11A02BU, 0x11B8C0U, 0x11C382U, 0x11DB69U, 0x11EABCU, 0x11F257U, 0x120179U, 0x121992U, + 0x122847U, 0x1230ACU, 0x124BEEU, 0x125305U, 0x1262D0U, 0x127A3BU, 0x128CBFU, 0x129454U, 0x12A581U, 0x12BD6AU, + 0x12C628U, 0x12DEC3U, 0x12EF16U, 0x12F7FDU, 0x13021EU, 0x131AF5U, 0x132B20U, 0x1333CBU, 0x134889U, 0x135062U, + 0x1361B7U, 0x13795CU, 0x138FD8U, 0x139733U, 0x13A6E6U, 0x13BE0DU, 0x13C54FU, 0x13DDA4U, 0x13EC71U, 0x13F49AU, + 0x140A2DU, 0x1412C6U, 0x142313U, 0x143BF8U, 0x1440BAU, 0x145851U, 0x146984U, 0x14716FU, 0x1487EBU, 0x149F00U, + 0x14AED5U, 0x14B63EU, 0x14CD7CU, 0x14D597U, 0x14E442U, 0x14FCA9U, 0x15094AU, 0x1511A1U, 0x152074U, 0x15389FU, + 0x1543DDU, 0x155B36U, 0x156AE3U, 0x157208U, 0x15848CU, 0x159C67U, 0x15ADB2U, 0x15B559U, 0x15CE1BU, 0x15D6F0U, + 0x15E725U, 0x15FFCEU, 0x160CE0U, 0x16140BU, 0x1625DEU, 0x163D35U, 0x164677U, 0x165E9CU, 0x166F49U, 0x1677A2U, + 0x168126U, 0x1699CDU, 0x16A818U, 0x16B0F3U, 0x16CBB1U, 0x16D35AU, 0x16E28FU, 0x16FA64U, 0x170F87U, 0x17176CU, + 0x1726B9U, 0x173E52U, 0x174510U, 0x175DFBU, 0x176C2EU, 0x1774C5U, 0x178241U, 0x179AAAU, 0x17AB7FU, 0x17B394U, + 0x17C8D6U, 0x17D03DU, 0x17E1E8U, 0x17F903U, 0x18046EU, 0x181C85U, 0x182D50U, 0x1835BBU, 0x184EF9U, 0x185612U, + 0x1867C7U, 0x187F2CU, 0x1889A8U, 0x189143U, 0x18A096U, 0x18B87DU, 0x18C33FU, 0x18DBD4U, 0x18EA01U, 0x18F2EAU, + 0x190709U, 0x191FE2U, 0x192E37U, 0x1936DCU, 0x194D9EU, 0x195575U, 0x1964A0U, 0x197C4BU, 0x198ACFU, 0x199224U, + 0x19A3F1U, 0x19BB1AU, 0x19C058U, 0x19D8B3U, 0x19E966U, 0x19F18DU, 0x1A02A3U, 0x1A1A48U, 0x1A2B9DU, 0x1A3376U, + 0x1A4834U, 0x1A50DFU, 0x1A610AU, 0x1A79E1U, 0x1A8F65U, 0x1A978EU, 0x1AA65BU, 0x1ABEB0U, 0x1AC5F2U, 0x1ADD19U, + 0x1AECCCU, 0x1AF427U, 0x1B01C4U, 0x1B192FU, 0x1B28FAU, 0x1B3011U, 0x1B4B53U, 0x1B53B8U, 0x1B626DU, 0x1B7A86U, + 0x1B8C02U, 0x1B94E9U, 0x1BA53CU, 0x1BBDD7U, 0x1BC695U, 0x1BDE7EU, 0x1BEFABU, 0x1BF740U, 0x1C09F7U, 0x1C111CU, + 0x1C20C9U, 0x1C3822U, 0x1C4360U, 0x1C5B8BU, 0x1C6A5EU, 0x1C72B5U, 0x1C8431U, 0x1C9CDAU, 0x1CAD0FU, 0x1CB5E4U, + 0x1CCEA6U, 0x1CD64DU, 0x1CE798U, 0x1CFF73U, 0x1D0A90U, 0x1D127BU, 0x1D23AEU, 0x1D3B45U, 0x1D4007U, 0x1D58ECU, + 0x1D6939U, 0x1D71D2U, 0x1D8756U, 0x1D9FBDU, 0x1DAE68U, 0x1DB683U, 0x1DCDC1U, 0x1DD52AU, 0x1DE4FFU, 0x1DFC14U, + 0x1E0F3AU, 0x1E17D1U, 0x1E2604U, 0x1E3EEFU, 0x1E45ADU, 0x1E5D46U, 0x1E6C93U, 0x1E7478U, 0x1E82FCU, 0x1E9A17U, + 0x1EABC2U, 0x1EB329U, 0x1EC86BU, 0x1ED080U, 0x1EE155U, 0x1EF9BEU, 0x1F0C5DU, 0x1F14B6U, 0x1F2563U, 0x1F3D88U, + 0x1F46CAU, 0x1F5E21U, 0x1F6FF4U, 0x1F771FU, 0x1F819BU, 0x1F9970U, 0x1FA8A5U, 0x1FB04EU, 0x1FCB0CU, 0x1FD3E7U, + 0x1FE232U, 0x1FFAD9U, 0x200F68U, 0x201783U, 0x202656U, 0x203EBDU, 0x2045FFU, 0x205D14U, 0x206CC1U, 0x20742AU, + 0x2082AEU, 0x209A45U, 0x20AB90U, 0x20B37BU, 0x20C839U, 0x20D0D2U, 0x20E107U, 0x20F9ECU, 0x210C0FU, 0x2114E4U, + 0x212531U, 0x213DDAU, 0x214698U, 0x215E73U, 0x216FA6U, 0x21774DU, 0x2181C9U, 0x219922U, 0x21A8F7U, 0x21B01CU, + 0x21CB5EU, 0x21D3B5U, 0x21E260U, 0x21FA8BU, 0x2209A5U, 0x22114EU, 0x22209BU, 0x223870U, 0x224332U, 0x225BD9U, + 0x226A0CU, 0x2272E7U, 0x228463U, 0x229C88U, 0x22AD5DU, 0x22B5B6U, 0x22CEF4U, 0x22D61FU, 0x22E7CAU, 0x22FF21U, + 0x230AC2U, 0x231229U, 0x2323FCU, 0x233B17U, 0x234055U, 0x2358BEU, 0x23696BU, 0x237180U, 0x238704U, 0x239FEFU, + 0x23AE3AU, 0x23B6D1U, 0x23CD93U, 0x23D578U, 0x23E4ADU, 0x23FC46U, 0x2402F1U, 0x241A1AU, 0x242BCFU, 0x243324U, + 0x244866U, 0x24508DU, 0x246158U, 0x2479B3U, 0x248F37U, 0x2497DCU, 0x24A609U, 0x24BEE2U, 0x24C5A0U, 0x24DD4BU, + 0x24EC9EU, 0x24F475U, 0x250196U, 0x25197DU, 0x2528A8U, 0x253043U, 0x254B01U, 0x2553EAU, 0x25623FU, 0x257AD4U, + 0x258C50U, 0x2594BBU, 0x25A56EU, 0x25BD85U, 0x25C6C7U, 0x25DE2CU, 0x25EFF9U, 0x25F712U, 0x26043CU, 0x261CD7U, + 0x262D02U, 0x2635E9U, 0x264EABU, 0x265640U, 0x266795U, 0x267F7EU, 0x2689FAU, 0x269111U, 0x26A0C4U, 0x26B82FU, + 0x26C36DU, 0x26DB86U, 0x26EA53U, 0x26F2B8U, 0x27075BU, 0x271FB0U, 0x272E65U, 0x27368EU, 0x274DCCU, 0x275527U, + 0x2764F2U, 0x277C19U, 0x278A9DU, 0x279276U, 0x27A3A3U, 0x27BB48U, 0x27C00AU, 0x27D8E1U, 0x27E934U, 0x27F1DFU, + 0x280CB2U, 0x281459U, 0x28258CU, 0x283D67U, 0x284625U, 0x285ECEU, 0x286F1BU, 0x2877F0U, 0x288174U, 0x28999FU, + 0x28A84AU, 0x28B0A1U, 0x28CBE3U, 0x28D308U, 0x28E2DDU, 0x28FA36U, 0x290FD5U, 0x29173EU, 0x2926EBU, 0x293E00U, + 0x294542U, 0x295DA9U, 0x296C7CU, 0x297497U, 0x298213U, 0x299AF8U, 0x29AB2DU, 0x29B3C6U, 0x29C884U, 0x29D06FU, + 0x29E1BAU, 0x29F951U, 0x2A0A7FU, 0x2A1294U, 0x2A2341U, 0x2A3BAAU, 0x2A40E8U, 0x2A5803U, 0x2A69D6U, 0x2A713DU, + 0x2A87B9U, 0x2A9F52U, 0x2AAE87U, 0x2AB66CU, 0x2ACD2EU, 0x2AD5C5U, 0x2AE410U, 0x2AFCFBU, 0x2B0918U, 0x2B11F3U, + 0x2B2026U, 0x2B38CDU, 0x2B438FU, 0x2B5B64U, 0x2B6AB1U, 0x2B725AU, 0x2B84DEU, 0x2B9C35U, 0x2BADE0U, 0x2BB50BU, + 0x2BCE49U, 0x2BD6A2U, 0x2BE777U, 0x2BFF9CU, 0x2C012BU, 0x2C19C0U, 0x2C2815U, 0x2C30FEU, 0x2C4BBCU, 0x2C5357U, + 0x2C6282U, 0x2C7A69U, 0x2C8CEDU, 0x2C9406U, 0x2CA5D3U, 0x2CBD38U, 0x2CC67AU, 0x2CDE91U, 0x2CEF44U, 0x2CF7AFU, + 0x2D024CU, 0x2D1AA7U, 0x2D2B72U, 0x2D3399U, 0x2D48DBU, 0x2D5030U, 0x2D61E5U, 0x2D790EU, 0x2D8F8AU, 0x2D9761U, + 0x2DA6B4U, 0x2DBE5FU, 0x2DC51DU, 0x2DDDF6U, 0x2DEC23U, 0x2DF4C8U, 0x2E07E6U, 0x2E1F0DU, 0x2E2ED8U, 0x2E3633U, + 0x2E4D71U, 0x2E559AU, 0x2E644FU, 0x2E7CA4U, 0x2E8A20U, 0x2E92CBU, 0x2EA31EU, 0x2EBBF5U, 0x2EC0B7U, 0x2ED85CU, + 0x2EE989U, 0x2EF162U, 0x2F0481U, 0x2F1C6AU, 0x2F2DBFU, 0x2F3554U, 0x2F4E16U, 0x2F56FDU, 0x2F6728U, 0x2F7FC3U, + 0x2F8947U, 0x2F91ACU, 0x2FA079U, 0x2FB892U, 0x2FC3D0U, 0x2FDB3BU, 0x2FEAEEU, 0x2FF205U, 0x3008DCU, 0x301037U, + 0x3021E2U, 0x303909U, 0x30424BU, 0x305AA0U, 0x306B75U, 0x30739EU, 0x30851AU, 0x309DF1U, 0x30AC24U, 0x30B4CFU, + 0x30CF8DU, 0x30D766U, 0x30E6B3U, 0x30FE58U, 0x310BBBU, 0x311350U, 0x312285U, 0x313A6EU, 0x31412CU, 0x3159C7U, + 0x316812U, 0x3170F9U, 0x31867DU, 0x319E96U, 0x31AF43U, 0x31B7A8U, 0x31CCEAU, 0x31D401U, 0x31E5D4U, 0x31FD3FU, + 0x320E11U, 0x3216FAU, 0x32272FU, 0x323FC4U, 0x324486U, 0x325C6DU, 0x326DB8U, 0x327553U, 0x3283D7U, 0x329B3CU, + 0x32AAE9U, 0x32B202U, 0x32C940U, 0x32D1ABU, 0x32E07EU, 0x32F895U, 0x330D76U, 0x33159DU, 0x332448U, 0x333CA3U, + 0x3347E1U, 0x335F0AU, 0x336EDFU, 0x337634U, 0x3380B0U, 0x33985BU, 0x33A98EU, 0x33B165U, 0x33CA27U, 0x33D2CCU, + 0x33E319U, 0x33FBF2U, 0x340545U, 0x341DAEU, 0x342C7BU, 0x343490U, 0x344FD2U, 0x345739U, 0x3466ECU, 0x347E07U, + 0x348883U, 0x349068U, 0x34A1BDU, 0x34B956U, 0x34C214U, 0x34DAFFU, 0x34EB2AU, 0x34F3C1U, 0x350622U, 0x351EC9U, + 0x352F1CU, 0x3537F7U, 0x354CB5U, 0x35545EU, 0x35658BU, 0x357D60U, 0x358BE4U, 0x35930FU, 0x35A2DAU, 0x35BA31U, + 0x35C173U, 0x35D998U, 0x35E84DU, 0x35F0A6U, 0x360388U, 0x361B63U, 0x362AB6U, 0x36325DU, 0x36491FU, 0x3651F4U, + 0x366021U, 0x3678CAU, 0x368E4EU, 0x3696A5U, 0x36A770U, 0x36BF9BU, 0x36C4D9U, 0x36DC32U, 0x36EDE7U, 0x36F50CU, + 0x3700EFU, 0x371804U, 0x3729D1U, 0x37313AU, 0x374A78U, 0x375293U, 0x376346U, 0x377BADU, 0x378D29U, 0x3795C2U, + 0x37A417U, 0x37BCFCU, 0x37C7BEU, 0x37DF55U, 0x37EE80U, 0x37F66BU, 0x380B06U, 0x3813EDU, 0x382238U, 0x383AD3U, + 0x384191U, 0x38597AU, 0x3868AFU, 0x387044U, 0x3886C0U, 0x389E2BU, 0x38AFFEU, 0x38B715U, 0x38CC57U, 0x38D4BCU, + 0x38E569U, 0x38FD82U, 0x390861U, 0x39108AU, 0x39215FU, 0x3939B4U, 0x3942F6U, 0x395A1DU, 0x396BC8U, 0x397323U, + 0x3985A7U, 0x399D4CU, 0x39AC99U, 0x39B472U, 0x39CF30U, 0x39D7DBU, 0x39E60EU, 0x39FEE5U, 0x3A0DCBU, 0x3A1520U, + 0x3A24F5U, 0x3A3C1EU, 0x3A475CU, 0x3A5FB7U, 0x3A6E62U, 0x3A7689U, 0x3A800DU, 0x3A98E6U, 0x3AA933U, 0x3AB1D8U, + 0x3ACA9AU, 0x3AD271U, 0x3AE3A4U, 0x3AFB4FU, 0x3B0EACU, 0x3B1647U, 0x3B2792U, 0x3B3F79U, 0x3B443BU, 0x3B5CD0U, + 0x3B6D05U, 0x3B75EEU, 0x3B836AU, 0x3B9B81U, 0x3BAA54U, 0x3BB2BFU, 0x3BC9FDU, 0x3BD116U, 0x3BE0C3U, 0x3BF828U, + 0x3C069FU, 0x3C1E74U, 0x3C2FA1U, 0x3C374AU, 0x3C4C08U, 0x3C54E3U, 0x3C6536U, 0x3C7DDDU, 0x3C8B59U, 0x3C93B2U, + 0x3CA267U, 0x3CBA8CU, 0x3CC1CEU, 0x3CD925U, 0x3CE8F0U, 0x3CF01BU, 0x3D05F8U, 0x3D1D13U, 0x3D2CC6U, 0x3D342DU, + 0x3D4F6FU, 0x3D5784U, 0x3D6651U, 0x3D7EBAU, 0x3D883EU, 0x3D90D5U, 0x3DA100U, 0x3DB9EBU, 0x3DC2A9U, 0x3DDA42U, + 0x3DEB97U, 0x3DF37CU, 0x3E0052U, 0x3E18B9U, 0x3E296CU, 0x3E3187U, 0x3E4AC5U, 0x3E522EU, 0x3E63FBU, 0x3E7B10U, + 0x3E8D94U, 0x3E957FU, 0x3EA4AAU, 0x3EBC41U, 0x3EC703U, 0x3EDFE8U, 0x3EEE3DU, 0x3EF6D6U, 0x3F0335U, 0x3F1BDEU, + 0x3F2A0BU, 0x3F32E0U, 0x3F49A2U, 0x3F5149U, 0x3F609CU, 0x3F7877U, 0x3F8EF3U, 0x3F9618U, 0x3FA7CDU, 0x3FBF26U, + 0x3FC464U, 0x3FDC8FU, 0x3FED5AU, 0x3FF5B1U, 0x40063BU, 0x401ED0U, 0x402F05U, 0x4037EEU, 0x404CACU, 0x405447U, + 0x406592U, 0x407D79U, 0x408BFDU, 0x409316U, 0x40A2C3U, 0x40BA28U, 0x40C16AU, 0x40D981U, 0x40E854U, 0x40F0BFU, + 0x41055CU, 0x411DB7U, 0x412C62U, 0x413489U, 0x414FCBU, 0x415720U, 0x4166F5U, 0x417E1EU, 0x41889AU, 0x419071U, + 0x41A1A4U, 0x41B94FU, 0x41C20DU, 0x41DAE6U, 0x41EB33U, 0x41F3D8U, 0x4200F6U, 0x42181DU, 0x4229C8U, 0x423123U, + 0x424A61U, 0x42528AU, 0x42635FU, 0x427BB4U, 0x428D30U, 0x4295DBU, 0x42A40EU, 0x42BCE5U, 0x42C7A7U, 0x42DF4CU, + 0x42EE99U, 0x42F672U, 0x430391U, 0x431B7AU, 0x432AAFU, 0x433244U, 0x434906U, 0x4351EDU, 0x436038U, 0x4378D3U, + 0x438E57U, 0x4396BCU, 0x43A769U, 0x43BF82U, 0x43C4C0U, 0x43DC2BU, 0x43EDFEU, 0x43F515U, 0x440BA2U, 0x441349U, + 0x44229CU, 0x443A77U, 0x444135U, 0x4459DEU, 0x44680BU, 0x4470E0U, 0x448664U, 0x449E8FU, 0x44AF5AU, 0x44B7B1U, + 0x44CCF3U, 0x44D418U, 0x44E5CDU, 0x44FD26U, 0x4508C5U, 0x45102EU, 0x4521FBU, 0x453910U, 0x454252U, 0x455AB9U, + 0x456B6CU, 0x457387U, 0x458503U, 0x459DE8U, 0x45AC3DU, 0x45B4D6U, 0x45CF94U, 0x45D77FU, 0x45E6AAU, 0x45FE41U, + 0x460D6FU, 0x461584U, 0x462451U, 0x463CBAU, 0x4647F8U, 0x465F13U, 0x466EC6U, 0x46762DU, 0x4680A9U, 0x469842U, + 0x46A997U, 0x46B17CU, 0x46CA3EU, 0x46D2D5U, 0x46E300U, 0x46FBEBU, 0x470E08U, 0x4716E3U, 0x472736U, 0x473FDDU, + 0x47449FU, 0x475C74U, 0x476DA1U, 0x47754AU, 0x4783CEU, 0x479B25U, 0x47AAF0U, 0x47B21BU, 0x47C959U, 0x47D1B2U, + 0x47E067U, 0x47F88CU, 0x4805E1U, 0x481D0AU, 0x482CDFU, 0x483434U, 0x484F76U, 0x48579DU, 0x486648U, 0x487EA3U, + 0x488827U, 0x4890CCU, 0x48A119U, 0x48B9F2U, 0x48C2B0U, 0x48DA5BU, 0x48EB8EU, 0x48F365U, 0x490686U, 0x491E6DU, + 0x492FB8U, 0x493753U, 0x494C11U, 0x4954FAU, 0x49652FU, 0x497DC4U, 0x498B40U, 0x4993ABU, 0x49A27EU, 0x49BA95U, + 0x49C1D7U, 0x49D93CU, 0x49E8E9U, 0x49F002U, 0x4A032CU, 0x4A1BC7U, 0x4A2A12U, 0x4A32F9U, 0x4A49BBU, 0x4A5150U, + 0x4A6085U, 0x4A786EU, 0x4A8EEAU, 0x4A9601U, 0x4AA7D4U, 0x4ABF3FU, 0x4AC47DU, 0x4ADC96U, 0x4AED43U, 0x4AF5A8U, + 0x4B004BU, 0x4B18A0U, 0x4B2975U, 0x4B319EU, 0x4B4ADCU, 0x4B5237U, 0x4B63E2U, 0x4B7B09U, 0x4B8D8DU, 0x4B9566U, + 0x4BA4B3U, 0x4BBC58U, 0x4BC71AU, 0x4BDFF1U, 0x4BEE24U, 0x4BF6CFU, 0x4C0878U, 0x4C1093U, 0x4C2146U, 0x4C39ADU, + 0x4C42EFU, 0x4C5A04U, 0x4C6BD1U, 0x4C733AU, 0x4C85BEU, 0x4C9D55U, 0x4CAC80U, 0x4CB46BU, 0x4CCF29U, 0x4CD7C2U, + 0x4CE617U, 0x4CFEFCU, 0x4D0B1FU, 0x4D13F4U, 0x4D2221U, 0x4D3ACAU, 0x4D4188U, 0x4D5963U, 0x4D68B6U, 0x4D705DU, + 0x4D86D9U, 0x4D9E32U, 0x4DAFE7U, 0x4DB70CU, 0x4DCC4EU, 0x4DD4A5U, 0x4DE570U, 0x4DFD9BU, 0x4E0EB5U, 0x4E165EU, + 0x4E278BU, 0x4E3F60U, 0x4E4422U, 0x4E5CC9U, 0x4E6D1CU, 0x4E75F7U, 0x4E8373U, 0x4E9B98U, 0x4EAA4DU, 0x4EB2A6U, + 0x4EC9E4U, 0x4ED10FU, 0x4EE0DAU, 0x4EF831U, 0x4F0DD2U, 0x4F1539U, 0x4F24ECU, 0x4F3C07U, 0x4F4745U, 0x4F5FAEU, + 0x4F6E7BU, 0x4F7690U, 0x4F8014U, 0x4F98FFU, 0x4FA92AU, 0x4FB1C1U, 0x4FCA83U, 0x4FD268U, 0x4FE3BDU, 0x4FFB56U, + 0x50018FU, 0x501964U, 0x5028B1U, 0x50305AU, 0x504B18U, 0x5053F3U, 0x506226U, 0x507ACDU, 0x508C49U, 0x5094A2U, + 0x50A577U, 0x50BD9CU, 0x50C6DEU, 0x50DE35U, 0x50EFE0U, 0x50F70BU, 0x5102E8U, 0x511A03U, 0x512BD6U, 0x51333DU, + 0x51487FU, 0x515094U, 0x516141U, 0x5179AAU, 0x518F2EU, 0x5197C5U, 0x51A610U, 0x51BEFBU, 0x51C5B9U, 0x51DD52U, + 0x51EC87U, 0x51F46CU, 0x520742U, 0x521FA9U, 0x522E7CU, 0x523697U, 0x524DD5U, 0x52553EU, 0x5264EBU, 0x527C00U, + 0x528A84U, 0x52926FU, 0x52A3BAU, 0x52BB51U, 0x52C013U, 0x52D8F8U, 0x52E92DU, 0x52F1C6U, 0x530425U, 0x531CCEU, + 0x532D1BU, 0x5335F0U, 0x534EB2U, 0x535659U, 0x53678CU, 0x537F67U, 0x5389E3U, 0x539108U, 0x53A0DDU, 0x53B836U, + 0x53C374U, 0x53DB9FU, 0x53EA4AU, 0x53F2A1U, 0x540C16U, 0x5414FDU, 0x542528U, 0x543DC3U, 0x544681U, 0x545E6AU, + 0x546FBFU, 0x547754U, 0x5481D0U, 0x54993BU, 0x54A8EEU, 0x54B005U, 0x54CB47U, 0x54D3ACU, 0x54E279U, 0x54FA92U, + 0x550F71U, 0x55179AU, 0x55264FU, 0x553EA4U, 0x5545E6U, 0x555D0DU, 0x556CD8U, 0x557433U, 0x5582B7U, 0x559A5CU, + 0x55AB89U, 0x55B362U, 0x55C820U, 0x55D0CBU, 0x55E11EU, 0x55F9F5U, 0x560ADBU, 0x561230U, 0x5623E5U, 0x563B0EU, + 0x56404CU, 0x5658A7U, 0x566972U, 0x567199U, 0x56871DU, 0x569FF6U, 0x56AE23U, 0x56B6C8U, 0x56CD8AU, 0x56D561U, + 0x56E4B4U, 0x56FC5FU, 0x5709BCU, 0x571157U, 0x572082U, 0x573869U, 0x57432BU, 0x575BC0U, 0x576A15U, 0x5772FEU, + 0x57847AU, 0x579C91U, 0x57AD44U, 0x57B5AFU, 0x57CEEDU, 0x57D606U, 0x57E7D3U, 0x57FF38U, 0x580255U, 0x581ABEU, + 0x582B6BU, 0x583380U, 0x5848C2U, 0x585029U, 0x5861FCU, 0x587917U, 0x588F93U, 0x589778U, 0x58A6ADU, 0x58BE46U, + 0x58C504U, 0x58DDEFU, 0x58EC3AU, 0x58F4D1U, 0x590132U, 0x5919D9U, 0x59280CU, 0x5930E7U, 0x594BA5U, 0x59534EU, + 0x59629BU, 0x597A70U, 0x598CF4U, 0x59941FU, 0x59A5CAU, 0x59BD21U, 0x59C663U, 0x59DE88U, 0x59EF5DU, 0x59F7B6U, + 0x5A0498U, 0x5A1C73U, 0x5A2DA6U, 0x5A354DU, 0x5A4E0FU, 0x5A56E4U, 0x5A6731U, 0x5A7FDAU, 0x5A895EU, 0x5A91B5U, + 0x5AA060U, 0x5AB88BU, 0x5AC3C9U, 0x5ADB22U, 0x5AEAF7U, 0x5AF21CU, 0x5B07FFU, 0x5B1F14U, 0x5B2EC1U, 0x5B362AU, + 0x5B4D68U, 0x5B5583U, 0x5B6456U, 0x5B7CBDU, 0x5B8A39U, 0x5B92D2U, 0x5BA307U, 0x5BBBECU, 0x5BC0AEU, 0x5BD845U, + 0x5BE990U, 0x5BF17BU, 0x5C0FCCU, 0x5C1727U, 0x5C26F2U, 0x5C3E19U, 0x5C455BU, 0x5C5DB0U, 0x5C6C65U, 0x5C748EU, + 0x5C820AU, 0x5C9AE1U, 0x5CAB34U, 0x5CB3DFU, 0x5CC89DU, 0x5CD076U, 0x5CE1A3U, 0x5CF948U, 0x5D0CABU, 0x5D1440U, + 0x5D2595U, 0x5D3D7EU, 0x5D463CU, 0x5D5ED7U, 0x5D6F02U, 0x5D77E9U, 0x5D816DU, 0x5D9986U, 0x5DA853U, 0x5DB0B8U, + 0x5DCBFAU, 0x5DD311U, 0x5DE2C4U, 0x5DFA2FU, 0x5E0901U, 0x5E11EAU, 0x5E203FU, 0x5E38D4U, 0x5E4396U, 0x5E5B7DU, + 0x5E6AA8U, 0x5E7243U, 0x5E84C7U, 0x5E9C2CU, 0x5EADF9U, 0x5EB512U, 0x5ECE50U, 0x5ED6BBU, 0x5EE76EU, 0x5EFF85U, + 0x5F0A66U, 0x5F128DU, 0x5F2358U, 0x5F3BB3U, 0x5F40F1U, 0x5F581AU, 0x5F69CFU, 0x5F7124U, 0x5F87A0U, 0x5F9F4BU, + 0x5FAE9EU, 0x5FB675U, 0x5FCD37U, 0x5FD5DCU, 0x5FE409U, 0x5FFCE2U, 0x600953U, 0x6011B8U, 0x60206DU, 0x603886U, + 0x6043C4U, 0x605B2FU, 0x606AFAU, 0x607211U, 0x608495U, 0x609C7EU, 0x60ADABU, 0x60B540U, 0x60CE02U, 0x60D6E9U, + 0x60E73CU, 0x60FFD7U, 0x610A34U, 0x6112DFU, 0x61230AU, 0x613BE1U, 0x6140A3U, 0x615848U, 0x61699DU, 0x617176U, + 0x6187F2U, 0x619F19U, 0x61AECCU, 0x61B627U, 0x61CD65U, 0x61D58EU, 0x61E45BU, 0x61FCB0U, 0x620F9EU, 0x621775U, + 0x6226A0U, 0x623E4BU, 0x624509U, 0x625DE2U, 0x626C37U, 0x6274DCU, 0x628258U, 0x629AB3U, 0x62AB66U, 0x62B38DU, + 0x62C8CFU, 0x62D024U, 0x62E1F1U, 0x62F91AU, 0x630CF9U, 0x631412U, 0x6325C7U, 0x633D2CU, 0x63466EU, 0x635E85U, + 0x636F50U, 0x6377BBU, 0x63813FU, 0x6399D4U, 0x63A801U, 0x63B0EAU, 0x63CBA8U, 0x63D343U, 0x63E296U, 0x63FA7DU, + 0x6404CAU, 0x641C21U, 0x642DF4U, 0x64351FU, 0x644E5DU, 0x6456B6U, 0x646763U, 0x647F88U, 0x64890CU, 0x6491E7U, + 0x64A032U, 0x64B8D9U, 0x64C39BU, 0x64DB70U, 0x64EAA5U, 0x64F24EU, 0x6507ADU, 0x651F46U, 0x652E93U, 0x653678U, + 0x654D3AU, 0x6555D1U, 0x656404U, 0x657CEFU, 0x658A6BU, 0x659280U, 0x65A355U, 0x65BBBEU, 0x65C0FCU, 0x65D817U, + 0x65E9C2U, 0x65F129U, 0x660207U, 0x661AECU, 0x662B39U, 0x6633D2U, 0x664890U, 0x66507BU, 0x6661AEU, 0x667945U, + 0x668FC1U, 0x66972AU, 0x66A6FFU, 0x66BE14U, 0x66C556U, 0x66DDBDU, 0x66EC68U, 0x66F483U, 0x670160U, 0x67198BU, + 0x67285EU, 0x6730B5U, 0x674BF7U, 0x67531CU, 0x6762C9U, 0x677A22U, 0x678CA6U, 0x67944DU, 0x67A598U, 0x67BD73U, + 0x67C631U, 0x67DEDAU, 0x67EF0FU, 0x67F7E4U, 0x680A89U, 0x681262U, 0x6823B7U, 0x683B5CU, 0x68401EU, 0x6858F5U, + 0x686920U, 0x6871CBU, 0x68874FU, 0x689FA4U, 0x68AE71U, 0x68B69AU, 0x68CDD8U, 0x68D533U, 0x68E4E6U, 0x68FC0DU, + 0x6909EEU, 0x691105U, 0x6920D0U, 0x69383BU, 0x694379U, 0x695B92U, 0x696A47U, 0x6972ACU, 0x698428U, 0x699CC3U, + 0x69AD16U, 0x69B5FDU, 0x69CEBFU, 0x69D654U, 0x69E781U, 0x69FF6AU, 0x6A0C44U, 0x6A14AFU, 0x6A257AU, 0x6A3D91U, + 0x6A46D3U, 0x6A5E38U, 0x6A6FEDU, 0x6A7706U, 0x6A8182U, 0x6A9969U, 0x6AA8BCU, 0x6AB057U, 0x6ACB15U, 0x6AD3FEU, + 0x6AE22BU, 0x6AFAC0U, 0x6B0F23U, 0x6B17C8U, 0x6B261DU, 0x6B3EF6U, 0x6B45B4U, 0x6B5D5FU, 0x6B6C8AU, 0x6B7461U, + 0x6B82E5U, 0x6B9A0EU, 0x6BABDBU, 0x6BB330U, 0x6BC872U, 0x6BD099U, 0x6BE14CU, 0x6BF9A7U, 0x6C0710U, 0x6C1FFBU, + 0x6C2E2EU, 0x6C36C5U, 0x6C4D87U, 0x6C556CU, 0x6C64B9U, 0x6C7C52U, 0x6C8AD6U, 0x6C923DU, 0x6CA3E8U, 0x6CBB03U, + 0x6CC041U, 0x6CD8AAU, 0x6CE97FU, 0x6CF194U, 0x6D0477U, 0x6D1C9CU, 0x6D2D49U, 0x6D35A2U, 0x6D4EE0U, 0x6D560BU, + 0x6D67DEU, 0x6D7F35U, 0x6D89B1U, 0x6D915AU, 0x6DA08FU, 0x6DB864U, 0x6DC326U, 0x6DDBCDU, 0x6DEA18U, 0x6DF2F3U, + 0x6E01DDU, 0x6E1936U, 0x6E28E3U, 0x6E3008U, 0x6E4B4AU, 0x6E53A1U, 0x6E6274U, 0x6E7A9FU, 0x6E8C1BU, 0x6E94F0U, + 0x6EA525U, 0x6EBDCEU, 0x6EC68CU, 0x6EDE67U, 0x6EEFB2U, 0x6EF759U, 0x6F02BAU, 0x6F1A51U, 0x6F2B84U, 0x6F336FU, + 0x6F482DU, 0x6F50C6U, 0x6F6113U, 0x6F79F8U, 0x6F8F7CU, 0x6F9797U, 0x6FA642U, 0x6FBEA9U, 0x6FC5EBU, 0x6FDD00U, + 0x6FECD5U, 0x6FF43EU, 0x700EE7U, 0x70160CU, 0x7027D9U, 0x703F32U, 0x704470U, 0x705C9BU, 0x706D4EU, 0x7075A5U, + 0x708321U, 0x709BCAU, 0x70AA1FU, 0x70B2F4U, 0x70C9B6U, 0x70D15DU, 0x70E088U, 0x70F863U, 0x710D80U, 0x71156BU, + 0x7124BEU, 0x713C55U, 0x714717U, 0x715FFCU, 0x716E29U, 0x7176C2U, 0x718046U, 0x7198ADU, 0x71A978U, 0x71B193U, + 0x71CAD1U, 0x71D23AU, 0x71E3EFU, 0x71FB04U, 0x72082AU, 0x7210C1U, 0x722114U, 0x7239FFU, 0x7242BDU, 0x725A56U, + 0x726B83U, 0x727368U, 0x7285ECU, 0x729D07U, 0x72ACD2U, 0x72B439U, 0x72CF7BU, 0x72D790U, 0x72E645U, 0x72FEAEU, + 0x730B4DU, 0x7313A6U, 0x732273U, 0x733A98U, 0x7341DAU, 0x735931U, 0x7368E4U, 0x73700FU, 0x73868BU, 0x739E60U, + 0x73AFB5U, 0x73B75EU, 0x73CC1CU, 0x73D4F7U, 0x73E522U, 0x73FDC9U, 0x74037EU, 0x741B95U, 0x742A40U, 0x7432ABU, + 0x7449E9U, 0x745102U, 0x7460D7U, 0x74783CU, 0x748EB8U, 0x749653U, 0x74A786U, 0x74BF6DU, 0x74C42FU, 0x74DCC4U, + 0x74ED11U, 0x74F5FAU, 0x750019U, 0x7518F2U, 0x752927U, 0x7531CCU, 0x754A8EU, 0x755265U, 0x7563B0U, 0x757B5BU, + 0x758DDFU, 0x759534U, 0x75A4E1U, 0x75BC0AU, 0x75C748U, 0x75DFA3U, 0x75EE76U, 0x75F69DU, 0x7605B3U, 0x761D58U, + 0x762C8DU, 0x763466U, 0x764F24U, 0x7657CFU, 0x76661AU, 0x767EF1U, 0x768875U, 0x76909EU, 0x76A14BU, 0x76B9A0U, + 0x76C2E2U, 0x76DA09U, 0x76EBDCU, 0x76F337U, 0x7706D4U, 0x771E3FU, 0x772FEAU, 0x773701U, 0x774C43U, 0x7754A8U, + 0x77657DU, 0x777D96U, 0x778B12U, 0x7793F9U, 0x77A22CU, 0x77BAC7U, 0x77C185U, 0x77D96EU, 0x77E8BBU, 0x77F050U, + 0x780D3DU, 0x7815D6U, 0x782403U, 0x783CE8U, 0x7847AAU, 0x785F41U, 0x786E94U, 0x78767FU, 0x7880FBU, 0x789810U, + 0x78A9C5U, 0x78B12EU, 0x78CA6CU, 0x78D287U, 0x78E352U, 0x78FBB9U, 0x790E5AU, 0x7916B1U, 0x792764U, 0x793F8FU, + 0x7944CDU, 0x795C26U, 0x796DF3U, 0x797518U, 0x79839CU, 0x799B77U, 0x79AAA2U, 0x79B249U, 0x79C90BU, 0x79D1E0U, + 0x79E035U, 0x79F8DEU, 0x7A0BF0U, 0x7A131BU, 0x7A22CEU, 0x7A3A25U, 0x7A4167U, 0x7A598CU, 0x7A6859U, 0x7A70B2U, + 0x7A8636U, 0x7A9EDDU, 0x7AAF08U, 0x7AB7E3U, 0x7ACCA1U, 0x7AD44AU, 0x7AE59FU, 0x7AFD74U, 0x7B0897U, 0x7B107CU, + 0x7B21A9U, 0x7B3942U, 0x7B4200U, 0x7B5AEBU, 0x7B6B3EU, 0x7B73D5U, 0x7B8551U, 0x7B9DBAU, 0x7BAC6FU, 0x7BB484U, + 0x7BCFC6U, 0x7BD72DU, 0x7BE6F8U, 0x7BFE13U, 0x7C00A4U, 0x7C184FU, 0x7C299AU, 0x7C3171U, 0x7C4A33U, 0x7C52D8U, + 0x7C630DU, 0x7C7BE6U, 0x7C8D62U, 0x7C9589U, 0x7CA45CU, 0x7CBCB7U, 0x7CC7F5U, 0x7CDF1EU, 0x7CEECBU, 0x7CF620U, + 0x7D03C3U, 0x7D1B28U, 0x7D2AFDU, 0x7D3216U, 0x7D4954U, 0x7D51BFU, 0x7D606AU, 0x7D7881U, 0x7D8E05U, 0x7D96EEU, + 0x7DA73BU, 0x7DBFD0U, 0x7DC492U, 0x7DDC79U, 0x7DEDACU, 0x7DF547U, 0x7E0669U, 0x7E1E82U, 0x7E2F57U, 0x7E37BCU, + 0x7E4CFEU, 0x7E5415U, 0x7E65C0U, 0x7E7D2BU, 0x7E8BAFU, 0x7E9344U, 0x7EA291U, 0x7EBA7AU, 0x7EC138U, 0x7ED9D3U, + 0x7EE806U, 0x7EF0EDU, 0x7F050EU, 0x7F1DE5U, 0x7F2C30U, 0x7F34DBU, 0x7F4F99U, 0x7F5772U, 0x7F66A7U, 0x7F7E4CU, + 0x7F88C8U, 0x7F9023U, 0x7FA1F6U, 0x7FB91DU, 0x7FC25FU, 0x7FDAB4U, 0x7FEB61U, 0x7FF38AU, 0x800C75U, 0x80149EU, + 0x80254BU, 0x803DA0U, 0x8046E2U, 0x805E09U, 0x806FDCU, 0x807737U, 0x8081B3U, 0x809958U, 0x80A88DU, 0x80B066U, + 0x80CB24U, 0x80D3CFU, 0x80E21AU, 0x80FAF1U, 0x810F12U, 0x8117F9U, 0x81262CU, 0x813EC7U, 0x814585U, 0x815D6EU, + 0x816CBBU, 0x817450U, 0x8182D4U, 0x819A3FU, 0x81ABEAU, 0x81B301U, 0x81C843U, 0x81D0A8U, 0x81E17DU, 0x81F996U, + 0x820AB8U, 0x821253U, 0x822386U, 0x823B6DU, 0x82402FU, 0x8258C4U, 0x826911U, 0x8271FAU, 0x82877EU, 0x829F95U, + 0x82AE40U, 0x82B6ABU, 0x82CDE9U, 0x82D502U, 0x82E4D7U, 0x82FC3CU, 0x8309DFU, 0x831134U, 0x8320E1U, 0x83380AU, + 0x834348U, 0x835BA3U, 0x836A76U, 0x83729DU, 0x838419U, 0x839CF2U, 0x83AD27U, 0x83B5CCU, 0x83CE8EU, 0x83D665U, + 0x83E7B0U, 0x83FF5BU, 0x8401ECU, 0x841907U, 0x8428D2U, 0x843039U, 0x844B7BU, 0x845390U, 0x846245U, 0x847AAEU, + 0x848C2AU, 0x8494C1U, 0x84A514U, 0x84BDFFU, 0x84C6BDU, 0x84DE56U, 0x84EF83U, 0x84F768U, 0x85028BU, 0x851A60U, + 0x852BB5U, 0x85335EU, 0x85481CU, 0x8550F7U, 0x856122U, 0x8579C9U, 0x858F4DU, 0x8597A6U, 0x85A673U, 0x85BE98U, + 0x85C5DAU, 0x85DD31U, 0x85ECE4U, 0x85F40FU, 0x860721U, 0x861FCAU, 0x862E1FU, 0x8636F4U, 0x864DB6U, 0x86555DU, + 0x866488U, 0x867C63U, 0x868AE7U, 0x86920CU, 0x86A3D9U, 0x86BB32U, 0x86C070U, 0x86D89BU, 0x86E94EU, 0x86F1A5U, + 0x870446U, 0x871CADU, 0x872D78U, 0x873593U, 0x874ED1U, 0x87563AU, 0x8767EFU, 0x877F04U, 0x878980U, 0x87916BU, + 0x87A0BEU, 0x87B855U, 0x87C317U, 0x87DBFCU, 0x87EA29U, 0x87F2C2U, 0x880FAFU, 0x881744U, 0x882691U, 0x883E7AU, + 0x884538U, 0x885DD3U, 0x886C06U, 0x8874EDU, 0x888269U, 0x889A82U, 0x88AB57U, 0x88B3BCU, 0x88C8FEU, 0x88D015U, + 0x88E1C0U, 0x88F92BU, 0x890CC8U, 0x891423U, 0x8925F6U, 0x893D1DU, 0x89465FU, 0x895EB4U, 0x896F61U, 0x89778AU, + 0x89810EU, 0x8999E5U, 0x89A830U, 0x89B0DBU, 0x89CB99U, 0x89D372U, 0x89E2A7U, 0x89FA4CU, 0x8A0962U, 0x8A1189U, + 0x8A205CU, 0x8A38B7U, 0x8A43F5U, 0x8A5B1EU, 0x8A6ACBU, 0x8A7220U, 0x8A84A4U, 0x8A9C4FU, 0x8AAD9AU, 0x8AB571U, + 0x8ACE33U, 0x8AD6D8U, 0x8AE70DU, 0x8AFFE6U, 0x8B0A05U, 0x8B12EEU, 0x8B233BU, 0x8B3BD0U, 0x8B4092U, 0x8B5879U, + 0x8B69ACU, 0x8B7147U, 0x8B87C3U, 0x8B9F28U, 0x8BAEFDU, 0x8BB616U, 0x8BCD54U, 0x8BD5BFU, 0x8BE46AU, 0x8BFC81U, + 0x8C0236U, 0x8C1ADDU, 0x8C2B08U, 0x8C33E3U, 0x8C48A1U, 0x8C504AU, 0x8C619FU, 0x8C7974U, 0x8C8FF0U, 0x8C971BU, + 0x8CA6CEU, 0x8CBE25U, 0x8CC567U, 0x8CDD8CU, 0x8CEC59U, 0x8CF4B2U, 0x8D0151U, 0x8D19BAU, 0x8D286FU, 0x8D3084U, + 0x8D4BC6U, 0x8D532DU, 0x8D62F8U, 0x8D7A13U, 0x8D8C97U, 0x8D947CU, 0x8DA5A9U, 0x8DBD42U, 0x8DC600U, 0x8DDEEBU, + 0x8DEF3EU, 0x8DF7D5U, 0x8E04FBU, 0x8E1C10U, 0x8E2DC5U, 0x8E352EU, 0x8E4E6CU, 0x8E5687U, 0x8E6752U, 0x8E7FB9U, + 0x8E893DU, 0x8E91D6U, 0x8EA003U, 0x8EB8E8U, 0x8EC3AAU, 0x8EDB41U, 0x8EEA94U, 0x8EF27FU, 0x8F079CU, 0x8F1F77U, + 0x8F2EA2U, 0x8F3649U, 0x8F4D0BU, 0x8F55E0U, 0x8F6435U, 0x8F7CDEU, 0x8F8A5AU, 0x8F92B1U, 0x8FA364U, 0x8FBB8FU, + 0x8FC0CDU, 0x8FD826U, 0x8FE9F3U, 0x8FF118U, 0x900BC1U, 0x90132AU, 0x9022FFU, 0x903A14U, 0x904156U, 0x9059BDU, + 0x906868U, 0x907083U, 0x908607U, 0x909EECU, 0x90AF39U, 0x90B7D2U, 0x90CC90U, 0x90D47BU, 0x90E5AEU, 0x90FD45U, + 0x9108A6U, 0x91104DU, 0x912198U, 0x913973U, 0x914231U, 0x915ADAU, 0x916B0FU, 0x9173E4U, 0x918560U, 0x919D8BU, + 0x91AC5EU, 0x91B4B5U, 0x91CFF7U, 0x91D71CU, 0x91E6C9U, 0x91FE22U, 0x920D0CU, 0x9215E7U, 0x922432U, 0x923CD9U, + 0x92479BU, 0x925F70U, 0x926EA5U, 0x92764EU, 0x9280CAU, 0x929821U, 0x92A9F4U, 0x92B11FU, 0x92CA5DU, 0x92D2B6U, + 0x92E363U, 0x92FB88U, 0x930E6BU, 0x931680U, 0x932755U, 0x933FBEU, 0x9344FCU, 0x935C17U, 0x936DC2U, 0x937529U, + 0x9383ADU, 0x939B46U, 0x93AA93U, 0x93B278U, 0x93C93AU, 0x93D1D1U, 0x93E004U, 0x93F8EFU, 0x940658U, 0x941EB3U, + 0x942F66U, 0x94378DU, 0x944CCFU, 0x945424U, 0x9465F1U, 0x947D1AU, 0x948B9EU, 0x949375U, 0x94A2A0U, 0x94BA4BU, + 0x94C109U, 0x94D9E2U, 0x94E837U, 0x94F0DCU, 0x95053FU, 0x951DD4U, 0x952C01U, 0x9534EAU, 0x954FA8U, 0x955743U, + 0x956696U, 0x957E7DU, 0x9588F9U, 0x959012U, 0x95A1C7U, 0x95B92CU, 0x95C26EU, 0x95DA85U, 0x95EB50U, 0x95F3BBU, + 0x960095U, 0x96187EU, 0x9629ABU, 0x963140U, 0x964A02U, 0x9652E9U, 0x96633CU, 0x967BD7U, 0x968D53U, 0x9695B8U, + 0x96A46DU, 0x96BC86U, 0x96C7C4U, 0x96DF2FU, 0x96EEFAU, 0x96F611U, 0x9703F2U, 0x971B19U, 0x972ACCU, 0x973227U, + 0x974965U, 0x97518EU, 0x97605BU, 0x9778B0U, 0x978E34U, 0x9796DFU, 0x97A70AU, 0x97BFE1U, 0x97C4A3U, 0x97DC48U, + 0x97ED9DU, 0x97F576U, 0x98081BU, 0x9810F0U, 0x982125U, 0x9839CEU, 0x98428CU, 0x985A67U, 0x986BB2U, 0x987359U, + 0x9885DDU, 0x989D36U, 0x98ACE3U, 0x98B408U, 0x98CF4AU, 0x98D7A1U, 0x98E674U, 0x98FE9FU, 0x990B7CU, 0x991397U, + 0x992242U, 0x993AA9U, 0x9941EBU, 0x995900U, 0x9968D5U, 0x99703EU, 0x9986BAU, 0x999E51U, 0x99AF84U, 0x99B76FU, + 0x99CC2DU, 0x99D4C6U, 0x99E513U, 0x99FDF8U, 0x9A0ED6U, 0x9A163DU, 0x9A27E8U, 0x9A3F03U, 0x9A4441U, 0x9A5CAAU, + 0x9A6D7FU, 0x9A7594U, 0x9A8310U, 0x9A9BFBU, 0x9AAA2EU, 0x9AB2C5U, 0x9AC987U, 0x9AD16CU, 0x9AE0B9U, 0x9AF852U, + 0x9B0DB1U, 0x9B155AU, 0x9B248FU, 0x9B3C64U, 0x9B4726U, 0x9B5FCDU, 0x9B6E18U, 0x9B76F3U, 0x9B8077U, 0x9B989CU, + 0x9BA949U, 0x9BB1A2U, 0x9BCAE0U, 0x9BD20BU, 0x9BE3DEU, 0x9BFB35U, 0x9C0582U, 0x9C1D69U, 0x9C2CBCU, 0x9C3457U, + 0x9C4F15U, 0x9C57FEU, 0x9C662BU, 0x9C7EC0U, 0x9C8844U, 0x9C90AFU, 0x9CA17AU, 0x9CB991U, 0x9CC2D3U, 0x9CDA38U, + 0x9CEBEDU, 0x9CF306U, 0x9D06E5U, 0x9D1E0EU, 0x9D2FDBU, 0x9D3730U, 0x9D4C72U, 0x9D5499U, 0x9D654CU, 0x9D7DA7U, + 0x9D8B23U, 0x9D93C8U, 0x9DA21DU, 0x9DBAF6U, 0x9DC1B4U, 0x9DD95FU, 0x9DE88AU, 0x9DF061U, 0x9E034FU, 0x9E1BA4U, + 0x9E2A71U, 0x9E329AU, 0x9E49D8U, 0x9E5133U, 0x9E60E6U, 0x9E780DU, 0x9E8E89U, 0x9E9662U, 0x9EA7B7U, 0x9EBF5CU, + 0x9EC41EU, 0x9EDCF5U, 0x9EED20U, 0x9EF5CBU, 0x9F0028U, 0x9F18C3U, 0x9F2916U, 0x9F31FDU, 0x9F4ABFU, 0x9F5254U, + 0x9F6381U, 0x9F7B6AU, 0x9F8DEEU, 0x9F9505U, 0x9FA4D0U, 0x9FBC3BU, 0x9FC779U, 0x9FDF92U, 0x9FEE47U, 0x9FF6ACU, + 0xA0031DU, 0xA01BF6U, 0xA02A23U, 0xA032C8U, 0xA0498AU, 0xA05161U, 0xA060B4U, 0xA0785FU, 0xA08EDBU, 0xA09630U, + 0xA0A7E5U, 0xA0BF0EU, 0xA0C44CU, 0xA0DCA7U, 0xA0ED72U, 0xA0F599U, 0xA1007AU, 0xA11891U, 0xA12944U, 0xA131AFU, + 0xA14AEDU, 0xA15206U, 0xA163D3U, 0xA17B38U, 0xA18DBCU, 0xA19557U, 0xA1A482U, 0xA1BC69U, 0xA1C72BU, 0xA1DFC0U, + 0xA1EE15U, 0xA1F6FEU, 0xA205D0U, 0xA21D3BU, 0xA22CEEU, 0xA23405U, 0xA24F47U, 0xA257ACU, 0xA26679U, 0xA27E92U, + 0xA28816U, 0xA290FDU, 0xA2A128U, 0xA2B9C3U, 0xA2C281U, 0xA2DA6AU, 0xA2EBBFU, 0xA2F354U, 0xA306B7U, 0xA31E5CU, + 0xA32F89U, 0xA33762U, 0xA34C20U, 0xA354CBU, 0xA3651EU, 0xA37DF5U, 0xA38B71U, 0xA3939AU, 0xA3A24FU, 0xA3BAA4U, + 0xA3C1E6U, 0xA3D90DU, 0xA3E8D8U, 0xA3F033U, 0xA40E84U, 0xA4166FU, 0xA427BAU, 0xA43F51U, 0xA44413U, 0xA45CF8U, + 0xA46D2DU, 0xA475C6U, 0xA48342U, 0xA49BA9U, 0xA4AA7CU, 0xA4B297U, 0xA4C9D5U, 0xA4D13EU, 0xA4E0EBU, 0xA4F800U, + 0xA50DE3U, 0xA51508U, 0xA524DDU, 0xA53C36U, 0xA54774U, 0xA55F9FU, 0xA56E4AU, 0xA576A1U, 0xA58025U, 0xA598CEU, + 0xA5A91BU, 0xA5B1F0U, 0xA5CAB2U, 0xA5D259U, 0xA5E38CU, 0xA5FB67U, 0xA60849U, 0xA610A2U, 0xA62177U, 0xA6399CU, + 0xA642DEU, 0xA65A35U, 0xA66BE0U, 0xA6730BU, 0xA6858FU, 0xA69D64U, 0xA6ACB1U, 0xA6B45AU, 0xA6CF18U, 0xA6D7F3U, + 0xA6E626U, 0xA6FECDU, 0xA70B2EU, 0xA713C5U, 0xA72210U, 0xA73AFBU, 0xA741B9U, 0xA75952U, 0xA76887U, 0xA7706CU, + 0xA786E8U, 0xA79E03U, 0xA7AFD6U, 0xA7B73DU, 0xA7CC7FU, 0xA7D494U, 0xA7E541U, 0xA7FDAAU, 0xA800C7U, 0xA8182CU, + 0xA829F9U, 0xA83112U, 0xA84A50U, 0xA852BBU, 0xA8636EU, 0xA87B85U, 0xA88D01U, 0xA895EAU, 0xA8A43FU, 0xA8BCD4U, + 0xA8C796U, 0xA8DF7DU, 0xA8EEA8U, 0xA8F643U, 0xA903A0U, 0xA91B4BU, 0xA92A9EU, 0xA93275U, 0xA94937U, 0xA951DCU, + 0xA96009U, 0xA978E2U, 0xA98E66U, 0xA9968DU, 0xA9A758U, 0xA9BFB3U, 0xA9C4F1U, 0xA9DC1AU, 0xA9EDCFU, 0xA9F524U, + 0xAA060AU, 0xAA1EE1U, 0xAA2F34U, 0xAA37DFU, 0xAA4C9DU, 0xAA5476U, 0xAA65A3U, 0xAA7D48U, 0xAA8BCCU, 0xAA9327U, + 0xAAA2F2U, 0xAABA19U, 0xAAC15BU, 0xAAD9B0U, 0xAAE865U, 0xAAF08EU, 0xAB056DU, 0xAB1D86U, 0xAB2C53U, 0xAB34B8U, + 0xAB4FFAU, 0xAB5711U, 0xAB66C4U, 0xAB7E2FU, 0xAB88ABU, 0xAB9040U, 0xABA195U, 0xABB97EU, 0xABC23CU, 0xABDAD7U, + 0xABEB02U, 0xABF3E9U, 0xAC0D5EU, 0xAC15B5U, 0xAC2460U, 0xAC3C8BU, 0xAC47C9U, 0xAC5F22U, 0xAC6EF7U, 0xAC761CU, + 0xAC8098U, 0xAC9873U, 0xACA9A6U, 0xACB14DU, 0xACCA0FU, 0xACD2E4U, 0xACE331U, 0xACFBDAU, 0xAD0E39U, 0xAD16D2U, + 0xAD2707U, 0xAD3FECU, 0xAD44AEU, 0xAD5C45U, 0xAD6D90U, 0xAD757BU, 0xAD83FFU, 0xAD9B14U, 0xADAAC1U, 0xADB22AU, + 0xADC968U, 0xADD183U, 0xADE056U, 0xADF8BDU, 0xAE0B93U, 0xAE1378U, 0xAE22ADU, 0xAE3A46U, 0xAE4104U, 0xAE59EFU, + 0xAE683AU, 0xAE70D1U, 0xAE8655U, 0xAE9EBEU, 0xAEAF6BU, 0xAEB780U, 0xAECCC2U, 0xAED429U, 0xAEE5FCU, 0xAEFD17U, + 0xAF08F4U, 0xAF101FU, 0xAF21CAU, 0xAF3921U, 0xAF4263U, 0xAF5A88U, 0xAF6B5DU, 0xAF73B6U, 0xAF8532U, 0xAF9DD9U, + 0xAFAC0CU, 0xAFB4E7U, 0xAFCFA5U, 0xAFD74EU, 0xAFE69BU, 0xAFFE70U, 0xB004A9U, 0xB01C42U, 0xB02D97U, 0xB0357CU, + 0xB04E3EU, 0xB056D5U, 0xB06700U, 0xB07FEBU, 0xB0896FU, 0xB09184U, 0xB0A051U, 0xB0B8BAU, 0xB0C3F8U, 0xB0DB13U, + 0xB0EAC6U, 0xB0F22DU, 0xB107CEU, 0xB11F25U, 0xB12EF0U, 0xB1361BU, 0xB14D59U, 0xB155B2U, 0xB16467U, 0xB17C8CU, + 0xB18A08U, 0xB192E3U, 0xB1A336U, 0xB1BBDDU, 0xB1C09FU, 0xB1D874U, 0xB1E9A1U, 0xB1F14AU, 0xB20264U, 0xB21A8FU, + 0xB22B5AU, 0xB233B1U, 0xB248F3U, 0xB25018U, 0xB261CDU, 0xB27926U, 0xB28FA2U, 0xB29749U, 0xB2A69CU, 0xB2BE77U, + 0xB2C535U, 0xB2DDDEU, 0xB2EC0BU, 0xB2F4E0U, 0xB30103U, 0xB319E8U, 0xB3283DU, 0xB330D6U, 0xB34B94U, 0xB3537FU, + 0xB362AAU, 0xB37A41U, 0xB38CC5U, 0xB3942EU, 0xB3A5FBU, 0xB3BD10U, 0xB3C652U, 0xB3DEB9U, 0xB3EF6CU, 0xB3F787U, + 0xB40930U, 0xB411DBU, 0xB4200EU, 0xB438E5U, 0xB443A7U, 0xB45B4CU, 0xB46A99U, 0xB47272U, 0xB484F6U, 0xB49C1DU, + 0xB4ADC8U, 0xB4B523U, 0xB4CE61U, 0xB4D68AU, 0xB4E75FU, 0xB4FFB4U, 0xB50A57U, 0xB512BCU, 0xB52369U, 0xB53B82U, + 0xB540C0U, 0xB5582BU, 0xB569FEU, 0xB57115U, 0xB58791U, 0xB59F7AU, 0xB5AEAFU, 0xB5B644U, 0xB5CD06U, 0xB5D5EDU, + 0xB5E438U, 0xB5FCD3U, 0xB60FFDU, 0xB61716U, 0xB626C3U, 0xB63E28U, 0xB6456AU, 0xB65D81U, 0xB66C54U, 0xB674BFU, + 0xB6823BU, 0xB69AD0U, 0xB6AB05U, 0xB6B3EEU, 0xB6C8ACU, 0xB6D047U, 0xB6E192U, 0xB6F979U, 0xB70C9AU, 0xB71471U, + 0xB725A4U, 0xB73D4FU, 0xB7460DU, 0xB75EE6U, 0xB76F33U, 0xB777D8U, 0xB7815CU, 0xB799B7U, 0xB7A862U, 0xB7B089U, + 0xB7CBCBU, 0xB7D320U, 0xB7E2F5U, 0xB7FA1EU, 0xB80773U, 0xB81F98U, 0xB82E4DU, 0xB836A6U, 0xB84DE4U, 0xB8550FU, + 0xB864DAU, 0xB87C31U, 0xB88AB5U, 0xB8925EU, 0xB8A38BU, 0xB8BB60U, 0xB8C022U, 0xB8D8C9U, 0xB8E91CU, 0xB8F1F7U, + 0xB90414U, 0xB91CFFU, 0xB92D2AU, 0xB935C1U, 0xB94E83U, 0xB95668U, 0xB967BDU, 0xB97F56U, 0xB989D2U, 0xB99139U, + 0xB9A0ECU, 0xB9B807U, 0xB9C345U, 0xB9DBAEU, 0xB9EA7BU, 0xB9F290U, 0xBA01BEU, 0xBA1955U, 0xBA2880U, 0xBA306BU, + 0xBA4B29U, 0xBA53C2U, 0xBA6217U, 0xBA7AFCU, 0xBA8C78U, 0xBA9493U, 0xBAA546U, 0xBABDADU, 0xBAC6EFU, 0xBADE04U, + 0xBAEFD1U, 0xBAF73AU, 0xBB02D9U, 0xBB1A32U, 0xBB2BE7U, 0xBB330CU, 0xBB484EU, 0xBB50A5U, 0xBB6170U, 0xBB799BU, + 0xBB8F1FU, 0xBB97F4U, 0xBBA621U, 0xBBBECAU, 0xBBC588U, 0xBBDD63U, 0xBBECB6U, 0xBBF45DU, 0xBC0AEAU, 0xBC1201U, + 0xBC23D4U, 0xBC3B3FU, 0xBC407DU, 0xBC5896U, 0xBC6943U, 0xBC71A8U, 0xBC872CU, 0xBC9FC7U, 0xBCAE12U, 0xBCB6F9U, + 0xBCCDBBU, 0xBCD550U, 0xBCE485U, 0xBCFC6EU, 0xBD098DU, 0xBD1166U, 0xBD20B3U, 0xBD3858U, 0xBD431AU, 0xBD5BF1U, + 0xBD6A24U, 0xBD72CFU, 0xBD844BU, 0xBD9CA0U, 0xBDAD75U, 0xBDB59EU, 0xBDCEDCU, 0xBDD637U, 0xBDE7E2U, 0xBDFF09U, + 0xBE0C27U, 0xBE14CCU, 0xBE2519U, 0xBE3DF2U, 0xBE46B0U, 0xBE5E5BU, 0xBE6F8EU, 0xBE7765U, 0xBE81E1U, 0xBE990AU, + 0xBEA8DFU, 0xBEB034U, 0xBECB76U, 0xBED39DU, 0xBEE248U, 0xBEFAA3U, 0xBF0F40U, 0xBF17ABU, 0xBF267EU, 0xBF3E95U, + 0xBF45D7U, 0xBF5D3CU, 0xBF6CE9U, 0xBF7402U, 0xBF8286U, 0xBF9A6DU, 0xBFABB8U, 0xBFB353U, 0xBFC811U, 0xBFD0FAU, + 0xBFE12FU, 0xBFF9C4U, 0xC00A4EU, 0xC012A5U, 0xC02370U, 0xC03B9BU, 0xC040D9U, 0xC05832U, 0xC069E7U, 0xC0710CU, + 0xC08788U, 0xC09F63U, 0xC0AEB6U, 0xC0B65DU, 0xC0CD1FU, 0xC0D5F4U, 0xC0E421U, 0xC0FCCAU, 0xC10929U, 0xC111C2U, + 0xC12017U, 0xC138FCU, 0xC143BEU, 0xC15B55U, 0xC16A80U, 0xC1726BU, 0xC184EFU, 0xC19C04U, 0xC1ADD1U, 0xC1B53AU, + 0xC1CE78U, 0xC1D693U, 0xC1E746U, 0xC1FFADU, 0xC20C83U, 0xC21468U, 0xC225BDU, 0xC23D56U, 0xC24614U, 0xC25EFFU, + 0xC26F2AU, 0xC277C1U, 0xC28145U, 0xC299AEU, 0xC2A87BU, 0xC2B090U, 0xC2CBD2U, 0xC2D339U, 0xC2E2ECU, 0xC2FA07U, + 0xC30FE4U, 0xC3170FU, 0xC326DAU, 0xC33E31U, 0xC34573U, 0xC35D98U, 0xC36C4DU, 0xC374A6U, 0xC38222U, 0xC39AC9U, + 0xC3AB1CU, 0xC3B3F7U, 0xC3C8B5U, 0xC3D05EU, 0xC3E18BU, 0xC3F960U, 0xC407D7U, 0xC41F3CU, 0xC42EE9U, 0xC43602U, + 0xC44D40U, 0xC455ABU, 0xC4647EU, 0xC47C95U, 0xC48A11U, 0xC492FAU, 0xC4A32FU, 0xC4BBC4U, 0xC4C086U, 0xC4D86DU, + 0xC4E9B8U, 0xC4F153U, 0xC504B0U, 0xC51C5BU, 0xC52D8EU, 0xC53565U, 0xC54E27U, 0xC556CCU, 0xC56719U, 0xC57FF2U, + 0xC58976U, 0xC5919DU, 0xC5A048U, 0xC5B8A3U, 0xC5C3E1U, 0xC5DB0AU, 0xC5EADFU, 0xC5F234U, 0xC6011AU, 0xC619F1U, + 0xC62824U, 0xC630CFU, 0xC64B8DU, 0xC65366U, 0xC662B3U, 0xC67A58U, 0xC68CDCU, 0xC69437U, 0xC6A5E2U, 0xC6BD09U, + 0xC6C64BU, 0xC6DEA0U, 0xC6EF75U, 0xC6F79EU, 0xC7027DU, 0xC71A96U, 0xC72B43U, 0xC733A8U, 0xC748EAU, 0xC75001U, + 0xC761D4U, 0xC7793FU, 0xC78FBBU, 0xC79750U, 0xC7A685U, 0xC7BE6EU, 0xC7C52CU, 0xC7DDC7U, 0xC7EC12U, 0xC7F4F9U, + 0xC80994U, 0xC8117FU, 0xC820AAU, 0xC83841U, 0xC84303U, 0xC85BE8U, 0xC86A3DU, 0xC872D6U, 0xC88452U, 0xC89CB9U, + 0xC8AD6CU, 0xC8B587U, 0xC8CEC5U, 0xC8D62EU, 0xC8E7FBU, 0xC8FF10U, 0xC90AF3U, 0xC91218U, 0xC923CDU, 0xC93B26U, + 0xC94064U, 0xC9588FU, 0xC9695AU, 0xC971B1U, 0xC98735U, 0xC99FDEU, 0xC9AE0BU, 0xC9B6E0U, 0xC9CDA2U, 0xC9D549U, + 0xC9E49CU, 0xC9FC77U, 0xCA0F59U, 0xCA17B2U, 0xCA2667U, 0xCA3E8CU, 0xCA45CEU, 0xCA5D25U, 0xCA6CF0U, 0xCA741BU, + 0xCA829FU, 0xCA9A74U, 0xCAABA1U, 0xCAB34AU, 0xCAC808U, 0xCAD0E3U, 0xCAE136U, 0xCAF9DDU, 0xCB0C3EU, 0xCB14D5U, + 0xCB2500U, 0xCB3DEBU, 0xCB46A9U, 0xCB5E42U, 0xCB6F97U, 0xCB777CU, 0xCB81F8U, 0xCB9913U, 0xCBA8C6U, 0xCBB02DU, + 0xCBCB6FU, 0xCBD384U, 0xCBE251U, 0xCBFABAU, 0xCC040DU, 0xCC1CE6U, 0xCC2D33U, 0xCC35D8U, 0xCC4E9AU, 0xCC5671U, + 0xCC67A4U, 0xCC7F4FU, 0xCC89CBU, 0xCC9120U, 0xCCA0F5U, 0xCCB81EU, 0xCCC35CU, 0xCCDBB7U, 0xCCEA62U, 0xCCF289U, + 0xCD076AU, 0xCD1F81U, 0xCD2E54U, 0xCD36BFU, 0xCD4DFDU, 0xCD5516U, 0xCD64C3U, 0xCD7C28U, 0xCD8AACU, 0xCD9247U, + 0xCDA392U, 0xCDBB79U, 0xCDC03BU, 0xCDD8D0U, 0xCDE905U, 0xCDF1EEU, 0xCE02C0U, 0xCE1A2BU, 0xCE2BFEU, 0xCE3315U, + 0xCE4857U, 0xCE50BCU, 0xCE6169U, 0xCE7982U, 0xCE8F06U, 0xCE97EDU, 0xCEA638U, 0xCEBED3U, 0xCEC591U, 0xCEDD7AU, + 0xCEECAFU, 0xCEF444U, 0xCF01A7U, 0xCF194CU, 0xCF2899U, 0xCF3072U, 0xCF4B30U, 0xCF53DBU, 0xCF620EU, 0xCF7AE5U, + 0xCF8C61U, 0xCF948AU, 0xCFA55FU, 0xCFBDB4U, 0xCFC6F6U, 0xCFDE1DU, 0xCFEFC8U, 0xCFF723U, 0xD00DFAU, 0xD01511U, + 0xD024C4U, 0xD03C2FU, 0xD0476DU, 0xD05F86U, 0xD06E53U, 0xD076B8U, 0xD0803CU, 0xD098D7U, 0xD0A902U, 0xD0B1E9U, + 0xD0CAABU, 0xD0D240U, 0xD0E395U, 0xD0FB7EU, 0xD10E9DU, 0xD11676U, 0xD127A3U, 0xD13F48U, 0xD1440AU, 0xD15CE1U, + 0xD16D34U, 0xD175DFU, 0xD1835BU, 0xD19BB0U, 0xD1AA65U, 0xD1B28EU, 0xD1C9CCU, 0xD1D127U, 0xD1E0F2U, 0xD1F819U, + 0xD20B37U, 0xD213DCU, 0xD22209U, 0xD23AE2U, 0xD241A0U, 0xD2594BU, 0xD2689EU, 0xD27075U, 0xD286F1U, 0xD29E1AU, + 0xD2AFCFU, 0xD2B724U, 0xD2CC66U, 0xD2D48DU, 0xD2E558U, 0xD2FDB3U, 0xD30850U, 0xD310BBU, 0xD3216EU, 0xD33985U, + 0xD342C7U, 0xD35A2CU, 0xD36BF9U, 0xD37312U, 0xD38596U, 0xD39D7DU, 0xD3ACA8U, 0xD3B443U, 0xD3CF01U, 0xD3D7EAU, + 0xD3E63FU, 0xD3FED4U, 0xD40063U, 0xD41888U, 0xD4295DU, 0xD431B6U, 0xD44AF4U, 0xD4521FU, 0xD463CAU, 0xD47B21U, + 0xD48DA5U, 0xD4954EU, 0xD4A49BU, 0xD4BC70U, 0xD4C732U, 0xD4DFD9U, 0xD4EE0CU, 0xD4F6E7U, 0xD50304U, 0xD51BEFU, + 0xD52A3AU, 0xD532D1U, 0xD54993U, 0xD55178U, 0xD560ADU, 0xD57846U, 0xD58EC2U, 0xD59629U, 0xD5A7FCU, 0xD5BF17U, + 0xD5C455U, 0xD5DCBEU, 0xD5ED6BU, 0xD5F580U, 0xD606AEU, 0xD61E45U, 0xD62F90U, 0xD6377BU, 0xD64C39U, 0xD654D2U, + 0xD66507U, 0xD67DECU, 0xD68B68U, 0xD69383U, 0xD6A256U, 0xD6BABDU, 0xD6C1FFU, 0xD6D914U, 0xD6E8C1U, 0xD6F02AU, + 0xD705C9U, 0xD71D22U, 0xD72CF7U, 0xD7341CU, 0xD74F5EU, 0xD757B5U, 0xD76660U, 0xD77E8BU, 0xD7880FU, 0xD790E4U, + 0xD7A131U, 0xD7B9DAU, 0xD7C298U, 0xD7DA73U, 0xD7EBA6U, 0xD7F34DU, 0xD80E20U, 0xD816CBU, 0xD8271EU, 0xD83FF5U, + 0xD844B7U, 0xD85C5CU, 0xD86D89U, 0xD87562U, 0xD883E6U, 0xD89B0DU, 0xD8AAD8U, 0xD8B233U, 0xD8C971U, 0xD8D19AU, + 0xD8E04FU, 0xD8F8A4U, 0xD90D47U, 0xD915ACU, 0xD92479U, 0xD93C92U, 0xD947D0U, 0xD95F3BU, 0xD96EEEU, 0xD97605U, + 0xD98081U, 0xD9986AU, 0xD9A9BFU, 0xD9B154U, 0xD9CA16U, 0xD9D2FDU, 0xD9E328U, 0xD9FBC3U, 0xDA08EDU, 0xDA1006U, + 0xDA21D3U, 0xDA3938U, 0xDA427AU, 0xDA5A91U, 0xDA6B44U, 0xDA73AFU, 0xDA852BU, 0xDA9DC0U, 0xDAAC15U, 0xDAB4FEU, + 0xDACFBCU, 0xDAD757U, 0xDAE682U, 0xDAFE69U, 0xDB0B8AU, 0xDB1361U, 0xDB22B4U, 0xDB3A5FU, 0xDB411DU, 0xDB59F6U, + 0xDB6823U, 0xDB70C8U, 0xDB864CU, 0xDB9EA7U, 0xDBAF72U, 0xDBB799U, 0xDBCCDBU, 0xDBD430U, 0xDBE5E5U, 0xDBFD0EU, + 0xDC03B9U, 0xDC1B52U, 0xDC2A87U, 0xDC326CU, 0xDC492EU, 0xDC51C5U, 0xDC6010U, 0xDC78FBU, 0xDC8E7FU, 0xDC9694U, + 0xDCA741U, 0xDCBFAAU, 0xDCC4E8U, 0xDCDC03U, 0xDCEDD6U, 0xDCF53DU, 0xDD00DEU, 0xDD1835U, 0xDD29E0U, 0xDD310BU, + 0xDD4A49U, 0xDD52A2U, 0xDD6377U, 0xDD7B9CU, 0xDD8D18U, 0xDD95F3U, 0xDDA426U, 0xDDBCCDU, 0xDDC78FU, 0xDDDF64U, + 0xDDEEB1U, 0xDDF65AU, 0xDE0574U, 0xDE1D9FU, 0xDE2C4AU, 0xDE34A1U, 0xDE4FE3U, 0xDE5708U, 0xDE66DDU, 0xDE7E36U, + 0xDE88B2U, 0xDE9059U, 0xDEA18CU, 0xDEB967U, 0xDEC225U, 0xDEDACEU, 0xDEEB1BU, 0xDEF3F0U, 0xDF0613U, 0xDF1EF8U, + 0xDF2F2DU, 0xDF37C6U, 0xDF4C84U, 0xDF546FU, 0xDF65BAU, 0xDF7D51U, 0xDF8BD5U, 0xDF933EU, 0xDFA2EBU, 0xDFBA00U, + 0xDFC142U, 0xDFD9A9U, 0xDFE87CU, 0xDFF097U, 0xE00526U, 0xE01DCDU, 0xE02C18U, 0xE034F3U, 0xE04FB1U, 0xE0575AU, + 0xE0668FU, 0xE07E64U, 0xE088E0U, 0xE0900BU, 0xE0A1DEU, 0xE0B935U, 0xE0C277U, 0xE0DA9CU, 0xE0EB49U, 0xE0F3A2U, + 0xE10641U, 0xE11EAAU, 0xE12F7FU, 0xE13794U, 0xE14CD6U, 0xE1543DU, 0xE165E8U, 0xE17D03U, 0xE18B87U, 0xE1936CU, + 0xE1A2B9U, 0xE1BA52U, 0xE1C110U, 0xE1D9FBU, 0xE1E82EU, 0xE1F0C5U, 0xE203EBU, 0xE21B00U, 0xE22AD5U, 0xE2323EU, + 0xE2497CU, 0xE25197U, 0xE26042U, 0xE278A9U, 0xE28E2DU, 0xE296C6U, 0xE2A713U, 0xE2BFF8U, 0xE2C4BAU, 0xE2DC51U, + 0xE2ED84U, 0xE2F56FU, 0xE3008CU, 0xE31867U, 0xE329B2U, 0xE33159U, 0xE34A1BU, 0xE352F0U, 0xE36325U, 0xE37BCEU, + 0xE38D4AU, 0xE395A1U, 0xE3A474U, 0xE3BC9FU, 0xE3C7DDU, 0xE3DF36U, 0xE3EEE3U, 0xE3F608U, 0xE408BFU, 0xE41054U, + 0xE42181U, 0xE4396AU, 0xE44228U, 0xE45AC3U, 0xE46B16U, 0xE473FDU, 0xE48579U, 0xE49D92U, 0xE4AC47U, 0xE4B4ACU, + 0xE4CFEEU, 0xE4D705U, 0xE4E6D0U, 0xE4FE3BU, 0xE50BD8U, 0xE51333U, 0xE522E6U, 0xE53A0DU, 0xE5414FU, 0xE559A4U, + 0xE56871U, 0xE5709AU, 0xE5861EU, 0xE59EF5U, 0xE5AF20U, 0xE5B7CBU, 0xE5CC89U, 0xE5D462U, 0xE5E5B7U, 0xE5FD5CU, + 0xE60E72U, 0xE61699U, 0xE6274CU, 0xE63FA7U, 0xE644E5U, 0xE65C0EU, 0xE66DDBU, 0xE67530U, 0xE683B4U, 0xE69B5FU, + 0xE6AA8AU, 0xE6B261U, 0xE6C923U, 0xE6D1C8U, 0xE6E01DU, 0xE6F8F6U, 0xE70D15U, 0xE715FEU, 0xE7242BU, 0xE73CC0U, + 0xE74782U, 0xE75F69U, 0xE76EBCU, 0xE77657U, 0xE780D3U, 0xE79838U, 0xE7A9EDU, 0xE7B106U, 0xE7CA44U, 0xE7D2AFU, + 0xE7E37AU, 0xE7FB91U, 0xE806FCU, 0xE81E17U, 0xE82FC2U, 0xE83729U, 0xE84C6BU, 0xE85480U, 0xE86555U, 0xE87DBEU, + 0xE88B3AU, 0xE893D1U, 0xE8A204U, 0xE8BAEFU, 0xE8C1ADU, 0xE8D946U, 0xE8E893U, 0xE8F078U, 0xE9059BU, 0xE91D70U, + 0xE92CA5U, 0xE9344EU, 0xE94F0CU, 0xE957E7U, 0xE96632U, 0xE97ED9U, 0xE9885DU, 0xE990B6U, 0xE9A163U, 0xE9B988U, + 0xE9C2CAU, 0xE9DA21U, 0xE9EBF4U, 0xE9F31FU, 0xEA0031U, 0xEA18DAU, 0xEA290FU, 0xEA31E4U, 0xEA4AA6U, 0xEA524DU, + 0xEA6398U, 0xEA7B73U, 0xEA8DF7U, 0xEA951CU, 0xEAA4C9U, 0xEABC22U, 0xEAC760U, 0xEADF8BU, 0xEAEE5EU, 0xEAF6B5U, + 0xEB0356U, 0xEB1BBDU, 0xEB2A68U, 0xEB3283U, 0xEB49C1U, 0xEB512AU, 0xEB60FFU, 0xEB7814U, 0xEB8E90U, 0xEB967BU, + 0xEBA7AEU, 0xEBBF45U, 0xEBC407U, 0xEBDCECU, 0xEBED39U, 0xEBF5D2U, 0xEC0B65U, 0xEC138EU, 0xEC225BU, 0xEC3AB0U, + 0xEC41F2U, 0xEC5919U, 0xEC68CCU, 0xEC7027U, 0xEC86A3U, 0xEC9E48U, 0xECAF9DU, 0xECB776U, 0xECCC34U, 0xECD4DFU, + 0xECE50AU, 0xECFDE1U, 0xED0802U, 0xED10E9U, 0xED213CU, 0xED39D7U, 0xED4295U, 0xED5A7EU, 0xED6BABU, 0xED7340U, + 0xED85C4U, 0xED9D2FU, 0xEDACFAU, 0xEDB411U, 0xEDCF53U, 0xEDD7B8U, 0xEDE66DU, 0xEDFE86U, 0xEE0DA8U, 0xEE1543U, + 0xEE2496U, 0xEE3C7DU, 0xEE473FU, 0xEE5FD4U, 0xEE6E01U, 0xEE76EAU, 0xEE806EU, 0xEE9885U, 0xEEA950U, 0xEEB1BBU, + 0xEECAF9U, 0xEED212U, 0xEEE3C7U, 0xEEFB2CU, 0xEF0ECFU, 0xEF1624U, 0xEF27F1U, 0xEF3F1AU, 0xEF4458U, 0xEF5CB3U, + 0xEF6D66U, 0xEF758DU, 0xEF8309U, 0xEF9BE2U, 0xEFAA37U, 0xEFB2DCU, 0xEFC99EU, 0xEFD175U, 0xEFE0A0U, 0xEFF84BU, + 0xF00292U, 0xF01A79U, 0xF02BACU, 0xF03347U, 0xF04805U, 0xF050EEU, 0xF0613BU, 0xF079D0U, 0xF08F54U, 0xF097BFU, + 0xF0A66AU, 0xF0BE81U, 0xF0C5C3U, 0xF0DD28U, 0xF0ECFDU, 0xF0F416U, 0xF101F5U, 0xF1191EU, 0xF128CBU, 0xF13020U, + 0xF14B62U, 0xF15389U, 0xF1625CU, 0xF17AB7U, 0xF18C33U, 0xF194D8U, 0xF1A50DU, 0xF1BDE6U, 0xF1C6A4U, 0xF1DE4FU, + 0xF1EF9AU, 0xF1F771U, 0xF2045FU, 0xF21CB4U, 0xF22D61U, 0xF2358AU, 0xF24EC8U, 0xF25623U, 0xF267F6U, 0xF27F1DU, + 0xF28999U, 0xF29172U, 0xF2A0A7U, 0xF2B84CU, 0xF2C30EU, 0xF2DBE5U, 0xF2EA30U, 0xF2F2DBU, 0xF30738U, 0xF31FD3U, + 0xF32E06U, 0xF336EDU, 0xF34DAFU, 0xF35544U, 0xF36491U, 0xF37C7AU, 0xF38AFEU, 0xF39215U, 0xF3A3C0U, 0xF3BB2BU, + 0xF3C069U, 0xF3D882U, 0xF3E957U, 0xF3F1BCU, 0xF40F0BU, 0xF417E0U, 0xF42635U, 0xF43EDEU, 0xF4459CU, 0xF45D77U, + 0xF46CA2U, 0xF47449U, 0xF482CDU, 0xF49A26U, 0xF4ABF3U, 0xF4B318U, 0xF4C85AU, 0xF4D0B1U, 0xF4E164U, 0xF4F98FU, + 0xF50C6CU, 0xF51487U, 0xF52552U, 0xF53DB9U, 0xF546FBU, 0xF55E10U, 0xF56FC5U, 0xF5772EU, 0xF581AAU, 0xF59941U, + 0xF5A894U, 0xF5B07FU, 0xF5CB3DU, 0xF5D3D6U, 0xF5E203U, 0xF5FAE8U, 0xF609C6U, 0xF6112DU, 0xF620F8U, 0xF63813U, + 0xF64351U, 0xF65BBAU, 0xF66A6FU, 0xF67284U, 0xF68400U, 0xF69CEBU, 0xF6AD3EU, 0xF6B5D5U, 0xF6CE97U, 0xF6D67CU, + 0xF6E7A9U, 0xF6FF42U, 0xF70AA1U, 0xF7124AU, 0xF7239FU, 0xF73B74U, 0xF74036U, 0xF758DDU, 0xF76908U, 0xF771E3U, + 0xF78767U, 0xF79F8CU, 0xF7AE59U, 0xF7B6B2U, 0xF7CDF0U, 0xF7D51BU, 0xF7E4CEU, 0xF7FC25U, 0xF80148U, 0xF819A3U, + 0xF82876U, 0xF8309DU, 0xF84BDFU, 0xF85334U, 0xF862E1U, 0xF87A0AU, 0xF88C8EU, 0xF89465U, 0xF8A5B0U, 0xF8BD5BU, + 0xF8C619U, 0xF8DEF2U, 0xF8EF27U, 0xF8F7CCU, 0xF9022FU, 0xF91AC4U, 0xF92B11U, 0xF933FAU, 0xF948B8U, 0xF95053U, + 0xF96186U, 0xF9796DU, 0xF98FE9U, 0xF99702U, 0xF9A6D7U, 0xF9BE3CU, 0xF9C57EU, 0xF9DD95U, 0xF9EC40U, 0xF9F4ABU, + 0xFA0785U, 0xFA1F6EU, 0xFA2EBBU, 0xFA3650U, 0xFA4D12U, 0xFA55F9U, 0xFA642CU, 0xFA7CC7U, 0xFA8A43U, 0xFA92A8U, + 0xFAA37DU, 0xFABB96U, 0xFAC0D4U, 0xFAD83FU, 0xFAE9EAU, 0xFAF101U, 0xFB04E2U, 0xFB1C09U, 0xFB2DDCU, 0xFB3537U, + 0xFB4E75U, 0xFB569EU, 0xFB674BU, 0xFB7FA0U, 0xFB8924U, 0xFB91CFU, 0xFBA01AU, 0xFBB8F1U, 0xFBC3B3U, 0xFBDB58U, + 0xFBEA8DU, 0xFBF266U, 0xFC0CD1U, 0xFC143AU, 0xFC25EFU, 0xFC3D04U, 0xFC4646U, 0xFC5EADU, 0xFC6F78U, 0xFC7793U, + 0xFC8117U, 0xFC99FCU, 0xFCA829U, 0xFCB0C2U, 0xFCCB80U, 0xFCD36BU, 0xFCE2BEU, 0xFCFA55U, 0xFD0FB6U, 0xFD175DU, + 0xFD2688U, 0xFD3E63U, 0xFD4521U, 0xFD5DCAU, 0xFD6C1FU, 0xFD74F4U, 0xFD8270U, 0xFD9A9BU, 0xFDAB4EU, 0xFDB3A5U, + 0xFDC8E7U, 0xFDD00CU, 0xFDE1D9U, 0xFDF932U, 0xFE0A1CU, 0xFE12F7U, 0xFE2322U, 0xFE3BC9U, 0xFE408BU, 0xFE5860U, + 0xFE69B5U, 0xFE715EU, 0xFE87DAU, 0xFE9F31U, 0xFEAEE4U, 0xFEB60FU, 0xFECD4DU, 0xFED5A6U, 0xFEE473U, 0xFEFC98U, + 0xFF097BU, 0xFF1190U, 0xFF2045U, 0xFF38AEU, 0xFF43ECU, 0xFF5B07U, 0xFF6AD2U, 0xFF7239U, 0xFF84BDU, 0xFF9C56U, + 0xFFAD83U, 0xFFB568U, 0xFFCE2AU, 0xFFD6C1U, 0xFFE714U, 0xFFFFFFU}; + +static const unsigned int DECODING_TABLE_23127[] = { + 0x000000U, 0x000001U, 0x000002U, 0x000003U, 0x000004U, 0x000005U, 0x000006U, 0x000007U, 0x000008U, 0x000009U, + 0x00000AU, 0x00000BU, 0x00000CU, 0x00000DU, 0x00000EU, 0x024020U, 0x000010U, 0x000011U, 0x000012U, 0x000013U, + 0x000014U, 0x000015U, 0x000016U, 0x412000U, 0x000018U, 0x000019U, 0x00001AU, 0x180800U, 0x00001CU, 0x200300U, + 0x048040U, 0x001480U, 0x000020U, 0x000021U, 0x000022U, 0x000023U, 0x000024U, 0x000025U, 0x000026U, 0x024008U, + 0x000028U, 0x000029U, 0x00002AU, 0x024004U, 0x00002CU, 0x024002U, 0x024001U, 0x024000U, 0x000030U, 0x000031U, + 0x000032U, 0x008180U, 0x000034U, 0x000C40U, 0x301000U, 0x0C0200U, 0x000038U, 0x043000U, 0x400600U, 0x210040U, + 0x090080U, 0x508000U, 0x002900U, 0x024010U, 0x000040U, 0x000041U, 0x000042U, 0x000043U, 0x000044U, 0x000045U, + 0x000046U, 0x280080U, 0x000048U, 0x000049U, 0x00004AU, 0x002500U, 0x00004CU, 0x111000U, 0x048010U, 0x400A00U, + 0x000050U, 0x000051U, 0x000052U, 0x021200U, 0x000054U, 0x000C20U, 0x048008U, 0x104100U, 0x000058U, 0x404080U, + 0x048004U, 0x210020U, 0x048002U, 0x0A2000U, 0x048000U, 0x048001U, 0x000060U, 0x000061U, 0x000062U, 0x540000U, + 0x000064U, 0x000C10U, 0x010300U, 0x00B000U, 0x000068U, 0x088200U, 0x001880U, 0x210010U, 0x602000U, 0x040180U, + 0x180400U, 0x024040U, 0x000070U, 0x000C04U, 0x086000U, 0x210008U, 0x000C01U, 0x000C00U, 0x420080U, 0x000C02U, + 0x120100U, 0x210002U, 0x210001U, 0x210000U, 0x005200U, 0x000C08U, 0x048020U, 0x210004U, 0x000080U, 0x000081U, + 0x000082U, 0x000083U, 0x000084U, 0x000085U, 0x000086U, 0x280040U, 0x000088U, 0x000089U, 0x00008AU, 0x050200U, + 0x00008CU, 0x00A800U, 0x500100U, 0x001410U, 0x000090U, 0x000091U, 0x000092U, 0x008120U, 0x000094U, 0x160000U, + 0x004A00U, 0x001408U, 0x000098U, 0x404040U, 0x222000U, 0x001404U, 0x090020U, 0x001402U, 0x001401U, 0x001400U, + 0x0000A0U, 0x0000A1U, 0x0000A2U, 0x008110U, 0x0000A4U, 0x401200U, 0x042400U, 0x110800U, 0x0000A8U, 0x300400U, + 0x001840U, 0x482000U, 0x090010U, 0x040140U, 0x208200U, 0x024080U, 0x0000B0U, 0x008102U, 0x008101U, 0x008100U, + 0x090008U, 0x206000U, 0x420040U, 0x008104U, 0x090004U, 0x020A00U, 0x144000U, 0x008108U, 0x090000U, 0x090001U, + 0x090002U, 0x001420U, 0x0000C0U, 0x0000C1U, 0x0000C2U, 0x280004U, 0x0000C4U, 0x280002U, 0x280001U, 0x280000U, + 0x0000C8U, 0x404010U, 0x001820U, 0x128000U, 0x020600U, 0x040120U, 0x016000U, 0x280008U, 0x0000D0U, 0x404008U, + 0x110400U, 0x042800U, 0x003100U, 0x018200U, 0x420020U, 0x280010U, 0x404001U, 0x404000U, 0x080300U, 0x404002U, + 0x300800U, 0x404004U, 0x048080U, 0x001440U, 0x0000E0U, 0x032000U, 0x001808U, 0x004600U, 0x10C000U, 0x040108U, + 0x420010U, 0x280020U, 0x001802U, 0x040104U, 0x001800U, 0x001801U, 0x040101U, 0x040100U, 0x001804U, 0x040102U, + 0x240200U, 0x181000U, 0x420004U, 0x008140U, 0x420002U, 0x000C80U, 0x420000U, 0x420001U, 0x00A400U, 0x404020U, + 0x001810U, 0x210080U, 0x090040U, 0x040110U, 0x420008U, 0x102200U, 0x000100U, 0x000101U, 0x000102U, 0x000103U, + 0x000104U, 0x000105U, 0x000106U, 0x041800U, 0x000108U, 0x000109U, 0x00010AU, 0x002440U, 0x00010CU, 0x200210U, + 0x500080U, 0x098000U, 0x000110U, 0x000111U, 0x000112U, 0x0080A0U, 0x000114U, 0x200208U, 0x0A0400U, 0x104040U, + 0x000118U, 0x200204U, 0x015000U, 0x460000U, 0x200201U, 0x200200U, 0x002820U, 0x200202U, 0x000120U, 0x000121U, + 0x000122U, 0x008090U, 0x000124U, 0x182000U, 0x010240U, 0x600400U, 0x000128U, 0x410800U, 0x2C0000U, 0x101200U, + 0x009400U, 0x0400C0U, 0x002810U, 0x024100U, 0x000130U, 0x008082U, 0x008081U, 0x008080U, 0x444000U, 0x031000U, + 0x002808U, 0x008084U, 0x120040U, 0x084400U, 0x002804U, 0x008088U, 0x002802U, 0x200220U, 0x002800U, 0x002801U, + 0x000140U, 0x000141U, 0x000142U, 0x002408U, 0x000144U, 0x428000U, 0x010220U, 0x104010U, 0x000148U, 0x002402U, + 0x002401U, 0x002400U, 0x084800U, 0x0400A0U, 0x221000U, 0x002404U, 0x000150U, 0x0D0000U, 0x600800U, 0x104004U, + 0x003080U, 0x104002U, 0x104001U, 0x104000U, 0x120020U, 0x009800U, 0x080280U, 0x002410U, 0x410400U, 0x200240U, + 0x048100U, 0x104008U, 0x000160U, 0x205000U, 0x010204U, 0x0A0800U, 0x010202U, 0x040088U, 0x010200U, 0x010201U, + 0x120010U, 0x040084U, 0x40C000U, 0x002420U, 0x040081U, 0x040080U, 0x010208U, 0x040082U, 0x120008U, 0x402200U, + 0x041400U, 0x0080C0U, 0x288000U, 0x000D00U, 0x010210U, 0x104020U, 0x120000U, 0x120001U, 0x120002U, 0x210100U, + 0x120004U, 0x040090U, 0x002840U, 0x481000U, 0x000180U, 0x000181U, 0x000182U, 0x008030U, 0x000184U, 0x014400U, + 0x500008U, 0x022200U, 0x000188U, 0x0A1000U, 0x500004U, 0x204800U, 0x500002U, 0x040060U, 0x500000U, 0x500001U, + 0x000190U, 0x008022U, 0x008021U, 0x008020U, 0x003040U, 0x480800U, 0x250000U, 0x008024U, 0x040C00U, 0x112000U, + 0x080240U, 0x008028U, 0x02C000U, 0x200280U, 0x500010U, 0x001500U, 0x0001A0U, 0x008012U, 0x008011U, 0x008010U, + 0x220800U, 0x040048U, 0x085000U, 0x008014U, 0x006200U, 0x040044U, 0x030400U, 0x008018U, 0x040041U, 0x040040U, + 0x500020U, 0x040042U, 0x008003U, 0x008002U, 0x008001U, 0x008000U, 0x100600U, 0x008006U, 0x008005U, 0x008004U, + 0x601000U, 0x00800AU, 0x008009U, 0x008008U, 0x090100U, 0x040050U, 0x002880U, 0x00800CU, 0x0001C0U, 0x100A00U, + 0x064000U, 0x411000U, 0x003010U, 0x040028U, 0x008C00U, 0x280100U, 0x218000U, 0x040024U, 0x080210U, 0x002480U, + 0x040021U, 0x040020U, 0x500040U, 0x040022U, 0x003004U, 0x220400U, 0x080208U, 0x008060U, 0x003000U, 0x003001U, + 0x003002U, 0x104080U, 0x080202U, 0x404100U, 0x080200U, 0x080201U, 0x003008U, 0x040030U, 0x080204U, 0x030800U, + 0x480400U, 0x04000CU, 0x302000U, 0x008050U, 0x040009U, 0x040008U, 0x010280U, 0x04000AU, 0x040005U, 0x040004U, + 0x001900U, 0x040006U, 0x040001U, 0x040000U, 0x040003U, 0x040002U, 0x014800U, 0x008042U, 0x008041U, 0x008040U, + 0x003020U, 0x040018U, 0x420100U, 0x008044U, 0x120080U, 0x040014U, 0x080220U, 0x008048U, 0x040011U, 0x040010U, + 0x204400U, 0x040012U, 0x000200U, 0x000201U, 0x000202U, 0x000203U, 0x000204U, 0x000205U, 0x000206U, 0x108400U, + 0x000208U, 0x000209U, 0x00020AU, 0x050080U, 0x00020CU, 0x200110U, 0x083000U, 0x400840U, 0x000210U, 0x000211U, + 0x000212U, 0x021040U, 0x000214U, 0x200108U, 0x004880U, 0x0C0020U, 0x000218U, 0x200104U, 0x400420U, 0x00E000U, + 0x200101U, 0x200100U, 0x130000U, 0x200102U, 0x000220U, 0x000221U, 0x000222U, 0x202800U, 0x000224U, 0x401080U, + 0x010140U, 0x0C0010U, 0x000228U, 0x088040U, 0x400410U, 0x101100U, 0x140800U, 0x012400U, 0x208080U, 0x024200U, + 0x000230U, 0x114000U, 0x400408U, 0x0C0004U, 0x02A000U, 0x0C0002U, 0x0C0001U, 0x0C0000U, 0x400402U, 0x020880U, + 0x400400U, 0x400401U, 0x005040U, 0x200120U, 0x400404U, 0x0C0008U, 0x000240U, 0x000241U, 0x000242U, 0x021010U, + 0x000244U, 0x046000U, 0x010120U, 0x400808U, 0x000248U, 0x088020U, 0x304000U, 0x400804U, 0x020480U, 0x400802U, + 0x400801U, 0x400800U, 0x000250U, 0x021002U, 0x021001U, 0x021000U, 0x580000U, 0x018080U, 0x202400U, 0x021004U, + 0x012800U, 0x140400U, 0x080180U, 0x021008U, 0x005020U, 0x200140U, 0x048200U, 0x400810U, 0x000260U, 0x088008U, + 0x010104U, 0x004480U, 0x010102U, 0x320000U, 0x010100U, 0x010101U, 0x088001U, 0x088000U, 0x062000U, 0x088002U, + 0x005010U, 0x088004U, 0x010108U, 0x400820U, 0x240080U, 0x402100U, 0x108800U, 0x021020U, 0x005008U, 0x000E00U, + 0x010110U, 0x0C0040U, 0x005004U, 0x088010U, 0x400440U, 0x210200U, 0x005000U, 0x005001U, 0x005002U, 0x102080U, + 0x000280U, 0x000281U, 0x000282U, 0x050008U, 0x000284U, 0x401020U, 0x004810U, 0x022100U, 0x000288U, 0x050002U, + 0x050001U, 0x050000U, 0x020440U, 0x184000U, 0x208020U, 0x050004U, 0x000290U, 0x082400U, 0x004804U, 0x700000U, + 0x004802U, 0x018040U, 0x004800U, 0x004801U, 0x109000U, 0x020820U, 0x080140U, 0x050010U, 0x442000U, 0x200180U, + 0x004808U, 0x001600U, 0x0002A0U, 0x401004U, 0x1A0000U, 0x004440U, 0x401001U, 0x401000U, 0x208008U, 0x401002U, + 0x006100U, 0x020810U, 0x208004U, 0x050020U, 0x208002U, 0x401008U, 0x208000U, 0x208001U, 0x240040U, 0x020808U, + 0x013000U, 0x008300U, 0x100500U, 0x401010U, 0x004820U, 0x0C0080U, 0x020801U, 0x020800U, 0x400480U, 0x020802U, + 0x090200U, 0x020804U, 0x208010U, 0x102040U, 0x0002C0U, 0x100900U, 0x40A000U, 0x004420U, 0x020408U, 0x018010U, + 0x141000U, 0x280200U, 0x020404U, 0x203000U, 0x080110U, 0x050040U, 0x020400U, 0x020401U, 0x020402U, 0x400880U, + 0x240020U, 0x018004U, 0x080108U, 0x021080U, 0x018001U, 0x018000U, 0x004840U, 0x018002U, 0x080102U, 0x404200U, + 0x080100U, 0x080101U, 0x020410U, 0x018008U, 0x080104U, 0x102020U, 0x240010U, 0x004402U, 0x004401U, 0x004400U, + 0x082800U, 0x401040U, 0x010180U, 0x004404U, 0x510000U, 0x088080U, 0x001A00U, 0x004408U, 0x020420U, 0x040300U, + 0x208040U, 0x102010U, 0x240000U, 0x240001U, 0x240002U, 0x004410U, 0x240004U, 0x018020U, 0x420200U, 0x102008U, + 0x240008U, 0x020840U, 0x080120U, 0x102004U, 0x005080U, 0x102002U, 0x102001U, 0x102000U, 0x000300U, 0x000301U, + 0x000302U, 0x484000U, 0x000304U, 0x200018U, 0x010060U, 0x022080U, 0x000308U, 0x200014U, 0x028800U, 0x101020U, + 0x200011U, 0x200010U, 0x044400U, 0x200012U, 0x000310U, 0x20000CU, 0x142000U, 0x010C00U, 0x200009U, 0x200008U, + 0x409000U, 0x20000AU, 0x200005U, 0x200004U, 0x0800C0U, 0x200006U, 0x200001U, 0x200000U, 0x200003U, 0x200002U, + 0x000320U, 0x060400U, 0x010044U, 0x101008U, 0x010042U, 0x00C800U, 0x010040U, 0x010041U, 0x006080U, 0x101002U, + 0x101001U, 0x101000U, 0x4A0000U, 0x200030U, 0x010048U, 0x101004U, 0x081800U, 0x402040U, 0x224000U, 0x008280U, + 0x100480U, 0x200028U, 0x010050U, 0x0C0100U, 0x058000U, 0x200024U, 0x400500U, 0x101010U, 0x200021U, 0x200020U, + 0x002A00U, 0x200022U, 0x000340U, 0x100880U, 0x010024U, 0x248000U, 0x010022U, 0x081400U, 0x010020U, 0x010021U, + 0x441000U, 0x034000U, 0x080090U, 0x002600U, 0x10A000U, 0x200050U, 0x010028U, 0x400900U, 0x00C400U, 0x402020U, + 0x080088U, 0x021100U, 0x060800U, 0x200048U, 0x010030U, 0x104200U, 0x080082U, 0x200044U, 0x080080U, 0x080081U, + 0x200041U, 0x200040U, 0x080084U, 0x200042U, 0x010006U, 0x402010U, 0x010004U, 0x010005U, 0x010002U, 0x010003U, + 0x010000U, 0x010001U, 0x200C00U, 0x088100U, 0x01000CU, 0x101040U, 0x01000AU, 0x040280U, 0x010008U, 0x010009U, + 0x402001U, 0x402000U, 0x010014U, 0x402002U, 0x010012U, 0x402004U, 0x010010U, 0x010011U, 0x120200U, 0x402008U, + 0x0800A0U, 0x044800U, 0x005100U, 0x200060U, 0x010018U, 0x028400U, 0x000380U, 0x100840U, 0x201400U, 0x022004U, + 0x0C8000U, 0x022002U, 0x022001U, 0x022000U, 0x006020U, 0x408400U, 0x080050U, 0x050100U, 0x011800U, 0x200090U, + 0x500200U, 0x022008U, 0x430000U, 0x045000U, 0x080048U, 0x008220U, 0x100420U, 0x200088U, 0x004900U, 0x022010U, + 0x080042U, 0x200084U, 0x080040U, 0x080041U, 0x200081U, 0x200080U, 0x080044U, 0x200082U, 0x006008U, 0x290000U, + 0x440800U, 0x008210U, 0x100410U, 0x401100U, 0x0100C0U, 0x022020U, 0x006000U, 0x006001U, 0x006002U, 0x101080U, + 0x006004U, 0x040240U, 0x208100U, 0x080C00U, 0x100404U, 0x008202U, 0x008201U, 0x008200U, 0x100400U, 0x100401U, + 0x100402U, 0x008204U, 0x006010U, 0x020900U, 0x080060U, 0x008208U, 0x100408U, 0x2000A0U, 0x061000U, 0x414000U, + 0x100801U, 0x100800U, 0x080018U, 0x100802U, 0x604000U, 0x100804U, 0x0100A0U, 0x022040U, 0x080012U, 0x100808U, + 0x080010U, 0x080011U, 0x020500U, 0x040220U, 0x080014U, 0x00D000U, 0x08000AU, 0x100810U, 0x080008U, 0x080009U, + 0x003200U, 0x018100U, 0x08000CU, 0x440400U, 0x080002U, 0x080003U, 0x080000U, 0x080001U, 0x080006U, 0x2000C0U, + 0x080004U, 0x080005U, 0x029000U, 0x100820U, 0x010084U, 0x004500U, 0x010082U, 0x040208U, 0x010080U, 0x010081U, + 0x006040U, 0x040204U, 0x080030U, 0x620000U, 0x040201U, 0x040200U, 0x010088U, 0x040202U, 0x240100U, 0x402080U, + 0x080028U, 0x008240U, 0x100440U, 0x0A4000U, 0x010090U, 0x201800U, 0x080022U, 0x011400U, 0x080020U, 0x080021U, + 0x408800U, 0x040210U, 0x080024U, 0x102100U, 0x000400U, 0x000401U, 0x000402U, 0x000403U, 0x000404U, 0x000405U, + 0x000406U, 0x108200U, 0x000408U, 0x000409U, 0x00040AU, 0x002140U, 0x00040CU, 0x4C0000U, 0x210800U, 0x001090U, + 0x000410U, 0x000411U, 0x000412U, 0x244000U, 0x000414U, 0x000860U, 0x0A0100U, 0x001088U, 0x000418U, 0x038000U, + 0x400220U, 0x001084U, 0x106000U, 0x001082U, 0x001081U, 0x001080U, 0x000420U, 0x000421U, 0x000422U, 0x091000U, + 0x000424U, 0x000850U, 0x042080U, 0x600100U, 0x000428U, 0x300080U, 0x400210U, 0x048800U, 0x009100U, 0x012200U, + 0x180040U, 0x024400U, 0x000430U, 0x000844U, 0x400208U, 0x122000U, 0x000841U, 0x000840U, 0x01C000U, 0x000842U, + 0x400202U, 0x084100U, 0x400200U, 0x400201U, 0x260000U, 0x000848U, 0x400204U, 0x0010A0U, 0x000440U, 0x000441U, + 0x000442U, 0x002108U, 0x000444U, 0x000830U, 0x405000U, 0x070000U, 0x000448U, 0x002102U, 0x002101U, 0x002100U, + 0x020280U, 0x20C000U, 0x180020U, 0x002104U, 0x000450U, 0x000824U, 0x110080U, 0x488000U, 0x000821U, 0x000820U, + 0x202200U, 0x000822U, 0x281000U, 0x140200U, 0x024800U, 0x002110U, 0x410100U, 0x000828U, 0x048400U, 0x0010C0U, + 0x000460U, 0x000814U, 0x228000U, 0x004280U, 0x000811U, 0x000810U, 0x180008U, 0x000812U, 0x054000U, 0x421000U, + 0x180004U, 0x002120U, 0x180002U, 0x000818U, 0x180000U, 0x180001U, 0x000805U, 0x000804U, 0x041100U, 0x000806U, + 0x000801U, 0x000800U, 0x000803U, 0x000802U, 0x00A080U, 0x00080CU, 0x400240U, 0x210400U, 0x000809U, 0x000808U, + 0x180010U, 0x00080AU, 0x000480U, 0x000481U, 0x000482U, 0x420800U, 0x000484U, 0x014100U, 0x042020U, 0x001018U, + 0x000488U, 0x300020U, 0x08C000U, 0x001014U, 0x020240U, 0x001012U, 0x001011U, 0x001010U, 0x000490U, 0x082200U, + 0x110040U, 0x00100CU, 0x608000U, 0x00100AU, 0x001009U, 0x001008U, 0x040900U, 0x001006U, 0x001005U, 0x001004U, + 0x001003U, 0x001002U, 0x001001U, 0x001000U, 0x0004A0U, 0x300008U, 0x042004U, 0x004240U, 0x042002U, 0x0A8000U, + 0x042000U, 0x042001U, 0x300001U, 0x300000U, 0x030100U, 0x300002U, 0x404800U, 0x300004U, 0x042008U, 0x001030U, + 0x025000U, 0x450000U, 0x280800U, 0x008500U, 0x100300U, 0x0008C0U, 0x042010U, 0x001028U, 0x00A040U, 0x300010U, + 0x400280U, 0x001024U, 0x090400U, 0x001022U, 0x001021U, 0x001020U, 0x0004C0U, 0x049000U, 0x110010U, 0x004220U, + 0x020208U, 0x502000U, 0x008900U, 0x280400U, 0x020204U, 0x090800U, 0x640000U, 0x002180U, 0x020200U, 0x020201U, + 0x020202U, 0x001050U, 0x110002U, 0x220100U, 0x110000U, 0x110001U, 0x0C4000U, 0x0008A0U, 0x110004U, 0x001048U, + 0x00A020U, 0x404400U, 0x110008U, 0x001044U, 0x020210U, 0x001042U, 0x001041U, 0x001040U, 0x480100U, 0x004202U, + 0x004201U, 0x004200U, 0x211000U, 0x000890U, 0x042040U, 0x004204U, 0x00A010U, 0x300040U, 0x001C00U, 0x004208U, + 0x020220U, 0x040500U, 0x180080U, 0x418000U, 0x00A008U, 0x000884U, 0x110020U, 0x004210U, 0x000881U, 0x000880U, + 0x420400U, 0x000882U, 0x00A000U, 0x00A001U, 0x00A002U, 0x0E0000U, 0x00A004U, 0x000888U, 0x204100U, 0x001060U, + 0x000500U, 0x000501U, 0x000502U, 0x002048U, 0x000504U, 0x014080U, 0x0A0010U, 0x600020U, 0x000508U, 0x002042U, + 0x002041U, 0x002040U, 0x009020U, 0x120800U, 0x044200U, 0x002044U, 0x000510U, 0x501000U, 0x0A0004U, 0x010A00U, + 0x0A0002U, 0x04A000U, 0x0A0000U, 0x0A0001U, 0x040880U, 0x084020U, 0x308000U, 0x002050U, 0x410040U, 0x200600U, + 0x0A0008U, 0x001180U, 0x000520U, 0x060200U, 0x104800U, 0x600004U, 0x009008U, 0x600002U, 0x600001U, 0x600000U, + 0x009004U, 0x084010U, 0x030080U, 0x002060U, 0x009000U, 0x009001U, 0x009002U, 0x600008U, 0x212000U, 0x084008U, + 0x041040U, 0x008480U, 0x100280U, 0x000940U, 0x0A0020U, 0x600010U, 0x084001U, 0x084000U, 0x400300U, 0x084002U, + 0x009010U, 0x084004U, 0x002C00U, 0x150000U, 0x000540U, 0x00200AU, 0x002009U, 0x002008U, 0x340000U, 0x081200U, + 0x008880U, 0x00200CU, 0x002003U, 0x002002U, 0x002001U, 0x002000U, 0x410010U, 0x002006U, 0x002005U, 0x002004U, + 0x00C200U, 0x220080U, 0x041020U, 0x002018U, 0x410008U, 0x000920U, 0x0A0040U, 0x104400U, 0x410004U, 0x002012U, + 0x002011U, 0x002010U, 0x410000U, 0x410001U, 0x410002U, 0x002014U, 0x480080U, 0x118000U, 0x041010U, 0x002028U, + 0x026000U, 0x000910U, 0x010600U, 0x600040U, 0x200A00U, 0x002022U, 0x002021U, 0x002020U, 0x009040U, 0x040480U, + 0x180100U, 0x002024U, 0x041002U, 0x000904U, 0x041000U, 0x041001U, 0x000901U, 0x000900U, 0x041004U, 0x000902U, + 0x120400U, 0x084040U, 0x041008U, 0x002030U, 0x410020U, 0x000908U, 0x204080U, 0x028200U, 0x000580U, 0x014004U, + 0x201200U, 0x1C0000U, 0x014001U, 0x014000U, 0x008840U, 0x014002U, 0x040810U, 0x408200U, 0x030020U, 0x0020C0U, + 0x282000U, 0x014008U, 0x500400U, 0x001110U, 0x040808U, 0x220040U, 0x406000U, 0x008420U, 0x100220U, 0x014010U, + 0x0A0080U, 0x001108U, 0x040800U, 0x040801U, 0x040802U, 0x001104U, 0x040804U, 0x001102U, 0x001101U, 0x001100U, + 0x480040U, 0x003800U, 0x030008U, 0x008410U, 0x100210U, 0x014020U, 0x042100U, 0x600080U, 0x030002U, 0x300100U, + 0x030000U, 0x030001U, 0x009080U, 0x040440U, 0x030004U, 0x080A00U, 0x100204U, 0x008402U, 0x008401U, 0x008400U, + 0x100200U, 0x100201U, 0x100202U, 0x008404U, 0x040820U, 0x084080U, 0x030010U, 0x008408U, 0x100208U, 0x422000U, + 0x204040U, 0x001120U, 0x480020U, 0x220010U, 0x008804U, 0x002088U, 0x008802U, 0x014040U, 0x008800U, 0x008801U, + 0x105000U, 0x002082U, 0x002081U, 0x002080U, 0x020300U, 0x040420U, 0x008808U, 0x002084U, 0x220001U, 0x220000U, + 0x110100U, 0x220002U, 0x003400U, 0x220004U, 0x008810U, 0x440200U, 0x040840U, 0x220008U, 0x080600U, 0x002090U, + 0x410080U, 0x188000U, 0x204020U, 0x001140U, 0x480000U, 0x480001U, 0x480002U, 0x004300U, 0x480004U, 0x040408U, + 0x008820U, 0x121000U, 0x480008U, 0x040404U, 0x030040U, 0x0020A0U, 0x040401U, 0x040400U, 0x204010U, 0x040402U, + 0x480010U, 0x220020U, 0x041080U, 0x008440U, 0x100240U, 0x000980U, 0x204008U, 0x092000U, 0x00A100U, 0x011200U, + 0x204004U, 0x500800U, 0x204002U, 0x040410U, 0x204000U, 0x204001U, 0x000600U, 0x000601U, 0x000602U, 0x108004U, + 0x000604U, 0x108002U, 0x108001U, 0x108000U, 0x000608U, 0x005800U, 0x400030U, 0x2A0000U, 0x0200C0U, 0x012020U, + 0x044100U, 0x108008U, 0x000610U, 0x082080U, 0x400028U, 0x010900U, 0x051000U, 0x424000U, 0x202040U, 0x108010U, + 0x400022U, 0x140040U, 0x400020U, 0x400021U, 0x088800U, 0x200500U, 0x400024U, 0x001280U, 0x000620U, 0x060100U, + 0x400018U, 0x0040C0U, 0x284000U, 0x012008U, 0x021800U, 0x108020U, 0x400012U, 0x012004U, 0x400010U, 0x400011U, + 0x012001U, 0x012000U, 0x400014U, 0x012002U, 0x40000AU, 0x209000U, 0x400008U, 0x400009U, 0x100180U, 0x000A40U, + 0x40000CU, 0x0C0400U, 0x400002U, 0x400003U, 0x400000U, 0x400001U, 0x400006U, 0x012010U, 0x400004U, 0x400005U, + 0x000640U, 0x610000U, 0x0C0800U, 0x0040A0U, 0x020088U, 0x081100U, 0x202010U, 0x108040U, 0x020084U, 0x140010U, + 0x019000U, 0x002300U, 0x020080U, 0x020081U, 0x020082U, 0x400C00U, 0x00C100U, 0x140008U, 0x202004U, 0x021400U, + 0x202002U, 0x000A20U, 0x202000U, 0x202001U, 0x140001U, 0x140000U, 0x400060U, 0x140002U, 0x020090U, 0x140004U, + 0x202008U, 0x094000U, 0x103000U, 0x004082U, 0x004081U, 0x004080U, 0x448000U, 0x000A10U, 0x010500U, 0x004084U, + 0x200900U, 0x088400U, 0x400050U, 0x004088U, 0x0200A0U, 0x012040U, 0x180200U, 0x241000U, 0x0B0000U, 0x000A04U, + 0x400048U, 0x004090U, 0x000A01U, 0x000A00U, 0x202020U, 0x000A02U, 0x400042U, 0x140020U, 0x400040U, 0x400041U, + 0x005400U, 0x000A08U, 0x400044U, 0x028100U, 0x000680U, 0x082010U, 0x201100U, 0x004060U, 0x020048U, 0x240800U, + 0x490000U, 0x108080U, 0x020044U, 0x408100U, 0x102800U, 0x050400U, 0x020040U, 0x020041U, 0x020042U, 0x001210U, + 0x082001U, 0x082000U, 0x068000U, 0x082002U, 0x100120U, 0x082004U, 0x004C00U, 0x001208U, 0x214000U, 0x082008U, + 0x4000A0U, 0x001204U, 0x020050U, 0x001202U, 0x001201U, 0x001200U, 0x018800U, 0x004042U, 0x004041U, 0x004040U, + 0x100110U, 0x401400U, 0x042200U, 0x004044U, 0x0C1000U, 0x300200U, 0x400090U, 0x004048U, 0x020060U, 0x012080U, + 0x208400U, 0x080900U, 0x100104U, 0x082020U, 0x400088U, 0x004050U, 0x100100U, 0x100101U, 0x100102U, 0x230000U, + 0x400082U, 0x020C00U, 0x400080U, 0x400081U, 0x100108U, 0x04C000U, 0x400084U, 0x001220U, 0x02000CU, 0x004022U, + 0x004021U, 0x004020U, 0x020008U, 0x020009U, 0x02000AU, 0x004024U, 0x020004U, 0x020005U, 0x020006U, 0x004028U, + 0x020000U, 0x020001U, 0x020002U, 0x020003U, 0x401800U, 0x082040U, 0x110200U, 0x004030U, 0x020018U, 0x018400U, + 0x202080U, 0x440100U, 0x020014U, 0x140080U, 0x080500U, 0x208800U, 0x020010U, 0x020011U, 0x020012U, 0x001240U, + 0x004003U, 0x004002U, 0x004001U, 0x004000U, 0x020028U, 0x004006U, 0x004005U, 0x004004U, 0x020024U, 0x00400AU, + 0x004009U, 0x004008U, 0x020020U, 0x020021U, 0x020022U, 0x00400CU, 0x240400U, 0x004012U, 0x004011U, 0x004010U, + 0x100140U, 0x000A80U, 0x089000U, 0x004014U, 0x00A200U, 0x011100U, 0x4000C0U, 0x004018U, 0x020030U, 0x680000U, + 0x050800U, 0x102400U, 0x000700U, 0x060020U, 0x201080U, 0x010810U, 0x402800U, 0x081040U, 0x044008U, 0x108100U, + 0x190000U, 0x408080U, 0x044004U, 0x002240U, 0x044002U, 0x200410U, 0x044000U, 0x044001U, 0x00C040U, 0x010802U, + 0x010801U, 0x010800U, 0x1000A0U, 0x200408U, 0x0A0200U, 0x010804U, 0x023000U, 0x200404U, 0x400120U, 0x010808U, + 0x200401U, 0x200400U, 0x044010U, 0x200402U, 0x060001U, 0x060000U, 0x08A000U, 0x060002U, 0x100090U, 0x060004U, + 0x010440U, 0x600200U, 0x200840U, 0x060008U, 0x400110U, 0x101400U, 0x009200U, 0x012100U, 0x044020U, 0x080880U, + 0x100084U, 0x060010U, 0x400108U, 0x010820U, 0x100080U, 0x100081U, 0x100082U, 0x007000U, 0x400102U, 0x084200U, + 0x400100U, 0x400101U, 0x100088U, 0x200420U, 0x400104U, 0x028040U, 0x00C010U, 0x081004U, 0x520000U, 0x002208U, + 0x081001U, 0x081000U, 0x010420U, 0x081002U, 0x200820U, 0x002202U, 0x002201U, 0x002200U, 0x020180U, 0x081008U, + 0x044040U, 0x002204U, 0x00C000U, 0x00C001U, 0x00C002U, 0x010840U, 0x00C004U, 0x081010U, 0x202100U, 0x440080U, + 0x00C008U, 0x140100U, 0x080480U, 0x002210U, 0x410200U, 0x200440U, 0x101800U, 0x028020U, 0x200808U, 0x060040U, + 0x010404U, 0x004180U, 0x010402U, 0x081020U, 0x010400U, 0x010401U, 0x200800U, 0x200801U, 0x200802U, 0x002220U, + 0x200804U, 0x504000U, 0x010408U, 0x028010U, 0x00C020U, 0x402400U, 0x041200U, 0x380000U, 0x1000C0U, 0x000B00U, + 0x010410U, 0x028008U, 0x200810U, 0x011080U, 0x400140U, 0x028004U, 0x0C2000U, 0x028002U, 0x028001U, 0x028000U, + 0x201002U, 0x408008U, 0x201000U, 0x201001U, 0x100030U, 0x014200U, 0x201004U, 0x022400U, 0x408001U, 0x408000U, + 0x201008U, 0x408002U, 0x020140U, 0x408004U, 0x044080U, 0x080820U, 0x100024U, 0x082100U, 0x201010U, 0x010880U, + 0x100020U, 0x100021U, 0x100022U, 0x440040U, 0x040A00U, 0x408010U, 0x080440U, 0x124000U, 0x100028U, 0x200480U, + 0x01A000U, 0x001300U, 0x100014U, 0x060080U, 0x201020U, 0x004140U, 0x100010U, 0x100011U, 0x100012U, 0x080808U, + 0x006400U, 0x408020U, 0x030200U, 0x080804U, 0x100018U, 0x080802U, 0x080801U, 0x080800U, 0x100004U, 0x100005U, + 0x100006U, 0x008600U, 0x100000U, 0x100001U, 0x100002U, 0x100003U, 0x10000CU, 0x011040U, 0x400180U, 0x242000U, + 0x100008U, 0x100009U, 0x10000AU, 0x080810U, 0x052000U, 0x100C00U, 0x201040U, 0x004120U, 0x020108U, 0x081080U, + 0x008A00U, 0x440010U, 0x020104U, 0x408040U, 0x080410U, 0x002280U, 0x020100U, 0x020101U, 0x020102U, 0x310000U, + 0x00C080U, 0x220200U, 0x080408U, 0x440004U, 0x100060U, 0x440002U, 0x440001U, 0x440000U, 0x080402U, 0x011020U, + 0x080400U, 0x080401U, 0x020110U, 0x006800U, 0x080404U, 0x440008U, 0x480200U, 0x004102U, 0x004101U, 0x004100U, + 0x100050U, 0x20A000U, 0x010480U, 0x004104U, 0x200880U, 0x011010U, 0x148000U, 0x004108U, 0x020120U, 0x040600U, + 0x403000U, 0x080840U, 0x100044U, 0x011008U, 0x022800U, 0x004110U, 0x100040U, 0x100041U, 0x100042U, 0x440020U, + 0x011001U, 0x011000U, 0x080420U, 0x011002U, 0x100048U, 0x011004U, 0x204200U, 0x028080U}; + +#define X22 0x00400000 /* vector representation of X^{22} */ +#define X11 0x00000800 /* vector representation of X^{11} */ +#define MASK12 0xfffff800 /* auxiliary vector for testing */ +#define GENPOL 0x00000c75 /* generator polinomial, g(x) */ + +static unsigned int get_syndrome_23127(unsigned int pattern) +/* + * Compute the syndrome corresponding to the given pattern, i.e., the + * remainder after dividing the pattern (when considering it as the vector + * representation of a polynomial) by the generator polynomial, GENPOL. + * In the program this pattern has several meanings: (1) pattern = infomation + * bits, when constructing the encoding table; (2) pattern = error pattern, + * when constructing the decoding table; and (3) pattern = received vector, to + * obtain its syndrome in decoding. + */ +{ + unsigned int aux = X22; + + if (pattern >= X11) { + while (pattern & MASK12) { + while (!(aux & pattern)) + aux = aux >> 1; + + pattern ^= (aux / X11) * GENPOL; + } + } + + return pattern; +} + +unsigned int CGolay24128::encode23127(unsigned int data) +{ + return ENCODING_TABLE_23127[data]; +} + +unsigned int CGolay24128::encode24128(unsigned int data) +{ + return ENCODING_TABLE_24128[data]; +} + +unsigned int CGolay24128::decode23127(unsigned int code) +{ + unsigned int syndrome = ::get_syndrome_23127(code); + unsigned int error_pattern = DECODING_TABLE_23127[syndrome]; + + code ^= error_pattern; + + return code >> 11; +} + +unsigned int CGolay24128::decode24128(unsigned int code) +{ + return decode23127(code >> 1); +} diff --git a/Golay24128.h b/Golay24128.h new file mode 100644 index 0000000..9630e50 --- /dev/null +++ b/Golay24128.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Golay24128_H +#define Golay24128_H + +class CGolay24128 { +public: + static unsigned int encode23127(unsigned int data); + static unsigned int encode24128(unsigned int data); + + static unsigned int decode23127(unsigned int code); + static unsigned int decode24128(unsigned int code); +}; + +#endif diff --git a/Hamming.cpp b/Hamming.cpp new file mode 100644 index 0000000..d291086 --- /dev/null +++ b/Hamming.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Hamming.h" + +// Hamming (15,11,3) check a boolean data array +bool CHamming::decode15113(bool* d) +{ + // Calculate the checksum this row should have + bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8]; + bool c1 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9]; + bool c2 = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10]; + bool c3 = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10]; + + unsigned char n = 0x00U; + n |= (c0 != d[11]) ? 0x01U : 0x00U; + n |= (c1 != d[12]) ? 0x02U : 0x00U; + n |= (c2 != d[13]) ? 0x04U : 0x00U; + n |= (c3 != d[14]) ? 0x08U : 0x00U; + + switch (n) { + // Parity bit errors + case 0x01U: d[11] = !d[11]; return true; + case 0x02U: d[12] = !d[12]; return true; + case 0x04U: d[13] = !d[13]; return true; + case 0x08U: d[14] = !d[14]; return true; + + // Data bit errors + case 0x09U: d[0] = !d[0]; return true; + case 0x0BU: d[1] = !d[1]; return true; + case 0x0FU: d[2] = !d[2]; return true; + case 0x07U: d[3] = !d[3]; return true; + case 0x0EU: d[4] = !d[4]; return true; + case 0x05U: d[5] = !d[5]; return true; + case 0x0AU: d[6] = !d[6]; return true; + case 0x0DU: d[7] = !d[7]; return true; + case 0x03U: d[8] = !d[8]; return true; + case 0x06U: d[9] = !d[9]; return true; + case 0x0CU: d[10] = !d[10]; return true; + + // No bit errors + default: return false; + } +} + +void CHamming::encode15113(bool* d) +{ + // Calculate the checksum this row should have + d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8]; + d[12] = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9]; + d[13] = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10]; + d[14] = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10]; +} + +// Hamming (13,9,3) check a boolean data array +bool CHamming::decode1393(bool* d) +{ + // Calculate the checksum this column should have + bool c0 = d[0] ^ d[1] ^ d[3] ^ d[5] ^ d[6]; + bool c1 = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7]; + bool c2 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8]; + bool c3 = d[0] ^ d[2] ^ d[4] ^ d[5] ^ d[8]; + + unsigned char n = 0x00U; + n |= (c0 != d[9]) ? 0x01U : 0x00U; + n |= (c1 != d[10]) ? 0x02U : 0x00U; + n |= (c2 != d[11]) ? 0x04U : 0x00U; + n |= (c3 != d[12]) ? 0x08U : 0x00U; + + switch (n) { + // Parity bit errors + case 0x01U: d[9] = !d[9]; return true; + case 0x02U: d[10] = !d[10]; return true; + case 0x04U: d[11] = !d[11]; return true; + case 0x08U: d[12] = !d[12]; return true; + + // Data bit erros + case 0x0FU: d[0] = !d[0]; return true; + case 0x07U: d[1] = !d[1]; return true; + case 0x0EU: d[2] = !d[2]; return true; + case 0x05U: d[3] = !d[3]; return true; + case 0x0AU: d[4] = !d[4]; return true; + case 0x0DU: d[5] = !d[5]; return true; + case 0x03U: d[6] = !d[6]; return true; + case 0x06U: d[7] = !d[7]; return true; + case 0x0CU: d[8] = !d[8]; return true; + + // No bit errors + default: return false; + } +} + +void CHamming::encode1393(bool* d) +{ + // Calculate the checksum this column should have + d[9] = d[0] ^ d[1] ^ d[3] ^ d[5] ^ d[6]; + d[10] = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7]; + d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8]; + d[12] = d[0] ^ d[2] ^ d[4] ^ d[5] ^ d[8]; +} + +// A Hamming (16,11,4) Check +bool CHamming::decode16114(bool* d) +{ + // Calculate the checksum this column should have + bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8]; + bool c1 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9]; + bool c2 = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10]; + bool c3 = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10]; + bool c4 = d[0] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[9] ^ d[10]; + + // Compare these with the actual bits + unsigned char n = 0x00U; + n |= (c0 != d[11]) ? 0x01U : 0x00U; + n |= (c1 != d[12]) ? 0x02U : 0x00U; + n |= (c2 != d[13]) ? 0x04U : 0x00U; + n |= (c3 != d[14]) ? 0x08U : 0x00U; + n |= (c4 != d[15]) ? 0x10U : 0x00U; + + switch (n) { + // Parity bit errors + case 0x01U: d[11] = !d[11]; return true; + case 0x02U: d[12] = !d[12]; return true; + case 0x04U: d[13] = !d[13]; return true; + case 0x08U: d[14] = !d[14]; return true; + case 0x10U: d[15] = !d[15]; return true; + + // Data bit errors + case 0x19U: d[0] = !d[0]; return true; + case 0x0BU: d[1] = !d[1]; return true; + case 0x1FU: d[2] = !d[2]; return true; + case 0x07U: d[3] = !d[3]; return true; + case 0x0EU: d[4] = !d[4]; return true; + case 0x15U: d[5] = !d[5]; return true; + case 0x1AU: d[6] = !d[6]; return true; + case 0x0DU: d[7] = !d[7]; return true; + case 0x13U: d[8] = !d[8]; return true; + case 0x16U: d[9] = !d[9]; return true; + case 0x1CU: d[10] = !d[10]; return true; + + // No bit errors + case 0x00U: return true; + + // Unrecoverable errors + default: return false; + } +} + +void CHamming::encode16114(bool* d) +{ + d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8]; + d[12] = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9]; + d[13] = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10]; + d[14] = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10]; + d[15] = d[0] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[9] ^ d[10]; +} + +// A Hamming (17,12,3) Check +bool CHamming::decode17123(bool* d) +{ + // Calculate the checksum this column should have + bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[6] ^ d[7] ^ d[9]; + bool c1 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[7] ^ d[8] ^ d[10]; + bool c2 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[8] ^ d[9] ^ d[11]; + bool c3 = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[10]; + bool c4 = d[0] ^ d[1] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[11]; + + // Compare these with the actual bits + unsigned char n = 0x00U; + n |= (c0 != d[12]) ? 0x01U : 0x00U; + n |= (c1 != d[13]) ? 0x02U : 0x00U; + n |= (c2 != d[14]) ? 0x04U : 0x00U; + n |= (c3 != d[15]) ? 0x08U : 0x00U; + n |= (c4 != d[16]) ? 0x10U : 0x00U; + + switch (n) { + // Parity bit errors + case 0x01U: d[12] = !d[12]; return true; + case 0x02U: d[13] = !d[13]; return true; + case 0x04U: d[14] = !d[14]; return true; + case 0x08U: d[15] = !d[15]; return true; + case 0x10U: d[16] = !d[16]; return true; + + // Data bit errors + case 0x1BU: d[0] = !d[0]; return true; + case 0x1FU: d[1] = !d[1]; return true; + case 0x17U: d[2] = !d[2]; return true; + case 0x07U: d[3] = !d[3]; return true; + case 0x0EU: d[4] = !d[4]; return true; + case 0x1CU: d[5] = !d[5]; return true; + case 0x11U: d[6] = !d[6]; return true; + case 0x0BU: d[7] = !d[7]; return true; + case 0x16U: d[8] = !d[8]; return true; + case 0x05U: d[9] = !d[9]; return true; + case 0x0AU: d[10] = !d[10]; return true; + case 0x14U: d[11] = !d[11]; return true; + + // No bit errors + case 0x00U: return true; + + // Unrecoverable errors + default: return false; + } +} + +void CHamming::encode17123(bool* d) +{ + d[12] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[6] ^ d[7] ^ d[9]; + d[13] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[7] ^ d[8] ^ d[10]; + d[14] = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[8] ^ d[9] ^ d[11]; + d[15] = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[10]; + d[16] = d[0] ^ d[1] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[11]; +} diff --git a/Hamming.h b/Hamming.h new file mode 100644 index 0000000..db0cad5 --- /dev/null +++ b/Hamming.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Hamming_H +#define Hamming_H + +class CHamming { +public: + static void encode15113(bool* d); + static bool decode15113(bool* d); + + static void encode1393(bool* d); + static bool decode1393(bool* d); + + static void encode16114(bool* d); + static bool decode16114(bool* d); + + static void encode17123(bool* d); + static bool decode17123(bool* d); +}; + +#endif diff --git a/HomebrewDMRIPSC.cpp b/HomebrewDMRIPSC.cpp new file mode 100644 index 0000000..7e8a2a6 --- /dev/null +++ b/HomebrewDMRIPSC.cpp @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "HomebrewDMRIPSC.h" +#include "StopWatch.h" +#include "SHA256.h" +#include "Utils.h" +#include "Log.h" + +#include + +const unsigned int BUFFER_LENGTH = 500U; + +const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 53U; + + +CHomebrewDMRIPSC::CHomebrewDMRIPSC(const std::string& address, unsigned int port, unsigned int id, const std::string& password, const char* software, const char* version, bool debug) : +m_address(), +m_port(port), +m_id(NULL), +m_password(password), +m_debug(debug), +m_software(software), +m_version(version), +m_socket(), +m_status(DISCONNECTED), +m_retryTimer(1000U, 10U), +m_timeoutTimer(1000U, 600U), +m_pingTimer(1000U, 5U), +m_buffer(NULL), +m_salt(NULL), +m_streamId(NULL), +m_rxData(1000U), +m_callsign(), +m_rxFrequency(0U), +m_txFrequency(0U), +m_power(0U), +m_colorCode(0U), +m_latitude(0.0F), +m_longitude(0.0F), +m_height(0), +m_location(), +m_description(), +m_url(), +m_beacon(false) +{ + assert(!address.empty()); + assert(port > 0U); + assert(id > 1000U); + assert(!password.empty()); + + m_address = CUDPSocket::lookup(address); + + m_buffer = new unsigned char[BUFFER_LENGTH]; + m_salt = new unsigned char[sizeof(uint32_t)]; + m_id = new uint8_t[4U]; + m_streamId = new uint32_t[2U]; + + m_streamId[0U] = 0x00U; + m_streamId[1U] = 0x00U; + + m_id[0U] = id >> 24; + m_id[1U] = id >> 16; + m_id[2U] = id >> 8; + m_id[3U] = id >> 0; + + CStopWatch stopWatch; + ::srand(stopWatch.start()); +} + +CHomebrewDMRIPSC::~CHomebrewDMRIPSC() +{ + delete[] m_buffer; + delete[] m_salt; + delete[] m_streamId; + delete[] m_id; +} + +void CHomebrewDMRIPSC::setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url) +{ + m_callsign = callsign; + m_rxFrequency = rxFrequency; + m_txFrequency = txFrequency; + m_power = power; + m_colorCode = colorCode; + m_latitude = latitude; + m_longitude = longitude; + m_height = height; + m_location = location; + m_description = description; + m_url = url; +} + +bool CHomebrewDMRIPSC::open() +{ + LogMessage("Opening DMR IPSC"); + + bool ret = m_socket.open(); + if (!ret) + return false; + + ret = writeLogin(); + if (!ret) { + m_socket.close(); + return false; + } + + m_status = WAITING_LOGIN; + m_timeoutTimer.start(); + m_retryTimer.start(); + + return true; +} + +bool CHomebrewDMRIPSC::read(CDMRData& data) +{ + if (m_status != RUNNING) + return false; + + if (m_rxData.isEmpty()) + return false; + + unsigned char length = 0U; + + m_rxData.getData(&length, 1U); + m_rxData.getData(m_buffer, length); + + // Is this a data packet? + if (::memcmp(m_buffer, "DMRD", 4U) != 0) + return false; + + unsigned char seqNo = m_buffer[4U]; + + unsigned int srcId = (m_buffer[5U] << 16) | (m_buffer[6U] << 8) | (m_buffer[7U] << 0); + + unsigned int dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0); + + unsigned int slotNo = (m_buffer[15U] & 0x80U) == 0x80U ? 2U : 1U; + + FLCO flco = (m_buffer[15U] & 0x40U) == 0x40U ? FLCO_USER_USER : FLCO_GROUP; + + data.setSeqNo(seqNo); + data.setSlotNo(slotNo); + data.setSrcId(srcId); + data.setDstId(dstId); + data.setFLCO(flco); + + bool dataSync = (m_buffer[15U] & 0x20U) == 0x20U; + bool voiceSync = (m_buffer[15U] & 0x10U) == 0x10U; + + if (dataSync) { + unsigned char dataType = m_buffer[15U] & 0x0FU; + data.setData(m_buffer + 20U); + data.setDataType(dataType); + data.setN(0U); + } else if (voiceSync) { + data.setData(m_buffer + 20U); + data.setDataType(DT_VOICE_SYNC); + data.setN(0U); + } else { + unsigned char n = m_buffer[15U] & 0x0FU; + data.setData(m_buffer + 20U); + data.setDataType(DT_VOICE); + data.setN(n); + } + + return true; +} + +bool CHomebrewDMRIPSC::write(const CDMRData& data) +{ + if (m_status != RUNNING) + return false; + + unsigned char buffer[HOMEBREW_DATA_PACKET_LENGTH]; + ::memset(buffer, 0x00U, HOMEBREW_DATA_PACKET_LENGTH); + + buffer[0U] = 'D'; + buffer[1U] = 'M'; + buffer[2U] = 'R'; + buffer[3U] = 'D'; + + unsigned int srcId = data.getSrcId(); + buffer[5U] = srcId >> 16; + buffer[6U] = srcId >> 8; + buffer[7U] = srcId >> 0; + + unsigned int dstId = data.getDstId(); + buffer[8U] = dstId >> 16; + buffer[9U] = dstId >> 8; + buffer[10U] = dstId >> 0; + + ::memcpy(buffer + 11U, m_id, 4U); + + unsigned int slotNo = data.getSlotNo(); + buffer[15U] = slotNo == 1U ? 0x00U : 0x80U; + + FLCO flco = data.getFLCO(); + buffer[15U] |= flco == FLCO_GROUP ? 0x00U : 0x40U; + + unsigned int slotIndex = slotNo - 1U; + + unsigned char dataType = data.getDataType(); + if (dataType == DT_VOICE_SYNC) { + buffer[15U] |= 0x10U; + } else if (dataType == DT_VOICE) { + buffer[15U] |= data.getN(); + } else { + if ((dataType == DT_VOICE_LC_HEADER || dataType == DT_DATA_HEADER) && data.getSeqNo() == 0U) + m_streamId[slotIndex] = ::rand() + 1U; + + buffer[15U] |= (0x20U | dataType); + } + + buffer[4U] = data.getSeqNo(); + + ::memcpy(buffer + 16U, m_streamId + slotIndex, 4U); + + data.getData(buffer + 20U); + + return write(buffer, HOMEBREW_DATA_PACKET_LENGTH); +} + +void CHomebrewDMRIPSC::close() +{ + LogMessage("Closing DMR IPSC"); + + unsigned char buffer[9U]; + ::memcpy(buffer + 0U, "RPTCL", 5U); + ::memcpy(buffer + 5U, m_id, 4U); + write(buffer, 9U); + + m_socket.close(); +} + +void CHomebrewDMRIPSC::clock(unsigned int ms) +{ + in_addr address; + unsigned int port; + int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, port); + + if (m_debug && length > 0) + CUtils::dump(1U, "IPSC Received", m_buffer, length); + + if (length > 0 && m_address.s_addr == address.s_addr && m_port == port) { + if (::memcmp(m_buffer, "DMRD", 4U) == 0) { + unsigned char len = length; + m_rxData.addData(&len, 1U); + m_rxData.addData(m_buffer, len); + } else if (::memcmp(m_buffer, "MSTNAK", 6U) == 0) { + LogError("Login to the master has failed"); + m_status = DISCONNECTED; // XXX + m_timeoutTimer.stop(); + m_retryTimer.stop(); + } else if (::memcmp(m_buffer, "RPTACK", 6U) == 0) { + switch (m_status) { + case WAITING_LOGIN: + ::memcpy(m_salt, m_buffer + 6U, sizeof(uint32_t)); + writeAuthorisation(); + m_status = WAITING_AUTHORISATION; + m_timeoutTimer.start(); + m_retryTimer.start(); + break; + case WAITING_AUTHORISATION: + writeConfig(); + m_status = WAITING_CONFIG; + m_timeoutTimer.start(); + m_retryTimer.start(); + break; + case WAITING_CONFIG: + LogMessage("Logged into the master succesfully"); + m_status = RUNNING; + m_timeoutTimer.start(); + m_retryTimer.stop(); + m_pingTimer.start(); + break; + default: + break; + } + } else if (::memcmp(m_buffer, "MSTCL", 5U) == 0) { + LogError("Master is closing down"); + m_status = DISCONNECTED; // XXX + m_timeoutTimer.stop(); + m_retryTimer.stop(); + } else if (::memcmp(m_buffer, "MSTPONG", 7U) == 0) { + m_timeoutTimer.start(); + } else if (::memcmp(m_buffer, "RPTSBKN", 7U) == 0) { + m_beacon = true; + } else { + CUtils::dump("Unknown packet from the master", m_buffer, length); + } + } + + if (m_status != RUNNING) { + m_retryTimer.clock(ms); + if (m_retryTimer.isRunning() && m_retryTimer.hasExpired()) { + switch (m_status) { + case WAITING_LOGIN: + writeLogin(); + break; + case WAITING_AUTHORISATION: + writeAuthorisation(); + break; + case WAITING_CONFIG: + writeConfig(); + break; + default: + break; + } + + m_retryTimer.start(); + } + } else { + m_pingTimer.clock(ms); + if (m_pingTimer.isRunning() && m_pingTimer.hasExpired()) { + writePing(); + m_pingTimer.start(); + } + } + + m_timeoutTimer.clock(ms); + if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) { + LogError("Connection to the master has timed out"); + m_status = DISCONNECTED; + m_timeoutTimer.stop(); + m_retryTimer.stop(); + } +} + +bool CHomebrewDMRIPSC::writeLogin() +{ + unsigned char buffer[8U]; + + ::memcpy(buffer + 0U, "RPTL", 4U); + ::memcpy(buffer + 4U, m_id, 4U); + + return write(buffer, 8U); +} + +bool CHomebrewDMRIPSC::writeAuthorisation() +{ + unsigned int size = m_password.size(); + + unsigned char* in = new unsigned char[size + sizeof(uint32_t)]; + ::memcpy(in, m_salt, sizeof(uint32_t)); + for (unsigned int i = 0U; i < size; i++) + in[i + sizeof(uint32_t)] = m_password.at(i); + + unsigned char out[40U]; + ::memcpy(out + 0U, "RPTK", 4U); + ::memcpy(out + 4U, m_id, 4U); + + CSHA256 sha256; + sha256.buffer(in, size + sizeof(uint32_t), out + 8U); + + delete[] in; + + return write(out, 40U); +} + +bool CHomebrewDMRIPSC::writeConfig() +{ + char buffer[400U]; + + ::memcpy(buffer + 0U, "RPTC", 4U); + ::memcpy(buffer + 4U, m_id, 4U); + + ::sprintf(buffer + 8U, "%-8.8s%09u%09u%02u%02u%-08f%-09f%03d%-20.20s%-20.20s%-124.124s%-40.40s%-40.40s", m_callsign.c_str(), + m_rxFrequency, m_txFrequency, m_power, m_colorCode, m_latitude, m_longitude, m_height, m_location.c_str(), + m_description.c_str(), m_url.c_str(), m_software, m_version); + + return write((unsigned char*)buffer, 302U); +} + +bool CHomebrewDMRIPSC::writePing() +{ + unsigned char buffer[11U]; + + ::memcpy(buffer + 0U, "RPTPING", 7U); + ::memcpy(buffer + 7U, m_id, 4U); + + return write(buffer, 11U); +} + +bool CHomebrewDMRIPSC::wantsBeacon() +{ + bool beacon = m_beacon; + + m_beacon = false; + + return beacon; +} + +bool CHomebrewDMRIPSC::write(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (m_debug) + CUtils::dump(1U, "IPSC Transmitted", data, length); + + return m_socket.write(data, length, m_address, m_port); +} diff --git a/HomebrewDMRIPSC.h b/HomebrewDMRIPSC.h new file mode 100644 index 0000000..85aa0e1 --- /dev/null +++ b/HomebrewDMRIPSC.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(HOMEBREWDMRIPSC_H) +#define HOMEBREWDMRIPSC_H + +#include "UDPSocket.h" +#include "Timer.h" +#include "RingBuffer.h" +#include "DMRData.h" + +#include + +#if defined(_WIN32) || defined(_WIN64) +typedef unsigned int uint32_t; +typedef unsigned short uint16_t; +typedef unsigned char uint8_t; +#else +#include +#endif + +class CHomebrewDMRIPSC +{ +public: + CHomebrewDMRIPSC(const std::string& address, unsigned int port, unsigned int id, const std::string& password, const char* software, const char* version, bool debug); + ~CHomebrewDMRIPSC(); + + void setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url); + + bool open(); + + bool read(CDMRData& data); + + bool write(const CDMRData& data); + + bool wantsBeacon(); + + void clock(unsigned int ms); + + void close(); + +private: + in_addr m_address; + unsigned int m_port; + uint8_t* m_id; + std::string m_password; + bool m_debug; + const char* m_software; + const char* m_version; + CUDPSocket m_socket; + + enum STATUS { + DISCONNECTED, + WAITING_LOGIN, + WAITING_AUTHORISATION, + WAITING_CONFIG, + RUNNING + }; + + STATUS m_status; + CTimer m_retryTimer; + CTimer m_timeoutTimer; + CTimer m_pingTimer; + unsigned char* m_buffer; + unsigned char* m_salt; + uint32_t* m_streamId; + + CRingBuffer m_rxData; + + std::string m_callsign; + unsigned int m_rxFrequency; + unsigned int m_txFrequency; + unsigned int m_power; + unsigned int m_colorCode; + float m_latitude; + float m_longitude; + int m_height; + std::string m_location; + std::string m_description; + std::string m_url; + + bool m_beacon; + + bool writeLogin(); + bool writeAuthorisation(); + bool writeConfig(); + bool writePing(); + + bool write(const unsigned char* data, unsigned int length); +}; + +#endif diff --git a/Images/ALL.bmp b/Images/ALL.bmp new file mode 100644 index 0000000..eb3f4fd Binary files /dev/null and b/Images/ALL.bmp differ diff --git a/Images/ALL_sm.bmp b/Images/ALL_sm.bmp new file mode 100644 index 0000000..4fe3269 Binary files /dev/null and b/Images/ALL_sm.bmp differ diff --git a/Images/DMR.bmp b/Images/DMR.bmp new file mode 100644 index 0000000..e537438 Binary files /dev/null and b/Images/DMR.bmp differ diff --git a/Images/DMR_sm.bmp b/Images/DMR_sm.bmp new file mode 100644 index 0000000..ff0bfe2 Binary files /dev/null and b/Images/DMR_sm.bmp differ diff --git a/Images/DStar.bmp b/Images/DStar.bmp new file mode 100644 index 0000000..abb23f6 Binary files /dev/null and b/Images/DStar.bmp differ diff --git a/Images/DStar_sm.bmp b/Images/DStar_sm.bmp new file mode 100644 index 0000000..cfe79f5 Binary files /dev/null and b/Images/DStar_sm.bmp differ diff --git a/Images/MMDVM.bmp b/Images/MMDVM.bmp new file mode 100644 index 0000000..5f740af Binary files /dev/null and b/Images/MMDVM.bmp differ diff --git a/Images/MMDVM_sm.bmp b/Images/MMDVM_sm.bmp new file mode 100644 index 0000000..213ac28 Binary files /dev/null and b/Images/MMDVM_sm.bmp differ diff --git a/Images/P25.bmp b/Images/P25.bmp new file mode 100644 index 0000000..c4a01e0 Binary files /dev/null and b/Images/P25.bmp differ diff --git a/Images/P25_sm.bmp b/Images/P25_sm.bmp new file mode 100644 index 0000000..938e83a Binary files /dev/null and b/Images/P25_sm.bmp differ diff --git a/Images/Tetra.bmp b/Images/Tetra.bmp new file mode 100644 index 0000000..68950d5 Binary files /dev/null and b/Images/Tetra.bmp differ diff --git a/Images/Tetra_sm.bmp b/Images/Tetra_sm.bmp new file mode 100644 index 0000000..846d7ef Binary files /dev/null and b/Images/Tetra_sm.bmp differ diff --git a/Images/YSF.bmp b/Images/YSF.bmp new file mode 100644 index 0000000..8a1c0af Binary files /dev/null and b/Images/YSF.bmp differ diff --git a/Images/YSF_sm.bmp b/Images/YSF_sm.bmp new file mode 100644 index 0000000..b12c98c Binary files /dev/null and b/Images/YSF_sm.bmp differ diff --git a/Images/dPMR.bmp b/Images/dPMR.bmp new file mode 100644 index 0000000..85da5d0 Binary files /dev/null and b/Images/dPMR.bmp differ diff --git a/Images/dPMR_sm.bmp b/Images/dPMR_sm.bmp new file mode 100644 index 0000000..f57a56a Binary files /dev/null and b/Images/dPMR_sm.bmp differ diff --git a/LC.cpp b/LC.cpp new file mode 100644 index 0000000..4d28033 --- /dev/null +++ b/LC.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "LC.h" + +#include "Utils.h" + +#include +#include + +CLC::CLC(FLCO flco, unsigned int srcId, unsigned int dstId) : +m_PF(false), +m_FLCO(flco), +m_FID(0U), +m_srcId(srcId), +m_dstId(dstId) +{ +} + +CLC::CLC(const unsigned char* bytes) : +m_PF(false), +m_FLCO(FLCO_GROUP), +m_FID(0U), +m_srcId(0U), +m_dstId(0U) +{ + assert(bytes != NULL); + + m_PF = (bytes[0U] & 0x80U) == 0x80U; + + m_FLCO = FLCO(bytes[0U] & 0x3FU); + + m_FID = bytes[1U]; + + m_dstId = bytes[3U] << 16 | bytes[4U] << 8 | bytes[5U]; + m_srcId = bytes[6U] << 16 | bytes[7U] << 8 | bytes[8U]; +} + +CLC::CLC(const bool* bits) : +m_PF(false), +m_FLCO(FLCO_GROUP), +m_FID(0U), +m_srcId(0U), +m_dstId(0U) +{ + assert(bits != NULL); + + m_PF = bits[0U]; + + unsigned char temp1, temp2; + CUtils::bitsToByteBE(bits + 0U, temp1); + m_FLCO = FLCO(temp1 & 0x3FU); + + CUtils::bitsToByteBE(bits + 8U, temp2); + m_FID = temp2; + + unsigned char d1, d2, d3; + CUtils::bitsToByteBE(bits + 24U, d1); + CUtils::bitsToByteBE(bits + 32U, d2); + CUtils::bitsToByteBE(bits + 40U, d3); + + unsigned char s1, s2, s3; + CUtils::bitsToByteBE(bits + 48U, s1); + CUtils::bitsToByteBE(bits + 56U, s2); + CUtils::bitsToByteBE(bits + 64U, s3); + + m_srcId = s1 << 16 | s2 << 8 | s3; + m_dstId = d1 << 16 | d2 << 8 | d3; +} + +CLC::CLC() : +m_PF(false), +m_FLCO(FLCO_GROUP), +m_FID(0U), +m_srcId(0U), +m_dstId(0U) +{ +} + +CLC::~CLC() +{ +} + +void CLC::getData(unsigned char* bytes) const +{ + assert(bytes != NULL); + + bytes[0U] = (unsigned char)m_FLCO; + + if (m_PF) + bytes[0U] |= 0x80U; + + bytes[1U] = m_FID; + + bytes[3U] = m_dstId >> 16; + bytes[4U] = m_dstId >> 8; + bytes[5U] = m_dstId >> 0; + + bytes[6U] = m_srcId >> 16; + bytes[7U] = m_srcId >> 8; + bytes[8U] = m_srcId >> 0; +} + +void CLC::getData(bool* bits) const +{ + unsigned char bytes[9U]; + getData(bytes); + + CUtils::byteToBitsBE(bytes[0U], bits + 0U); + CUtils::byteToBitsBE(bytes[1U], bits + 8U); + CUtils::byteToBitsBE(bytes[2U], bits + 16U); + CUtils::byteToBitsBE(bytes[3U], bits + 24U); + CUtils::byteToBitsBE(bytes[4U], bits + 32U); + CUtils::byteToBitsBE(bytes[5U], bits + 40U); + CUtils::byteToBitsBE(bytes[6U], bits + 48U); + CUtils::byteToBitsBE(bytes[7U], bits + 56U); + CUtils::byteToBitsBE(bytes[8U], bits + 64U); +} + +bool CLC::getPF() const +{ + return m_PF; +} + +void CLC::setPF(bool pf) +{ + m_PF = pf; +} + +FLCO CLC::getFLCO() const +{ + return m_FLCO; +} + +void CLC::setFLCO(FLCO flco) +{ + m_FLCO = flco; +} + +unsigned char CLC::getFID() const +{ + return m_FID; +} + +void CLC::setFID(unsigned char fid) +{ + m_FID = fid; +} + +unsigned int CLC::getSrcId() const +{ + return m_srcId; +} + +void CLC::setSrcId(unsigned int id) +{ + m_srcId = id; +} + +unsigned int CLC::getDstId() const +{ + return m_dstId; +} + +void CLC::setDstId(unsigned int id) +{ + m_dstId = id; +} diff --git a/LC.h b/LC.h new file mode 100644 index 0000000..ca31914 --- /dev/null +++ b/LC.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(LC_H) +#define LC_H + +#include "DMRDefines.h" + +class CLC +{ +public: + CLC(FLCO flco, unsigned int srcId, unsigned int dstId); + CLC(const unsigned char* bytes); + CLC(const bool* bits); + CLC(); + ~CLC(); + + void getData(unsigned char* bytes) const; + void getData(bool* bits) const; + + bool getPF() const; + void setPF(bool pf); + + FLCO getFLCO() const; + void setFLCO(FLCO flco); + + unsigned char getFID() const; + void setFID(unsigned char fid); + + unsigned int getSrcId() const; + void setSrcId(unsigned int id); + + unsigned int getDstId() const; + void setDstId(unsigned int id); + +private: + bool m_PF; + FLCO m_FLCO; + unsigned char m_FID; + unsigned int m_srcId; + unsigned int m_dstId; +}; + +#endif + diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/LICENCE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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; either version 2 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Log.cpp b/Log.cpp new file mode 100644 index 0000000..19a6198 --- /dev/null +++ b/Log.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Log.h" + +#include +#include +#include +#include +#include + +static std::string m_path; +static std::string m_root; + +static FILE* m_fpLog = NULL; + +static bool m_display = true; + +static unsigned int m_level = 2U; + +static struct tm m_tm; + +static char LEVELS[] = " DMIWEF"; + +static bool LogOpen() +{ + time_t now; + ::time(&now); + + struct tm* tm = ::gmtime(&now); + + if (tm->tm_mday == m_tm.tm_mday && tm->tm_mon == m_tm.tm_mon && tm->tm_year == m_tm.tm_year) { + if (m_fpLog != NULL) + return true; + } else { + if (m_fpLog != NULL) + ::fclose(m_fpLog); + } + + char filename[50U]; +#if defined(WIN32) + ::sprintf(filename, "%s\\%s-%04d-%02d-%02d.log", m_path.c_str(), m_root.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +#else + ::sprintf(filename, "%s/%s-%04d-%02d-%02d.log", m_path.c_str(), m_root.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +#endif + + m_fpLog = ::fopen(filename, "a+t"); + m_tm = *tm; + + return m_fpLog != NULL; +} + +bool LogInitialise(const std::string& path, const std::string& root, bool display) +{ + m_path = path; + m_root = root; + m_display = display; + return ::LogOpen(); +} + +void LogFinalise() +{ + if (m_fpLog != NULL) + ::fclose(m_fpLog); +} + +void LogSetLevel(unsigned int level) +{ + m_level = level; +} + +void Log(unsigned int level, const char* fmt, ...) +{ + assert(level < 7U); + assert(fmt != NULL); + + if (level < m_level) + return; + + bool ret = ::LogOpen(); + if (!ret) + return; + + time_t now; + ::time(&now); + + struct tm* tm = ::gmtime(&now); + + ::fprintf(m_fpLog, "%c: %04d-%02d-%02d %02d:%02d:%02d ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + if (m_display) + ::fprintf(stdout, "%c: %04d-%02d-%02d %02d:%02d:%02d ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + va_list vl; + va_start(vl, fmt); + vfprintf(m_fpLog, fmt, vl); + if (m_display) + vfprintf(stdout, fmt, vl); + va_end(vl); + + ::fprintf(m_fpLog, "\n"); + ::fflush(m_fpLog); + + if (m_display) { + ::fprintf(stdout, "\n"); + ::fflush(stdout); + } + + if (level == 6U) { // Fatal + ::fclose(m_fpLog); + exit(1); + } +} diff --git a/Log.h b/Log.h new file mode 100644 index 0000000..a1dc869 --- /dev/null +++ b/Log.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(LOG_H) +#define LOG_H + +#include + +#define LogDebug(fmt, ...) Log(1U, fmt, ##__VA_ARGS__) +#define LogMessage(fmt, ...) Log(2U, fmt, ##__VA_ARGS__) +#define LogInfo(fmt, ...) Log(3U, fmt, ##__VA_ARGS__) +#define LogWarning(fmt, ...) Log(4U, fmt, ##__VA_ARGS__) +#define LogError(fmt, ...) Log(5U, fmt, ##__VA_ARGS__) +#define LogFatal(fmt, ...) Log(6U, fmt, ##__VA_ARGS__) + +extern void Log(unsigned int level, const char* fmt, ...); + +extern bool LogInitialise(const std::string& path, const std::string& root, bool display); +extern void LogFinalise(); + +extern void LogSetLevel(unsigned int level); + +#endif diff --git a/MMDVM.ini b/MMDVM.ini new file mode 100644 index 0000000..21e3220 --- /dev/null +++ b/MMDVM.ini @@ -0,0 +1,70 @@ +[General] +Callsign=G9BF +Timeout=120 +Duplex=1 +ModeHang=10 +Display=None + +[Info] +RXFrequency=435000000 +TXFrequency=435000000 +Power=1 +Latitude=51.0 +Longitude=-1.0 +Height=0 +Location=Nowhere +Description=Multi-Mode Repeater +URL=www.google.co.uk + +[Log] +# Logging levels, 0=No logging +Level=1 +Path=. +Root=MMDVM +Display=1 + +[Modem] +# Port=/dev/ttyACM0 +Port=\\.\COM3 +TXInvert=1 +RXInvert=0 +PTTInvert=0 +TXDelay=100 +RXLevel=50 +TXLevel=50 +Debug=0 + +[D-Star] +Enable=1 +Module=C + +[DMR] +Enable=1 +Id=123456 +ColorCode=1 + +[System Fusion] +Enable=1 + +[D-Star Network] +Enable=1 +GatewayAddress=127.0.0.1 +GatewayPort=20010 +LocalPort=20011 +Debug=0 + +[DMR Network] +Enable=1 +Address=44.131.4.1 +Port=62031 +Password=PASSWORD +Debug=1 + +[System Fusion Network] +Enable=0 +Address=44.131.4.1 +Port=32768 +Debug=1 + +[TFT Serial] +Port=/dev/ttyAMA0 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp new file mode 100644 index 0000000..b21c1f4 --- /dev/null +++ b/MMDVMHost.cpp @@ -0,0 +1,473 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "MMDVMHost.h" +#include "Log.h" +#include "Version.h" +#include "StopWatch.h" +#include "Defines.h" +#include "DMRControl.h" +#include "TFTSerial.h" +#include "NullDisplay.h" + +#include "DStarEcho.h" +#include "YSFEcho.h" + +#include + +#if !defined(WIN32) +#include +#include +#endif + +static bool m_killed = false; + +#if !defined(WIN32) +static void sigHandler(int) +{ + m_killed = true; +} +#endif + +const char* HEADER1 = "This software is for use on amateur radio networks only."; +const char* HEADER2 = "Its use on commercial networks is strictly prohibited."; +const char* HEADER3 = "Copyright(C) 2015, 2016 by Jonathan Naylor, G4KLX"; + +int main(int argc, char** argv) +{ + if (argc == 1) { + ::fprintf(stderr, "Usage: MMDVMHost \n"); + return 1; + } + +#if !defined(WIN32) + ::signal(SIGUSR1, sigHandler); +#endif + + CMMDVMHost* host = new CMMDVMHost(std::string(argv[1])); + int ret2 = host->run(); + + delete host; + + ::LogFinalise(); + + return ret2; +} + +CMMDVMHost::CMMDVMHost(const std::string& confFile) : +m_conf(confFile), +m_modem(NULL), +m_dmrNetwork(NULL), +m_display(NULL), +m_dstarEnabled(false), +m_dmrEnabled(false), +m_ysfEnabled(false) +{ +} + +CMMDVMHost::~CMMDVMHost() +{ +} + +int CMMDVMHost::run() +{ + bool ret = m_conf.read(); + if (!ret) { + ::fprintf(stderr, "MMDVMHost: cannot read the .ini file\n"); + return 1; + } + + ret = ::LogInitialise(m_conf.getLogPath(), m_conf.getLogRoot(), m_conf.getLogDisplay()); + if (!ret) { + ::fprintf(stderr, "MMDVMHost: unable to open the log file\n"); + return 1; + } + + ::LogSetLevel(m_conf.getLogLevel()); + + LogInfo(HEADER1); + LogInfo(HEADER2); + LogInfo(HEADER3); + + LogMessage("MMDVMHost-%s is starting", VERSION); + + readParams(); + + ret = createModem(); + if (!ret) + return 1; + + createDisplay(); + + if (m_dmrEnabled) { + ret = createDMRNetwork(); + if (!ret) + return 1; + } + + CStopWatch stopWatch; + stopWatch.start(); + + CDStarEcho* dstar = NULL; + if (m_dstarEnabled) + dstar = new CDStarEcho(2U, 10000U); + + CDMRControl* dmr = NULL; + if (m_dmrEnabled) { + unsigned int id = m_conf.getDMRId(); + unsigned int colorCode = m_conf.getDMRColorCode(); + unsigned int timeout = m_conf.getTimeout(); + + LogInfo("DMR Parameters"); + LogInfo(" Id: %u", id); + LogInfo(" Color Code: %u", colorCode); + LogInfo(" Timeout: %us", timeout); + + dmr = new CDMRControl(id, colorCode, timeout, m_modem, m_dmrNetwork, m_display); + } + + CYSFEcho* ysf = NULL; + if (m_ysfEnabled) + ysf = new CYSFEcho(2U, 10000U); + + unsigned char mode = MODE_IDLE; + CTimer modeTimer(1000U, m_conf.getModeHang()); + + m_display->setIdle(); + + while (!m_killed) { + unsigned char data[200U]; + unsigned int len; + bool ret; + + len = m_modem->readDStarData(data); + if (dstar != NULL && len > 0U) { + if (mode == MODE_IDLE && (data[0U] == TAG_HEADER || data[0U] == TAG_DATA)) { + LogMessage("Mode set to D-Star"); + mode = MODE_DSTAR; + m_display->setDStar(); + m_modem->setMode(MODE_DSTAR); + modeTimer.start(); + } + if (mode != MODE_DSTAR) { + LogWarning("D-Star data received when in mode %u", mode); + } else { + if (data[0U] == TAG_HEADER || data[0U] == TAG_DATA || data[0U] == TAG_EOT) { + dstar->writeData(data, len); + modeTimer.start(); + } + } + } + + len = m_modem->readDMRData1(data); + if (dmr != NULL && len > 0U) { + if (mode == MODE_IDLE) { + bool ret = dmr->processWakeup(data); + if (ret) { + LogMessage("Mode set to DMR"); + mode = MODE_DMR; + m_display->setDMR(); + // This sets the mode to DMR within the modem + m_modem->writeDMRStart(true); + modeTimer.start(); + } + } else if (mode == MODE_DMR) { + dmr->writeModemSlot1(data); + modeTimer.start(); + } else { + LogWarning("DMR data received when in mode %u", mode); + } + } + + len = m_modem->readDMRData2(data); + if (dmr != NULL && len > 0U) { + if (mode == MODE_IDLE) { + bool ret = dmr->processWakeup(data); + if (ret) { + LogMessage("Mode set to DMR"); + mode = MODE_DMR; + m_display->setDMR(); + // This sets the mode to DMR within the modem + m_modem->writeDMRStart(true); + modeTimer.start(); + } + } else if (mode == MODE_DMR) { + dmr->writeModemSlot2(data); + modeTimer.start(); + } else { + LogWarning("DMR data received when in mode %u", mode); + } + } + + len = m_modem->readYSFData(data); + if (ysf != NULL && len > 0U) { + if (mode == MODE_IDLE && data[0U] == TAG_DATA) { + LogMessage("Mode set to System Fusion"); + mode = MODE_YSF; + m_display->setFusion(); + m_modem->setMode(MODE_YSF); + modeTimer.start(); + } + if (mode != MODE_YSF) { + LogWarning("System Fusion data received when in mode %u", mode); + } else { + if (data[0U] == TAG_DATA) { + data[1U] = 0x00U; // FICH digest + ysf->writeData(data, len); + modeTimer.start(); + } + } + } + + if (modeTimer.isRunning() && modeTimer.hasExpired()) { + LogMessage("Mode set to Idle"); + + if (mode == MODE_DMR) + m_modem->writeDMRStart(false); + + mode = MODE_IDLE; + m_display->setIdle(); + m_modem->setMode(MODE_IDLE); + modeTimer.stop(); + } + + if (dstar != NULL) { + ret = dstar->hasData(); + if (ret) { + ret = m_modem->hasDStarSpace(); + if (ret) { + len = dstar->readData(data); + if (mode != MODE_DSTAR) { + LogWarning("D-Star echo data received when in mode %u", mode); + } else { + m_modem->writeDStarData(data, len); + modeTimer.start(); + } + } + } + } + + if (dmr != NULL) { + ret = m_modem->hasDMRSpace1(); + if (ret) { + len = dmr->readModemSlot1(data); + if (len > 0U && mode == MODE_IDLE) { + m_display->setDMR(); + mode = MODE_DMR; + } + if (len > 0U && mode == MODE_DMR) { + m_modem->writeDMRData1(data, len); + modeTimer.start(); + } + } + + ret = m_modem->hasDMRSpace2(); + if (ret) { + len = dmr->readModemSlot2(data); + if (len > 0U && mode == MODE_IDLE) { + m_display->setDMR(); + mode = MODE_DMR; + } + if (len > 0U && mode == MODE_DMR) { + m_modem->writeDMRData2(data, len); + modeTimer.start(); + } + } + } + + if (ysf != NULL) { + ret = ysf->hasData(); + if (ret) { + ret = m_modem->hasYSFSpace(); + if (ret) { + len = ysf->readData(data); + if (mode != MODE_YSF) { + LogWarning("System Fusion echo data received when in mode %u", mode); + } else { + m_modem->writeYSFData(data, len); + modeTimer.start(); + } + } + } + } + + unsigned int ms = stopWatch.elapsed(); + m_modem->clock(ms); + modeTimer.clock(ms); + if (dstar != NULL) + dstar->clock(ms); + if (dmr != NULL) + dmr->clock(ms); + if (ysf != NULL) + ysf->clock(ms); + stopWatch.start(); + + if (ms < 5U) { +#if defined(WIN32) + ::Sleep(5UL); // 5ms +#else + ::usleep(5000); // 5ms +#endif + } + } + + LogMessage("MMDVMHost is exiting on receipt of SIGHUP1"); + + m_display->setIdle(); + + m_modem->close(); + delete m_modem; + + m_display->close(); + delete m_display; + + if (m_dmrNetwork != NULL) { + m_dmrNetwork->close(); + delete m_dmrNetwork; + } + + delete dstar; + delete dmr; + delete ysf; + + return 0; +} + +bool CMMDVMHost::createModem() +{ + std::string port = m_conf.getModemPort(); + bool rxInvert = m_conf.getModemRXInvert(); + bool txInvert = m_conf.getModemTXInvert(); + bool pttInvert = m_conf.getModemPTTInvert(); + unsigned int txDelay = m_conf.getModemTXDelay(); + unsigned int rxLevel = m_conf.getModemRXLevel(); + unsigned int txLevel = m_conf.getModemTXLevel(); + bool debug = m_conf.getModemDebug(); + unsigned int colorCode = m_conf.getDMRColorCode(); + + LogInfo("Modem Parameters"); + LogInfo(" Port: %s", port.c_str()); + LogInfo(" RX Invert: %s", rxInvert ? "yes" : "no"); + LogInfo(" TX Invert: %s", txInvert ? "yes" : "no"); + LogInfo(" PTT Invert: %s", pttInvert ? "yes" : "no"); + LogInfo(" TX Delay: %u", txDelay); + LogInfo(" RX Level: %u", rxLevel); + LogInfo(" TX Level: %u", txLevel); + + m_modem = new CModem(port, rxInvert, txInvert, pttInvert, txDelay, rxLevel, txLevel, debug); + m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled); + m_modem->setDMRParams(colorCode); + + bool ret = m_modem->open(); + if (!ret) { + delete m_modem; + m_modem = NULL; + return false; + } + + return true; +} + +bool CMMDVMHost::createDMRNetwork() +{ + if (!m_conf.getDMRNetworkEnabled()) + return false; + + std::string address = m_conf.getDMRNetworkAddress(); + unsigned int port = m_conf.getDMRNetworkPort(); + unsigned int id = m_conf.getDMRId(); + std::string password = m_conf.getDMRNetworkPassword(); + bool debug = m_conf.getDMRNetworkDebug(); + + LogInfo("DMR Network Parameters"); + LogInfo(" Address: %s", address.c_str()); + LogInfo(" Port: %u", port); + + m_dmrNetwork = new CHomebrewDMRIPSC(address, port, id, password, VERSION, "MMDVMHost", debug); + + std::string callsign = m_conf.getCallsign(); + unsigned int rxFrequency = m_conf.getRxFrequency(); + unsigned int txFrequency = m_conf.getTxFrequency(); + unsigned int power = m_conf.getPower(); + unsigned int colorCode = m_conf.getDMRColorCode(); + float latitude = m_conf.getLatitude(); + float longitude = m_conf.getLongitude(); + int height = m_conf.getHeight(); + std::string location = m_conf.getLocation(); + std::string description = m_conf.getDescription(); + std::string url = m_conf.getURL(); + + LogInfo("Info Parameters"); + LogInfo(" Callsign: %s", callsign.c_str()); + LogInfo(" RX Frequency: %uHz", rxFrequency); + LogInfo(" TX Frequency: %uHz", txFrequency); + LogInfo(" Power: %uW", power); + LogInfo(" Latitude: %fdeg N", latitude); + LogInfo(" Longitude: %fdeg E", longitude); + LogInfo(" Height: %um", height); + LogInfo(" Location: \"%s\"", location.c_str()); + LogInfo(" Description: \"%s\"", description.c_str()); + LogInfo(" URL: \"%s\"", url.c_str()); + + m_dmrNetwork->setConfig(callsign, rxFrequency, txFrequency, power, colorCode, latitude, longitude, height, location, description, url); + + bool ret = m_dmrNetwork->open(); + if (!ret) { + delete m_dmrNetwork; + m_dmrNetwork = NULL; + return false; + } + + return true; +} + +void CMMDVMHost::readParams() +{ + m_dstarEnabled = m_conf.getDStarEnabled(); + m_dmrEnabled = m_conf.getDMREnabled(); + m_ysfEnabled = m_conf.getFusionEnabled(); + + if (!m_conf.getDuplex() && m_dmrEnabled) { + LogWarning("DMR operation disabled because system is not duplex"); + m_dmrEnabled = false; + } +} + +void CMMDVMHost::createDisplay() +{ + std::string type = m_conf.getDisplay(); + + LogInfo("Display Parameters"); + LogInfo(" Type: %s", type.c_str()); + + if (type == "TFT Serial") { + std::string port = m_conf.getTFTSerialPort(); + + LogInfo(" Port: %s", port.c_str()); + + m_display = new CTFTSerial(port); + } else { + m_display = new CNullDisplay; + } + + bool ret = m_display->open(); + if (!ret) { + delete m_display; + m_display = new CNullDisplay; + } +} diff --git a/MMDVMHost.h b/MMDVMHost.h new file mode 100644 index 0000000..3e9e2b8 --- /dev/null +++ b/MMDVMHost.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(MMDVMHOST_H) +#define MMDVMHOST_H + +#include "HomebrewDMRIPSC.h" +#include "Display.h" +#include "Modem.h" +#include "Conf.h" + +#include + +class CMMDVMHost +{ +public: + CMMDVMHost(const std::string& confFile); + ~CMMDVMHost(); + + int run(); + +private: + CConf m_conf; + CModem* m_modem; + CHomebrewDMRIPSC* m_dmrNetwork; + IDisplay* m_display; + bool m_dstarEnabled; + bool m_dmrEnabled; + bool m_ysfEnabled; + + void readParams(); + bool createModem(); + bool createDMRNetwork(); + void createDisplay(); +}; + +#endif diff --git a/MMDVMHost.sln b/MMDVMHost.sln new file mode 100644 index 0000000..b130c28 --- /dev/null +++ b/MMDVMHost.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MMDVMHost", "MMDVMHost.vcxproj", "{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Debug|x64.ActiveCfg = Debug|x64 + {1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Debug|x64.Build.0 = Debug|x64 + {1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Debug|x86.ActiveCfg = Debug|Win32 + {1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Debug|x86.Build.0 = Debug|Win32 + {1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Release|x64.ActiveCfg = Release|x64 + {1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Release|x64.Build.0 = Release|x64 + {1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Release|x86.ActiveCfg = Release|Win32 + {1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj new file mode 100644 index 0000000..0eecefa --- /dev/null +++ b/MMDVMHost.vcxproj @@ -0,0 +1,228 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92} + Win32Proj + MMDVMHost + 8.1 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters new file mode 100644 index 0000000..3a21235 --- /dev/null +++ b/MMDVMHost.vcxproj.filters @@ -0,0 +1,236 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bbeb58d --- /dev/null +++ b/Makefile @@ -0,0 +1,120 @@ +CC = g++ +CFLAGS = -O2 -Wall -std=c++11 +LIBS = +LDFLAGS = + +all: MMDVMHost + +MMDVMHost: BPTC19696.o Conf.o CRC.o CSBK.o Display.o DMRControl.o DMRData.o DMRSlot.o DMRSync.o DStarEcho.o EMB.o EmbeddedLC.o FullLC.o Golay2087.o Golay24128.o \ + Hamming.o HomebrewDMRIPSC.o LC.o Log.o MMDVMHost.o Modem.o NullDisplay.o QR1676.o RS129.o SerialController.o SHA256.o ShortLC.o SlotType.o StopWatch.o \ + TFTSerial.o Timer.o UDPSocket.o Utils.o YSFEcho.o + $(CC) $(LDFLAGS) -o MMDVMHost BPTC19696.o Conf.o CRC.o CSBK.o Display.o DMRControl.o DMRData.o DMRSlot.o DMRSync.o DStarEcho.o EMB.o EmbeddedLC.o FullLC.o \ + Golay2087.o Golay24128.o Hamming.o HomebrewDMRIPSC.o LC.o Log.o MMDVMHost.o Modem.o NullDisplay.o QR1676.o RS129.o SerialController.o SHA256.o ShortLC.o \ + SlotType.o StopWatch.o TFTSerial.o Timer.o UDPSocket.o Utils.o YSFEcho.o $(LIBS) + +BPTC19696.o: BPTC19696.cpp BPTC19696.h Utils.h Hamming.h + $(CC) $(CFLAGS) -c BPTC19696.cpp + +Conf.o: Conf.cpp Conf.h Log.h + $(CC) $(CFLAGS) -c Conf.cpp + +CRC.o: CRC.cpp CRC.h Utils.h + $(CC) $(CFLAGS) -c CRC.cpp + +CSBK.o: CSBK.cpp CSBK.h Utils.h DMRDefines.h BPTC19696.h CRC.h Log.h + $(CC) $(CFLAGS) -c CSBK.cpp + +Display.o: Display.cpp Display.h + $(CC) $(CFLAGS) -c Display.cpp + +DMRControl.o: DMRControl.cpp DMRControl.h DMRSlot.h DMRData.h Modem.h HomebrewDMRIPSC.h Defines.h CSBK.h Log.h DIsplay.h + $(CC) $(CFLAGS) -c DMRControl.cpp + +DMRData.o: DMRData.cpp DMRData.h DMRDefines.h Utils.h Log.h + $(CC) $(CFLAGS) -c DMRData.cpp + +DMRSlot.o: DMRSlot.cpp DMRSlot.h DMRData.h Modem.h HomebrewDMRIPSC.h Defines.h Log.h EmbeddedLC.h RingBuffer.h Timer.h LC.h SlotType.h DMRSync.h FullLC.h \ + EMB.h CRC.h CSBK.h ShortLC.h Utils.h Display.h + $(CC) $(CFLAGS) -c DMRSlot.cpp + +DMRSync.o: DMRSync.cpp DMRSync.h DMRDefines.h + $(CC) $(CFLAGS) -c DMRSync.cpp + +DStarEcho.o: DStarEcho.cpp DStarEcho.h RingBuffer.h Timer.h + $(CC) $(CFLAGS) -c DStarEcho.cpp + +EMB.o: EMB.cpp EMB.h + $(CC) $(CFLAGS) -c EMB.cpp + +EmbeddedLC.o: EmbeddedLC.cpp EmbeddedLC.h CRC.h Utils.h LC.h Hamming.h Log.h + $(CC) $(CFLAGS) -c EmbeddedLC.cpp + +FullLC.o: FullLC.cpp FullLC.h BPTC19696.h LC.h SlotType.h Log.h DMRDefines.h RS129.h + $(CC) $(CFLAGS) -c FullLC.cpp + +Golay2087.o: Golay2087.cpp Golay2087.h + $(CC) $(CFLAGS) -c Golay2087.cpp + +Golay24128.o: Golay24128.cpp Golay24128.h + $(CC) $(CFLAGS) -c Golay24128.cpp + +Hamming.o: Hamming.cpp Hamming.h + $(CC) $(CFLAGS) -c Hamming.cpp + +HomebrewDMRIPSC.o: HomebrewDMRIPSC.cpp HomebrewDMRIPSC.h Log.h UDPSocket.h Timer.h DMRData.h RingBuffer.h Utils.h SHA256.h StopWatch.h + $(CC) $(CFLAGS) -c HomebrewDMRIPSC.cpp + +LC.o: LC.cpp LC.h Utils.h DMRDefines.h + $(CC) $(CFLAGS) -c LC.cpp + +Log.o: Log.cpp Log.h + $(CC) $(CFLAGS) -c Log.cpp + +MMDVMHost.o: MMDVMHost.cpp MMDVMHost.h Conf.h Log.h Version.h Modem.h StopWatch.h Defines.h DMRSync.h DStarEcho.h YSFEcho.h DMRControl.h HomebrewDMRIPSC.h \ + Display.h TFTSerial.h NullDisplay.h + $(CC) $(CFLAGS) -c MMDVMHost.cpp + +Modem.o: Modem.cpp Modem.h Log.h SerialController.h Timer.h RingBuffer.h Utils.o DMRDefines.h DStarDefines.h YSFDefines.h Defines.h + $(CC) $(CFLAGS) -c Modem.cpp + +NullDisplay.o: NullDisplay.cpp NullDisplay.h Display.h + $(CC) $(CFLAGS) -c NullDisplay.cpp + +QR1676.o: QR1676.cpp QR1676.h Log.h + $(CC) $(CFLAGS) -c QR1676.cpp + +RS129.o: RS129.cpp RS129.h + $(CC) $(CFLAGS) -c RS129.cpp + +SerialController.o: SerialController.cpp SerialController.h Log.h + $(CC) $(CFLAGS) -c SerialController.cpp + +SHA256.o: SHA256.cpp SHA256.h + $(CC) $(CFLAGS) -c SHA256.cpp + +ShortLC.o: ShortLC.cpp ShortLC.h Utils.h Hamming.h + $(CC) $(CFLAGS) -c ShortLC.cpp + +SlotType.o: SlotType.cpp SlotType.h Golay2087.h + $(CC) $(CFLAGS) -c SlotType.cpp + +StopWatch.o: StopWatch.cpp StopWatch.h + $(CC) $(CFLAGS) -c StopWatch.cpp + +TFTSerial.o: TFTSerial.cpp TFTSerial.h Display.h SerialController.h Log.h + $(CC) $(CFLAGS) -c TFTSerial.cpp + +Timer.o: Timer.cpp Timer.h + $(CC) $(CFLAGS) -c Timer.cpp + +UDPSocket.o: UDPSocket.cpp UDPSocket.h Log.h + $(CC) $(CFLAGS) -c UDPSocket.cpp + +Utils.o: Utils.cpp Utils.h Log.h + $(CC) $(CFLAGS) -c Utils.cpp + +YSFEcho.o: YSFEcho.cpp YSFEcho.h YSFDefines.h RingBuffer.h Timer.h + $(CC) $(CFLAGS) -c YSFEcho.cpp + +clean: + $(RM) MMDVMHost *.o *.bak *~ diff --git a/Modem.cpp b/Modem.cpp new file mode 100644 index 0000000..a09cc1e --- /dev/null +++ b/Modem.cpp @@ -0,0 +1,884 @@ +/* + * Copyright (C) 2011-2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "DStarDefines.h" +#include "DMRDefines.h" +#include "YSFDefines.h" +#include "Defines.h" +#include "Modem.h" +#include "Utils.h" +#include "Log.h" + +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +typedef unsigned int uint32_t; +#else +#include +#include +#endif + +const unsigned char MMDVM_FRAME_START = 0xE0U; + +const unsigned char MMDVM_GET_VERSION = 0x00U; +const unsigned char MMDVM_GET_STATUS = 0x01U; +const unsigned char MMDVM_SET_CONFIG = 0x02U; +const unsigned char MMDVM_SET_MODE = 0x03U; + +const unsigned char MMDVM_DSTAR_HEADER = 0x10U; +const unsigned char MMDVM_DSTAR_DATA = 0x11U; +const unsigned char MMDVM_DSTAR_LOST = 0x12U; +const unsigned char MMDVM_DSTAR_EOT = 0x13U; + +const unsigned char MMDVM_DMR_DATA1 = 0x18U; +const unsigned char MMDVM_DMR_LOST1 = 0x19U; +const unsigned char MMDVM_DMR_DATA2 = 0x1AU; +const unsigned char MMDVM_DMR_LOST2 = 0x1BU; +const unsigned char MMDVM_DMR_SHORTLC = 0x1CU; +const unsigned char MMDVM_DMR_START = 0x1DU; + +const unsigned char MMDVM_YSF_DATA = 0x20U; +const unsigned char MMDVM_YSF_LOST = 0x21U; + +const unsigned char MMDVM_ACK = 0x70U; +const unsigned char MMDVM_NAK = 0x7FU; + +const unsigned char MMDVM_DUMP = 0xF0U; +const unsigned char MMDVM_DEBUG1 = 0xF1U; +const unsigned char MMDVM_DEBUG2 = 0xF2U; +const unsigned char MMDVM_DEBUG3 = 0xF3U; +const unsigned char MMDVM_DEBUG4 = 0xF4U; +const unsigned char MMDVM_DEBUG5 = 0xF5U; +const unsigned char MMDVM_SAMPLES = 0xF8U; + +const unsigned int MAX_RESPONSES = 30U; + +const unsigned int BUFFER_LENGTH = 500U; + + +CModem::CModem(const std::string& port, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel, bool debug) : +m_port(port), +m_colorCode(0U), +m_rxInvert(rxInvert), +m_txInvert(txInvert), +m_pttInvert(pttInvert), +m_txDelay(txDelay), +m_rxLevel(rxLevel), +m_txLevel(txLevel), +m_debug(debug), +m_dstarEnabled(false), +m_dmrEnabled(false), +m_ysfEnabled(false), +m_serial(port, SERIAL_115200, true), +m_buffer(NULL), +m_rxDStarData(1000U), +m_txDStarData(1000U), +m_rxDMRData1(1000U), +m_rxDMRData2(1000U), +m_txDMRData1(1000U), +m_txDMRData2(1000U), +m_rxYSFData(1000U), +m_txYSFData(1000U), +m_statusTimer(1000U, 0U, 100U), +m_dstarSpace(0U), +m_dmrSpace1(0U), +m_dmrSpace2(0U), +m_ysfSpace(0U), +m_tx(false) +{ + assert(!port.empty()); + + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CModem::~CModem() +{ + delete[] m_buffer; +} + +void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled) +{ + m_dstarEnabled = dstarEnabled; + m_dmrEnabled = dmrEnabled; + m_ysfEnabled = ysfEnabled; +} + +void CModem::setDMRParams(unsigned int colorCode) +{ + assert(colorCode < 16U); + + m_colorCode = colorCode; +} + +bool CModem::open() +{ + ::LogMessage("Opening the MMDVM"); + + bool ret = m_serial.open(); + if (!ret) + return false; + + ret = readVersion(); + if (!ret) { + m_serial.close(); + return false; + } + + ret = setConfig(); + if (!ret) { + m_serial.close(); + return false; + } + + m_statusTimer.start(); + + return true; +} + +void CModem::clock(unsigned int ms) +{ + // Poll the modem status every 100ms + m_statusTimer.clock(ms); + if (m_statusTimer.hasExpired()) { + readStatus(); + m_statusTimer.start(); + } + + unsigned int length; + RESP_TYPE_MMDVM type = getResponse(m_buffer, length); + + if (type == RTM_TIMEOUT) { + // Nothing to do + } else if (type == RTM_ERROR) { + LogError("Error when reading from the MMDVM"); + } else { + // type == RTM_OK + switch (m_buffer[2U]) { + case MMDVM_DSTAR_HEADER: { + if (m_debug) + CUtils::dump(1U, "RX D-Star Header", m_buffer, length); + + unsigned char data = length - 2U; + m_rxDStarData.addData(&data, 1U); + + data = TAG_HEADER; + m_rxDStarData.addData(&data, 1U); + + m_rxDStarData.addData(m_buffer + 3U, length - 3U); + } + break; + + case MMDVM_DSTAR_DATA: { + if (m_debug) + CUtils::dump(1U, "RX D-Star Data", m_buffer, length); + + unsigned char data = length - 2U; + m_rxDStarData.addData(&data, 1U); + + data = TAG_DATA; + m_rxDStarData.addData(&data, 1U); + + m_rxDStarData.addData(m_buffer + 3U, length - 3U); + } + break; + + case MMDVM_DSTAR_LOST: { + if (m_debug) + CUtils::dump(1U, "RX D-Star Lost", m_buffer, length); + + unsigned char data = 1U; + m_rxDStarData.addData(&data, 1U); + + data = TAG_LOST; + m_rxDStarData.addData(&data, 1U); + } + break; + + case MMDVM_DSTAR_EOT: { + if (m_debug) + CUtils::dump(1U, "RX D-Star EOT", m_buffer, length); + + unsigned char data = 1U; + m_rxDStarData.addData(&data, 1U); + + data = TAG_EOT; + m_rxDStarData.addData(&data, 1U); + } + break; + + case MMDVM_DMR_DATA1: { + if (m_debug) + CUtils::dump(1U, "RX DMR Data 1", m_buffer, length); + + unsigned char data = length - 2U; + m_rxDMRData1.addData(&data, 1U); + + if (m_buffer[3U] == (DMR_SYNC_DATA | DT_TERMINATOR_WITH_LC)) + data = TAG_EOT; + else + data = TAG_DATA; + m_rxDMRData1.addData(&data, 1U); + + m_rxDMRData1.addData(m_buffer + 3U, length - 3U); + } + break; + + case MMDVM_DMR_DATA2: { + if (m_debug) + CUtils::dump(1U, "RX DMR Data 2", m_buffer, length); + + unsigned char data = length - 2U; + m_rxDMRData2.addData(&data, 1U); + + if (m_buffer[3U] == (DMR_SYNC_DATA | DT_TERMINATOR_WITH_LC)) + data = TAG_EOT; + else + data = TAG_DATA; + m_rxDMRData2.addData(&data, 1U); + + m_rxDMRData2.addData(m_buffer + 3U, length - 3U); + } + break; + + case MMDVM_DMR_LOST1: { + if (m_debug) + CUtils::dump(1U, "RX DMR Lost 1", m_buffer, length); + + unsigned char data = 1U; + m_rxDMRData1.addData(&data, 1U); + + data = TAG_LOST; + m_rxDMRData1.addData(&data, 1U); + } + break; + + case MMDVM_DMR_LOST2: { + if (m_debug) + CUtils::dump(1U, "RX DMR Lost 2", m_buffer, length); + + unsigned char data = 1U; + m_rxDMRData2.addData(&data, 1U); + + data = TAG_LOST; + m_rxDMRData2.addData(&data, 1U); + } + break; + + case MMDVM_YSF_DATA: { + if (m_debug) + CUtils::dump(1U, "RX YSF Data", m_buffer, length); + + unsigned char data = length - 2U; + m_rxYSFData.addData(&data, 1U); + + if ((m_buffer[3U] & (YSF_CKSUM_OK | YSF_FI_MASK)) == (YSF_CKSUM_OK | YSF_DT_TERMINATOR_CHANNEL)) + data = TAG_EOT; + else + data = TAG_DATA; + m_rxYSFData.addData(&data, 1U); + + m_rxYSFData.addData(m_buffer + 3U, length - 3U); + } + break; + + case MMDVM_YSF_LOST: { + if (m_debug) + CUtils::dump(1U, "RX YSF Lost", m_buffer, length); + + unsigned char data = 1U; + m_rxYSFData.addData(&data, 1U); + + data = TAG_LOST; + m_rxYSFData.addData(&data, 1U); + } + break; + + case MMDVM_GET_STATUS: { + // if (m_debug) + // CUtils::dump(1U, "GET_STATUS", m_buffer, length); + + m_tx = (m_buffer[5U] & 0x01U) == 0x01U; + + bool adcOverflow = (m_buffer[5U] & 0x02U) == 0x02U; + if (adcOverflow) + LogWarning("MMDVM ADC levels have overflowed"); + + m_dstarSpace = m_buffer[6U]; + m_dmrSpace1 = m_buffer[7U]; + m_dmrSpace2 = m_buffer[8U]; + m_ysfSpace = m_buffer[9U]; + // LogMessage("tx=%d, space=%u,%u,%u,%u", int(m_tx), m_dstarSpace, m_dmrSpace1, m_dmrSpace2, m_ysfSpace); + } + break; + + // These should not be received, but don't complain if we do + case MMDVM_GET_VERSION: + case MMDVM_ACK: + break; + + case MMDVM_NAK: + LogWarning("Received a NAK from the MMDVM, command = 0x%02X, reason = %u", m_buffer[3U], m_buffer[4U]); + break; + + case MMDVM_DEBUG1: + case MMDVM_DEBUG2: + case MMDVM_DEBUG3: + case MMDVM_DEBUG4: + case MMDVM_DEBUG5: + printDebug(); + break; + + case MMDVM_SAMPLES: + // printSamples(); + break; + + default: + LogMessage("Unknown message, type: %02X", m_buffer[2U]); + CUtils::dump("Buffer dump", m_buffer, length); + break; + } + } + + if (m_dstarSpace > 1U && !m_txDStarData.isEmpty()) { + unsigned char len = 0U; + m_txDStarData.getData(&len, 1U); + m_txDStarData.getData(m_buffer, len); + + if (m_debug) { + if (len > (DSTAR_FRAME_LENGTH_BYTES + 3U)) + CUtils::dump(1U, "TX D-Star Header", m_buffer, len); + else if (len == (DSTAR_FRAME_LENGTH_BYTES + 3U)) + CUtils::dump(1U, "TX D-Star Data", m_buffer, len); + else + CUtils::dump(1U, "TX D-Star EOT", m_buffer, len); + } + + int ret = m_serial.write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing D-Star data to the MMDVM"); + + // Headers take four slots in the queues in the modem + if (len > (DSTAR_FRAME_LENGTH_BYTES + 3U)) + m_dstarSpace -= 4U; + else + m_dstarSpace -= 1U; + } + + if (m_dmrSpace1 > 1U && !m_txDMRData1.isEmpty()) { + unsigned char len = 0U; + m_txDMRData1.getData(&len, 1U); + m_txDMRData1.getData(m_buffer, len); + + if (m_debug) + CUtils::dump(1U, "TX DMR Data 1", m_buffer, len); + + int ret = m_serial.write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing DMR data to the MMDVM"); + + m_dmrSpace1--; + } + + if (m_dmrSpace2 > 1U && !m_txDMRData2.isEmpty()) { + unsigned char len = 0U; + m_txDMRData2.getData(&len, 1U); + m_txDMRData2.getData(m_buffer, len); + + if (m_debug) + CUtils::dump(1U, "TX DMR Data 2", m_buffer, len); + + int ret = m_serial.write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing DMR data to the MMDVM"); + + m_dmrSpace2--; + } + + if (m_ysfSpace > 1U && !m_txYSFData.isEmpty()) { + unsigned char len = 0U; + m_txYSFData.getData(&len, 1U); + m_txYSFData.getData(m_buffer, len); + + if (m_debug) + CUtils::dump(1U, "TX YSF Data", m_buffer, len); + + int ret = m_serial.write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing YSF data to the MMDVM"); + + m_ysfSpace--; + } +} + +void CModem::close() +{ + ::LogMessage("Closing the MMDVM"); + + delete[] m_buffer; + + m_serial.close(); +} + +unsigned int CModem::readDStarData(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxDStarData.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxDStarData.getData(&len, 1U); + m_rxDStarData.getData(data, len); + + return len; +} + +unsigned int CModem::readDMRData1(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxDMRData1.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxDMRData1.getData(&len, 1U); + m_rxDMRData1.getData(data, len); + + return len; +} + +unsigned int CModem::readDMRData2(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxDMRData2.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxDMRData2.getData(&len, 1U); + m_rxDMRData2.getData(data, len); + + return len; +} + +unsigned int CModem::readYSFData(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxYSFData.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxYSFData.getData(&len, 1U); + m_rxYSFData.getData(data, len); + + return len; +} + +bool CModem::hasDStarSpace() const +{ + unsigned int space = m_txDStarData.freeSpace() / (DSTAR_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CModem::writeDStarData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + unsigned char buffer[50U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + + switch (data[0U]) { + case TAG_HEADER: buffer[2U] = MMDVM_DSTAR_HEADER; break; + case TAG_DATA: buffer[2U] = MMDVM_DSTAR_DATA; break; + case TAG_EOT: buffer[2U] = MMDVM_DSTAR_EOT; break; + default: return false; + } + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txDStarData.addData(&len, 1U); + m_txDStarData.addData(buffer, len); + + return true; +} + +bool CModem::hasDMRSpace1() const +{ + unsigned int space = m_txDMRData1.freeSpace() / (DMR_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CModem::hasDMRSpace2() const +{ + unsigned int space = m_txDMRData2.freeSpace() / (DMR_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CModem::writeDMRData1(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) + return false; + + unsigned char buffer[40U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + buffer[2U] = MMDVM_DMR_DATA1; + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txDMRData1.addData(&len, 1U); + m_txDMRData1.addData(buffer, len); + + return true; +} + +bool CModem::writeDMRData2(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) + return false; + + unsigned char buffer[40U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + buffer[2U] = MMDVM_DMR_DATA2; + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txDMRData2.addData(&len, 1U); + m_txDMRData2.addData(buffer, len); + + return true; +} + +bool CModem::hasYSFSpace() const +{ + unsigned int space = m_txYSFData.freeSpace() / (YSF_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CModem::writeYSFData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) + return false; + + unsigned char buffer[130U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + buffer[2U] = MMDVM_YSF_DATA; + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txYSFData.addData(&len, 1U); + m_txYSFData.addData(buffer, len); + + return true; +} + +bool CModem::readVersion() +{ +#if defined(_WIN32) || defined(_WIN64) + ::Sleep(2000UL); // 2s +#else + ::sleep(2); // 2s +#endif + + for (unsigned int i = 0U; i < 6U; i++) { + unsigned char buffer[3U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 3U; + buffer[2U] = MMDVM_GET_VERSION; + + // CUtils::dump("Written", buffer, 3U); + + int ret = m_serial.write(buffer, 3U); + if (ret != 3) + return false; + + for (unsigned int count = 0U; count < MAX_RESPONSES; count++) { +#if defined(_WIN32) || defined(_WIN64) + ::Sleep(10UL); +#else + ::usleep(10000UL); +#endif + unsigned int length; + RESP_TYPE_MMDVM resp = getResponse(m_buffer, length); + if (resp == RTM_OK && m_buffer[2U] == MMDVM_GET_VERSION) { + LogInfo("MMDVM protocol version: %u, description: %.*s", m_buffer[3U], length - 4U, m_buffer + 4U); + return true; + } + } + +#if defined(_WIN32) || defined(_WIN64) + ::Sleep(1000UL); // 1s +#else + ::sleep(1UL); // 1s +#endif + } + + LogError("Unable to read the firmware version after six attempts"); + + return false; +} + +bool CModem::readStatus() +{ + unsigned char buffer[3U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 3U; + buffer[2U] = MMDVM_GET_STATUS; + + return m_serial.write(buffer, 3U) == 3; +} + +bool CModem::setConfig() +{ + unsigned char buffer[10U]; + + buffer[0U] = MMDVM_FRAME_START; + + buffer[1U] = 10U; + + buffer[2U] = MMDVM_SET_CONFIG; + + buffer[3U] = 0x00U; + if (m_rxInvert) + buffer[3U] |= 0x01U; + if (m_txInvert) + buffer[3U] |= 0x02U; + if (m_pttInvert) + buffer[3U] |= 0x04U; + + buffer[4U] = 0x00U; + if (m_dstarEnabled) + buffer[4U] |= 0x01U; + if (m_dmrEnabled) + buffer[4U] |= 0x02U; + if (m_ysfEnabled) + buffer[4U] |= 0x04U; + + buffer[5U] = m_txDelay / 10U; // In 10ms units + + buffer[6U] = MODE_IDLE; + + buffer[7U] = (m_rxLevel * 255U) / 100U; + buffer[8U] = (m_txLevel * 255U) / 100U; + + buffer[9U] = m_colorCode; + + // CUtils::dump("Written", buffer, 10U); + + int ret = m_serial.write(buffer, 10U); + if (ret != 10) + return false; + + unsigned int count = 0U; + unsigned int length; + RESP_TYPE_MMDVM resp; + do { +#if defined(_WIN32) || defined(_WIN64) + ::Sleep(10UL); +#else + ::usleep(10000UL); +#endif + resp = getResponse(m_buffer, length); + + if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { + count++; + if (count >= MAX_RESPONSES) { + LogError("The MMDVM is not responding to the SET_CONFIG command"); + return false; + } + } + } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); + + // CUtils::dump("Response", m_buffer, length); + + if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { + LogError("Received a NAK to the SET_CONFIG command from the modem"); + return false; + } + + return true; +} + +RESP_TYPE_MMDVM CModem::getResponse(unsigned char *buffer, unsigned int& length) +{ + // Get the start of the frame or nothing at all + int ret = m_serial.read(buffer + 0U, 1U); + if (ret < 0) { + LogError("Error when reading from the modem"); + return RTM_ERROR; + } + + if (ret == 0) + return RTM_TIMEOUT; + + if (buffer[0U] != MMDVM_FRAME_START) + return RTM_TIMEOUT; + + ret = m_serial.read(buffer + 1U, 1U); + if (ret < 0) { + LogError("Error when reading from the modem"); + return RTM_ERROR; + } + + if (ret == 0) + return RTM_TIMEOUT; + + length = buffer[1U]; + + if (length >= 200U) { + LogError("Invalid data received from the modem"); + return RTM_ERROR; + } + + unsigned int offset = 2U; + + while (offset < length) { + int ret = m_serial.read(buffer + offset, length - offset); + if (ret < 0) { + LogError("Error when reading from the modem"); + return RTM_ERROR; + } + + if (ret > 0) + offset += ret; + + if (ret == 0) +#if defined(_WIN32) || defined(_WIN64) + ::Sleep(5UL); // 5ms +#else + ::usleep(5000); // 5ms +#endif + } + + // CUtils::dump("Received", buffer, length); + + return RTM_OK; +} + +bool CModem::setMode(unsigned char mode) +{ + unsigned char buffer[4U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 4U; + buffer[2U] = MMDVM_SET_MODE; + buffer[3U] = mode; + + // CUtils::dump("Written", buffer, 4U); + + return m_serial.write(buffer, 4U) == 4; +} + +bool CModem::writeDMRStart(bool tx) +{ + if (tx && m_tx) + return true; + if (!tx && !m_tx) + return true; + + unsigned char buffer[4U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 4U; + buffer[2U] = MMDVM_DMR_START; + buffer[3U] = tx ? 0x01U : 0x00U; + + // CUtils::dump("Written", buffer, 4U); + + return m_serial.write(buffer, 4U) == 4; +} + +bool CModem::writeDMRShortLC(const unsigned char* lc) +{ + assert(lc != NULL); + + unsigned char buffer[12U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 12U; + buffer[2U] = MMDVM_DMR_SHORTLC; + buffer[3U] = lc[0U]; + buffer[4U] = lc[1U]; + buffer[5U] = lc[2U]; + buffer[6U] = lc[3U]; + buffer[7U] = lc[4U]; + buffer[8U] = lc[5U]; + buffer[9U] = lc[6U]; + buffer[10U] = lc[7U]; + buffer[11U] = lc[8U]; + + // CUtils::dump("Written", buffer, 12U); + + return m_serial.write(buffer, 12U) == 12; +} + +void CModem::printDebug() +{ + unsigned int length = m_buffer[1U]; + if (m_buffer[2U] == 0xF1U) { + LogMessage("Debug: %.*s", length - 3U, m_buffer + 3U); + } else if (m_buffer[2U] == 0xF2U) { + short val1 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U]; + LogMessage("Debug: %.*s %d", length - 5U, m_buffer + 3U, val1); + } else if (m_buffer[2U] == 0xF3U) { + short val1 = (m_buffer[length - 4U] << 8) | m_buffer[length - 3U]; + short val2 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U]; + LogMessage("Debug: %.*s %d %d", length - 7U, m_buffer + 3U, val1, val2); + } else if (m_buffer[2U] == 0xF4U) { + short val1 = (m_buffer[length - 6U] << 8) | m_buffer[length - 5U]; + short val2 = (m_buffer[length - 4U] << 8) | m_buffer[length - 3U]; + short val3 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U]; + LogMessage("Debug: %.*s %d %d %d", length - 9U, m_buffer + 3U, val1, val2, val3); + } else if (m_buffer[2U] == 0xF5U) { + short val1 = (m_buffer[length - 8U] << 8) | m_buffer[length - 7U]; + short val2 = (m_buffer[length - 6U] << 8) | m_buffer[length - 5U]; + short val3 = (m_buffer[length - 4U] << 8) | m_buffer[length - 3U]; + short val4 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U]; + LogMessage("Debug: %.*s %d %d %d %d", length - 11U, m_buffer + 3U, val1, val2, val3, val4); + } +} diff --git a/Modem.h b/Modem.h new file mode 100644 index 0000000..ed0e3db --- /dev/null +++ b/Modem.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2011-2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef MODEM_H +#define MODEM_H + +#include "SerialController.h" +#include "RingBuffer.h" +#include "Timer.h" + +#include + +enum RESP_TYPE_MMDVM { + RTM_OK, + RTM_TIMEOUT, + RTM_ERROR +}; + +class CModem { +public: + CModem(const std::string& port, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel, bool debug = false); + virtual ~CModem(); + + virtual void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled); + virtual void setDMRParams(unsigned int colorCode); + + virtual bool open(); + + virtual unsigned int readDStarData(unsigned char* data); + virtual unsigned int readDMRData1(unsigned char* data); + virtual unsigned int readDMRData2(unsigned char* data); + virtual unsigned int readYSFData(unsigned char* data); + + virtual bool hasDStarSpace() const; + virtual bool hasDMRSpace1() const; + virtual bool hasDMRSpace2() const; + virtual bool hasYSFSpace() const; + + virtual bool writeDStarData(const unsigned char* data, unsigned int length); + virtual bool writeDMRData1(const unsigned char* data, unsigned int length); + virtual bool writeDMRData2(const unsigned char* data, unsigned int length); + virtual bool writeYSFData(const unsigned char* data, unsigned int length); + + virtual bool writeDMRStart(bool tx); + virtual bool writeDMRShortLC(const unsigned char* lc); + + virtual bool setMode(unsigned char mode); + + virtual void clock(unsigned int ms); + + virtual void close(); + +private: + std::string m_port; + unsigned int m_colorCode; + bool m_rxInvert; + bool m_txInvert; + bool m_pttInvert; + unsigned int m_txDelay; + unsigned int m_rxLevel; + unsigned int m_txLevel; + bool m_debug; + bool m_dstarEnabled; + bool m_dmrEnabled; + bool m_ysfEnabled; + CSerialController m_serial; + unsigned char* m_buffer; + CRingBuffer m_rxDStarData; + CRingBuffer m_txDStarData; + CRingBuffer m_rxDMRData1; + CRingBuffer m_rxDMRData2; + CRingBuffer m_txDMRData1; + CRingBuffer m_txDMRData2; + CRingBuffer m_rxYSFData; + CRingBuffer m_txYSFData; + CTimer m_statusTimer; + unsigned int m_dstarSpace; + unsigned int m_dmrSpace1; + unsigned int m_dmrSpace2; + unsigned int m_ysfSpace; + bool m_tx; + + bool readVersion(); + bool readStatus(); + bool setConfig(); + + void printDebug(); + + RESP_TYPE_MMDVM getResponse(unsigned char* buffer, unsigned int& length); +}; + +#endif diff --git a/NullDisplay.cpp b/NullDisplay.cpp new file mode 100644 index 0000000..2f6c58d --- /dev/null +++ b/NullDisplay.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "NullDisplay.h" + +CNullDisplay::CNullDisplay() +{ +} + +CNullDisplay::~CNullDisplay() +{ +} + +bool CNullDisplay::open() +{ + return true; +} + +void CNullDisplay::setIdle() +{ +} + +void CNullDisplay::setDStar() +{ +} + +void CNullDisplay::writeDStar(const std::string& call1, const std::string& call2) +{ +} + +void CNullDisplay::clearDStar() +{ +} + +void CNullDisplay::setDMR() +{ +} + +void CNullDisplay::writeDMR(unsigned int slotNo, unsigned int srcId, bool group, unsigned int dstId) +{ +} + +void CNullDisplay::clearDMR(unsigned int slotNo) +{ +} + +void CNullDisplay::setFusion() +{ +} + +void CNullDisplay::writeFusion(const std::string& callsign) +{ +} + +void CNullDisplay::clearFusion() +{ +} + +void CNullDisplay::close() +{ +} diff --git a/NullDisplay.h b/NullDisplay.h new file mode 100644 index 0000000..715f24c --- /dev/null +++ b/NullDisplay.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(NULLDISPLAY_H) +#define NULLDISPLAY_H + +#include "Display.h" + +#include + +class CNullDisplay : public IDisplay +{ +public: + CNullDisplay(); + virtual ~CNullDisplay(); + + virtual bool open(); + + virtual void setIdle(); + + virtual void setDStar(); + virtual void writeDStar(const std::string& call1, const std::string& call2); + virtual void clearDStar(); + + virtual void setDMR(); + virtual void writeDMR(unsigned int slotNo, unsigned int srdId, bool group, unsigned int dstId); + virtual void clearDMR(unsigned int slotNo); + + virtual void setFusion(); + virtual void writeFusion(const std::string& callsign); + virtual void clearFusion(); + + virtual void close(); + +private: +}; + +#endif diff --git a/QR1676.cpp b/QR1676.cpp new file mode 100644 index 0000000..1548fc5 --- /dev/null +++ b/QR1676.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "QR1676.h" +#include "Log.h" + +#include +#include + +const unsigned int ENCODING_TABLE_1676[] = + {0x0000U, 0x0273U, 0x04E5U, 0x0696U, 0x09C9U, 0x0BBAU, 0x0D2CU, 0x0F5FU, 0x11E2U, 0x1391U, 0x1507U, 0x1774U, + 0x182BU, 0x1A58U, 0x1CCEU, 0x1EBDU, 0x21B7U, 0x23C4U, 0x2552U, 0x2721U, 0x287EU, 0x2A0DU, 0x2C9BU, 0x2EE8U, + 0x3055U, 0x3226U, 0x34B0U, 0x36C3U, 0x399CU, 0x3BEFU, 0x3D79U, 0x3F0AU, 0x411EU, 0x436DU, 0x45FBU, 0x4788U, + 0x48D7U, 0x4AA4U, 0x4C32U, 0x4E41U, 0x50FCU, 0x528FU, 0x5419U, 0x566AU, 0x5935U, 0x5B46U, 0x5DD0U, 0x5FA3U, + 0x60A9U, 0x62DAU, 0x644CU, 0x663FU, 0x6960U, 0x6B13U, 0x6D85U, 0x6FF6U, 0x714BU, 0x7338U, 0x75AEU, 0x77DDU, + 0x7882U, 0x7AF1U, 0x7C67U, 0x7E14U, 0x804FU, 0x823CU, 0x84AAU, 0x86D9U, 0x8986U, 0x8BF5U, 0x8D63U, 0x8F10U, + 0x91ADU, 0x93DEU, 0x9548U, 0x973BU, 0x9864U, 0x9A17U, 0x9C81U, 0x9EF2U, 0xA1F8U, 0xA38BU, 0xA51DU, 0xA76EU, + 0xA831U, 0xAA42U, 0xACD4U, 0xAEA7U, 0xB01AU, 0xB269U, 0xB4FFU, 0xB68CU, 0xB9D3U, 0xBBA0U, 0xBD36U, 0xBF45U, + 0xC151U, 0xC322U, 0xC5B4U, 0xC7C7U, 0xC898U, 0xCAEBU, 0xCC7DU, 0xCE0EU, 0xD0B3U, 0xD2C0U, 0xD456U, 0xD625U, + 0xD97AU, 0xDB09U, 0xDD9FU, 0xDFECU, 0xE0E6U, 0xE295U, 0xE403U, 0xE670U, 0xE92FU, 0xEB5CU, 0xEDCAU, 0xEFB9U, + 0xF104U, 0xF377U, 0xF5E1U, 0xF792U, 0xF8CDU, 0xFABEU, 0xFC28U, 0xFE5BU}; + +const unsigned int DECODING_TABLE_1576[] = + {0x0000U, 0x0001U, 0x0002U, 0x0003U, 0x0004U, 0x0005U, 0x0006U, 0x4020U, 0x0008U, 0x0009U, 0x000AU, 0x000BU, + 0x000CU, 0x000DU, 0x2081U, 0x2080U, 0x0010U, 0x0011U, 0x0012U, 0x0013U, 0x0014U, 0x0C00U, 0x0016U, 0x0C02U, + 0x0018U, 0x0120U, 0x001AU, 0x0122U, 0x4102U, 0x0124U, 0x4100U, 0x4101U, 0x0020U, 0x0021U, 0x0022U, 0x4004U, + 0x0024U, 0x4002U, 0x4001U, 0x4000U, 0x0028U, 0x0110U, 0x1800U, 0x1801U, 0x002CU, 0x400AU, 0x4009U, 0x4008U, + 0x0030U, 0x0108U, 0x0240U, 0x0241U, 0x0034U, 0x4012U, 0x4011U, 0x4010U, 0x0101U, 0x0100U, 0x0103U, 0x0102U, + 0x0105U, 0x0104U, 0x1401U, 0x1400U, 0x0040U, 0x0041U, 0x0042U, 0x0043U, 0x0044U, 0x0045U, 0x0046U, 0x4060U, + 0x0048U, 0x0049U, 0x0301U, 0x0300U, 0x004CU, 0x1600U, 0x0305U, 0x0304U, 0x0050U, 0x0051U, 0x0220U, 0x0221U, + 0x3000U, 0x4200U, 0x3002U, 0x4202U, 0x0058U, 0x1082U, 0x1081U, 0x1080U, 0x3008U, 0x4208U, 0x2820U, 0x1084U, + 0x0060U, 0x0061U, 0x0210U, 0x0211U, 0x0480U, 0x0481U, 0x4041U, 0x4040U, 0x0068U, 0x2402U, 0x2401U, 0x2400U, + 0x0488U, 0x3100U, 0x2810U, 0x2404U, 0x0202U, 0x0880U, 0x0200U, 0x0201U, 0x0206U, 0x0884U, 0x0204U, 0x0205U, + 0x0141U, 0x0140U, 0x0208U, 0x0209U, 0x2802U, 0x0144U, 0x2800U, 0x2801U, 0x0080U, 0x0081U, 0x0082U, 0x0A00U, + 0x0084U, 0x0085U, 0x2009U, 0x2008U, 0x0088U, 0x0089U, 0x2005U, 0x2004U, 0x2003U, 0x2002U, 0x2001U, 0x2000U, + 0x0090U, 0x0091U, 0x0092U, 0x1048U, 0x0602U, 0x0C80U, 0x0600U, 0x0601U, 0x0098U, 0x1042U, 0x1041U, 0x1040U, + 0x2013U, 0x2012U, 0x2011U, 0x2010U, 0x00A0U, 0x00A1U, 0x00A2U, 0x4084U, 0x0440U, 0x0441U, 0x4081U, 0x4080U, + 0x6000U, 0x1200U, 0x6002U, 0x1202U, 0x6004U, 0x2022U, 0x2021U, 0x2020U, 0x0841U, 0x0840U, 0x2104U, 0x0842U, + 0x2102U, 0x0844U, 0x2100U, 0x2101U, 0x0181U, 0x0180U, 0x0B00U, 0x0182U, 0x5040U, 0x0184U, 0x2108U, 0x2030U, + 0x00C0U, 0x00C1U, 0x4401U, 0x4400U, 0x0420U, 0x0421U, 0x0422U, 0x4404U, 0x0900U, 0x0901U, 0x1011U, 0x1010U, + 0x0904U, 0x2042U, 0x2041U, 0x2040U, 0x0821U, 0x0820U, 0x1009U, 0x1008U, 0x4802U, 0x0824U, 0x4800U, 0x4801U, + 0x1003U, 0x1002U, 0x1001U, 0x1000U, 0x0501U, 0x0500U, 0x1005U, 0x1004U, 0x0404U, 0x0810U, 0x1100U, 0x1101U, + 0x0400U, 0x0401U, 0x0402U, 0x0403U, 0x040CU, 0x0818U, 0x1108U, 0x1030U, 0x0408U, 0x0409U, 0x040AU, 0x2060U, + 0x0801U, 0x0800U, 0x0280U, 0x0802U, 0x0410U, 0x0804U, 0x0412U, 0x0806U, 0x0809U, 0x0808U, 0x1021U, 0x1020U, + 0x5000U, 0x2200U, 0x5002U, 0x2202U}; + +#define X14 0x00004000 /* vector representation of X^{14} */ +#define X8 0x00000100 /* vector representation of X^{8} */ +#define MASK7 0xffffff00 /* auxiliary vector for testing */ +#define GENPOL 0x00000139 /* generator polinomial, g(x) */ + +unsigned int CQR1676::getSyndrome1576(unsigned int pattern) +/* + * Compute the syndrome corresponding to the given pattern, i.e., the + * remainder after dividing the pattern (when considering it as the vector + * representation of a polynomial) by the generator polynomial, GENPOL. + * In the program this pattern has several meanings: (1) pattern = infomation + * bits, when constructing the encoding table; (2) pattern = error pattern, + * when constructing the decoding table; and (3) pattern = received vector, to + * obtain its syndrome in decoding. + */ +{ + unsigned int aux = X14; + + if (pattern >= X8) { + while (pattern & MASK7) { + while (!(aux & pattern)) + aux = aux >> 1; + + pattern ^= (aux / X8) * GENPOL; + } + } + + return pattern; +} + +// Compute the EMB against a precomputed list of correct words +void CQR1676::encode(unsigned char* data) +{ + assert(data != NULL); + + unsigned int value = (data[0U] >> 1) & 0x7FU; + unsigned int cksum = ENCODING_TABLE_1676[value]; + + data[0U] = cksum >> 8; + data[1U] = cksum & 0xFFU; +} + +unsigned char CQR1676::decode(const unsigned char* data) +{ + assert(data != NULL); + + unsigned int code = (data[0U] << 7) + (data[1U] >> 1); + unsigned int syndrome = getSyndrome1576(code); + unsigned int error_pattern = DECODING_TABLE_1576[syndrome]; + + code ^= error_pattern; + + return code >> 7; +} diff --git a/QR1676.h b/QR1676.h new file mode 100644 index 0000000..dac2c0f --- /dev/null +++ b/QR1676.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef QR1676_H +#define QR1676_H + +class CQR1676 { +public: + static void encode(unsigned char* data); + + static unsigned char decode(const unsigned char* data); + +private: + static unsigned int getSyndrome1576(unsigned int pattern); +}; + +#endif diff --git a/README.md b/README.md new file mode 100644 index 0000000..bd35feb --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +These are the source files for builing the MMDVMHost, the program that +interfaces to the MMDVM on one side, and a network on the other. + +It supports D-Star, DMR, and System Fusion. + +It builds on Linux as well as Windows using VS2015 on x86 and x64. diff --git a/RS129.cpp b/RS129.cpp new file mode 100644 index 0000000..8cad52c --- /dev/null +++ b/RS129.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "RS129.h" + +#include +#include +#include + +const unsigned int NPAR = 3U; + +/* Maximum degree of various polynomials. */ +const unsigned int MAXDEG = NPAR * 2U; + +/* Generator Polynomial */ +const unsigned char POLY[] = {64U, 56U, 14U, 1U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U}; + +const unsigned char EXP_TABLE[] = { + 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, 0x1DU, 0x3AU, 0x74U, 0xE8U, 0xCDU, 0x87U, 0x13U, 0x26U, + 0x4CU, 0x98U, 0x2DU, 0x5AU, 0xB4U, 0x75U, 0xEAU, 0xC9U, 0x8FU, 0x03U, 0x06U, 0x0CU, 0x18U, 0x30U, 0x60U, 0xC0U, + 0x9DU, 0x27U, 0x4EU, 0x9CU, 0x25U, 0x4AU, 0x94U, 0x35U, 0x6AU, 0xD4U, 0xB5U, 0x77U, 0xEEU, 0xC1U, 0x9FU, 0x23U, + 0x46U, 0x8CU, 0x05U, 0x0AU, 0x14U, 0x28U, 0x50U, 0xA0U, 0x5DU, 0xBAU, 0x69U, 0xD2U, 0xB9U, 0x6FU, 0xDEU, 0xA1U, + 0x5FU, 0xBEU, 0x61U, 0xC2U, 0x99U, 0x2FU, 0x5EU, 0xBCU, 0x65U, 0xCAU, 0x89U, 0x0FU, 0x1EU, 0x3CU, 0x78U, 0xF0U, + 0xFDU, 0xE7U, 0xD3U, 0xBBU, 0x6BU, 0xD6U, 0xB1U, 0x7FU, 0xFEU, 0xE1U, 0xDFU, 0xA3U, 0x5BU, 0xB6U, 0x71U, 0xE2U, + 0xD9U, 0xAFU, 0x43U, 0x86U, 0x11U, 0x22U, 0x44U, 0x88U, 0x0DU, 0x1AU, 0x34U, 0x68U, 0xD0U, 0xBDU, 0x67U, 0xCEU, + 0x81U, 0x1FU, 0x3EU, 0x7CU, 0xF8U, 0xEDU, 0xC7U, 0x93U, 0x3BU, 0x76U, 0xECU, 0xC5U, 0x97U, 0x33U, 0x66U, 0xCCU, + 0x85U, 0x17U, 0x2EU, 0x5CU, 0xB8U, 0x6DU, 0xDAU, 0xA9U, 0x4FU, 0x9EU, 0x21U, 0x42U, 0x84U, 0x15U, 0x2AU, 0x54U, + 0xA8U, 0x4DU, 0x9AU, 0x29U, 0x52U, 0xA4U, 0x55U, 0xAAU, 0x49U, 0x92U, 0x39U, 0x72U, 0xE4U, 0xD5U, 0xB7U, 0x73U, + 0xE6U, 0xD1U, 0xBFU, 0x63U, 0xC6U, 0x91U, 0x3FU, 0x7EU, 0xFCU, 0xE5U, 0xD7U, 0xB3U, 0x7BU, 0xF6U, 0xF1U, 0xFFU, + 0xE3U, 0xDBU, 0xABU, 0x4BU, 0x96U, 0x31U, 0x62U, 0xC4U, 0x95U, 0x37U, 0x6EU, 0xDCU, 0xA5U, 0x57U, 0xAEU, 0x41U, + 0x82U, 0x19U, 0x32U, 0x64U, 0xC8U, 0x8DU, 0x07U, 0x0EU, 0x1CU, 0x38U, 0x70U, 0xE0U, 0xDDU, 0xA7U, 0x53U, 0xA6U, + 0x51U, 0xA2U, 0x59U, 0xB2U, 0x79U, 0xF2U, 0xF9U, 0xEFU, 0xC3U, 0x9BU, 0x2BU, 0x56U, 0xACU, 0x45U, 0x8AU, 0x09U, + 0x12U, 0x24U, 0x48U, 0x90U, 0x3DU, 0x7AU, 0xF4U, 0xF5U, 0xF7U, 0xF3U, 0xFBU, 0xEBU, 0xCBU, 0x8BU, 0x0BU, 0x16U, + 0x2CU, 0x58U, 0xB0U, 0x7DU, 0xFAU, 0xE9U, 0xCFU, 0x83U, 0x1BU, 0x36U, 0x6CU, 0xD8U, 0xADU, 0x47U, 0x8EU, 0x01U, + 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, 0x1DU, 0x3AU, 0x74U, 0xE8U, 0xCDU, 0x87U, 0x13U, 0x26U, 0x4CU, + 0x98U, 0x2DU, 0x5AU, 0xB4U, 0x75U, 0xEAU, 0xC9U, 0x8FU, 0x03U, 0x06U, 0x0CU, 0x18U, 0x30U, 0x60U, 0xC0U, 0x9DU, + 0x27U, 0x4EU, 0x9CU, 0x25U, 0x4AU, 0x94U, 0x35U, 0x6AU, 0xD4U, 0xB5U, 0x77U, 0xEEU, 0xC1U, 0x9FU, 0x23U, 0x46U, + 0x8CU, 0x05U, 0x0AU, 0x14U, 0x28U, 0x50U, 0xA0U, 0x5DU, 0xBAU, 0x69U, 0xD2U, 0xB9U, 0x6FU, 0xDEU, 0xA1U, 0x5FU, + 0xBEU, 0x61U, 0xC2U, 0x99U, 0x2FU, 0x5EU, 0xBCU, 0x65U, 0xCAU, 0x89U, 0x0FU, 0x1EU, 0x3CU, 0x78U, 0xF0U, 0xFDU, + 0xE7U, 0xD3U, 0xBBU, 0x6BU, 0xD6U, 0xB1U, 0x7FU, 0xFEU, 0xE1U, 0xDFU, 0xA3U, 0x5BU, 0xB6U, 0x71U, 0xE2U, 0xD9U, + 0xAFU, 0x43U, 0x86U, 0x11U, 0x22U, 0x44U, 0x88U, 0x0DU, 0x1AU, 0x34U, 0x68U, 0xD0U, 0xBDU, 0x67U, 0xCEU, 0x81U, + 0x1FU, 0x3EU, 0x7CU, 0xF8U, 0xEDU, 0xC7U, 0x93U, 0x3BU, 0x76U, 0xECU, 0xC5U, 0x97U, 0x33U, 0x66U, 0xCCU, 0x85U, + 0x17U, 0x2EU, 0x5CU, 0xB8U, 0x6DU, 0xDAU, 0xA9U, 0x4FU, 0x9EU, 0x21U, 0x42U, 0x84U, 0x15U, 0x2AU, 0x54U, 0xA8U, + 0x4DU, 0x9AU, 0x29U, 0x52U, 0xA4U, 0x55U, 0xAAU, 0x49U, 0x92U, 0x39U, 0x72U, 0xE4U, 0xD5U, 0xB7U, 0x73U, 0xE6U, + 0xD1U, 0xBFU, 0x63U, 0xC6U, 0x91U, 0x3FU, 0x7EU, 0xFCU, 0xE5U, 0xD7U, 0xB3U, 0x7BU, 0xF6U, 0xF1U, 0xFFU, 0xE3U, + 0xDBU, 0xABU, 0x4BU, 0x96U, 0x31U, 0x62U, 0xC4U, 0x95U, 0x37U, 0x6EU, 0xDCU, 0xA5U, 0x57U, 0xAEU, 0x41U, 0x82U, + 0x19U, 0x32U, 0x64U, 0xC8U, 0x8DU, 0x07U, 0x0EU, 0x1CU, 0x38U, 0x70U, 0xE0U, 0xDDU, 0xA7U, 0x53U, 0xA6U, 0x51U, + 0xA2U, 0x59U, 0xB2U, 0x79U, 0xF2U, 0xF9U, 0xEFU, 0xC3U, 0x9BU, 0x2BU, 0x56U, 0xACU, 0x45U, 0x8AU, 0x09U, 0x12U, + 0x24U, 0x48U, 0x90U, 0x3DU, 0x7AU, 0xF4U, 0xF5U, 0xF7U, 0xF3U, 0xFBU, 0xEBU, 0xCBU, 0x8BU, 0x0BU, 0x16U, 0x2CU, + 0x58U, 0xB0U, 0x7DU, 0xFAU, 0xE9U, 0xCFU, 0x83U, 0x1BU, 0x36U, 0x6CU, 0xD8U, 0xADU, 0x47U, 0x8EU, 0x01U, 0x00U}; + +const unsigned char LOG_TABLE[] = { + 0x00U, 0x00U, 0x01U, 0x19U, 0x02U, 0x32U, 0x1AU, 0xC6U, 0x03U, 0xDFU, 0x33U, 0xEEU, 0x1BU, 0x68U, 0xC7U, 0x4BU, + 0x04U, 0x64U, 0xE0U, 0x0EU, 0x34U, 0x8DU, 0xEFU, 0x81U, 0x1CU, 0xC1U, 0x69U, 0xF8U, 0xC8U, 0x08U, 0x4CU, 0x71U, + 0x05U, 0x8AU, 0x65U, 0x2FU, 0xE1U, 0x24U, 0x0FU, 0x21U, 0x35U, 0x93U, 0x8EU, 0xDAU, 0xF0U, 0x12U, 0x82U, 0x45U, + 0x1DU, 0xB5U, 0xC2U, 0x7DU, 0x6AU, 0x27U, 0xF9U, 0xB9U, 0xC9U, 0x9AU, 0x09U, 0x78U, 0x4DU, 0xE4U, 0x72U, 0xA6U, + 0x06U, 0xBFU, 0x8BU, 0x62U, 0x66U, 0xDDU, 0x30U, 0xFDU, 0xE2U, 0x98U, 0x25U, 0xB3U, 0x10U, 0x91U, 0x22U, 0x88U, + 0x36U, 0xD0U, 0x94U, 0xCEU, 0x8FU, 0x96U, 0xDBU, 0xBDU, 0xF1U, 0xD2U, 0x13U, 0x5CU, 0x83U, 0x38U, 0x46U, 0x40U, + 0x1EU, 0x42U, 0xB6U, 0xA3U, 0xC3U, 0x48U, 0x7EU, 0x6EU, 0x6BU, 0x3AU, 0x28U, 0x54U, 0xFAU, 0x85U, 0xBAU, 0x3DU, + 0xCAU, 0x5EU, 0x9BU, 0x9FU, 0x0AU, 0x15U, 0x79U, 0x2BU, 0x4EU, 0xD4U, 0xE5U, 0xACU, 0x73U, 0xF3U, 0xA7U, 0x57U, + 0x07U, 0x70U, 0xC0U, 0xF7U, 0x8CU, 0x80U, 0x63U, 0x0DU, 0x67U, 0x4AU, 0xDEU, 0xEDU, 0x31U, 0xC5U, 0xFEU, 0x18U, + 0xE3U, 0xA5U, 0x99U, 0x77U, 0x26U, 0xB8U, 0xB4U, 0x7CU, 0x11U, 0x44U, 0x92U, 0xD9U, 0x23U, 0x20U, 0x89U, 0x2EU, + 0x37U, 0x3FU, 0xD1U, 0x5BU, 0x95U, 0xBCU, 0xCFU, 0xCDU, 0x90U, 0x87U, 0x97U, 0xB2U, 0xDCU, 0xFCU, 0xBEU, 0x61U, + 0xF2U, 0x56U, 0xD3U, 0xABU, 0x14U, 0x2AU, 0x5DU, 0x9EU, 0x84U, 0x3CU, 0x39U, 0x53U, 0x47U, 0x6DU, 0x41U, 0xA2U, + 0x1FU, 0x2DU, 0x43U, 0xD8U, 0xB7U, 0x7BU, 0xA4U, 0x76U, 0xC4U, 0x17U, 0x49U, 0xECU, 0x7FU, 0x0CU, 0x6FU, 0xF6U, + 0x6CU, 0xA1U, 0x3BU, 0x52U, 0x29U, 0x9DU, 0x55U, 0xAAU, 0xFBU, 0x60U, 0x86U, 0xB1U, 0xBBU, 0xCCU, 0x3EU, 0x5AU, + 0xCBU, 0x59U, 0x5FU, 0xB0U, 0x9CU, 0xA9U, 0xA0U, 0x51U, 0x0BU, 0xF5U, 0x16U, 0xEBU, 0x7AU, 0x75U, 0x2CU, 0xD7U, + 0x4FU, 0xAEU, 0xD5U, 0xE9U, 0xE6U, 0xE7U, 0xADU, 0xE8U, 0x74U, 0xD6U, 0xF4U, 0xEAU, 0xA8U, 0x50U, 0x58U, 0xAFU}; + +/* multiplication using logarithms */ +static unsigned char gmult(unsigned char a, unsigned char b) +{ + if (a == 0U || b == 0U) + return 0U; + + unsigned int i = LOG_TABLE[a]; + unsigned int j = LOG_TABLE[b]; + + return EXP_TABLE[i + j]; +} + +/* Simulate a LFSR with generator polynomial for n byte RS code. + * Pass in a pointer to the data array, and amount of data. + * + * The parity bytes are deposited into parity. + */ +void CRS129::encode(const unsigned char* msg, unsigned int nbytes, unsigned char* parity) +{ + assert(msg != NULL); + assert(parity != NULL); + + for (unsigned int i = 0U; i < NPAR + 1U; i++) + parity[i] = 0x00U; + + for (unsigned int i = 0U; i < nbytes; i++) { + unsigned char dbyte = msg[i] ^ parity[NPAR - 1U]; + + for (int j = NPAR - 1; j > 0; j--) + parity[j] = parity[j - 1] ^ ::gmult(POLY[j], dbyte); + + parity[0] = ::gmult(POLY[0], dbyte); + } +} + +// Reed-Solomon (12,9) check +bool CRS129::check(const unsigned char* in) +{ + assert(in != NULL); + + unsigned char parity[4U]; + encode(in, 9U, parity); + + return in[9U] == parity[2U] && in[10U] == parity[1U] && in[11U] == parity[0U]; +} + diff --git a/RS129.h b/RS129.h new file mode 100644 index 0000000..60f99bd --- /dev/null +++ b/RS129.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(RS129_H) +#define RS129_H + +class CRS129 +{ +public: + static bool check(const unsigned char* in); + + static void encode(const unsigned char* msg, unsigned int nbytes, unsigned char* parity); +}; + +#endif diff --git a/RingBuffer.h b/RingBuffer.h new file mode 100644 index 0000000..07d4798 --- /dev/null +++ b/RingBuffer.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2006-2009,2012,2013,2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef RingBuffer_H +#define RingBuffer_H + +#include +#include + +template class CRingBuffer { +public: + CRingBuffer(unsigned int length) : + m_length(length), + m_buffer(NULL), + m_iPtr(0U), + m_oPtr(0U) + { + assert(length > 0U); + + m_buffer = new T[length]; + + ::memset(m_buffer, 0x00, m_length * sizeof(T)); + } + + ~CRingBuffer() + { + delete[] m_buffer; + } + + unsigned int addData(const T* buffer, unsigned int nSamples) + { + if (nSamples > freeSpace()) + return 0U; + + for (unsigned int i = 0U; i < nSamples; i++) { + m_buffer[m_iPtr++] = buffer[i]; + + if (m_iPtr == m_length) + m_iPtr = 0U; + } + + return nSamples; + } + + unsigned int getData(T* buffer, unsigned int nSamples) + { + unsigned int data = dataSize(); + + if (data < nSamples) + nSamples = data; + + for (unsigned int i = 0U; i < nSamples; i++) { + buffer[i] = m_buffer[m_oPtr++]; + + if (m_oPtr == m_length) + m_oPtr = 0U; + } + + return nSamples; + } + + unsigned int peek(T* buffer, unsigned int nSamples) + { + unsigned int data = dataSize(); + + if (data < nSamples) + nSamples = data; + + unsigned int ptr = m_oPtr; + for (unsigned int i = 0U; i < nSamples; i++) { + buffer[i] = m_buffer[ptr++]; + + if (ptr == m_length) + ptr = 0U; + } + + return nSamples; + } + + void clear() + { + m_iPtr = 0U; + m_oPtr = 0U; + + ::memset(m_buffer, 0x00, m_length * sizeof(T)); + } + + unsigned int freeSpace() const + { + if (m_oPtr == m_iPtr) + return m_length - 1U; + + if (m_oPtr > m_iPtr) + return m_oPtr - m_iPtr - 1U; + + return m_length - (m_iPtr - m_oPtr) - 1U; + } + + bool hasSpace(unsigned int length) const + { + return freeSpace() > length; + } + + bool hasData() const + { + return m_oPtr != m_iPtr; + } + + bool isEmpty() const + { + return m_oPtr == m_iPtr; + } + +private: + unsigned int m_length; + T* m_buffer; + volatile unsigned int m_iPtr; + volatile unsigned int m_oPtr; + + unsigned int dataSize() const + { + if (m_iPtr >= m_oPtr) + return m_iPtr - m_oPtr; + + return m_length - (m_oPtr - m_iPtr); + } +}; + +#endif diff --git a/SHA256.cpp b/SHA256.cpp new file mode 100644 index 0000000..b3366e0 --- /dev/null +++ b/SHA256.cpp @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2005, 2006, 2008 Free Software Foundation, Inc. + * Copyright (C) 2011,2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "SHA256.h" + +#include +#include +#include + +#ifdef WORDS_BIGENDIAN +# define SWAP(n) (n) +#else +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#endif + +#define BLOCKSIZE 4096 +#if BLOCKSIZE % 64 != 0 +# error "invalid BLOCKSIZE" +#endif + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* + Takes a pointer to a 256 bit block of data (eight 32 bit ints) and + intializes it to the start constants of the SHA256 algorithm. This + must be called before using hash in the call to sha256_hash +*/ +CSHA256::CSHA256() : +m_state(NULL), +m_total(NULL), +m_buflen(0U), +m_buffer(NULL) +{ + m_state = new uint32_t[8U]; + m_total = new uint32_t[2U]; + m_buffer = new uint32_t[32U]; + + init(); +} + +CSHA256::~CSHA256() +{ + delete[] m_state; + delete[] m_total; + delete[] m_buffer; +} + +void CSHA256::init() +{ + m_state[0] = 0x6a09e667UL; + m_state[1] = 0xbb67ae85UL; + m_state[2] = 0x3c6ef372UL; + m_state[3] = 0xa54ff53aUL; + m_state[4] = 0x510e527fUL; + m_state[5] = 0x9b05688cUL; + m_state[6] = 0x1f83d9abUL; + m_state[7] = 0x5be0cd19UL; + + m_total[0] = m_total[1] = 0; + m_buflen = 0; +} + +/* Copy the value from v into the memory location pointed to by *cp, + If your architecture allows unaligned access this is equivalent to + * (uint32_t *) cp = v */ +static inline void set_uint32(unsigned char* cp, uint32_t v) +{ + assert(cp != NULL); + + ::memcpy(cp, &v, sizeof v); +} + +/* Put result from CTX in first 32 bytes following RESBUF. The result + must be in little endian byte order. */ +unsigned char* CSHA256::read(unsigned char* resbuf) +{ + assert(resbuf != NULL); + + for (unsigned int i = 0U; i < 8U; i++) + set_uint32(resbuf + i * sizeof(m_state[0]), SWAP(m_state[i])); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. */ +void CSHA256::conclude() +{ + /* Take yet unprocessed bytes into account. */ + unsigned int bytes = m_buflen; + unsigned int size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; + + /* Now count remaining bytes. */ + m_total[0] += bytes; + if (m_total[0] < bytes) + ++m_total[1]; + + /* Put the 64-bit file length in *bits* at the end of the buffer. + Use set_uint32 rather than a simple assignment, to avoid risk of + unaligned access. */ + set_uint32((unsigned char*)&m_buffer[size - 2], SWAP((m_total[1] << 3) | (m_total[0] >> 29))); + set_uint32((unsigned char*)&m_buffer[size - 1], SWAP(m_total[0] << 3)); + + ::memcpy(&((char*)m_buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); + + /* Process last bytes. */ + processBlock((unsigned char*)m_buffer, size * 4); +} + +unsigned char* CSHA256::finish(unsigned char* resbuf) +{ + assert(resbuf != NULL); + + conclude(); + + return read(resbuf); +} + +/* Compute SHA256 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +unsigned char* CSHA256::buffer(const unsigned char* buffer, unsigned int len, unsigned char* resblock) +{ + assert(buffer != NULL); + assert(resblock != NULL); + + /* Initialize the computation context. */ + init(); + + /* Process whole buffer but last len % 64 bytes. */ + processBytes(buffer, len); + + /* Put result in desired memory area. */ + return finish(resblock); +} + +void CSHA256::processBytes(const unsigned char* buffer, unsigned int len) +{ + assert(buffer != NULL); + + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (m_buflen != 0U) { + unsigned int left_over = m_buflen; + unsigned int add = 128U - left_over > len ? len : 128U - left_over; + + ::memcpy(&((char*)m_buffer)[left_over], buffer, add); + m_buflen += add; + + if (m_buflen > 64U) { + processBlock((unsigned char*)m_buffer, m_buflen & ~63U); + + m_buflen &= 63U; + + /* The regions in the following copy operation cannot overlap. */ + ::memcpy(m_buffer, &((char*)m_buffer)[(left_over + add) & ~63U], m_buflen); + } + + buffer += add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64U) { +//#if !_STRING_ARCH_unaligned +//# define alignof(type) offsetof (struct { char c; type x; }, x) +//# define UNALIGNED_P(p) (((unsigned int) p) % alignof (uint32_t) != 0) +// if (UNALIGNED_P (buffer)) { +// while (len > 64U) { +// ::memcpy(m_buffer, buffer, 64U); +// processBlock((unsigned char*)m_buffer, 64U); +// buffer += 64U; +// len -= 64U; +// } +// } else +//#endif + { + processBlock(buffer, len & ~63U); + buffer += (len & ~63U); + len &= 63U; + } + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0U) { + unsigned int left_over = m_buflen; + + ::memcpy(&((char*)m_buffer)[left_over], buffer, len); + left_over += len; + + if (left_over >= 64U) { + processBlock((unsigned char*)m_buffer, 64U); + left_over -= 64U; + ::memcpy(m_buffer, &m_buffer[16], left_over); + } + + m_buflen = left_over; + } +} + +/* --- Code below is the primary difference between sha1.c and sha256.c --- */ + +/* SHA256 round constants */ +#define K(I) roundConstants[I] +static const uint32_t roundConstants[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL, +}; + +/* Round functions. */ +#define F2(A,B,C) ( ( A & B ) | ( C & ( A | B ) ) ) +#define F1(E,F,G) ( G ^ ( E & ( F ^ G ) ) ) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. + Most of this code comes from GnuPG's cipher/sha1.c. */ + +void CSHA256::processBlock(const unsigned char* buffer, unsigned int len) +{ + assert(buffer != NULL); + + const uint32_t* words = (uint32_t*)buffer; + unsigned int nwords = len / sizeof(uint32_t); + const uint32_t* endp = words + nwords; + uint32_t x[16]; + uint32_t a = m_state[0]; + uint32_t b = m_state[1]; + uint32_t c = m_state[2]; + uint32_t d = m_state[3]; + uint32_t e = m_state[4]; + uint32_t f = m_state[5]; + uint32_t g = m_state[6]; + uint32_t h = m_state[7]; + + /* First increment the byte count. FIPS PUB 180-2 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + m_total[0] += len; + if (m_total[0] < len) + ++m_total[1]; + + #define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + #define S0(x) (rol(x,25)^rol(x,14)^(x>>3)) + #define S1(x) (rol(x,15)^rol(x,13)^(x>>10)) + #define SS0(x) (rol(x,30)^rol(x,19)^rol(x,10)) + #define SS1(x) (rol(x,26)^rol(x,21)^rol(x,7)) + + #define M(I) (tm = S1(x[(I-2)&0x0f]) + x[(I-7)&0x0f] + S0(x[(I-15)&0x0f]) + x[I&0x0f], x[I&0x0f] = tm) + + #define R(A,B,C,D,E,F,G,H,K,M) do { t0 = SS0(A) + F2(A,B,C); \ + t1 = H + SS1(E) + F1(E,F,G) + K + M; \ + D += t1; H = t0 + t1; \ + } while(0) + + while (words < endp) { + uint32_t tm; + uint32_t t0, t1; + /* FIXME: see sha1.c for a better implementation. */ + for (unsigned int t = 0U; t < 16U; t++) { + x[t] = SWAP(*words); + words++; + } + + R( a, b, c, d, e, f, g, h, K( 0), x[ 0] ); + R( h, a, b, c, d, e, f, g, K( 1), x[ 1] ); + R( g, h, a, b, c, d, e, f, K( 2), x[ 2] ); + R( f, g, h, a, b, c, d, e, K( 3), x[ 3] ); + R( e, f, g, h, a, b, c, d, K( 4), x[ 4] ); + R( d, e, f, g, h, a, b, c, K( 5), x[ 5] ); + R( c, d, e, f, g, h, a, b, K( 6), x[ 6] ); + R( b, c, d, e, f, g, h, a, K( 7), x[ 7] ); + R( a, b, c, d, e, f, g, h, K( 8), x[ 8] ); + R( h, a, b, c, d, e, f, g, K( 9), x[ 9] ); + R( g, h, a, b, c, d, e, f, K(10), x[10] ); + R( f, g, h, a, b, c, d, e, K(11), x[11] ); + R( e, f, g, h, a, b, c, d, K(12), x[12] ); + R( d, e, f, g, h, a, b, c, K(13), x[13] ); + R( c, d, e, f, g, h, a, b, K(14), x[14] ); + R( b, c, d, e, f, g, h, a, K(15), x[15] ); + R( a, b, c, d, e, f, g, h, K(16), M(16) ); + R( h, a, b, c, d, e, f, g, K(17), M(17) ); + R( g, h, a, b, c, d, e, f, K(18), M(18) ); + R( f, g, h, a, b, c, d, e, K(19), M(19) ); + R( e, f, g, h, a, b, c, d, K(20), M(20) ); + R( d, e, f, g, h, a, b, c, K(21), M(21) ); + R( c, d, e, f, g, h, a, b, K(22), M(22) ); + R( b, c, d, e, f, g, h, a, K(23), M(23) ); + R( a, b, c, d, e, f, g, h, K(24), M(24) ); + R( h, a, b, c, d, e, f, g, K(25), M(25) ); + R( g, h, a, b, c, d, e, f, K(26), M(26) ); + R( f, g, h, a, b, c, d, e, K(27), M(27) ); + R( e, f, g, h, a, b, c, d, K(28), M(28) ); + R( d, e, f, g, h, a, b, c, K(29), M(29) ); + R( c, d, e, f, g, h, a, b, K(30), M(30) ); + R( b, c, d, e, f, g, h, a, K(31), M(31) ); + R( a, b, c, d, e, f, g, h, K(32), M(32) ); + R( h, a, b, c, d, e, f, g, K(33), M(33) ); + R( g, h, a, b, c, d, e, f, K(34), M(34) ); + R( f, g, h, a, b, c, d, e, K(35), M(35) ); + R( e, f, g, h, a, b, c, d, K(36), M(36) ); + R( d, e, f, g, h, a, b, c, K(37), M(37) ); + R( c, d, e, f, g, h, a, b, K(38), M(38) ); + R( b, c, d, e, f, g, h, a, K(39), M(39) ); + R( a, b, c, d, e, f, g, h, K(40), M(40) ); + R( h, a, b, c, d, e, f, g, K(41), M(41) ); + R( g, h, a, b, c, d, e, f, K(42), M(42) ); + R( f, g, h, a, b, c, d, e, K(43), M(43) ); + R( e, f, g, h, a, b, c, d, K(44), M(44) ); + R( d, e, f, g, h, a, b, c, K(45), M(45) ); + R( c, d, e, f, g, h, a, b, K(46), M(46) ); + R( b, c, d, e, f, g, h, a, K(47), M(47) ); + R( a, b, c, d, e, f, g, h, K(48), M(48) ); + R( h, a, b, c, d, e, f, g, K(49), M(49) ); + R( g, h, a, b, c, d, e, f, K(50), M(50) ); + R( f, g, h, a, b, c, d, e, K(51), M(51) ); + R( e, f, g, h, a, b, c, d, K(52), M(52) ); + R( d, e, f, g, h, a, b, c, K(53), M(53) ); + R( c, d, e, f, g, h, a, b, K(54), M(54) ); + R( b, c, d, e, f, g, h, a, K(55), M(55) ); + R( a, b, c, d, e, f, g, h, K(56), M(56) ); + R( h, a, b, c, d, e, f, g, K(57), M(57) ); + R( g, h, a, b, c, d, e, f, K(58), M(58) ); + R( f, g, h, a, b, c, d, e, K(59), M(59) ); + R( e, f, g, h, a, b, c, d, K(60), M(60) ); + R( d, e, f, g, h, a, b, c, K(61), M(61) ); + R( c, d, e, f, g, h, a, b, K(62), M(62) ); + R( b, c, d, e, f, g, h, a, K(63), M(63) ); + + a = m_state[0] += a; + b = m_state[1] += b; + c = m_state[2] += c; + d = m_state[3] += d; + e = m_state[4] += e; + f = m_state[5] += f; + g = m_state[6] += g; + h = m_state[7] += h; + } +} diff --git a/SHA256.h b/SHA256.h new file mode 100644 index 0000000..24d7be5 --- /dev/null +++ b/SHA256.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005, 2006, 2008, 2009 Free Software Foundation, Inc. + * Copyright (C) 2011,2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef SHA256_H +#define SHA256_H + +#if defined(WIN32) +typedef unsigned int uint32_t; +#else +#include +#endif + +enum { + SHA256_DIGEST_SIZE = 256 / 8 +}; + +class CSHA256 { +public: + CSHA256(); + ~CSHA256(); + + /* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ + void processBlock(const unsigned char* buffer, unsigned int len); + + /* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is NOT required that LEN is a multiple of 64. */ + void processBytes(const unsigned char* buffer, unsigned int len); + + /* Process the remaining bytes in the buffer and put result from CTX + in first 32 bytes following RESBUF. The result is always in little + endian byte order, so that a byte-wise output yields to the wanted + ASCII representation of the message digest. */ + unsigned char* finish(unsigned char* resbuf); + + /* Put result from CTX in first 32 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. */ + unsigned char* read(unsigned char* resbuf); + + /* Compute SHA256 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ + unsigned char* buffer(const unsigned char* buffer, unsigned int len, unsigned char* resblock); + +private: + uint32_t* m_state; + uint32_t* m_total; + unsigned int m_buflen; + uint32_t* m_buffer; + + void init(); + void conclude(); +}; + +#endif diff --git a/SerialController.cpp b/SerialController.cpp new file mode 100644 index 0000000..25a6eaf --- /dev/null +++ b/SerialController.cpp @@ -0,0 +1,467 @@ +/* + * Copyright (C) 2002-2004,2007-2011,2013,2014,2015 by Jonathan Naylor G4KLX + * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "SerialController.h" +#include "Log.h" + +#include + +#include + +#if defined(WIN32) +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + + +#if defined(WIN32) + +const unsigned int BUFFER_LENGTH = 1000U; + +CSerialController::CSerialController(const std::string& device, SERIAL_SPEED speed, bool assertRTS) : +m_device(device), +m_speed(speed), +m_assertRTS(assertRTS), +m_handle(INVALID_HANDLE_VALUE), +m_readOverlapped(), +m_writeOverlapped(), +m_readBuffer(NULL), +m_readLength(0U), +m_readPending(false) +{ + assert(!device.empty()); + + m_readBuffer = new unsigned char[BUFFER_LENGTH]; +} + +CSerialController::~CSerialController() +{ + delete[] m_readBuffer; +} + +bool CSerialController::open() +{ + assert(m_handle == INVALID_HANDLE_VALUE); + + DWORD errCode; + + std::string baseName = m_device.substr(4U); // Convert "\\.\COM10" to "COM10" + + m_handle = ::CreateFileA(m_device.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (m_handle == INVALID_HANDLE_VALUE) { + LogError("Cannot open device - %s, err=%04lx", m_device.c_str(), ::GetLastError()); + return false; + } + + DCB dcb; + if (::GetCommState(m_handle, &dcb) == 0) { + LogError("Cannot get the attributes for %s, err=%04lx", m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + dcb.BaudRate = DWORD(m_speed); + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; + dcb.fParity = FALSE; + dcb.StopBits = ONESTOPBIT; + dcb.fInX = FALSE; + dcb.fOutX = FALSE; + dcb.fOutxCtsFlow = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.fDtrControl = DTR_CONTROL_DISABLE; + dcb.fRtsControl = RTS_CONTROL_DISABLE; + + if (::SetCommState(m_handle, &dcb) == 0) { + LogError("Cannot set the attributes for %s, err=%04lx", m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + COMMTIMEOUTS timeouts; + if (!::GetCommTimeouts(m_handle, &timeouts)) { + LogError("Cannot get the timeouts for %s, err=%04lx", m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutMultiplier = 0UL; + timeouts.ReadTotalTimeoutConstant = 0UL; + + if (!::SetCommTimeouts(m_handle, &timeouts)) { + LogError("Cannot set the timeouts for %s, err=%04lx", m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + if (::EscapeCommFunction(m_handle, CLRDTR) == 0) { + LogError("Cannot clear DTR for %s, err=%04lx", m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + if (::EscapeCommFunction(m_handle, m_assertRTS ? SETRTS : CLRRTS) == 0) { + LogError("Cannot set/clear RTS for %s, err=%04lx", m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + ::ClearCommError(m_handle, &errCode, NULL); + + ::memset(&m_readOverlapped, 0x00U, sizeof(OVERLAPPED)); + ::memset(&m_writeOverlapped, 0x00U, sizeof(OVERLAPPED)); + + m_readOverlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); + m_writeOverlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); + + m_readLength = 0U; + m_readPending = false; + ::memset(m_readBuffer, 0x00U, BUFFER_LENGTH); + + return true; +} + +int CSerialController::read(unsigned char* buffer, unsigned int length) +{ + assert(m_handle != INVALID_HANDLE_VALUE); + assert(buffer != NULL); + + unsigned int ptr = 0U; + + while (ptr < length) { + int ret = readNonblock(buffer + ptr, length - ptr); + if (ret < 0) { + return ret; + } else if (ret == 0) { + if (ptr == 0U) + return 0; + } else { + ptr += ret; + } + } + + return int(length); +} + +int CSerialController::readNonblock(unsigned char* buffer, unsigned int length) +{ + assert(m_handle != INVALID_HANDLE_VALUE); + assert(buffer != NULL); + + if (length > BUFFER_LENGTH) + length = BUFFER_LENGTH; + + if (m_readPending && length != m_readLength) { + ::CancelIo(m_handle); + m_readPending = false; + } + + m_readLength = length; + + if (length == 0U) + return 0; + + if (!m_readPending) { + DWORD bytes = 0UL; + BOOL res = ::ReadFile(m_handle, m_readBuffer, m_readLength, &bytes, &m_readOverlapped); + if (res) { + ::memcpy(buffer, m_readBuffer, bytes); + return int(bytes); + } + + DWORD error = ::GetLastError(); + if (error != ERROR_IO_PENDING) { + LogError("Error from ReadFile: %04lx", error); + return -1; + } + + m_readPending = true; + } + + BOOL res = HasOverlappedIoCompleted(&m_readOverlapped); + if (!res) + return 0; + + DWORD bytes = 0UL; + res = ::GetOverlappedResult(m_handle, &m_readOverlapped, &bytes, TRUE); + if (!res) { + LogError("Error from GetOverlappedResult (ReadFile): %04lx", ::GetLastError()); + return -1; + } + + ::memcpy(buffer, m_readBuffer, bytes); + m_readPending = false; + + return int(bytes); +} + +int CSerialController::write(const unsigned char* buffer, unsigned int length) +{ + assert(m_handle != INVALID_HANDLE_VALUE); + assert(buffer != NULL); + + if (length == 0U) + return 0; + + unsigned int ptr = 0U; + + while (ptr < length) { + DWORD bytes = 0UL; + BOOL res = ::WriteFile(m_handle, buffer + ptr, length - ptr, &bytes, &m_writeOverlapped); + if (!res) { + DWORD error = ::GetLastError(); + if (error != ERROR_IO_PENDING) { + LogError("Error from WriteFile: %04lx", error); + return -1; + } + + res = ::GetOverlappedResult(m_handle, &m_writeOverlapped, &bytes, TRUE); + if (!res) { + LogError("Error from GetOverlappedResult (WriteFile): %04lx", ::GetLastError()); + return -1; + } + } + + ptr += bytes; + } + + return int(length); +} + +void CSerialController::close() +{ + assert(m_handle != INVALID_HANDLE_VALUE); + + ::CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; + + ::CloseHandle(m_readOverlapped.hEvent); + ::CloseHandle(m_writeOverlapped.hEvent); +} + +#else + +CSerialController::CSerialController(const std::string& device, SERIAL_SPEED speed, bool assertRTS) : +m_device(device), +m_speed(speed), +m_assertRTS(assertRTS), +m_fd(-1) +{ + assert(!device.empty()); +} + +CSerialController::~CSerialController() +{ +} + +bool CSerialController::open() +{ + assert(m_fd == -1); + + m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0); + if (m_fd < 0) { + LogError("Cannot open device - %s", m_device.c_str()); + return false; + } + + if (::isatty(m_fd) == 0) { + LogError("%s is not a TTY device", m_device.c_str()); + ::close(m_fd); + return false; + } + + termios termios; + if (::tcgetattr(m_fd, &termios) < 0) { + LogError("Cannot get the attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; + } + + termios.c_lflag &= ~(ECHO | ECHOE | ICANON | IEXTEN | ISIG); + termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON | IXOFF | IXANY); + termios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS); + termios.c_cflag |= CS8; + termios.c_oflag &= ~(OPOST); + termios.c_cc[VMIN] = 0; + termios.c_cc[VTIME] = 10; + + switch (m_speed) { + case SERIAL_1200: + ::cfsetospeed(&termios, B1200); + ::cfsetispeed(&termios, B1200); + break; + case SERIAL_2400: + ::cfsetospeed(&termios, B2400); + ::cfsetispeed(&termios, B2400); + break; + case SERIAL_4800: + ::cfsetospeed(&termios, B4800); + ::cfsetispeed(&termios, B4800); + break; + case SERIAL_9600: + ::cfsetospeed(&termios, B9600); + ::cfsetispeed(&termios, B9600); + break; + case SERIAL_19200: + ::cfsetospeed(&termios, B19200); + ::cfsetispeed(&termios, B19200); + break; + case SERIAL_38400: + ::cfsetospeed(&termios, B38400); + ::cfsetispeed(&termios, B38400); + break; + case SERIAL_115200: + ::cfsetospeed(&termios, B115200); + ::cfsetispeed(&termios, B115200); + break; + case SERIAL_230400: + ::cfsetospeed(&termios, B230400); + ::cfsetispeed(&termios, B230400); + break; + default: + LogError("Unsupported serial port speed - %d", int(m_speed)); + ::close(m_fd); + return false; + } + + if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) { + LogError("Cannot set the attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; + } + + if (m_assertRTS) { + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) { + LogError("Cannot get the control attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; + } + + y |= TIOCM_RTS; + + if (::ioctl(m_fd, TIOCMSET, &y) < 0) { + LogError("Cannot set the control attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; + } + } + + return true; +} + +int CSerialController::read(unsigned char* buffer, unsigned int length) +{ + assert(buffer != NULL); + assert(m_fd != -1); + + if (length == 0U) + return 0; + + unsigned int offset = 0U; + + while (offset < length) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(m_fd, &fds); + + int n; + if (offset == 0U) { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + + n = ::select(m_fd + 1, &fds, NULL, NULL, &tv); + if (n == 0) + return 0; + } else { + n = ::select(m_fd + 1, &fds, NULL, NULL, NULL); + } + + if (n < 0) { + LogError("Error from select(), errno=%d", errno); + return -1; + } + + if (n > 0) { + ssize_t len = ::read(m_fd, buffer + offset, length - offset); + if (len < 0) { + if (errno != EAGAIN) { + LogError("Error from read(), errno=%d", errno); + return -1; + } + } + + if (len > 0) + offset += len; + } + } + + return length; +} + +int CSerialController::write(const unsigned char* buffer, unsigned int length) +{ + assert(buffer != NULL); + assert(m_fd != -1); + + if (length == 0U) + return 0; + + unsigned int ptr = 0U; + + while (ptr < length) { + ssize_t n = ::write(m_fd, buffer + ptr, length - ptr); + if (n < 0) { + if (errno != EAGAIN) { + LogError("Error returned from write(), errno=%d", errno); + return -1; + } + } + + if (n > 0) + ptr += n; + } + + return length; +} + +void CSerialController::close() +{ + assert(m_fd != -1); + + ::close(m_fd); + m_fd = -1; +} + +#endif diff --git a/SerialController.h b/SerialController.h new file mode 100644 index 0000000..ade0ad6 --- /dev/null +++ b/SerialController.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2002-2004,2007-2009,2011-2013,2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef SerialController_H +#define SerialController_H + +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +#endif + +enum SERIAL_SPEED { + SERIAL_1200 = 1200, + SERIAL_2400 = 2400, + SERIAL_4800 = 4800, + SERIAL_9600 = 9600, + SERIAL_19200 = 19200, + SERIAL_38400 = 38400, + SERIAL_76800 = 76800, + SERIAL_115200 = 115200, + SERIAL_230400 = 230400 +}; + +class CSerialController { +public: + CSerialController(const std::string& device, SERIAL_SPEED speed, bool assertRTS = false); + ~CSerialController(); + + bool open(); + + int read(unsigned char* buffer, unsigned int length); + int write(const unsigned char* buffer, unsigned int length); + + void close(); + +private: + std::string m_device; + SERIAL_SPEED m_speed; + bool m_assertRTS; +#if defined(_WIN32) || defined(_WIN64) + HANDLE m_handle; + OVERLAPPED m_readOverlapped; + OVERLAPPED m_writeOverlapped; + unsigned char* m_readBuffer; + unsigned int m_readLength; + bool m_readPending; +#else + int m_fd; +#endif + +#if defined(_WIN32) || defined(_WIN64) + int readNonblock(unsigned char* buffer, unsigned int length); +#endif +}; + +#endif diff --git a/ShortLC.cpp b/ShortLC.cpp new file mode 100644 index 0000000..8b6c56a --- /dev/null +++ b/ShortLC.cpp @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "ShortLC.h" + +#include "Hamming.h" +#include "Utils.h" + +#include +#include +#include + +CShortLC::CShortLC() : +m_rawData(NULL), +m_deInterData(NULL) +{ + m_rawData = new bool[72U]; + m_deInterData = new bool[68U]; +} + +CShortLC::~CShortLC() +{ + delete[] m_rawData; + delete[] m_deInterData; +} + +// The main decode function +bool CShortLC::decode(const unsigned char* in, unsigned char* out) +{ + assert(in != NULL); + assert(out != NULL); + + // Get the raw binary + decodeExtractBinary(in); + + // Deinterleave + decodeDeInterleave(); + + // Error check + bool ret = decodeErrorCheck(); + if (!ret) + return false; + + // Extract Data + decodeExtractData(out); + + return true; +} + +// The main encode function +void CShortLC::encode(const unsigned char* in, unsigned char* out) +{ + assert(in != NULL); + assert(out != NULL); + + // Extract Data + encodeExtractData(in); + + // Error check + encodeErrorCheck(); + + // Deinterleave + encodeInterleave(); + + // Get the raw binary + encodeExtractBinary(out); +} + +void CShortLC::decodeExtractBinary(const unsigned char* in) +{ + CUtils::byteToBitsBE(in[0U], m_rawData + 0U); + CUtils::byteToBitsBE(in[1U], m_rawData + 8U); + CUtils::byteToBitsBE(in[2U], m_rawData + 16U); + CUtils::byteToBitsBE(in[3U], m_rawData + 24U); + CUtils::byteToBitsBE(in[4U], m_rawData + 32U); + CUtils::byteToBitsBE(in[5U], m_rawData + 40U); + CUtils::byteToBitsBE(in[6U], m_rawData + 48U); + CUtils::byteToBitsBE(in[7U], m_rawData + 56U); + CUtils::byteToBitsBE(in[8U], m_rawData + 64U); +} + +// Deinterleave the raw data +void CShortLC::decodeDeInterleave() +{ + for (unsigned int i = 0U; i < 68U; i++) + m_deInterData[i] = false; + + for (unsigned int a = 0U; a < 67U; a++) { + // Calculate the interleave sequence + unsigned int interleaveSequence = (a * 4U) % 67U; + // Shuffle the data + m_deInterData[a] = m_rawData[interleaveSequence]; + } + + m_deInterData[67U] = m_rawData[67U]; +} + +// Check each row with a Hamming (17,12,3) code and each column with a parity bit +bool CShortLC::decodeErrorCheck() +{ + // Run through each of the 3 rows containing data + CHamming::decode17123(m_deInterData + 0U); + CHamming::decode17123(m_deInterData + 17U); + CHamming::decode17123(m_deInterData + 34U); + + // Run through each of the 17 columns + for (unsigned int c = 0U; c < 17U; c++) { + bool bit = m_deInterData[c + 0U] ^ m_deInterData[c + 17U] ^ m_deInterData[c + 34U]; + if (bit != m_deInterData[c + 51U]) + return false; + } + + return true; +} + +// Extract the 36 bits of payload +void CShortLC::decodeExtractData(unsigned char* data) const +{ + bool bData[40U]; + + for (unsigned int i = 0U; i < 40U; i++) + bData[i] = false; + + unsigned int pos = 4U; + for (unsigned int a = 0U; a < 12U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 17U; a < 29U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 34U; a < 46U; a++, pos++) + bData[pos] = m_deInterData[a]; + + CUtils::bitsToByteBE(bData + 0U, data[0U]); + CUtils::bitsToByteBE(bData + 8U, data[1U]); + CUtils::bitsToByteBE(bData + 16U, data[2U]); + CUtils::bitsToByteBE(bData + 24U, data[3U]); + CUtils::bitsToByteBE(bData + 32U, data[4U]); +} + +// Extract the 36 bits of payload +void CShortLC::encodeExtractData(const unsigned char* in) const +{ + bool bData[40U]; + CUtils::byteToBitsBE(in[0U], bData + 0U); + CUtils::byteToBitsBE(in[1U], bData + 8U); + CUtils::byteToBitsBE(in[2U], bData + 16U); + CUtils::byteToBitsBE(in[3U], bData + 24U); + CUtils::byteToBitsBE(in[4U], bData + 32U); + + for (unsigned int i = 0U; i < 68U; i++) + m_deInterData[i] = false; + + unsigned int pos = 4U; + for (unsigned int a = 0U; a < 12U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 17U; a < 29U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 34U; a < 46U; a++, pos++) + m_deInterData[a] = bData[pos]; +} + +// Check each row with a Hamming (17,12,3) code and each column with a parity bit +void CShortLC::encodeErrorCheck() +{ + // Run through each of the 3 rows containing data + CHamming::encode17123(m_deInterData + 0U); + CHamming::encode17123(m_deInterData + 17U); + CHamming::encode17123(m_deInterData + 34U); + + // Run through each of the 17 columns + for (unsigned int c = 0U; c < 17U; c++) + m_deInterData[c + 51U] = m_deInterData[c + 0U] ^ m_deInterData[c + 17U] ^ m_deInterData[c + 34U]; +} + +// Interleave the raw data +void CShortLC::encodeInterleave() +{ + for (unsigned int i = 0U; i < 72U; i++) + m_rawData[i] = false; + + for (unsigned int a = 0U; a < 67U; a++) { + // Calculate the interleave sequence + unsigned int interleaveSequence = (a * 4U) % 67U; + // Unshuffle the data + m_rawData[interleaveSequence] = m_deInterData[a]; + } + + m_rawData[67U] = m_deInterData[67U]; +} + +void CShortLC::encodeExtractBinary(unsigned char* data) +{ + CUtils::bitsToByteBE(m_rawData + 0U, data[0U]); + CUtils::bitsToByteBE(m_rawData + 8U, data[1U]); + CUtils::bitsToByteBE(m_rawData + 16U, data[2U]); + CUtils::bitsToByteBE(m_rawData + 24U, data[3U]); + CUtils::bitsToByteBE(m_rawData + 32U, data[4U]); + CUtils::bitsToByteBE(m_rawData + 40U, data[5U]); + CUtils::bitsToByteBE(m_rawData + 48U, data[6U]); + CUtils::bitsToByteBE(m_rawData + 56U, data[7U]); + CUtils::bitsToByteBE(m_rawData + 64U, data[8U]); +} diff --git a/ShortLC.h b/ShortLC.h new file mode 100644 index 0000000..04e69a5 --- /dev/null +++ b/ShortLC.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(SHORTLC_H) +#define SHORTLC_H + +class CShortLC +{ +public: + CShortLC(); + ~CShortLC(); + + bool decode(const unsigned char* in, unsigned char* out); + + void encode(const unsigned char* in, unsigned char* out); + +private: + bool* m_rawData; + bool* m_deInterData; + + void decodeExtractBinary(const unsigned char* in); + bool decodeErrorCheck(); + void decodeDeInterleave(); + void decodeExtractData(unsigned char* data) const; + + void encodeExtractData(const unsigned char* in) const; + void encodeInterleave(); + void encodeErrorCheck(); + void encodeExtractBinary(unsigned char* data); +}; + +#endif diff --git a/SlotType.cpp b/SlotType.cpp new file mode 100644 index 0000000..2319ba6 --- /dev/null +++ b/SlotType.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "SlotType.h" + +#include "Golay2087.h" + +#include +#include + +CSlotType::CSlotType() : +m_colorCode(0U), +m_dataType(0U) +{ +} + +CSlotType::~CSlotType() +{ +} + +void CSlotType::putData(const unsigned char* data) +{ + assert(data != NULL); + + unsigned char slotType[3U]; + slotType[0U] = (data[12U] << 2) & 0xFCU; + slotType[0U] |= (data[13U] >> 6) & 0x03U; + + slotType[1U] = (data[13U] << 2) & 0xC0U; + slotType[1U] |= (data[19U] << 2) & 0x3CU; + slotType[1U] |= (data[20U] >> 6) & 0x03U; + + slotType[2U] = (data[20U] << 2) & 0xF0U; + + unsigned char code = CGolay2087::decode(slotType); + + m_colorCode = (code >> 4) & 0x0FU; + m_dataType = (code >> 0) & 0x0FU; +} + +void CSlotType::getData(unsigned char* data) const +{ + assert(data != NULL); + + unsigned char slotType[3U]; + slotType[0U] = (m_colorCode << 4) & 0xF0U; + slotType[0U] |= (m_dataType << 0) & 0x0FU; + slotType[1U] = 0x00U; + slotType[2U] = 0x00U; + + CGolay2087::encode(slotType); + + data[12U] = (data[12U] & 0xC0U) | ((slotType[0U] >> 2) & 0x3FU); + data[13U] = (data[13U] & 0x0FU) | ((slotType[0U] << 6) & 0xC0U) | ((slotType[1U] >> 2) & 0x30U); + data[19U] = (data[19U] & 0xF0U) | ((slotType[1U] >> 2) & 0x0FU); + data[20U] = (data[20U] & 0x03U) | ((slotType[1U] << 6) & 0xC0U) | ((slotType[2U] >> 2) & 0x3CU); +} + +unsigned char CSlotType::getColorCode() const +{ + return m_colorCode; +} + +void CSlotType::setColorCode(unsigned char code) +{ + m_colorCode = code; +} + +unsigned char CSlotType::getDataType() const +{ + return m_dataType; +} + +void CSlotType::setDataType(unsigned char type) +{ + m_dataType = type; +} diff --git a/SlotType.h b/SlotType.h new file mode 100644 index 0000000..07440df --- /dev/null +++ b/SlotType.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(SLOTTYPE_H) +#define SLOTTYPE_H + +class CSlotType +{ +public: + CSlotType(); + ~CSlotType(); + + void putData(const unsigned char* data); + void getData(unsigned char* data) const; + + unsigned char getColorCode() const; + void setColorCode(unsigned char code); + + unsigned char getDataType() const; + void setDataType(unsigned char type); + +private: + unsigned char m_colorCode; + unsigned char m_dataType; +}; + +#endif diff --git a/StopWatch.cpp b/StopWatch.cpp new file mode 100644 index 0000000..beb4bbe --- /dev/null +++ b/StopWatch.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "StopWatch.h" + +#if defined(_WIN32) || defined(_WIN64) + +CStopWatch::CStopWatch() : +m_frequency(), +m_start() +{ + ::QueryPerformanceFrequency(&m_frequency); +} + +CStopWatch::~CStopWatch() +{ +} + +unsigned long CStopWatch::start() +{ + ::QueryPerformanceCounter(&m_start); + + return (unsigned long)(m_start.QuadPart / m_frequency.QuadPart); +} + +unsigned int CStopWatch::elapsed() +{ + LARGE_INTEGER now; + ::QueryPerformanceCounter(&now); + + LARGE_INTEGER temp; + temp.QuadPart = (now.QuadPart - m_start.QuadPart) * 1000; + + return (unsigned int)(temp.QuadPart / m_frequency.QuadPart); +} + +#else + +CStopWatch::CStopWatch() : +m_start() +{ +} + +CStopWatch::~CStopWatch() +{ +} + +unsigned long CStopWatch::start() +{ + ::clock_gettime(CLOCK_MONOTONIC, &m_start); + + return m_start.tv_nsec; +} + +unsigned int CStopWatch::elapsed() +{ + timespec now; + ::clock_gettime(CLOCK_MONOTONIC, &now); + + if (m_start.tv_sec == now.tv_sec) { + return (now.tv_nsec - m_start.tv_nsec) / 1000000U; + } else { + long temp = -m_start.tv_nsec / 1000000L; + temp += (now.tv_sec - m_start.tv_sec) * 1000L; + temp += m_start.tv_nsec / 1000000L; + return temp; + } +} + +#endif diff --git a/StopWatch.h b/StopWatch.h new file mode 100644 index 0000000..645664c --- /dev/null +++ b/StopWatch.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(STOPWATCH_H) +#define STOPWATCH_H + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +class CStopWatch +{ +public: + CStopWatch(); + ~CStopWatch(); + + unsigned long start(); + unsigned int elapsed(); + +private: +#if defined(WIN32) + LARGE_INTEGER m_frequency; + LARGE_INTEGER m_start; +#else + timespec m_start; +#endif +}; + +#endif diff --git a/TFTSerial.cpp b/TFTSerial.cpp new file mode 100644 index 0000000..00d1257 --- /dev/null +++ b/TFTSerial.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "TFTSerial.h" +#include "Log.h" + +CTFTSerial::CTFTSerial(const std::string& port) : +m_serial(port, SERIAL_9600) +{ +} + +CTFTSerial::~CTFTSerial() +{ +} + +bool CTFTSerial::open() +{ + bool ret = m_serial.open(); + if (!ret) { + LogError("Cannot open the port for the TFT Serial"); + return false; + } + + // Set background white + m_serial.write((unsigned char*)"\x1B\x02\x07\xFF", 4U); + + // Set foreground black + m_serial.write((unsigned char*)"\x1B\x01\x00\xFF", 4U); + + setIdle(); + + return true; +} + +void CTFTSerial::setIdle() +{ + // Clear the screen + m_serial.write((unsigned char*)"\x1B\x00\xFF", 3U); + + // Draw MMDVM logo + m_serial.write((unsigned char*)"\x1B\x0D\x00\x00MMDVM_sm.bmp\xFF", 15U); + + // Draw all mode insignias + m_serial.write((unsigned char*)"\x1B\x0D\x00\x00ALL_sm.bmp\xFF", 15U); +} + +void CTFTSerial::setDStar() +{ + // Clear the screen + m_serial.write((unsigned char*)"\x1B\x00\xFF", 3U); + + // Draw MMDVM logo + m_serial.write((unsigned char*)"\x1B\x0D\x00\x00MMDVM_sm.bmp\xFF", 15U); + + // Draw D-Star insignia + m_serial.write((unsigned char*)"\x1B\x0D\x00\x00DStar_sm.bmp\xFF", 17U); + + m_serial.write((unsigned char*)"\x1B\x06\x00\x08\xFF", 5U); + m_serial.write((unsigned char*)"Listening", 9U); +} + +void CTFTSerial::writeDStar(const std::string& call1, const std::string& call2) +{ + m_serial.write((unsigned char*)"\x1B\x06\x00\x08\xFF", 5U); + + char text[20U]; + ::sprintf(text, "%s/%s", call1.c_str(), call2.c_str()); + + m_serial.write((unsigned char*)text, ::strlen(text)); +} + +void CTFTSerial::clearDStar() +{ + // Draw MMDVM logo + m_serial.write((unsigned char*)"\x1B\x0D\x00\x00MMDVM_sm.bmp\xFF", 15U); + + // Draw D-Star insignia + m_serial.write((unsigned char*)"\x1B\x0D\x00\x00DStar_sm.bmp\xFF", 17U); + + m_serial.write((unsigned char*)"\x1B\x06\x00\x08\xFF", 5U); + m_serial.write((unsigned char*)"Listening", 9U); +} + +void CTFTSerial::setDMR() +{ + // Clear the screen + m_serial.write((unsigned char*)"\x1B\x00\xFF", 3U); + + // Draw MMDVM logo + m_serial.write((unsigned char*)"\x1B\x0D\x00\x00MMDVM_sm.bmp\xFF", 15U); + + // Draw DMR insignia + m_serial.write((unsigned char*)"\x1B\x0D\x00\x00DMR_sm.bmp\xFF", 15U); + + m_serial.write((unsigned char*)"\x1B\x06\x00\x08\xFF", 5U); + m_serial.write((unsigned char*)"1: Listening", 9U); + + m_serial.write((unsigned char*)"\x1B\x06\x00\x09\xFF", 5U); + m_serial.write((unsigned char*)"2: Listening", 9U); +} + +void CTFTSerial::writeDMR(unsigned int slotNo, unsigned int srcId, bool group, unsigned int dstId) +{ + char text[20U]; + ::sprintf(text, "%u: %u %s%u", slotNo, srcId, group ? "TG " : "", dstId); + + if (slotNo == 1U) + m_serial.write((unsigned char*)"\x1B\x06\x00\x08\xFF", 5U); + else + m_serial.write((unsigned char*)"\x1B\x06\x00\x09\xFF", 5U); + + m_serial.write((unsigned char*)text, ::strlen(text)); +} + +void CTFTSerial::clearDMR(unsigned int slotNo) +{ + if (slotNo == 1U) { + m_serial.write((unsigned char*)"\x1B\x06\x00\x08\xFF", 5U); + m_serial.write((unsigned char*)"1: Listening", 11U); + } else { + m_serial.write((unsigned char*)"\x1B\x06\x00\x09\xFF", 5U); + m_serial.write((unsigned char*)"2: Listening", 11U); + } +} + +void CTFTSerial::setFusion() +{ + // Clear the screen + m_serial.write((unsigned char*)"\x1B\x00\xFF", 3U); + + // Draw MMDVM logo + m_serial.write((unsigned char*)"\x1B\x0D\x00\x00MMDVM_sm.bmp\xFF", 15U); + + // Draw the System Fusion insignia + m_serial.write((unsigned char*)"\x1B\x0D\x00\x00YSF_sm.bmp\xFF", 15U); + + m_serial.write((unsigned char*)"\x1B\x06\x00\x08\xFF", 5U); + m_serial.write((unsigned char*)"Listening", 9U); +} + +void CTFTSerial::writeFusion(const std::string& callsign) +{ + m_serial.write((unsigned char*)"\x1B\x06\x00\x08\xFF", 5U); + + char text[20U]; + ::sprintf(text, "%s", callsign.c_str()); + + m_serial.write((unsigned char*)text, ::strlen(text)); +} + +void CTFTSerial::clearFusion() +{ + // Draw MMDVM logo + m_serial.write((unsigned char*)"\x1B\x0D\x00\x00MMDVM_sm.bmp\xFF", 15U); + + // Draw the System Fusion insignia + m_serial.write((unsigned char*)"\x1B\x0D\x00\x00YSF_sm.bmp\xFF", 15U); + + m_serial.write((unsigned char*)"\x1B\x06\x00\x08\xFF", 5U); + m_serial.write((unsigned char*)"Listening", 9U); +} + +void CTFTSerial::close() +{ + m_serial.close(); +} diff --git a/TFTSerial.h b/TFTSerial.h new file mode 100644 index 0000000..4a88a78 --- /dev/null +++ b/TFTSerial.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(TFTSERIAL_H) +#define TFTSERIAL_H + +#include "Display.h" +#include "SerialController.h" + +#include + +class CTFTSerial : public IDisplay +{ +public: + CTFTSerial(const std::string& port); + virtual ~CTFTSerial(); + + virtual bool open(); + + virtual void setIdle(); + + virtual void setDStar(); + virtual void writeDStar(const std::string& call1, const std::string& call2); + virtual void clearDStar(); + + virtual void setDMR(); + virtual void writeDMR(unsigned int slotNo, unsigned int srdId, bool group, unsigned int dstId); + virtual void clearDMR(unsigned int slotNo); + + virtual void setFusion(); + virtual void writeFusion(const std::string& callsign); + virtual void clearFusion(); + + virtual void close(); + +private: + CSerialController m_serial; +}; + +#endif diff --git a/Timer.cpp b/Timer.cpp new file mode 100644 index 0000000..53956e4 --- /dev/null +++ b/Timer.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009,2010,2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Timer.h" + +#include +#include + +CTimer::CTimer(unsigned int ticksPerSec, unsigned int secs, unsigned int msecs) : +m_ticksPerSec(ticksPerSec), +m_timeout(0U), +m_timer(0U) +{ + assert(ticksPerSec > 0U); + + if (secs > 0U || msecs > 0U) { + // m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U; + unsigned long long temp = (secs * 1000ULL + msecs) * m_ticksPerSec; + m_timeout = (unsigned int)(temp / 1000ULL + 1ULL); + } +} + +CTimer::~CTimer() +{ +} + +void CTimer::setTimeout(unsigned int secs, unsigned int msecs) +{ + if (secs > 0U || msecs > 0U) { + // m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U; + unsigned long long temp = (secs * 1000ULL + msecs) * m_ticksPerSec; + m_timeout = (unsigned int)(temp / 1000ULL + 1ULL); + } else { + m_timeout = 0U; + m_timer = 0U; + } +} + +unsigned int CTimer::getTimeout() const +{ + if (m_timeout == 0U) + return 0U; + + return (m_timeout - 1U) / m_ticksPerSec; +} + +unsigned int CTimer::getTimer() const +{ + if (m_timer == 0U) + return 0U; + + return (m_timer - 1U) / m_ticksPerSec; +} diff --git a/Timer.h b/Timer.h new file mode 100644 index 0000000..87d68f5 --- /dev/null +++ b/Timer.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009,2010,2011,2014 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Timer_H +#define Timer_H + +class CTimer { +public: + CTimer(unsigned int ticksPerSec, unsigned int secs = 0U, unsigned int msecs = 0U); + ~CTimer(); + + void setTimeout(unsigned int secs, unsigned int msecs = 0U); + + unsigned int getTimeout() const; + unsigned int getTimer() const; + + unsigned int getRemaining() + { + if (m_timeout == 0U || m_timer == 0U) + return 0U; + + if (m_timer >= m_timeout) + return 0U; + + return (m_timeout - m_timer) / m_ticksPerSec; + } + + bool isRunning() + { + return m_timer > 0U; + } + + void start(unsigned int secs, unsigned int msecs = 0U) + { + setTimeout(secs, msecs); + + start(); + } + + void start() + { + if (m_timeout > 0U) + m_timer = 1U; + } + + void stop() + { + m_timer = 0U; + } + + bool hasExpired() + { + if (m_timeout == 0U || m_timer == 0U) + return false; + + if (m_timer >= m_timeout) + return true; + + return false; + } + + void clock(unsigned int ticks = 1U) + { + if (m_timer > 0U && m_timeout > 0U) + m_timer += ticks; + } + +private: + unsigned int m_ticksPerSec; + unsigned int m_timeout; + unsigned int m_timer; +}; + +#endif diff --git a/UDPSocket.cpp b/UDPSocket.cpp new file mode 100644 index 0000000..281f73a --- /dev/null +++ b/UDPSocket.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2006-2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "UDPSocket.h" +#include "Log.h" + +#include + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#endif + + +CUDPSocket::CUDPSocket(const std::string& address, unsigned int port) : +m_address(address), +m_port(port), +m_fd(-1) +{ + assert(!address.empty()); + assert(port > 0U); + +#if defined(_WIN32) || defined(_WIN64) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + LogError("Error from WSAStartup"); +#endif +} + +CUDPSocket::CUDPSocket() : +m_address(), +m_port(0U), +m_fd(-1) +{ +#if defined(_WIN32) || defined(_WIN64) + WSAData data; + int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); + if (wsaRet != 0) + LogError("Error from WSAStartup"); +#endif +} + +CUDPSocket::~CUDPSocket() +{ +#if defined(_WIN32) || defined(_WIN64) + ::WSACleanup(); +#endif +} + +in_addr CUDPSocket::lookup(const std::string& hostname) +{ + in_addr addr; +#if defined(_WIN32) || defined(_WIN64) + unsigned long address = ::inet_addr(hostname.c_str()); + if (address != INADDR_NONE && address != INADDR_ANY) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.c_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); + return addr; + } + + LogError("Cannot find address for host %s", hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +#else + in_addr_t address = ::inet_addr(hostname.c_str()); + if (address != in_addr_t(-1)) { + addr.s_addr = address; + return addr; + } + + struct hostent* hp = ::gethostbyname(hostname.c_str()); + if (hp != NULL) { + ::memcpy(&addr, hp->h_addr_list[0], sizeof(struct in_addr)); + return addr; + } + + LogError("Cannot find address for host %s", hostname.c_str()); + + addr.s_addr = INADDR_NONE; + return addr; +#endif +} + +bool CUDPSocket::open() +{ + m_fd = ::socket(PF_INET, SOCK_DGRAM, 0); + if (m_fd < 0) { +#if defined(_WIN32) || defined(_WIN64) + LogError("Cannot create the UDP socket, err: %lu", ::GetLastError()); +#else + LogError("Cannot create the UDP socket, err: %d", errno); +#endif + return false; + } + + if (m_port > 0U) { + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(m_port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (!m_address.empty()) { +#if defined(_WIN32) || defined(_WIN64) + addr.sin_addr.s_addr = ::inet_addr(m_address.c_str()); +#else + addr.sin_addr.s_addr = ::inet_addr(m_address.c_str()); +#endif + if (addr.sin_addr.s_addr == INADDR_NONE) { + LogError("The local address is invalid - %s", m_address.c_str()); + return false; + } + } + + int reuse = 1; + if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) { +#if defined(_WIN32) || defined(_WIN64) + LogError("Cannot set the UDP socket option, err: %lu", ::GetLastError()); +#else + LogError("Cannot set the UDP socket option, err: %d", errno); +#endif + return false; + } + + if (::bind(m_fd, (sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { +#if defined(_WIN32) || defined(_WIN64) + LogError("Cannot bind the UDP address, err: %lu", ::GetLastError()); +#else + LogError("Cannot bind the UDP address, err: %d", errno); +#endif + return false; + } + } + + return true; +} + +int CUDPSocket::read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port) +{ + assert(buffer != NULL); + assert(length > 0U); + + // Check that the readfrom() won't block + fd_set readFds; + FD_ZERO(&readFds); +#if defined(_WIN32) || defined(_WIN64) + FD_SET((unsigned int)m_fd, &readFds); +#else + FD_SET(m_fd, &readFds); +#endif + + // Return immediately + timeval tv; + tv.tv_sec = 0L; + tv.tv_usec = 0L; + + int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); + if (ret < 0) { +#if defined(_WIN32) || defined(_WIN64) + LogError("Error returned from UDP select, err: %lu", ::GetLastError()); +#else + LogError("Error returned from UDP select, err: %d", errno); +#endif + return -1; + } + + if (ret == 0) + return 0; + + sockaddr_in addr; +#if defined(_WIN32) || defined(_WIN64) + int size = sizeof(sockaddr_in); +#else + socklen_t size = sizeof(sockaddr_in); +#endif + +#if defined(_WIN32) || defined(_WIN64) + int len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&addr, &size); +#else + ssize_t len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&addr, &size); +#endif + if (len <= 0) { +#if defined(_WIN32) || defined(_WIN64) + LogError("Error returned from recvfrom, err: %lu", ::GetLastError()); +#else + LogError("Error returned from recvfrom, err: %d", errno); +#endif + return -1; + } + + address = addr.sin_addr; + port = ntohs(addr.sin_port); + + return len; +} + +bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port) +{ + assert(buffer != NULL); + assert(length > 0U); + + sockaddr_in addr; + ::memset(&addr, 0x00, sizeof(sockaddr_in)); + + addr.sin_family = AF_INET; + addr.sin_addr = address; + addr.sin_port = htons(port); + +#if defined(_WIN32) || defined(_WIN64) + int ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(sockaddr_in)); +#else + ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(sockaddr_in)); +#endif + if (ret < 0) { +#if defined(_WIN32) || defined(_WIN64) + LogError("Error returned from sendto, err: %lu", ::GetLastError()); +#else + LogError("Error returned from sendto, err: %d", errno); +#endif + return false; + } + +#if defined(_WIN32) || defined(_WIN64) + if (ret != int(length)) + return false; +#else + if (ret != ssize_t(length)) + return false; +#endif + + return true; +} + +void CUDPSocket::close() +{ +#if defined(_WIN32) || defined(_WIN64) + ::closesocket(m_fd); +#else + ::close(m_fd); +#endif +} diff --git a/UDPSocket.h b/UDPSocket.h new file mode 100644 index 0000000..1d06ee2 --- /dev/null +++ b/UDPSocket.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009-2011,2013,2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef UDPSocket_H +#define UDPSocket_H + +#include + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#endif + +class CUDPSocket { +public: + CUDPSocket(const std::string& address, unsigned int port); + CUDPSocket(); + ~CUDPSocket(); + + bool open(); + + int read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port); + bool write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port); + + void close(); + + static in_addr lookup(const std::string& hostName); + +private: + std::string m_address; + unsigned short m_port; + int m_fd; +}; + +#endif diff --git a/Utils.cpp b/Utils.cpp new file mode 100644 index 0000000..52d9063 --- /dev/null +++ b/Utils.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2009,2014,2015 Jonathan Naylor, G4KLX + * + * 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; version 2 of the License. + * + * 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 for more details. + */ + +#include "Utils.h" +#include "Log.h" + +#include +#include + +void CUtils::dump(const std::string& title, const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + dump(2U, title, data, length); +} + +void CUtils::dump(int level, const std::string& title, const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + ::Log(level, "%s", title.c_str()); + + unsigned int offset = 0U; + + while (length > 0U) { + std::string output; + + unsigned int bytes = (length > 16U) ? 16U : length; + + for (unsigned i = 0U; i < bytes; i++) { + char temp[10U]; + ::sprintf(temp, "%02X ", data[offset + i]); + output += temp; + } + + for (unsigned int i = bytes; i < 16U; i++) + output += " "; + + output += " *"; + + for (unsigned i = 0U; i < bytes; i++) { + unsigned char c = data[offset + i]; + + if (::isprint(c)) + output += c; + else + output += '.'; + } + + output += '*'; + + ::Log(level, "%04X: %s", offset, output.c_str()); + + offset += 16U; + + if (length >= 16U) + length -= 16U; + else + length = 0U; + } +} + +void CUtils::dump(const std::string& title, const bool* bits, unsigned int length) +{ + assert(bits != NULL); + + dump(2U, title, bits, length); +} + +void CUtils::dump(int level, const std::string& title, const bool* bits, unsigned int length) +{ + assert(bits != NULL); + + unsigned char bytes[100U]; + unsigned int nBytes = 0U; + for (unsigned int n = 0U; n < length; n += 8U, nBytes++) + bitsToByteBE(bits + n, bytes[nBytes]); + + dump(level, title, bytes, nBytes); +} + +void CUtils::byteToBitsBE(unsigned char byte, bool* bits) +{ + bits[0U] = (byte & 0x80U) == 0x80U; + bits[1U] = (byte & 0x40U) == 0x40U; + bits[2U] = (byte & 0x20U) == 0x20U; + bits[3U] = (byte & 0x10U) == 0x10U; + bits[4U] = (byte & 0x08U) == 0x08U; + bits[5U] = (byte & 0x04U) == 0x04U; + bits[6U] = (byte & 0x02U) == 0x02U; + bits[7U] = (byte & 0x01U) == 0x01U; +} + +void CUtils::byteToBitsLE(unsigned char byte, bool* bits) +{ + bits[0U] = (byte & 0x01U) == 0x01U; + bits[1U] = (byte & 0x02U) == 0x02U; + bits[2U] = (byte & 0x04U) == 0x04U; + bits[3U] = (byte & 0x08U) == 0x08U; + bits[4U] = (byte & 0x10U) == 0x10U; + bits[5U] = (byte & 0x20U) == 0x20U; + bits[6U] = (byte & 0x40U) == 0x40U; + bits[7U] = (byte & 0x80U) == 0x80U; +} + +void CUtils::bitsToByteBE(const bool* bits, unsigned char& byte) +{ + byte = bits[0U] ? 0x80U : 0x00U; + byte |= bits[1U] ? 0x40U : 0x00U; + byte |= bits[2U] ? 0x20U : 0x00U; + byte |= bits[3U] ? 0x10U : 0x00U; + byte |= bits[4U] ? 0x08U : 0x00U; + byte |= bits[5U] ? 0x04U : 0x00U; + byte |= bits[6U] ? 0x02U : 0x00U; + byte |= bits[7U] ? 0x01U : 0x00U; +} + +void CUtils::bitsToByteLE(const bool* bits, unsigned char& byte) +{ + byte = bits[0U] ? 0x01U : 0x00U; + byte |= bits[1U] ? 0x02U : 0x00U; + byte |= bits[2U] ? 0x04U : 0x00U; + byte |= bits[3U] ? 0x08U : 0x00U; + byte |= bits[4U] ? 0x10U : 0x00U; + byte |= bits[5U] ? 0x20U : 0x00U; + byte |= bits[6U] ? 0x40U : 0x00U; + byte |= bits[7U] ? 0x80U : 0x00U; +} diff --git a/Utils.h b/Utils.h new file mode 100644 index 0000000..ade28c0 --- /dev/null +++ b/Utils.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009,2014,2015 by Jonathan Naylor, G4KLX + * + * 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; version 2 of the License. + * + * 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 for more details. + */ + +#ifndef Utils_H +#define Utils_H + +#include + +class CUtils { +public: + static void dump(const std::string& title, const unsigned char* data, unsigned int length); + static void dump(int level, const std::string& title, const unsigned char* data, unsigned int length); + + static void dump(const std::string& title, const bool* bits, unsigned int length); + static void dump(int level, const std::string& title, const bool* bits, unsigned int length); + + static void byteToBitsBE(unsigned char byte, bool* bits); + static void byteToBitsLE(unsigned char byte, bool* bits); + + static void bitsToByteBE(const bool* bits, unsigned char& byte); + static void bitsToByteLE(const bool* bits, unsigned char& byte); + +private: +}; + +#endif diff --git a/Version.h b/Version.h new file mode 100644 index 0000000..0ea15c9 --- /dev/null +++ b/Version.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(VERSION_H) +#define VERSION_H + +const char* VERSION = "20160113"; + +#endif diff --git a/YSFDefines.h b/YSFDefines.h new file mode 100644 index 0000000..f713e5b --- /dev/null +++ b/YSFDefines.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(YSFDefines_H) +#define YSFDefines_H + +const unsigned int YSF_FRAME_LENGTH_BYTES = 120U; + +const unsigned char YSF_SYNC_BYTES[] = {0xD4U, 0x71U, 0xC9U, 0x63U, 0x4DU}; + +const unsigned char YSF_FI_MASK = 0xC0U; +const unsigned char YSF_DT_MASK = 0x30U; + +const unsigned char YSF_DT_TERMINATOR_CHANNEL = 0x80U; + +const unsigned char YSF_CKSUM_OK = 0x01U; + +#endif diff --git a/YSFEcho.cpp b/YSFEcho.cpp new file mode 100644 index 0000000..5bcf54f --- /dev/null +++ b/YSFEcho.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "YSFEcho.h" + +#include "YSFDefines.h" + +CYSFEcho::CYSFEcho(unsigned int delay, unsigned int space) : +m_buffer(space), +m_timer(1000U, delay) +{ +} + +CYSFEcho::~CYSFEcho() +{ +} + +unsigned int CYSFEcho::readData(unsigned char* data) +{ + if (!hasData()) + return 0U; + + unsigned char len; + m_buffer.getData(&len, 1U); + + m_buffer.getData(data, len); + + // If the FICH is valid, regenerate the sync + if ((data[1U] & 0x01U) == 0x01U) { + data[2U] = YSF_SYNC_BYTES[0U]; + data[3U] = YSF_SYNC_BYTES[1U]; + data[4U] = YSF_SYNC_BYTES[2U]; + data[5U] = YSF_SYNC_BYTES[3U]; + data[6U] = YSF_SYNC_BYTES[4U]; + } + + if (!hasData()) + m_timer.stop(); + + return len; +} + +bool CYSFEcho::writeData(const unsigned char* data, unsigned int length) +{ + bool ret = m_buffer.hasSpace(length + 1U); + if (!ret) + return false; + + unsigned char len = length; + m_buffer.addData(&len, 1U); + + m_buffer.addData(data, length); + + m_timer.start(); + + return true; +} + +bool CYSFEcho::hasData() +{ + if (m_timer.isRunning() && m_timer.hasExpired()) + return m_buffer.hasData(); + else + return false; +} + +void CYSFEcho::clock(unsigned int ms) +{ + m_timer.clock(ms); +} diff --git a/YSFEcho.h b/YSFEcho.h new file mode 100644 index 0000000..3218a6e --- /dev/null +++ b/YSFEcho.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * 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; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(YSFECHO_H) +#define YSFECHO_H + +#include "RingBuffer.h" +#include "Timer.h" + +class CYSFEcho { +public: + CYSFEcho(unsigned int delay, unsigned int space); + ~CYSFEcho(); + + unsigned int readData(unsigned char* data); + + bool writeData(const unsigned char* data, unsigned int length); + + bool hasData(); + + void clock(unsigned int ms); + +private: + CRingBuffer m_buffer; + CTimer m_timer; +}; + +#endif