Add M172DMR

This commit is contained in:
Doug McLain 2020-11-03 19:04:48 -05:00
parent 7d6140cbe0
commit 53101450df
93 changed files with 177648 additions and 7 deletions

View File

@ -33,7 +33,7 @@ const char* DEFAULT_INI_FILE = "/etc/DMR2M17.ini";
const char* HEADER1 = "This software is for use on amateur radio networks only,";
const char* HEADER2 = "it is to be used for educational purposes only. Its use on";
const char* HEADER3 = "commercial networks is strictly prohibited.";
const char* HEADER4 = "Copyright(C) 2018 by CA6JAU, G4KLX and others";
const char* HEADER4 = "Copyright(C) 2018 by AD8DP, CA6JAU, G4KLX and others";
#define M17CHARACTERS " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/."
@ -370,7 +370,7 @@ int CDMR2M17::run()
m17Watch.start();
}
else if(m17FrameType == TAG_DATA) {
//CUtils::dump(1U, "P25 Data", m_p25Frame, 11U);
//CUtils::dump(1U, "M17 Data", m_p25Frame, 11U);
m17_cnt++;
memcpy(buffer, "M17 ", 4);
memcpy(buffer+4, &streamid, 2);
@ -386,7 +386,7 @@ int CDMR2M17::run()
}
while (m_m17Network->readData(m_m17Frame, 54U) > 0U) {
//CUtils::dump(1U, "P25 Data", m_p25Frame, 22U);
//CUtils::dump(1U, "M17 Data", m_p25Frame, 22U);
if (!memcmp(m_m17Frame, "M17 ", 4)) {
if (m_m17Frame[34] == 0 && m_m17Frame[35] == 0) {
m_m17Frames = 0;
@ -655,7 +655,7 @@ int CDMR2M17::run()
pollTimer.start();
}
if (ms < 5U) CThread::sleep(5U);
}
m_m17Network->close();

View File

@ -47,7 +47,7 @@ const char* DEFAULT_INI_FILE = "/etc/DMR2YSF.ini";
const char* HEADER1 = "This software is for use on amateur radio networks only,";
const char* HEADER2 = "it is to be used for educational purposes only. Its use on";
const char* HEADER3 = "commercial networks is strictly prohibited.";
const char* HEADER4 = "Copyright(C) 2018 by CA6JAU, G4KLX and others";
const char* HEADER4 = "Copyright(C) 2018 by CA6JAU, G4KLX, AD8DP and others";
#include <functional>
#include <algorithm>

347
M172DMR/BPTC19696.cpp Normal file
View File

@ -0,0 +1,347 @@
/*
* 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 <cstdio>
#include <cassert>
#include <cstring>
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_2(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 9 rows containing data
for (unsigned int r = 0U; r < 9U; r++) {
unsigned int pos = (r * 15U) + 1U;
CHamming::encode15113_2(m_deInterData + pos);
}
// 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;
}
}
}
// 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[20U] = (data[20U] & 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]);
}

47
M172DMR/BPTC19696.h Normal file
View File

@ -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

254
M172DMR/CRC.cpp Normal file
View File

@ -0,0 +1,254 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
*
* 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 "Log.h"
#include <cstdint>
#include <cstdio>
#include <cassert>
#include <cmath>
const uint8_t CRC8_TABLE[] = {
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31,
0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9,
0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1,
0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE,
0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16,
0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80,
0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8,
0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 0x19, 0x1E, 0x17, 0x10,
0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F,
0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE, 0xA9, 0xA0, 0xA7,
0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF,
0xFA, 0xFD, 0xF4, 0xF3, 0x01 };
const uint16_t CCITT16_TABLE1[] = {
0x0000U, 0x1189U, 0x2312U, 0x329bU, 0x4624U, 0x57adU, 0x6536U, 0x74bfU,
0x8c48U, 0x9dc1U, 0xaf5aU, 0xbed3U, 0xca6cU, 0xdbe5U, 0xe97eU, 0xf8f7U,
0x1081U, 0x0108U, 0x3393U, 0x221aU, 0x56a5U, 0x472cU, 0x75b7U, 0x643eU,
0x9cc9U, 0x8d40U, 0xbfdbU, 0xae52U, 0xdaedU, 0xcb64U, 0xf9ffU, 0xe876U,
0x2102U, 0x308bU, 0x0210U, 0x1399U, 0x6726U, 0x76afU, 0x4434U, 0x55bdU,
0xad4aU, 0xbcc3U, 0x8e58U, 0x9fd1U, 0xeb6eU, 0xfae7U, 0xc87cU, 0xd9f5U,
0x3183U, 0x200aU, 0x1291U, 0x0318U, 0x77a7U, 0x662eU, 0x54b5U, 0x453cU,
0xbdcbU, 0xac42U, 0x9ed9U, 0x8f50U, 0xfbefU, 0xea66U, 0xd8fdU, 0xc974U,
0x4204U, 0x538dU, 0x6116U, 0x709fU, 0x0420U, 0x15a9U, 0x2732U, 0x36bbU,
0xce4cU, 0xdfc5U, 0xed5eU, 0xfcd7U, 0x8868U, 0x99e1U, 0xab7aU, 0xbaf3U,
0x5285U, 0x430cU, 0x7197U, 0x601eU, 0x14a1U, 0x0528U, 0x37b3U, 0x263aU,
0xdecdU, 0xcf44U, 0xfddfU, 0xec56U, 0x98e9U, 0x8960U, 0xbbfbU, 0xaa72U,
0x6306U, 0x728fU, 0x4014U, 0x519dU, 0x2522U, 0x34abU, 0x0630U, 0x17b9U,
0xef4eU, 0xfec7U, 0xcc5cU, 0xddd5U, 0xa96aU, 0xb8e3U, 0x8a78U, 0x9bf1U,
0x7387U, 0x620eU, 0x5095U, 0x411cU, 0x35a3U, 0x242aU, 0x16b1U, 0x0738U,
0xffcfU, 0xee46U, 0xdcddU, 0xcd54U, 0xb9ebU, 0xa862U, 0x9af9U, 0x8b70U,
0x8408U, 0x9581U, 0xa71aU, 0xb693U, 0xc22cU, 0xd3a5U, 0xe13eU, 0xf0b7U,
0x0840U, 0x19c9U, 0x2b52U, 0x3adbU, 0x4e64U, 0x5fedU, 0x6d76U, 0x7cffU,
0x9489U, 0x8500U, 0xb79bU, 0xa612U, 0xd2adU, 0xc324U, 0xf1bfU, 0xe036U,
0x18c1U, 0x0948U, 0x3bd3U, 0x2a5aU, 0x5ee5U, 0x4f6cU, 0x7df7U, 0x6c7eU,
0xa50aU, 0xb483U, 0x8618U, 0x9791U, 0xe32eU, 0xf2a7U, 0xc03cU, 0xd1b5U,
0x2942U, 0x38cbU, 0x0a50U, 0x1bd9U, 0x6f66U, 0x7eefU, 0x4c74U, 0x5dfdU,
0xb58bU, 0xa402U, 0x9699U, 0x8710U, 0xf3afU, 0xe226U, 0xd0bdU, 0xc134U,
0x39c3U, 0x284aU, 0x1ad1U, 0x0b58U, 0x7fe7U, 0x6e6eU, 0x5cf5U, 0x4d7cU,
0xc60cU, 0xd785U, 0xe51eU, 0xf497U, 0x8028U, 0x91a1U, 0xa33aU, 0xb2b3U,
0x4a44U, 0x5bcdU, 0x6956U, 0x78dfU, 0x0c60U, 0x1de9U, 0x2f72U, 0x3efbU,
0xd68dU, 0xc704U, 0xf59fU, 0xe416U, 0x90a9U, 0x8120U, 0xb3bbU, 0xa232U,
0x5ac5U, 0x4b4cU, 0x79d7U, 0x685eU, 0x1ce1U, 0x0d68U, 0x3ff3U, 0x2e7aU,
0xe70eU, 0xf687U, 0xc41cU, 0xd595U, 0xa12aU, 0xb0a3U, 0x8238U, 0x93b1U,
0x6b46U, 0x7acfU, 0x4854U, 0x59ddU, 0x2d62U, 0x3cebU, 0x0e70U, 0x1ff9U,
0xf78fU, 0xe606U, 0xd49dU, 0xc514U, 0xb1abU, 0xa022U, 0x92b9U, 0x8330U,
0x7bc7U, 0x6a4eU, 0x58d5U, 0x495cU, 0x3de3U, 0x2c6aU, 0x1ef1U, 0x0f78U };
const uint16_t CCITT16_TABLE2[] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 };
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;
}
void CCRC::addCCITT162(unsigned char *in, unsigned int length)
{
assert(in != NULL);
assert(length > 2U);
union {
uint16_t crc16;
uint8_t crc8[2U];
};
crc16 = 0U;
for (unsigned i = 0U; i < (length - 2U); i++)
crc16 = (uint16_t(crc8[0U]) << 8) ^ CCITT16_TABLE2[crc8[1U] ^ in[i]];
crc16 = ~crc16;
in[length - 1U] = crc8[0U];
in[length - 2U] = crc8[1U];
}
bool CCRC::checkCCITT162(const unsigned char *in, unsigned int length)
{
assert(in != NULL);
assert(length > 2U);
union {
uint16_t crc16;
uint8_t crc8[2U];
};
crc16 = 0U;
for (unsigned i = 0U; i < (length - 2U); i++)
crc16 = (uint16_t(crc8[0U]) << 8) ^ CCITT16_TABLE2[crc8[1U] ^ in[i]];
crc16 = ~crc16;
return crc8[0U] == in[length - 1U] && crc8[1U] == in[length - 2U];
}
void CCRC::addCCITT161(unsigned char *in, unsigned int length)
{
assert(in != NULL);
assert(length > 2U);
union {
uint16_t crc16;
uint8_t crc8[2U];
};
crc16 = 0xFFFFU;
for (unsigned int i = 0U; i < (length - 2U); i++)
crc16 = uint16_t(crc8[1U]) ^ CCITT16_TABLE1[crc8[0U] ^ in[i]];
crc16 = ~crc16;
in[length - 2U] = crc8[0U];
in[length - 1U] = crc8[1U];
}
bool CCRC::checkCCITT161(const unsigned char *in, unsigned int length)
{
assert(in != NULL);
assert(length > 2U);
union {
uint16_t crc16;
uint8_t crc8[2U];
};
crc16 = 0xFFFFU;
for (unsigned int i = 0U; i < (length - 2U); i++)
crc16 = uint16_t(crc8[1U]) ^ CCITT16_TABLE1[crc8[0U] ^ in[i]];
crc16 = ~crc16;
return crc8[0U] == in[length - 2U] && crc8[1U] == in[length - 1U];
}
unsigned char CCRC::crc8(const unsigned char *in, unsigned int length)
{
assert(in != NULL);
uint8_t crc = 0U;
for (unsigned int i = 0U; i < length; i++)
crc = CRC8_TABLE[crc ^ in[i]];
return crc;
}
unsigned char CCRC::addCRC(const unsigned char* in, unsigned int length)
{
assert(in != NULL);
unsigned char crc = 0U;
for (unsigned int i = 0U; i < length; i++)
crc += in[i];
return crc;
}

40
M172DMR/CRC.h Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
*
* 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 void addCCITT161(unsigned char* in, unsigned int length);
static void addCCITT162(unsigned char* in, unsigned int length);
static bool checkCCITT161(const unsigned char* in, unsigned int length);
static bool checkCCITT162(const unsigned char* in, unsigned int length);
static unsigned char crc8(const unsigned char* in, unsigned int length);
static unsigned char addCRC(const unsigned char* in, unsigned int length);
};
#endif

423
M172DMR/Conf.cpp Normal file
View File

@ -0,0 +1,423 @@
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
*
* 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 <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
const int BUFFER_SIZE = 500;
enum SECTION {
SECTION_NONE,
SECTION_INFO,
SECTION_M17_NETWORK,
SECTION_DMR_NETWORK,
SECTION_DMRID_LOOKUP,
SECTION_LOG
};
CConf::CConf(const std::string& file) :
m_file(file),
m_callsign(),
m_tg(20U),
m_dstAddress(),
m_dstPort(0U),
m_localAddress(),
m_localPort(0U),
m_defaultID(65519U),
m_daemon(false),
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_dmrId(0U),
m_dmrXLXFile(),
m_dmrXLXModule(),
m_dmrXLXReflector(950U),
m_dmrDstId(9990U),
m_dmrPC(true),
m_dmrNetworkAddress(),
m_dmrNetworkPort(0U),
m_dmrNetworkLocal(0U),
m_dmrNetworkPassword(),
m_dmrNetworkOptions(),
m_dmrNetworkDebug(false),
m_dmrNetworkJitterEnabled(true),
m_dmrNetworkJitter(500U),
m_dmrIdLookupFile(),
m_dmrIdLookupTime(0U),
m_m17DstId(0U),
m_m17DstName(),
m_m17DstAddress(),
m_m17DstPort(0U),
m_m17LocalAddress(),
m_m17LocalPort(0U),
m_m17GainAdjDb(),
m_m17NetworkDebug(false),
m_logDisplayLevel(0U),
m_logFileLevel(0U),
m_logFilePath(),
m_logFileRoot()
{
}
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, "[Info]", 6U) == 0)
section = SECTION_INFO;
else if (::strncmp(buffer, "[M17 Network]", 13U) == 0)
section = SECTION_M17_NETWORK;
else if (::strncmp(buffer, "[DMR Network]", 13U) == 0)
section = SECTION_DMR_NETWORK;
else if (::strncmp(buffer, "[DMR Id Lookup]", 15U) == 0)
section = SECTION_DMRID_LOOKUP;
else if (::strncmp(buffer, "[Log]", 5U) == 0)
section = SECTION_LOG;
else
section = SECTION_NONE;
continue;
}
char* key = ::strtok(buffer, " \t=\r\n");
if (key == NULL)
continue;
char* value = ::strtok(NULL, "\r\n");
if (value == NULL)
continue;
// Remove quotes from the value
size_t len = ::strlen(value);
if (len > 1U && *value == '"' && value[len - 1U] == '"') {
value[len - 1U] = '\0';
value++;
}
::fprintf(stderr, "CConf key:val:section == %s:%s:%d\n", key, value, section);
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_DMR_NETWORK) {
if (::strcmp(key, "Id") == 0)
m_dmrId = (unsigned int)::atoi(value);
else if (::strcmp(key, "XLXFile") == 0)
m_dmrXLXFile = value;
else if (::strcmp(key, "XLXModule") == 0) {
for (unsigned int i = 0U; value[i] != 0; i++)
value[i] = ::toupper(value[i]);
m_dmrXLXModule = value;
}
else if (::strcmp(key, "XLXReflector") == 0)
m_dmrXLXReflector = (unsigned int)::atoi(value);
else if (::strcmp(key, "StartupDstId") == 0)
m_dmrDstId = (unsigned int)::atoi(value);
else if (::strcmp(key, "StartupPC") == 0)
m_dmrPC = ::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, "Local") == 0)
m_dmrNetworkLocal = (unsigned int)::atoi(value);
else if (::strcmp(key, "Password") == 0)
m_dmrNetworkPassword = value;
else if (::strcmp(key, "Options") == 0)
m_dmrNetworkOptions = value;
else if (::strcmp(key, "Debug") == 0)
m_dmrNetworkDebug = ::atoi(value) == 1;
else if (::strcmp(key, "JitterEnabled") == 0)
m_dmrNetworkJitterEnabled = ::atoi(value) == 1;
else if (::strcmp(key, "Jitter") == 0)
m_dmrNetworkJitter = (unsigned int)::atoi(value);
} else if (section == SECTION_M17_NETWORK) {
if (::strcmp(key, "Callsign") == 0)
m_callsign = value;
else if (::strcmp(key, "StartupDstId") == 0)
m_m17DstId = (unsigned int)::atoi(value);
else if (::strcmp(key, "LocalAddress") == 0)
m_m17LocalAddress = value;
else if (::strcmp(key, "LocalPort") == 0)
m_m17LocalPort = (unsigned int)::atoi(value);
else if (::strcmp(key, "DstName") == 0)
m_m17DstName = value;
else if (::strcmp(key, "DstAddress") == 0)
m_m17DstAddress = value;
else if (::strcmp(key, "DstPort") == 0)
m_m17DstPort = (unsigned int)::atoi(value);
else if (::strcmp(key, "GainAdjustdB") == 0)
m_m17GainAdjDb = value;
else if (::strcmp(key, "Debug") == 0)
m_m17NetworkDebug = ::atoi(value) == 1;
}
else if (section == SECTION_DMRID_LOOKUP) {
if (::strcmp(key, "File") == 0)
m_dmrIdLookupFile = value;
else if (::strcmp(key, "Time") == 0)
m_dmrIdLookupTime = (unsigned int)::atoi(value);
} else if (section == SECTION_LOG) {
if (::strcmp(key, "FilePath") == 0)
m_logFilePath = value;
else if (::strcmp(key, "FileRoot") == 0)
m_logFileRoot = value;
else if (::strcmp(key, "FileLevel") == 0)
m_logFileLevel = (unsigned int)::atoi(value);
else if (::strcmp(key, "DisplayLevel") == 0)
m_logDisplayLevel = (unsigned int)::atoi(value);
}
}
::fclose(fp);
return true;
}
std::string CConf::getCallsign() const
{
return m_callsign;
}
bool CConf::getDaemon() const
{
return m_daemon;
}
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::getDMRId() const
{
return m_dmrId;
}
std::string CConf::getDMRXLXFile() const
{
return m_dmrXLXFile;
}
std::string CConf::getDMRXLXModule() const
{
return m_dmrXLXModule;
}
unsigned int CConf::getDMRXLXReflector() const
{
return m_dmrXLXReflector;
}
unsigned int CConf::getDMRDstId() const
{
return m_dmrDstId;
}
bool CConf::getDMRPC() const
{
return m_dmrPC;
}
std::string CConf::getDMRNetworkAddress() const
{
return m_dmrNetworkAddress;
}
unsigned int CConf::getDMRNetworkPort() const
{
return m_dmrNetworkPort;
}
unsigned int CConf::getDMRNetworkLocal() const
{
return m_dmrNetworkLocal;
}
std::string CConf::getDMRNetworkPassword() const
{
return m_dmrNetworkPassword;
}
std::string CConf::getDMRNetworkOptions() const
{
return m_dmrNetworkOptions;
}
bool CConf::getDMRNetworkDebug() const
{
return m_dmrNetworkDebug;
}
bool CConf::getDMRNetworkJitterEnabled() const
{
return m_dmrNetworkJitterEnabled;
}
unsigned int CConf::getDMRNetworkJitter() const
{
return m_dmrNetworkJitter;
}
std::string CConf::getDMRIdLookupFile() const
{
return m_dmrIdLookupFile;
}
unsigned int CConf::getDMRIdLookupTime() const
{
return m_dmrIdLookupTime;
}
std::string CConf::getM17DstName() const
{
return m_m17DstName;
}
std::string CConf::getM17DstAddress() const
{
return m_m17DstAddress;
}
unsigned int CConf::getM17DstPort() const
{
return m_m17DstPort;
}
std::string CConf::getM17LocalAddress() const
{
return m_m17LocalAddress;
}
unsigned int CConf::getM17LocalPort() const
{
return m_m17LocalPort;
}
std::string CConf::getM17GainAdjDb() const
{
return m_m17GainAdjDb;
}
bool CConf::getM17NetworkDebug() const
{
return m_m17NetworkDebug;
}
unsigned int CConf::getLogDisplayLevel() const
{
return m_logDisplayLevel;
}
unsigned int CConf::getLogFileLevel() const
{
return m_logFileLevel;
}
std::string CConf::getLogFilePath() const
{
return m_logFilePath;
}
std::string CConf::getLogFileRoot() const
{
return m_logFileRoot;
}

139
M172DMR/Conf.h Normal file
View File

@ -0,0 +1,139 @@
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
*
* 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 <string>
#include <vector>
class CConf
{
public:
CConf(const std::string& file);
~CConf();
bool read();
// The M17 Network section
std::string getCallsign() const;
bool getDaemon() const;
unsigned int getM17DstId() const;
std::string getM17DstName() const;
std::string getM17DstAddress() const;
unsigned int getM17DstPort() const;
std::string getM17LocalAddress() const;
unsigned int getM17LocalPort() const;
std::string getM17GainAdjDb() const;
bool getM17NetworkDebug() 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 DMR Network section
unsigned int getDMRId() const;
std::string getDMRXLXFile() const;
std::string getDMRXLXModule() const;
unsigned int getDMRXLXReflector() const;
unsigned int getDMRDstId() const;
bool getDMRPC() const;
std::string getDMRNetworkAddress() const;
unsigned int getDMRNetworkPort() const;
unsigned int getDMRNetworkLocal() const;
std::string getDMRNetworkPassword() const;
std::string getDMRNetworkOptions() const;
bool getDMRNetworkDebug() const;
bool getDMRNetworkJitterEnabled() const;
unsigned int getDMRNetworkJitter() const;
// The DMR Id section
std::string getDMRIdLookupFile() const;
unsigned int getDMRIdLookupTime() const;
// The Log section
unsigned int getLogDisplayLevel() const;
unsigned int getLogFileLevel() const;
std::string getLogFilePath() const;
std::string getLogFileRoot() const;
private:
std::string m_file;
std::string m_callsign;
unsigned int m_tg;
std::string m_dstAddress;
unsigned int m_dstPort;
std::string m_localAddress;
unsigned int m_localPort;
unsigned int m_defaultID;
bool m_daemon;
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_dmrId;
std::string m_dmrXLXFile;
std::string m_dmrXLXModule;
unsigned int m_dmrXLXReflector;
unsigned int m_dmrDstId;
bool m_dmrPC;
std::string m_dmrNetworkAddress;
unsigned int m_dmrNetworkPort;
unsigned int m_dmrNetworkLocal;
std::string m_dmrNetworkPassword;
std::string m_dmrNetworkOptions;
bool m_dmrNetworkDebug;
bool m_dmrNetworkJitterEnabled;
unsigned int m_dmrNetworkJitter;
std::string m_dmrIdLookupFile;
unsigned int m_dmrIdLookupTime;
unsigned int m_m17DstId;
std::string m_m17DstName;
std::string m_m17DstAddress;
unsigned int m_m17DstPort;
std::string m_m17LocalAddress;
unsigned int m_m17LocalPort;
std::string m_m17GainAdjDb;
bool m_m17NetworkDebug;
unsigned int m_logDisplayLevel;
unsigned int m_logFileLevel;
std::string m_logFilePath;
std::string m_logFileRoot;
};
#endif

211
M172DMR/DMRData.cpp Normal file
View File

@ -0,0 +1,211 @@
/*
* Copyright (C) 2015,2016,2017 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 <cstdio>
#include <cstring>
#include <cassert>
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_missing(data.m_missing),
m_n(data.m_n),
m_ber(data.m_ber),
m_rssi(data.m_rssi),
m_streamId(data.m_streamId)
{
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_missing(false),
m_n(0U),
m_ber(0U),
m_rssi(0U),
m_streamId(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_missing = data.m_missing;
m_n = data.m_n;
m_ber = data.m_ber;
m_rssi = data.m_rssi;
m_streamId = data.m_streamId;
}
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;
}
bool CDMRData::isMissing() const
{
return m_missing;
}
void CDMRData::setMissing(bool missing)
{
m_missing = missing;
}
unsigned char CDMRData::getN() const
{
return m_n;
}
void CDMRData::setN(unsigned char n)
{
m_n = n;
}
unsigned char CDMRData::getBER() const
{
return m_ber;
}
void CDMRData::setBER(unsigned char ber)
{
m_ber = ber;
}
unsigned char CDMRData::getRSSI() const
{
return m_rssi;
}
void CDMRData::setRSSI(unsigned char rssi)
{
m_rssi = rssi;
}
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);
}
unsigned int CDMRData::getStreamId() const
{
return m_streamId;
}
void CDMRData::setStreamId(unsigned int id)
{
m_streamId = id;
}

78
M172DMR/DMRData.h Normal file
View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2015,2016,2017 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);
bool isMissing() const;
void setMissing(bool missing);
unsigned char getBER() const;
void setBER(unsigned char ber);
unsigned char getRSSI() const;
void setRSSI(unsigned char rssi);
void setData(const unsigned char* buffer);
unsigned int getData(unsigned char* buffer) const;
void setStreamId(unsigned int id);
unsigned int getStreamId() 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;
bool m_missing;
unsigned char m_n;
unsigned char m_ber;
unsigned char m_rssi;
unsigned int m_streamId;
};
#endif

122
M172DMR/DMRDefines.h Normal file
View File

@ -0,0 +1,122 @@
/*
* 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
#include "Defines.h" // For TAG_DATA
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 DMR_IDLE_DATA[] = {TAG_DATA, 0x00U,
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};
// A silence frame only
const unsigned char DMR_SILENCE_DATA[] = {0xB9U, 0xE8U, 0x81U, 0x52U, 0x61U, 0x73U, 0x00U, 0x2AU, 0x6BU, 0xB9U, 0xE8U,
0x81U, 0x52U, 0x60U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x73U, 0x00U,
0x2AU, 0x6BU, 0xB9U, 0xE8U, 0x81U, 0x52U, 0x61U, 0x73U, 0x00U, 0x2AU, 0x6BU};
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 PI_HEADER_CRC_MASK[] = {0x69U, 0x69U};
const unsigned char DATA_HEADER_CRC_MASK[] = {0xCCU, 0xCCU};
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_RATE_12_DATA = 0x07U;
const unsigned char DT_RATE_34_DATA = 0x08U;
const unsigned char DT_IDLE = 0x09U;
const unsigned char DT_RATE_1_DATA = 0x0AU;
// 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 DPF_UDT = 0x00U;
const unsigned char DPF_RESPONSE = 0x01U;
const unsigned char DPF_UNCONFIRMED_DATA = 0x02U;
const unsigned char DPF_CONFIRMED_DATA = 0x03U;
const unsigned char DPF_DEFINED_SHORT = 0x0DU;
const unsigned char DPF_DEFINED_RAW = 0x0EU;
const unsigned char DPF_PROPRIETARY = 0x0FU;
const unsigned char FID_ETSI = 0U;
const unsigned char FID_DMRA = 16U;
enum FLCO {
FLCO_GROUP = 0,
FLCO_USER_USER = 3,
FLCO_TALKER_ALIAS_HEADER = 4,
FLCO_TALKER_ALIAS_BLOCK1 = 5,
FLCO_TALKER_ALIAS_BLOCK2 = 6,
FLCO_TALKER_ALIAS_BLOCK3 = 7,
FLCO_GPS_INFO = 8
};
#endif

100
M172DMR/DMREMB.cpp Normal file
View File

@ -0,0 +1,100 @@
/*
* 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 "DMREMB.h"
#include "QR1676.h"
#include <cstdio>
#include <cassert>
CDMREMB::CDMREMB() :
m_colorCode(0U),
m_PI(false),
m_LCSS(0U)
{
}
CDMREMB::~CDMREMB()
{
}
void CDMREMB::putData(const unsigned char* data)
{
assert(data != NULL);
unsigned char DMREMB[2U];
DMREMB[0U] = (data[13U] << 4) & 0xF0U;
DMREMB[0U] |= (data[14U] >> 4) & 0x0FU;
DMREMB[1U] = (data[18U] << 4) & 0xF0U;
DMREMB[1U] |= (data[19U] >> 4) & 0x0FU;
CQR1676::decode(DMREMB);
m_colorCode = (DMREMB[0U] >> 4) & 0x0FU;
m_PI = (DMREMB[0U] & 0x08U) == 0x08U;
m_LCSS = (DMREMB[0U] >> 1) & 0x03U;
}
void CDMREMB::getData(unsigned char* data) const
{
assert(data != NULL);
unsigned char DMREMB[2U];
DMREMB[0U] = (m_colorCode << 4) & 0xF0U;
DMREMB[0U] |= m_PI ? 0x08U : 0x00U;
DMREMB[0U] |= (m_LCSS << 1) & 0x06U;
DMREMB[1U] = 0x00U;
CQR1676::encode(DMREMB);
data[13U] = (data[13U] & 0xF0U) | ((DMREMB[0U] >> 4U) & 0x0FU);
data[14U] = (data[14U] & 0x0FU) | ((DMREMB[0U] << 4U) & 0xF0U);
data[18U] = (data[18U] & 0xF0U) | ((DMREMB[1U] >> 4U) & 0x0FU);
data[19U] = (data[19U] & 0x0FU) | ((DMREMB[1U] << 4U) & 0xF0U);
}
unsigned char CDMREMB::getColorCode() const
{
return m_colorCode;
}
void CDMREMB::setColorCode(unsigned char code)
{
m_colorCode = code;
}
bool CDMREMB::getPI() const
{
return m_PI;
}
void CDMREMB::setPI(bool pi)
{
m_PI = pi;
}
unsigned char CDMREMB::getLCSS() const
{
return m_LCSS;
}
void CDMREMB::setLCSS(unsigned char lcss)
{
m_LCSS = lcss;
}

46
M172DMR/DMREMB.h Normal file
View File

@ -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(DMREMB_H)
#define DMREMB_H
class CDMREMB
{
public:
CDMREMB();
~CDMREMB();
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

322
M172DMR/DMREmbeddedData.cpp Normal file
View File

@ -0,0 +1,322 @@
/*
* Copyright (C) 2015,2016,2017 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 "DMREmbeddedData.h"
#include "Hamming.h"
#include "Utils.h"
#include "CRC.h"
#include <cstdio>
#include <cassert>
#include <cstring>
CDMREmbeddedData::CDMREmbeddedData() :
m_raw(NULL),
m_state(LCS_NONE),
m_data(NULL),
m_FLCO(FLCO_GROUP),
m_valid(false)
{
m_raw = new bool[128U];
m_data = new bool[72U];
}
CDMREmbeddedData::~CDMREmbeddedData()
{
delete[] m_raw;
delete[] m_data;
}
// Add LC data (which may consist of 4 blocks) to the data store
bool CDMREmbeddedData::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_raw[a] = rawData[a + 4U];
// Show we are ready for the next LC block
m_state = LCS_FIRST;
m_valid = false;
return false;
}
// 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_raw[a + 32U] = rawData[a + 4U];
// Show we are ready for the next LC block
m_state = LCS_SECOND;
return false;
}
// 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_raw[a + 64U] = rawData[a + 4U];
// Show we are ready for the final LC block
m_state = LCS_THIRD;
return false;
}
// 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_raw[a + 96U] = rawData[a + 4U];
// Show that we're not ready for any more data
m_state = LCS_NONE;
// Process the complete data block
decodeEmbeddedData();
if (m_valid)
encodeEmbeddedData();
return m_valid;
}
return false;
}
void CDMREmbeddedData::setLC(const CDMRLC& lc)
{
lc.getData(m_data);
m_FLCO = lc.getFLCO();
m_valid = true;
encodeEmbeddedData();
}
void CDMREmbeddedData::encodeEmbeddedData()
{
unsigned int crc;
CCRC::encodeFiveBit(m_data, 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] = m_data[b];
for (unsigned int a = 16U; a < 27U; a++, b++)
data[a] = m_data[b];
for (unsigned int a = 32U; a < 42U; a++, b++)
data[a] = m_data[b];
for (unsigned int a = 48U; a < 58U; a++, b++)
data[a] = m_data[b];
for (unsigned int a = 64U; a < 74U; a++, b++)
data[a] = m_data[b];
for (unsigned int a = 80U; a < 90U; a++, b++)
data[a] = m_data[b];
for (unsigned int a = 96U; a < 106U; a++, b++)
data[a] = m_data[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_raw[a] = data[b];
b += 16U;
if (b > 127U)
b -= 127U;
}
}
unsigned char CDMREmbeddedData::getData(unsigned char* data, unsigned char n) const
{
assert(data != NULL);
if (n >= 1U && n < 5U) {
n--;
bool bits[40U];
::memset(bits, 0x00U, 40U * sizeof(bool));
::memcpy(bits + 4U, m_raw + 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);
switch (n) {
case 0U:
return 1U;
case 3U:
return 2U;
default:
return 3U;
}
} else {
data[14U] &= 0xF0U;
data[15U] = 0x00U;
data[16U] = 0x00U;
data[17U] = 0x00U;
data[18U] &= 0x0FU;
return 0U;
}
}
// Unpack and error check an embedded LC
void CDMREmbeddedData::decodeEmbeddedData()
{
// 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_raw[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))
return;
}
// 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)
return;
}
// We have passed the Hamming check so extract the actual payload
b = 0U;
for (unsigned int a = 0U; a < 11U; a++, b++)
m_data[b] = data[a];
for (unsigned int a = 16U; a < 27U; a++, b++)
m_data[b] = data[a];
for (unsigned int a = 32U; a < 42U; a++, b++)
m_data[b] = data[a];
for (unsigned int a = 48U; a < 58U; a++, b++)
m_data[b] = data[a];
for (unsigned int a = 64U; a < 74U; a++, b++)
m_data[b] = data[a];
for (unsigned int a = 80U; a < 90U; a++, b++)
m_data[b] = data[a];
for (unsigned int a = 96U; a < 106U; a++, b++)
m_data[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(m_data, crc))
return;
m_valid = true;
// Extract the FLCO
unsigned char flco;
CUtils::bitsToByteBE(m_data + 0U, flco);
m_FLCO = FLCO(flco & 0x3FU);
}
CDMRLC* CDMREmbeddedData::getLC() const
{
if (!m_valid)
return NULL;
if (m_FLCO != FLCO_GROUP && m_FLCO != FLCO_USER_USER)
return NULL;
return new CDMRLC(m_data);
}
bool CDMREmbeddedData::isValid() const
{
return m_valid;
}
FLCO CDMREmbeddedData::getFLCO() const
{
return m_FLCO;
}
void CDMREmbeddedData::reset()
{
m_state = LCS_NONE;
m_valid = false;
}
bool CDMREmbeddedData::getRawData(unsigned char* data) const
{
assert(data != NULL);
if (!m_valid)
return false;
CUtils::bitsToByteBE(m_data + 0U, data[0U]);
CUtils::bitsToByteBE(m_data + 8U, data[1U]);
CUtils::bitsToByteBE(m_data + 16U, data[2U]);
CUtils::bitsToByteBE(m_data + 24U, data[3U]);
CUtils::bitsToByteBE(m_data + 32U, data[4U]);
CUtils::bitsToByteBE(m_data + 40U, data[5U]);
CUtils::bitsToByteBE(m_data + 48U, data[6U]);
CUtils::bitsToByteBE(m_data + 56U, data[7U]);
CUtils::bitsToByteBE(m_data + 64U, data[8U]);
return true;
}

63
M172DMR/DMREmbeddedData.h Normal file
View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2015,2016,2017 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 DMREmbeddedData_H
#define DMREmbeddedData_H
#include "DMRDefines.h"
#include "DMRLC.h"
enum LC_STATE {
LCS_NONE,
LCS_FIRST,
LCS_SECOND,
LCS_THIRD
};
class CDMREmbeddedData
{
public:
CDMREmbeddedData();
~CDMREmbeddedData();
bool addData(const unsigned char* data, unsigned char lcss);
CDMRLC* getLC() const;
void setLC(const CDMRLC& lc);
unsigned char getData(unsigned char* data, unsigned char n) const;
bool getRawData(unsigned char* data) const;
bool isValid() const;
FLCO getFLCO() const;
void reset();
private:
bool* m_raw;
LC_STATE m_state;
bool* m_data;
FLCO m_FLCO;
bool m_valid;
void decodeEmbeddedData();
void encodeEmbeddedData();
};
#endif

99
M172DMR/DMRFullLC.cpp Normal file
View File

@ -0,0 +1,99 @@
/*
* 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 "DMRFullLC.h"
#include "DMRDefines.h"
#include "RS129.h"
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
CDMRFullLC::CDMRFullLC() :
m_bptc()
{
}
CDMRFullLC::~CDMRFullLC()
{
}
CDMRLC* CDMRFullLC::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))
return NULL;
return new CDMRLC(lcData);
}
void CDMRFullLC::encode(const CDMRLC& 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);
}

42
M172DMR/DMRFullLC.h Normal file
View File

@ -0,0 +1,42 @@
/*
* 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.
*/
#ifndef DMRFullLC_H
#define DMRFullLC_H
#include "DMRLC.h"
#include "DMRSlotType.h"
#include "BPTC19696.h"
class CDMRFullLC
{
public:
CDMRFullLC();
~CDMRFullLC();
CDMRLC* decode(const unsigned char* data, unsigned char type);
void encode(const CDMRLC& lc, unsigned char* data, unsigned char type);
private:
CBPTC19696 m_bptc;
};
#endif

160785
M172DMR/DMRIds.dat Normal file

File diff suppressed because it is too large Load Diff

205
M172DMR/DMRLC.cpp Normal file
View File

@ -0,0 +1,205 @@
/*
* 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 "DMRLC.h"
#include "Utils.h"
#include <cstdio>
#include <cassert>
CDMRLC::CDMRLC(FLCO flco, unsigned int srcId, unsigned int dstId) :
m_PF(false),
m_R(false),
m_FLCO(flco),
m_FID(0U),
m_options(0U),
m_srcId(srcId),
m_dstId(dstId)
{
}
CDMRLC::CDMRLC(const unsigned char* bytes) :
m_PF(false),
m_R(false),
m_FLCO(FLCO_GROUP),
m_FID(0U),
m_options(0U),
m_srcId(0U),
m_dstId(0U)
{
assert(bytes != NULL);
m_PF = (bytes[0U] & 0x80U) == 0x80U;
m_R = (bytes[0U] & 0x40U) == 0x40U;
m_FLCO = FLCO(bytes[0U] & 0x3FU);
m_FID = bytes[1U];
m_options = bytes[2U];
m_dstId = bytes[3U] << 16 | bytes[4U] << 8 | bytes[5U];
m_srcId = bytes[6U] << 16 | bytes[7U] << 8 | bytes[8U];
}
CDMRLC::CDMRLC(const bool* bits) :
m_PF(false),
m_R(false),
m_FLCO(FLCO_GROUP),
m_FID(0U),
m_options(0U),
m_srcId(0U),
m_dstId(0U)
{
assert(bits != NULL);
m_PF = bits[0U];
m_R = bits[1U];
unsigned char temp1, temp2, temp3;
CUtils::bitsToByteBE(bits + 0U, temp1);
m_FLCO = FLCO(temp1 & 0x3FU);
CUtils::bitsToByteBE(bits + 8U, temp2);
m_FID = temp2;
CUtils::bitsToByteBE(bits + 16U, temp3);
m_options = temp3;
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;
}
CDMRLC::CDMRLC() :
m_PF(false),
m_R(false),
m_FLCO(FLCO_GROUP),
m_FID(0U),
m_options(0U),
m_srcId(0U),
m_dstId(0U)
{
}
CDMRLC::~CDMRLC()
{
}
void CDMRLC::getData(unsigned char* bytes) const
{
assert(bytes != NULL);
bytes[0U] = (unsigned char)m_FLCO;
if (m_PF)
bytes[0U] |= 0x80U;
if (m_R)
bytes[0U] |= 0x40U;
bytes[1U] = m_FID;
bytes[2U] = m_options;
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 CDMRLC::getData(bool* bits) const
{
assert(bits != NULL);
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 CDMRLC::getPF() const
{
return m_PF;
}
void CDMRLC::setPF(bool pf)
{
m_PF = pf;
}
FLCO CDMRLC::getFLCO() const
{
return m_FLCO;
}
void CDMRLC::setFLCO(FLCO flco)
{
m_FLCO = flco;
}
unsigned char CDMRLC::getFID() const
{
return m_FID;
}
void CDMRLC::setFID(unsigned char fid)
{
m_FID = fid;
}
unsigned int CDMRLC::getSrcId() const
{
return m_srcId;
}
void CDMRLC::setSrcId(unsigned int id)
{
m_srcId = id;
}
unsigned int CDMRLC::getDstId() const
{
return m_dstId;
}
void CDMRLC::setDstId(unsigned int id)
{
m_dstId = id;
}

62
M172DMR/DMRLC.h Normal file
View File

@ -0,0 +1,62 @@
/*
* 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(DMRLC_H)
#define DMRLC_H
#include "DMRDefines.h"
class CDMRLC
{
public:
CDMRLC(FLCO flco, unsigned int srcId, unsigned int dstId);
CDMRLC(const unsigned char* bytes);
CDMRLC(const bool* bits);
CDMRLC();
~CDMRLC();
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;
bool m_R;
FLCO m_FLCO;
unsigned char m_FID;
unsigned char m_options;
unsigned int m_srcId;
unsigned int m_dstId;
};
#endif

179
M172DMR/DMRLookup.cpp Normal file
View File

@ -0,0 +1,179 @@
/*
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
*
* 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 "DMRLookup.h"
#include "Timer.h"
#include "Log.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
CDMRLookup::CDMRLookup(const std::string& filename, unsigned int reloadTime) :
CThread(),
m_filename(filename),
m_reloadTime(reloadTime),
m_table(),
m_cstable(),
m_mutex(),
m_stop(false)
{
}
CDMRLookup::~CDMRLookup()
{
}
bool CDMRLookup::read()
{
bool ret = load();
if (m_reloadTime > 0U)
run();
return ret;
}
void CDMRLookup::entry()
{
LogInfo("Started the DMR Id lookup reload thread");
CTimer timer(1U, 3600U * m_reloadTime);
timer.start();
while (!m_stop) {
sleep(1000U);
timer.clock();
if (timer.hasExpired()) {
load();
timer.start();
}
}
LogInfo("Stopped the DMR Id lookup reload thread");
}
void CDMRLookup::stop()
{
if (m_reloadTime == 0U) {
delete this;
return;
}
m_stop = true;
wait();
}
std::string CDMRLookup::findCS(unsigned int id)
{
std::string callsign;
if (id == 0xFFFFFFU)
return std::string("ALL");
m_mutex.lock();
try {
callsign = m_table.at(id);
} catch (...) {
char text[10U];
::sprintf(text, "%u", id);
callsign = std::string(text);
}
m_mutex.unlock();
return callsign;
}
unsigned int CDMRLookup::findID(std::string cs)
{
unsigned int dmrID;
m_mutex.lock();
try {
dmrID = m_cstable.at(cs);
} catch (...) {
dmrID = 0U;
}
m_mutex.unlock();
return dmrID;
}
bool CDMRLookup::exists(unsigned int id)
{
m_mutex.lock();
bool found = m_table.count(id) == 1U;
m_mutex.unlock();
return found;
}
bool CDMRLookup::load()
{
FILE* fp = ::fopen(m_filename.c_str(), "rt");
if (fp == NULL) {
LogWarning("Cannot open the DMR Id lookup file - %s", m_filename.c_str());
return false;
}
m_mutex.lock();
// Remove the old entries
m_table.clear();
m_cstable.clear();
char buffer[100U];
while (::fgets(buffer, 100U, fp) != NULL) {
if (buffer[0U] == '#')
continue;
char* p1 = ::strtok(buffer, " \t\r\n");
char* p2 = ::strtok(NULL, " \t\r\n");
if (p1 != NULL && p2 != NULL) {
unsigned int id = (unsigned int)::atoi(p1);
for (char* p = p2; *p != 0x00U; p++)
*p = ::toupper(*p);
m_table[id] = std::string(p2);
m_cstable[p2] = id;
}
}
m_mutex.unlock();
::fclose(fp);
size_t size = m_table.size();
if (size == 0U)
return false;
LogInfo("Loaded %u Ids to the DMR callsign lookup table", size);
return true;
}

56
M172DMR/DMRLookup.h Normal file
View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
*
* 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 DMRLookup_H
#define DMRLookup_H
#include "Thread.h"
#include "Mutex.h"
#include <string>
#include <unordered_map>
class CDMRLookup : public CThread {
public:
CDMRLookup(const std::string& filename, unsigned int reloadTime);
virtual ~CDMRLookup();
bool read();
virtual void entry();
std::string findCS(unsigned int id);
unsigned int findID(std::string cs);
bool exists(unsigned int id);
void stop();
private:
std::string m_filename;
unsigned int m_reloadTime;
std::unordered_map<unsigned int, std::string> m_table;
std::unordered_map<std::string, unsigned int> m_cstable;
CMutex m_mutex;
bool m_stop;
bool load();
};
#endif

673
M172DMR/DMRNetwork.cpp Normal file
View File

@ -0,0 +1,673 @@
/*
* Copyright (C) 2015,2016,2017,2018 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 "DMRNetwork.h"
#include "StopWatch.h"
#include "SHA256.h"
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
const unsigned int BUFFER_LENGTH = 500U;
const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U;
CDMRNetwork::CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType, unsigned int jitter) :
m_address(),
m_port(port),
m_id(NULL),
m_password(password),
m_duplex(duplex),
m_version(version),
m_debug(debug),
m_socket(local),
m_enabled(false),
m_slot1(slot1),
m_slot2(slot2),
m_delayBuffers(NULL),
m_hwType(hwType),
m_status(WAITING_CONNECT),
m_retryTimer(1000U, 10U),
m_timeoutTimer(1000U, 60U),
m_buffer(NULL),
m_salt(NULL),
m_streamId(NULL),
m_options(),
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());
assert(jitter > 0U);
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_delayBuffers = new CDelayBuffer*[3U];
m_delayBuffers[1U] = new CDelayBuffer("DMR Slot 1", HOMEBREW_DATA_PACKET_LENGTH, DMR_SLOT_TIME, jitter, debug);
m_delayBuffers[2U] = new CDelayBuffer("DMR Slot 2", HOMEBREW_DATA_PACKET_LENGTH, DMR_SLOT_TIME, jitter, debug);
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());
m_streamId[0U] = ::rand() + 1U;
m_streamId[1U] = ::rand() + 1U;
}
CDMRNetwork::~CDMRNetwork()
{
delete m_delayBuffers[1U];
delete m_delayBuffers[2U];
delete[] m_buffer;
delete[] m_salt;
delete[] m_streamId;
delete[] m_id;
delete[] m_delayBuffers;
}
void CDMRNetwork::setOptions(const std::string& options)
{
m_options = options;
}
void CDMRNetwork::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 CDMRNetwork::open()
{
LogMessage("DMR, Opening DMR Network");
m_status = WAITING_CONNECT;
m_timeoutTimer.stop();
m_retryTimer.start();
return true;
}
void CDMRNetwork::enable(bool enabled)
{
m_enabled = enabled;
}
bool CDMRNetwork::read(CDMRData& data)
{
if (m_status != RUNNING)
return false;
for (unsigned int slotNo = 1U; slotNo <= 2U; slotNo++) {
unsigned int length = 0U;
B_STATUS status = BS_NO_DATA;
status = m_delayBuffers[slotNo]->getData(m_buffer, length);
if (status != BS_NO_DATA) {
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);
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);
data.setMissing(status == BS_MISSING);
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;
}
}
return false;
}
bool CDMRNetwork::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();
// Individual slot disabling
if (slotNo == 1U && !m_slot1)
return false;
if (slotNo == 2U && !m_slot2)
return false;
buffer[15U] = slotNo == 1U ? 0x00U : 0x80U;
FLCO flco = data.getFLCO();
buffer[15U] |= flco == FLCO_GROUP ? 0x00U : 0x40U;
unsigned int slotIndex = slotNo - 1U;
unsigned int count = 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)
count = 2U;
buffer[15U] |= (0x20U | dataType);
}
buffer[4U] = data.getSeqNo();
::memcpy(buffer + 16U, m_streamId + slotIndex, 4U);
data.getData(buffer + 20U);
buffer[53U] = data.getBER();
buffer[54U] = data.getRSSI();
if (m_debug)
CUtils::dump(1U, "Network Transmitted", buffer, HOMEBREW_DATA_PACKET_LENGTH);
for (unsigned int i = 0U; i < count; i++)
write(buffer, HOMEBREW_DATA_PACKET_LENGTH);
return true;
}
bool CDMRNetwork::writePosition(unsigned int id, const unsigned char* data)
{
if (m_status != RUNNING)
return false;
unsigned char buffer[20U];
::memcpy(buffer + 0U, "DMRG", 4U);
::memcpy(buffer + 4U, m_id, 4U);
buffer[8U] = id >> 16;
buffer[9U] = id >> 8;
buffer[10U] = id >> 0;
::memcpy(buffer + 11U, data + 2U, 7U);
return write(buffer, 18U);
}
bool CDMRNetwork::writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data)
{
if (m_status != RUNNING)
return false;
unsigned char buffer[20U];
::memcpy(buffer + 0U, "DMRA", 4U);
::memcpy(buffer + 4U, m_id, 4U);
buffer[8U] = id >> 16;
buffer[9U] = id >> 8;
buffer[10U] = id >> 0;
buffer[11U] = type;
::memcpy(buffer + 12U, data + 2U, 7U);
return write(buffer, 19U);
}
void CDMRNetwork::close()
{
LogMessage("DMR, Closing DMR Network");
if (m_status == RUNNING) {
unsigned char buffer[9U];
::memcpy(buffer + 0U, "RPTCL", 5U);
::memcpy(buffer + 5U, m_id, 4U);
write(buffer, 9U);
}
m_socket.close();
m_retryTimer.stop();
m_timeoutTimer.stop();
}
bool CDMRNetwork::clock(unsigned int ms)
{
bool r = false;
m_delayBuffers[1U]->clock(ms);
m_delayBuffers[2U]->clock(ms);
if (m_status == WAITING_CONNECT) {
m_retryTimer.clock(ms);
if (m_retryTimer.isRunning() && m_retryTimer.hasExpired()) {
bool ret = m_socket.open();
if (ret) {
ret = writeLogin();
if (!ret)
return true;
m_status = WAITING_LOGIN;
m_timeoutTimer.start();
}
m_retryTimer.start();
}
return false;
}
in_addr address;
unsigned int port;
int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, port);
if (length < 0) {
LogError("DMR, Socket has failed, retrying connection to the master");
close();
open();
return true;
}
// if (m_debug && length > 0)
// CUtils::dump(1U, "Network Received", m_buffer, length);
if (length > 0 && m_address.s_addr == address.s_addr && m_port == port) {
if (::memcmp(m_buffer, "DMRD", 4U) == 0) {
if (m_enabled) {
if (m_debug)
CUtils::dump(1U, "Network Received", m_buffer, length);
receiveData(m_buffer, length);
}
} else if (::memcmp(m_buffer, "MSTNAK", 6U) == 0) {
if (m_status == RUNNING) {
LogWarning("DMR, Login to the master has failed, retrying login ...");
m_status = WAITING_LOGIN;
m_timeoutTimer.start();
m_retryTimer.start();
} else {
/* Once the modem death spiral has been prevented in Modem.cpp
the Network sometimes times out and reaches here.
We want it to reconnect so... */
LogError("DMR, Login to the master has failed, retrying network ...");
close();
open();
return true;
}
} else if (::memcmp(m_buffer, "RPTACK", 6U) == 0) {
switch (m_status) {
case WAITING_LOGIN:
LogDebug("DMR, Sending authorisation");
::memcpy(m_salt, m_buffer + 6U, sizeof(uint32_t));
writeAuthorisation();
m_status = WAITING_AUTHORISATION;
m_timeoutTimer.start();
m_retryTimer.start();
break;
case WAITING_AUTHORISATION:
LogDebug("DMR, Sending configuration");
writeConfig();
m_status = WAITING_CONFIG;
m_timeoutTimer.start();
m_retryTimer.start();
break;
case WAITING_CONFIG:
if (m_options.empty()) {
LogMessage("DMR, Logged into the master successfully");
m_status = RUNNING;
} else {
LogDebug("DMR, Sending options");
writeOptions();
m_status = WAITING_OPTIONS;
}
m_timeoutTimer.start();
m_retryTimer.start();
break;
case WAITING_OPTIONS:
LogMessage("DMR, Logged into the master successfully");
m_status = RUNNING;
m_timeoutTimer.start();
m_retryTimer.start();
break;
default:
break;
}
} else if (::memcmp(m_buffer, "MSTCL", 5U) == 0) {
LogError("DMR, Master is closing down");
close();
open();
r = true;
} 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);
}
}
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_OPTIONS:
writeOptions();
break;
case WAITING_CONFIG:
writeConfig();
break;
case RUNNING:
writePing();
break;
default:
break;
}
m_retryTimer.start();
}
m_timeoutTimer.clock(ms);
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) {
LogError("DMR, Connection to the master has timed out, retrying connection");
close();
open();
r = true;
}
return r;
}
void CDMRNetwork::reset(unsigned int slotNo)
{
assert(slotNo == 1U || slotNo == 2U);
if (slotNo == 1U) {
m_delayBuffers[1U]->reset();
m_streamId[0U] = ::rand() + 1U;
} else {
m_delayBuffers[2U]->reset();
m_streamId[1U] = ::rand() + 1U;
}
}
bool CDMRNetwork::isConnected() const
{
return m_status == RUNNING;
}
void CDMRNetwork::receiveData(const unsigned char* data, unsigned int length)
{
assert(data != NULL);
assert(length > 0U);
unsigned int slotNo = (data[15U] & 0x80U) == 0x80U ? 2U : 1U;
// DMO mode slot disabling
if (slotNo == 1U && !m_duplex)
return;
// Individual slot disabling
if (slotNo == 1U && !m_slot1)
return;
if (slotNo == 2U && !m_slot2)
return;
m_delayBuffers[slotNo]->addData(data, length);
}
bool CDMRNetwork::writeLogin()
{
unsigned char buffer[8U];
::memcpy(buffer + 0U, "RPTL", 4U);
::memcpy(buffer + 4U, m_id, 4U);
return write(buffer, 8U);
}
bool CDMRNetwork::writeAuthorisation()
{
size_t size = m_password.size();
unsigned char* in = new unsigned char[size + sizeof(uint32_t)];
::memcpy(in, m_salt, sizeof(uint32_t));
for (size_t 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, (unsigned int)(size + sizeof(uint32_t)), out + 8U);
delete[] in;
return write(out, 40U);
}
bool CDMRNetwork::writeOptions()
{
char buffer[300U];
::memcpy(buffer + 0U, "RPTO", 4U);
::memcpy(buffer + 4U, m_id, 4U);
::strcpy(buffer + 8U, m_options.c_str());
return write((unsigned char*)buffer, (unsigned int)m_options.length() + 8U);
}
bool CDMRNetwork::writeConfig()
{
const char* software;
char slots = '0';
if (m_duplex) {
if (m_slot1 && m_slot2)
slots = '3';
else if (m_slot1 && !m_slot2)
slots = '1';
else if (!m_slot1 && m_slot2)
slots = '2';
switch (m_hwType) {
case HWT_MMDVM:
software = "MMDVM";
break;
case HWT_MMDVM_HS:
software = "MMDVM_MMDVM_HS";
break;
default:
software = "MMDVM_Unknown";
break;
}
} else {
slots = '4';
switch (m_hwType) {
case HWT_MMDVM:
software = "MMDVM_DMO";
break;
case HWT_DVMEGA:
software = "MMDVM_DVMega";
break;
case HWT_MMDVM_ZUMSPOT:
software = "MMDVM_ZUMspot";
break;
case HWT_MMDVM_HS_HAT:
software = "MMDVM_MMDVM_HS_Hat";
break;
case HWT_MMDVM_HS:
software = "MMDVM_MMDVM_HS";
break;
default:
software = "MMDVM_Unknown";
break;
}
}
char buffer[400U];
::memcpy(buffer + 0U, "RPTC", 4U);
::memcpy(buffer + 4U, m_id, 4U);
char latitude[20U];
::sprintf(latitude, "%08f", m_latitude);
char longitude[20U];
::sprintf(longitude, "%09f", m_longitude);
unsigned int power = m_power;
if (power > 99U)
power = 99U;
int height = m_height;
if (height > 999)
height = 999;
::sprintf(buffer + 8U, "%-8.8s%09u%09u%02u%02u%8.8s%9.9s%03d%-20.20s%-19.19s%c%-124.124s%-40.40s%-40.40s", m_callsign.c_str(),
m_rxFrequency, m_txFrequency, power, m_colorCode, latitude, longitude, height, m_location.c_str(),
m_description.c_str(), slots, m_url.c_str(), m_version, software);
return write((unsigned char*)buffer, 302U);
}
bool CDMRNetwork::writePing()
{
unsigned char buffer[11U];
::memcpy(buffer + 0U, "RPTPING", 7U);
::memcpy(buffer + 7U, m_id, 4U);
if (m_debug)
CUtils::dump(1U, "DMR Network Ping Sent", buffer, 11U);
return write(buffer, 11U);
}
bool CDMRNetwork::wantsBeacon()
{
bool beacon = m_beacon;
m_beacon = false;
return beacon;
}
bool CDMRNetwork::write(const unsigned char* data, unsigned int length)
{
assert(data != NULL);
assert(length > 0U);
// if (m_debug)
// CUtils::dump(1U, "Network Transmitted", data, length);
bool ret = m_socket.write(data, length, m_address, m_port);
if (!ret) {
LogError("DMR, Socket has failed when writing data to the master, retrying connection");
m_socket.close();
open();
return false;
}
return true;
}

121
M172DMR/DMRNetwork.h Normal file
View File

@ -0,0 +1,121 @@
/*
* Copyright (C) 2015,2016,2017,2018 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(DMRNetwork_H)
#define DMRNetwork_H
#include "DelayBuffer.h"
#include "UDPSocket.h"
#include "Timer.h"
#include "DMRData.h"
#include "Defines.h"
#include <string>
#include <cstdint>
class CDMRNetwork
{
public:
CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType, unsigned int jitter);
~CDMRNetwork();
void setOptions(const std::string& options);
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();
void enable(bool enabled);
bool read(CDMRData& data);
bool write(const CDMRData& data);
bool writePosition(unsigned int id, const unsigned char* data);
bool writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data);
bool wantsBeacon();
bool clock(unsigned int ms);
void reset(unsigned int slotNo);
bool isConnected() const;
void close();
private:
in_addr m_address;
unsigned int m_port;
uint8_t* m_id;
std::string m_password;
bool m_duplex;
const char* m_version;
bool m_debug;
CUDPSocket m_socket;
bool m_enabled;
bool m_slot1;
bool m_slot2;
CDelayBuffer** m_delayBuffers;
HW_TYPE m_hwType;
enum STATUS {
WAITING_CONNECT,
WAITING_LOGIN,
WAITING_AUTHORISATION,
WAITING_CONFIG,
WAITING_OPTIONS,
RUNNING
};
STATUS m_status;
CTimer m_retryTimer;
CTimer m_timeoutTimer;
unsigned char* m_buffer;
unsigned char* m_salt;
uint32_t* m_streamId;
std::string m_options;
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 writeOptions();
bool writeConfig();
bool writePing();
bool write(const unsigned char* data, unsigned int length);
void receiveData(const unsigned char* data, unsigned int length);
};
#endif

92
M172DMR/DMRSlotType.cpp Normal file
View File

@ -0,0 +1,92 @@
/*
* 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 "DMRSlotType.h"
#include "Golay2087.h"
#include <cstdio>
#include <cassert>
CDMRSlotType::CDMRSlotType() :
m_colorCode(0U),
m_dataType(0U)
{
}
CDMRSlotType::~CDMRSlotType()
{
}
void CDMRSlotType::putData(const unsigned char* data)
{
assert(data != NULL);
unsigned char DMRSlotType[3U];
DMRSlotType[0U] = (data[12U] << 2) & 0xFCU;
DMRSlotType[0U] |= (data[13U] >> 6) & 0x03U;
DMRSlotType[1U] = (data[13U] << 2) & 0xC0U;
DMRSlotType[1U] |= (data[19U] << 2) & 0x3CU;
DMRSlotType[1U] |= (data[20U] >> 6) & 0x03U;
DMRSlotType[2U] = (data[20U] << 2) & 0xF0U;
unsigned char code = CGolay2087::decode(DMRSlotType);
m_colorCode = (code >> 4) & 0x0FU;
m_dataType = (code >> 0) & 0x0FU;
}
void CDMRSlotType::getData(unsigned char* data) const
{
assert(data != NULL);
unsigned char DMRSlotType[3U];
DMRSlotType[0U] = (m_colorCode << 4) & 0xF0U;
DMRSlotType[0U] |= (m_dataType << 0) & 0x0FU;
DMRSlotType[1U] = 0x00U;
DMRSlotType[2U] = 0x00U;
CGolay2087::encode(DMRSlotType);
data[12U] = (data[12U] & 0xC0U) | ((DMRSlotType[0U] >> 2) & 0x3FU);
data[13U] = (data[13U] & 0x0FU) | ((DMRSlotType[0U] << 6) & 0xC0U) | ((DMRSlotType[1U] >> 2) & 0x30U);
data[19U] = (data[19U] & 0xF0U) | ((DMRSlotType[1U] >> 2) & 0x0FU);
data[20U] = (data[20U] & 0x03U) | ((DMRSlotType[1U] << 6) & 0xC0U) | ((DMRSlotType[2U] >> 2) & 0x3CU);
}
unsigned char CDMRSlotType::getColorCode() const
{
return m_colorCode;
}
void CDMRSlotType::setColorCode(unsigned char code)
{
m_colorCode = code;
}
unsigned char CDMRSlotType::getDataType() const
{
return m_dataType;
}
void CDMRSlotType::setDataType(unsigned char type)
{
m_dataType = type;
}

42
M172DMR/DMRSlotType.h Normal file
View File

@ -0,0 +1,42 @@
/*
* 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(DMRSLOTTYPE_H)
#define DMRSLOTTYPE_H
class CDMRSlotType
{
public:
CDMRSlotType();
~CDMRSlotType();
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

69
M172DMR/Defines.h Normal file
View File

@ -0,0 +1,69 @@
/*
* Copyright (C) 2015,2016,2017 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 MODE_P25 = 4U;
const unsigned char MODE_NXDN = 5U;
const unsigned char MODE_CW = 98U;
const unsigned char MODE_LOCKOUT = 99U;
const unsigned char MODE_ERROR = 100U;
const unsigned char TAG_HEADER = 0x00U;
const unsigned char TAG_DATA = 0x01U;
const unsigned char TAG_LOST = 0x02U;
const unsigned char TAG_EOT = 0x03U;
const unsigned char TAG_NODATA = 0x04U;
enum HW_TYPE {
HWT_MMDVM,
HWT_DVMEGA,
HWT_MMDVM_ZUMSPOT,
HWT_MMDVM_HS_HAT,
HWT_NANO_HOTSPOT,
HWT_MMDVM_HS,
HWT_UNKNOWN
};
enum RPT_RF_STATE {
RS_RF_LISTENING,
RS_RF_LATE_ENTRY,
RS_RF_AUDIO,
RS_RF_DATA,
RS_RF_REJECTED,
RS_RF_INVALID
};
enum RPT_NET_STATE {
RS_NET_IDLE,
RS_NET_AUDIO,
RS_NET_DATA
};
enum B_STATUS {
BS_NO_DATA,
BS_DATA,
BS_MISSING
};
#endif

159
M172DMR/DelayBuffer.cpp Normal file
View File

@ -0,0 +1,159 @@
/*
* Copyright (C) 2018 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 "DelayBuffer.h"
#include "DMRDefines.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
CDelayBuffer::CDelayBuffer(const std::string& name, unsigned int blockSize, unsigned int blockTime, unsigned int jitterTime, bool debug) :
m_name(name),
m_blockSize(blockSize),
m_blockTime(blockTime),
m_debug(debug),
m_timer(1000U, 0U, jitterTime),
m_stopWatch(),
m_running(false),
m_buffer(5000U, name.c_str()),
m_outputCount(0U),
m_lastData(NULL),
m_lastDataLength(0U),
m_lastDataValid(false)
{
assert(blockSize > 0U);
assert(blockTime > 0U);
assert(jitterTime > 0U);
m_lastData = new unsigned char[m_blockSize];
reset();
}
CDelayBuffer::~CDelayBuffer()
{
delete[] m_lastData;
}
bool CDelayBuffer::addData(const unsigned char* data, unsigned int length)
{
assert(data != NULL);
assert(length > 0U);
assert(length == m_blockSize);
if (m_debug)
LogDebug("%s, DelayBuffer: appending data", m_name.c_str());
m_buffer.addData(data, length);
if (!m_timer.isRunning()) {
if (m_debug)
LogDebug("%s, DelayBuffer: starting the timer from append", m_name.c_str());
m_timer.start();
}
return true;
}
B_STATUS CDelayBuffer::getData(unsigned char* data, unsigned int& length)
{
assert(data != NULL);
if (!m_running)
return BS_NO_DATA;
unsigned int needed = m_stopWatch.elapsed() / m_blockTime + 2U;
if (needed <= m_outputCount)
return BS_NO_DATA;
if (!m_buffer.isEmpty()) {
if (m_debug)
LogDebug("%s, DelayBuffer: returning data, elapsed=%ums", m_name.c_str(), m_stopWatch.elapsed());
if (m_buffer.getData(data, m_blockSize)) {
length = m_blockSize;
// Save this data in case no more data is available next time
::memcpy(m_lastData, data, length);
m_lastDataLength = length;
m_lastDataValid = true;
m_outputCount++;
return BS_DATA;
}
}
if (m_debug)
LogDebug("%s, DelayBuffer: no data available, elapsed=%ums", m_name.c_str(), m_stopWatch.elapsed());
// Return the last data frame if we have it
if (m_lastDataLength > 0U) {
if(m_lastDataValid) {
if (m_debug)
LogDebug("%s, DelayBuffer: returning the last received frame", m_name.c_str());
// Copy last valid data
::memcpy(data, m_lastData, m_lastDataLength);
} else {
if (m_debug)
LogDebug("%s, DelayBuffer: returning a silence frame", m_name.c_str());
// Copy last network header data
::memcpy(data, m_lastData, 20U);
// We only need to copy silence AMBE data, don't care about LC data for next YSF conversion stage
::memcpy(data + 20U, DMR_SILENCE_DATA, 33U);
data[53U] = 0U;
data[54U] = 0U;
}
m_lastDataValid = false;
length = m_lastDataLength;
m_outputCount++;
return BS_MISSING;
}
return BS_NO_DATA;
}
void CDelayBuffer::reset()
{
m_buffer.clear();
m_lastDataLength = 0U;
m_outputCount = 0U;
m_timer.stop();
m_running = false;
}
void CDelayBuffer::clock(unsigned int ms)
{
m_timer.clock(ms);
if (m_timer.isRunning() && m_timer.hasExpired()) {
if (!m_running) {
m_stopWatch.start();
m_running = true;
}
}
}

58
M172DMR/DelayBuffer.h Normal file
View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2018 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(DELAYBUFFER_H)
#define DELAYBUFFER_H
#include "RingBuffer.h"
#include "StopWatch.h"
#include "Defines.h"
#include "Timer.h"
#include <string>
class CDelayBuffer {
public:
CDelayBuffer(const std::string& name, unsigned int blockSize, unsigned int blockTime, unsigned int jitterTime, bool debug);
~CDelayBuffer();
bool addData(const unsigned char* data, unsigned int length);
B_STATUS getData(unsigned char* data, unsigned int& length);
void reset();
void clock(unsigned int ms);
private:
std::string m_name;
unsigned int m_blockSize;
unsigned int m_blockTime;
bool m_debug;
CTimer m_timer;
CStopWatch m_stopWatch;
bool m_running;
CRingBuffer<unsigned char> m_buffer;
unsigned int m_outputCount;
unsigned char* m_lastData;
unsigned int m_lastDataLength;
bool m_lastDataValid;
};
#endif

262
M172DMR/Golay2087.cpp Normal file
View File

@ -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 <cstdio>
#include <cassert>
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;
}

32
M172DMR/Golay2087.h Normal file
View File

@ -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

1108
M172DMR/Golay24128.cpp Normal file

File diff suppressed because it is too large Load Diff

32
M172DMR/Golay24128.h Normal file
View File

@ -0,0 +1,32 @@
/*
* 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);
static unsigned int decode24128(unsigned char* bytes);
};
#endif

349
M172DMR/Hamming.cpp Normal file
View File

@ -0,0 +1,349 @@
/*
* 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 "Hamming.h"
#include <cstdio>
#include <cassert>
// Hamming (15,11,3) check a boolean data array
bool CHamming::decode15113_1(bool* d)
{
assert(d != NULL);
// Calculate the parity it should have
bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[6];
bool c1 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[7] ^ d[8] ^ d[9];
bool c2 = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[8] ^ d[10];
bool c3 = d[0] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[9] ^ d[10];
unsigned char n = 0U;
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 0x0FU: d[0] = !d[0]; return true;
case 0x07U: d[1] = !d[1]; return true;
case 0x0BU: d[2] = !d[2]; return true;
case 0x03U: d[3] = !d[3]; return true;
case 0x0DU: d[4] = !d[4]; return true;
case 0x05U: d[5] = !d[5]; return true;
case 0x09U: d[6] = !d[6]; return true;
case 0x0EU: d[7] = !d[7]; return true;
case 0x06U: d[8] = !d[8]; return true;
case 0x0AU: d[9] = !d[9]; return true;
case 0x0CU: d[10] = !d[10]; return true;
// No bit errors
default: return false;
}
}
void CHamming::encode15113_1(bool* d)
{
assert(d != NULL);
// Calculate the checksum this row should have
d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[6];
d[12] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[7] ^ d[8] ^ d[9];
d[13] = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[8] ^ d[10];
d[14] = d[0] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[9] ^ d[10];
}
// Hamming (15,11,3) check a boolean data array
bool CHamming::decode15113_2(bool* d)
{
assert(d != NULL);
// 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_2(bool* d)
{
assert(d != NULL);
// 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)
{
assert(d != NULL);
// 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)
{
assert(d != NULL);
// 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];
}
// Hamming (10,6,3) check a boolean data array
bool CHamming::decode1063(bool* d)
{
assert(d != NULL);
// Calculate the checksum this column should have
bool c0 = d[0] ^ d[1] ^ d[2] ^ d[5];
bool c1 = d[0] ^ d[1] ^ d[3] ^ d[5];
bool c2 = d[0] ^ d[2] ^ d[3] ^ d[4];
bool c3 = d[1] ^ d[2] ^ d[3] ^ d[4];
unsigned char n = 0x00U;
n |= (c0 != d[6]) ? 0x01U : 0x00U;
n |= (c1 != d[7]) ? 0x02U : 0x00U;
n |= (c2 != d[8]) ? 0x04U : 0x00U;
n |= (c3 != d[9]) ? 0x08U : 0x00U;
switch (n) {
// Parity bit errors
case 0x01U: d[6] = !d[6]; return true;
case 0x02U: d[7] = !d[7]; return true;
case 0x04U: d[8] = !d[8]; return true;
case 0x08U: d[9] = !d[9]; return true;
// Data bit erros
case 0x07U: d[0] = !d[0]; return true;
case 0x0BU: d[1] = !d[1]; return true;
case 0x0DU: d[2] = !d[2]; return true;
case 0x0EU: d[3] = !d[3]; return true;
case 0x0CU: d[4] = !d[4]; return true;
case 0x03U: d[5] = !d[5]; return true;
// No bit errors
default: return false;
}
}
void CHamming::encode1063(bool* d)
{
assert(d != NULL);
// Calculate the checksum this column should have
d[6] = d[0] ^ d[1] ^ d[2] ^ d[5];
d[7] = d[0] ^ d[1] ^ d[3] ^ d[5];
d[8] = d[0] ^ d[2] ^ d[3] ^ d[4];
d[9] = d[1] ^ d[2] ^ d[3] ^ d[4];
}
// A Hamming (16,11,4) Check
bool CHamming::decode16114(bool* d)
{
assert(d != NULL);
// 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)
{
assert(d != NULL);
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)
{
assert(d != NULL);
// 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)
{
assert(d != NULL);
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];
}

43
M172DMR/Hamming.h Normal file
View File

@ -0,0 +1,43 @@
/*
* 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.
*/
#ifndef Hamming_H
#define Hamming_H
class CHamming {
public:
static void encode15113_1(bool* d);
static bool decode15113_1(bool* d);
static void encode15113_2(bool* d);
static bool decode15113_2(bool* d);
static void encode1393(bool* d);
static bool decode1393(bool* d);
static void encode1063(bool* d);
static bool decode1063(bool* d);
static void encode16114(bool* d);
static bool decode16114(bool* d);
static void encode17123(bool* d);
static bool decode17123(bool* d);
};
#endif

340
M172DMR/LICENSE Normal file
View File

@ -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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.
<signature of Ty Coon>, 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.

136
M172DMR/Log.cpp Normal file
View File

@ -0,0 +1,136 @@
/*
* 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 "Log.h"
#if defined(_WIN32) || defined(_WIN64)
#include <Windows.h>
#else
#include <sys/time.h>
#endif
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <ctime>
#include <cassert>
#include <cstring>
static unsigned int m_fileLevel = 2U;
static std::string m_filePath;
static std::string m_fileRoot;
static FILE* m_fpLog = NULL;
static unsigned int m_displayLevel = 2U;
static struct tm m_tm;
static char LEVELS[] = " DMIWEF";
static bool LogOpen()
{
if (m_fileLevel == 0U)
return true;
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[100U];
#if defined(_WIN32) || defined(_WIN64)
::sprintf(filename, "%s\\%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
#else
::sprintf(filename, "%s/%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.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& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel)
{
m_filePath = filePath;
m_fileRoot = fileRoot;
m_fileLevel = fileLevel;
m_displayLevel = displayLevel;
return ::LogOpen();
}
void LogFinalise()
{
if (m_fpLog != NULL)
::fclose(m_fpLog);
}
void Log(unsigned int level, const char* fmt, ...)
{
assert(fmt != NULL);
char buffer[300U];
#if defined(_WIN32) || defined(_WIN64)
SYSTEMTIME st;
::GetSystemTime(&st);
::sprintf(buffer, "%c: %04u-%02u-%02u %02u:%02u:%02u.%03u ", LEVELS[level], st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
#else
struct timeval now;
::gettimeofday(&now, NULL);
struct tm* tm = ::gmtime(&now.tv_sec);
::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec / 1000U);
#endif
va_list vl;
va_start(vl, fmt);
::vsprintf(buffer + ::strlen(buffer), fmt, vl);
va_end(vl);
if (level >= m_fileLevel && m_fileLevel != 0U) {
bool ret = ::LogOpen();
if (!ret)
return;
::fprintf(m_fpLog, "%s\n", buffer);
::fflush(m_fpLog);
}
if (level >= m_displayLevel && m_displayLevel != 0U) {
::fprintf(stdout, "%s\n", buffer);
::fflush(stdout);
}
if (level == 6U) { // Fatal
::fclose(m_fpLog);
exit(1);
}
}

36
M172DMR/Log.h Normal file
View File

@ -0,0 +1,36 @@
/*
* 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(LOG_H)
#define LOG_H
#include <string>
#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& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel);
extern void LogFinalise();
#endif

863
M172DMR/M172DMR.cpp Normal file
View File

@ -0,0 +1,863 @@
/*
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
* Copyright (C) 2020 by Doug McLain AD8DP
*
* 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 "M172DMR.h"
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <pwd.h>
#define DMR_FRAME_PER 55U
#define M17_FRAME_PER 35U
#define XLX_SLOT 2U
#define XLX_COLOR_CODE 3U
const char* DEFAULT_INI_FILE = "/etc/M172DMR.ini";
const char* HEADER1 = "This software is for use on amateur radio networks only,";
const char* HEADER2 = "it is to be used for educational purposes only. Its use on";
const char* HEADER3 = "commercial networks is strictly prohibited.";
const char* HEADER4 = "Copyright(C) 2018 by AD8DP, CA6JAU, G4KLX and others";
#define M17CHARACTERS " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/."
#include <functional>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <clocale>
#include <cctype>
int end = 0;
void sig_handler(int signo)
{
if (signo == SIGTERM) {
end = 1;
::fprintf(stdout, "Received SIGTERM\n");
}
}
void encode_callsign(uint8_t *callsign)
{
const std::string m17_alphabet(M17CHARACTERS);
char cs[10];
memset(cs, 0, sizeof(cs));
memcpy(cs, callsign, strlen((char *)callsign));
uint64_t encoded = 0;
for(int i = std::strlen((char *)callsign)-1; i >= 0; i--) {
auto pos = m17_alphabet.find(cs[i]);
if (pos == std::string::npos) {
pos = 0;
}
encoded *= 40;
encoded += pos;
}
for (int i=0; i<6; i++) {
callsign[i] = (encoded >> (8*(5-i)) & 0xFFU);
}
}
void decode_callsign(uint8_t *callsign)
{
const std::string m17_alphabet(M17CHARACTERS);
uint8_t code[6];
uint64_t coded = callsign[0];
for (int i=1; i<6; i++)
coded = (coded << 8) | callsign[i];
if (coded > 0xee6b27ffffffu) {
//std::cerr << "Callsign code is too large, 0x" << std::hex << coded << std::endl;
return;
}
memcpy(code, callsign, 6);
memset(callsign, 0, 10);
int i = 0;
while (coded) {
callsign[i++] = m17_alphabet[coded % 40];
coded /= 40;
}
}
int main(int argc, char** argv)
{
const char* iniFile = DEFAULT_INI_FILE;
if (argc > 1) {
for (int currentArg = 1; currentArg < argc; ++currentArg) {
std::string arg = argv[currentArg];
if ((arg == "-v") || (arg == "--version")) {
::fprintf(stdout, "P252DMR version %s\n", VERSION);
return 0;
} else if (arg.substr(0, 1) == "-") {
::fprintf(stderr, "Usage: P252DMR [-v|--version] [filename]\n");
return 1;
} else {
iniFile = argv[currentArg];
}
}
}
// Capture SIGTERM to finish gracelessly
if (signal(SIGTERM, sig_handler) == SIG_ERR)
::fprintf(stdout, "Can't catch SIGTERM\n");
CM172DMR* gateway = new CM172DMR(std::string(iniFile));
int ret = gateway->run();
delete gateway;
return ret;
}
CM172DMR::CM172DMR(const std::string& configFile) :
m_callsign(),
m_m17Ref(),
m_conf(configFile),
m_dmrNetwork(NULL),
m_dmrlookup(NULL),
m_conv(),
m_colorcode(1U),
m_srcHS(1U),
m_defsrcid(1U),
m_dstid(1U),
m_dmrpc(false),
m_dmrSrc(1U),
m_dmrDst(1U),
m_dmrLastDT(0U),
m_m17Frame(NULL),
m_m17Frames(0U),
m_dmrFrame(NULL),
m_dmrFrames(0U),
m_EmbeddedLC(),
m_dmrflco(FLCO_GROUP),
m_dmrinfo(false),
m_xlxmodule(),
m_xlxConnected(false),
m_xlxReflectors(NULL),
m_xlxrefl(0U),
m_firstSync(false)
{
m_m17Frame = new unsigned char[100U];
m_dmrFrame = new unsigned char[50U];
::memset(m_m17Frame, 0U, 100U);
::memset(m_dmrFrame, 0U, 50U);
}
CM172DMR::~CM172DMR()
{
delete[] m_m17Frame;
delete[] m_dmrFrame;
}
int CM172DMR::run()
{
bool ret = m_conf.read();
if (!ret) {
::fprintf(stderr, "M172DMR: cannot read the .ini file\n");
return 1;
}
setlocale(LC_ALL, "C");
unsigned int logDisplayLevel = m_conf.getLogDisplayLevel();
if(m_conf.getDaemon())
logDisplayLevel = 0U;
bool m_daemon = m_conf.getDaemon();
if (m_daemon) {
// Create new process
pid_t pid = ::fork();
if (pid == -1) {
::fprintf(stderr, "Couldn't fork() , exiting\n");
return -1;
} else if (pid != 0)
exit(EXIT_SUCCESS);
// Create new session and process group
if (::setsid() == -1) {
::fprintf(stderr, "Couldn't setsid(), exiting\n");
return -1;
}
// Set the working directory to the root directory
if (::chdir("/") == -1) {
::fprintf(stderr, "Couldn't cd /, exiting\n");
return -1;
}
// If we are currently root...
if (getuid() == 0) {
struct passwd* user = ::getpwnam("mmdvm");
if (user == NULL) {
::fprintf(stderr, "Could not get the mmdvm user, exiting\n");
return -1;
}
uid_t mmdvm_uid = user->pw_uid;
gid_t mmdvm_gid = user->pw_gid;
// Set user and group ID's to mmdvm:mmdvm
if (setgid(mmdvm_gid) != 0) {
::fprintf(stderr, "Could not set mmdvm GID, exiting\n");
return -1;
}
if (setuid(mmdvm_uid) != 0) {
::fprintf(stderr, "Could not set mmdvm UID, exiting\n");
return -1;
}
// Double check it worked (AKA Paranoia)
if (setuid(0) != -1) {
::fprintf(stderr, "It's possible to regain root - something is wrong!, exiting\n");
return -1;
}
}
}
ret = ::LogInitialise(m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), logDisplayLevel);
if (!ret) {
::fprintf(stderr, "M172DMR: unable to open the log file\n");
return 1;
}
if (m_daemon) {
::close(STDIN_FILENO);
::close(STDOUT_FILENO);
::close(STDERR_FILENO);
}
LogInfo(HEADER1);
LogInfo(HEADER2);
LogInfo(HEADER3);
LogInfo(HEADER4);
m_callsign = m_conf.getCallsign();
m_m17Ref = m_conf.getM17DstName();
char module = m_m17Ref.c_str()[m_m17Ref.find(' ')+1];
std::string m17_dstAddress = m_conf.getM17DstAddress();
unsigned int m17_dstPort = m_conf.getM17DstPort();
std::string m17_localAddress = m_conf.getM17LocalAddress();
unsigned int m17_localPort = m_conf.getM17LocalPort();
bool m17_debug = m_conf.getM17NetworkDebug();
m_conv.setM17GainAdjDb(m_conf.getM17GainAdjDb());
uint16_t streamid = 0;
unsigned char m17_src[10];
unsigned char m17_dst[10];
memcpy(m17_src, m_callsign.c_str(), 9);
m17_src[9] = 0x00;
encode_callsign(m17_src);
std::string fileName = m_conf.getDMRXLXFile();
m_xlxReflectors = new CReflectors(fileName, 60U);
m_xlxReflectors->load();
m_m17Network = new CM17Network(m17_localAddress, m17_localPort, m17_dstAddress, m17_dstPort, m17_src, m17_debug);
ret = m_m17Network->open();
if (!ret) {
::LogError("Cannot open the M17 network port");
::LogFinalise();
return 1;
}
ret = createDMRNetwork();
if (!ret) {
::LogError("Cannot open DMR Network");
::LogFinalise();
return 1;
}
std::string lookupFile = m_conf.getDMRIdLookupFile();
unsigned int reloadTime = m_conf.getDMRIdLookupTime();
m_dmrlookup = new CDMRLookup(lookupFile, reloadTime);
m_dmrlookup->read();
if (m_dmrpc)
m_dmrflco = FLCO_USER_USER;
else
m_dmrflco = FLCO_GROUP;
CTimer networkWatchdog(100U, 0U, 1500U);
CTimer pollTimer(1000U, 5U);
std::string name = m_conf.getDescription();
CStopWatch stopWatch;
CStopWatch m17Watch;
CStopWatch dmrWatch;
stopWatch.start();
m17Watch.start();
dmrWatch.start();
pollTimer.start();
unsigned char m17_cnt = 0;
unsigned char dmr_cnt = 0;
m_m17Network->writeLink(module);
LogMessage("Starting M172DMR-%s", VERSION);
for (; end == 0;) {
unsigned char buffer[2000U];
CDMRData tx_dmrdata;
unsigned int ms = stopWatch.elapsed();
if (m_dmrNetwork->isConnected() && !m_xlxmodule.empty() && !m_xlxConnected) {
writeXLXLink(m_defsrcid, m_dstid, m_dmrNetwork);
LogMessage("XLX, Linking to reflector XLX%03u, module %s", m_xlxrefl, m_xlxmodule.c_str());
m_xlxConnected = true;
}
while (m_m17Network->readData(m_m17Frame, 54U) > 0U) {
//CUtils::dump(1U, "M17 Data", m_p25Frame, 22U);
if (!memcmp(m_m17Frame, "M17 ", 4)) {
if (m_m17Frame[34] == 0 && m_m17Frame[35] == 0) {
m_m17Frames = 0;
m_conv.putM17Header();
}
else if (m_m17Frame[34U] & 0x80U) {
LogMessage("M17 received end of voice transmission, %.1f seconds", float(m_m17Frames) / 25.0F);
m_conv.putM17EOT();
}
else{
m_conv.putM17(m_m17Frame);
}
uint8_t cs[10];
memcpy(cs, m_m17Frame+12, 6);
decode_callsign(cs);
std::string css((char *)cs);
css = css.substr(0, css.find(' '));
int dmrid = m_dmrlookup->findID(css);
if(dmrid){
m_dmrSrc = dmrid;
}
m_m17Frames++;
}
}
if (dmrWatch.elapsed() > DMR_FRAME_PER) {
unsigned int dmrFrameType = m_conv.getDMR(m_dmrFrame);
if(dmrFrameType == TAG_HEADER) {
LogMessage("Sending DMR Header");
CDMRData rx_dmrdata;
dmr_cnt = 0U;
rx_dmrdata.setSlotNo(2U);
rx_dmrdata.setSrcId(m_dmrSrc);
rx_dmrdata.setDstId(m_dstid);
rx_dmrdata.setFLCO(m_dmrflco);
rx_dmrdata.setN(0U);
rx_dmrdata.setSeqNo(0U);
rx_dmrdata.setBER(0U);
rx_dmrdata.setRSSI(0U);
rx_dmrdata.setDataType(DT_VOICE_LC_HEADER);
// Add sync
CSync::addDMRDataSync(m_dmrFrame, 0);
// Add SlotType
CDMRSlotType slotType;
slotType.setColorCode(m_colorcode);
slotType.setDataType(DT_VOICE_LC_HEADER);
slotType.getData(m_dmrFrame);
// Full LC
CDMRLC dmrLC = CDMRLC(m_dmrflco, m_dmrSrc, m_dstid);
CDMRFullLC fullLC;
fullLC.encode(dmrLC, m_dmrFrame, DT_VOICE_LC_HEADER);
m_EmbeddedLC.setLC(dmrLC);
rx_dmrdata.setData(m_dmrFrame);
//CUtils::dump(1U, "DMR data:", m_dmrFrame, 33U);
for (unsigned int i = 0U; i < 3U; i++) {
rx_dmrdata.setSeqNo(dmr_cnt);
m_dmrNetwork->write(rx_dmrdata);
dmr_cnt++;
}
dmrWatch.start();
}
else if(dmrFrameType == TAG_EOT) {
LogMessage("Sending DMR EOT");
CDMRData rx_dmrdata;
unsigned int n_dmr = (dmr_cnt - 3U) % 6U;
unsigned int fill = (6U - n_dmr);
if (n_dmr) {
for (unsigned int i = 0U; i < fill; i++) {
CDMREMB emb;
CDMRData rx_dmrdata;
rx_dmrdata.setSlotNo(2U);
rx_dmrdata.setSrcId(m_dmrSrc);
rx_dmrdata.setDstId(m_dstid);
rx_dmrdata.setFLCO(m_dmrflco);
rx_dmrdata.setN(n_dmr);
rx_dmrdata.setSeqNo(dmr_cnt);
rx_dmrdata.setBER(0U);
rx_dmrdata.setRSSI(0U);
rx_dmrdata.setDataType(DT_VOICE);
::memcpy(m_dmrFrame, DMR_SILENCE_DATA, DMR_FRAME_LENGTH_BYTES);
// Generate the Embedded LC
unsigned char lcss = m_EmbeddedLC.getData(m_dmrFrame, n_dmr);
// Generate the EMB
emb.setColorCode(m_colorcode);
emb.setLCSS(lcss);
emb.getData(m_dmrFrame);
rx_dmrdata.setData(m_dmrFrame);
//CUtils::dump(1U, "DMR data:", m_dmrFrame, 33U);
m_dmrNetwork->write(rx_dmrdata);
n_dmr++;
dmr_cnt++;
//m_p25Frames = 0;
}
}
rx_dmrdata.setSlotNo(2U);
rx_dmrdata.setSrcId(m_dmrSrc);
rx_dmrdata.setDstId(m_dstid);
rx_dmrdata.setFLCO(m_dmrflco);
rx_dmrdata.setN(n_dmr);
rx_dmrdata.setSeqNo(dmr_cnt);
rx_dmrdata.setBER(0U);
rx_dmrdata.setRSSI(0U);
rx_dmrdata.setDataType(DT_TERMINATOR_WITH_LC);
// Add sync
CSync::addDMRDataSync(m_dmrFrame, 0);
// Add SlotType
CDMRSlotType slotType;
slotType.setColorCode(m_colorcode);
slotType.setDataType(DT_TERMINATOR_WITH_LC);
slotType.getData(m_dmrFrame);
// Full LC
CDMRLC dmrLC = CDMRLC(m_dmrflco, m_dmrSrc, m_dstid);
CDMRFullLC fullLC;
fullLC.encode(dmrLC, m_dmrFrame, DT_TERMINATOR_WITH_LC);
rx_dmrdata.setData(m_dmrFrame);
//CUtils::dump(1U, "DMR data:", m_dmrFrame, 33U);
m_dmrNetwork->write(rx_dmrdata);
dmrWatch.start();
}
else if(dmrFrameType == TAG_DATA) {
LogMessage("Sending DMR Data");
CDMREMB emb;
CDMRData rx_dmrdata;
unsigned int n_dmr = (dmr_cnt - 3U) % 6U;
rx_dmrdata.setSlotNo(2U);
rx_dmrdata.setSrcId(m_dmrSrc);
rx_dmrdata.setDstId(m_dstid);
rx_dmrdata.setFLCO(m_dmrflco);
rx_dmrdata.setN(n_dmr);
rx_dmrdata.setSeqNo(dmr_cnt);
rx_dmrdata.setBER(0U);
rx_dmrdata.setRSSI(0U);
if (!n_dmr) {
rx_dmrdata.setDataType(DT_VOICE_SYNC);
// Add sync
CSync::addDMRAudioSync(m_dmrFrame, 0U);
// Prepare Full LC data
CDMRLC dmrLC = CDMRLC(m_dmrflco, m_dmrSrc, m_dstid);
// Configure the Embedded LC
m_EmbeddedLC.setLC(dmrLC);
}
else {
rx_dmrdata.setDataType(DT_VOICE);
// Generate the Embedded LC
unsigned char lcss = m_EmbeddedLC.getData(m_dmrFrame, n_dmr);
// Generate the EMB
emb.setColorCode(m_colorcode);
emb.setLCSS(lcss);
emb.getData(m_dmrFrame);
}
rx_dmrdata.setData(m_dmrFrame);
//CUtils::dump(1U, "DMR data:", m_dmrFrame, 33U);
m_dmrNetwork->write(rx_dmrdata);
dmr_cnt++;
dmrWatch.start();
}
}
while (m_dmrNetwork->read(tx_dmrdata) > 0U) {
m_dmrSrc = tx_dmrdata.getSrcId();
m_dmrDst = tx_dmrdata.getDstId();
if(!m_dmrSrc){
m_dmrSrc = m_srcHS;
}
memset(m17_src, 0, 10);
std::string css = m_dmrlookup->findCS(m_dmrSrc);
memcpy(m17_src, css.c_str(), css.size());
m17_src[css.size()] = ' ';
m17_src[css.size()+1] = 'D';
encode_callsign(m17_src);
FLCO netflco = tx_dmrdata.getFLCO();
unsigned char DataType = tx_dmrdata.getDataType();
if (!tx_dmrdata.isMissing()) {
networkWatchdog.start();
if(DataType == DT_TERMINATOR_WITH_LC) {
if (m_dmrFrames == 0U) {
m_dmrNetwork->reset(2U);
networkWatchdog.stop();
m_dmrinfo = false;
m_firstSync = false;
break;
}
LogMessage("DMR received end of voice transmission, %.1f seconds", float(m_dmrFrames) / 16.667F);
m_conv.putDMREOT();
m_dmrNetwork->reset(2U);
networkWatchdog.stop();
m_dmrFrames = 0U;
m_dmrinfo = false;
m_firstSync = false;
}
if((DataType == DT_VOICE_LC_HEADER) && (DataType != m_dmrLastDT)) {
std::string netSrc = m_dmrlookup->findCS(m_dmrSrc);
std::string netDst = (netflco == FLCO_GROUP ? "TG " : "") + m_dmrlookup->findCS(m_dmrDst);
m_conv.putDMRHeader();
LogMessage("DMR header received from %s to %s", netSrc.c_str(), netDst.c_str());
m_dmrinfo = true;
m_dmrFrames = 0U;
m_firstSync = false;
}
if(DataType == DT_VOICE_SYNC)
m_firstSync = true;
if((DataType == DT_VOICE_SYNC || DataType == DT_VOICE) && m_firstSync) {
unsigned char dmr_frame[50];
tx_dmrdata.getData(dmr_frame);
if (!m_dmrinfo) {
std::string netSrc = m_dmrlookup->findCS(m_dmrSrc);
std::string netDst = (netflco == FLCO_GROUP ? "TG " : "") + m_dmrlookup->findCS(m_dmrDst);
m_conv.putDMRHeader();
LogMessage("DMR late entry from %s to %s", netSrc.c_str(), netDst.c_str());
m_dmrinfo = true;
}
m_conv.putDMR(dmr_frame);
m_dmrFrames++;
}
}
else {
if(DataType == DT_VOICE_SYNC || DataType == DT_VOICE) {
unsigned char dmr_frame[50];
tx_dmrdata.getData(dmr_frame);
m_conv.putDMR(dmr_frame);
m_dmrFrames++;
}
networkWatchdog.clock(ms);
if (networkWatchdog.hasExpired()) {
LogDebug("Network watchdog has expired, %.1f seconds", float(m_dmrFrames) / 16.667F);
m_dmrNetwork->reset(2U);
networkWatchdog.stop();
m_dmrFrames = 0U;
m_dmrinfo = false;
}
}
m_dmrLastDT = DataType;
}
if (m17Watch.elapsed() > M17_FRAME_PER) {
unsigned int m17FrameType = m_conv.getM17(m_m17Frame);
if(m17FrameType == TAG_HEADER) {
m17_cnt = 0U;
m17Watch.start();
streamid = static_cast<uint16_t>((::rand() & 0xFFFF));
memcpy(m17_dst, m_m17Ref.c_str(), m_m17Ref.size());
m17_dst[9] = 0x00;
encode_callsign(m17_dst);
memcpy(buffer, "M17 ", 4);
memcpy(buffer+4, &streamid, 2);
memcpy(buffer+6, m17_dst, 6);
memcpy(buffer+12, m17_src, 6);
buffer[19] = 0x05;
memcpy(buffer+36, m_m17Frame, 16);
m_m17Network->writeData(buffer, 54U);
}
else if(m17FrameType == TAG_EOT) {
m17_cnt |= 0x8000;
memcpy(buffer, "M17 ", 4);
memcpy(buffer+4, &streamid, 2);
memcpy(buffer+6, m17_dst, 6);
memcpy(buffer+12, m17_src, 6);
buffer[19] = 0x05;
buffer[34] = m17_cnt >> 8;
buffer[35] = m17_cnt & 0xff;
memcpy(buffer+36, m_m17Frame, 16);
m_m17Network->writeData(buffer, 54U);
m17Watch.start();
}
else if(m17FrameType == TAG_DATA) {
//CUtils::dump(1U, "M17 Data", m_p25Frame, 11U);
m17_cnt++;
memcpy(buffer, "M17 ", 4);
memcpy(buffer+4, &streamid, 2);
memcpy(buffer+6, m17_dst, 6);
memcpy(buffer+12, m17_src, 6);
buffer[19] = 0x05;
buffer[34] = m17_cnt >> 8;
buffer[35] = m17_cnt & 0xff;
memcpy(buffer+36, m_m17Frame, 16);
m_m17Network->writeData(buffer, 54U);
m17Watch.start();
}
}
stopWatch.start();
pollTimer.clock(ms);
if (pollTimer.isRunning() && pollTimer.hasExpired()) {
m_m17Network->writePoll();
pollTimer.start();
}
if(m_dmrNetwork->clock(ms)){
m_xlxConnected = false;
}
if (m_xlxReflectors != NULL)
m_xlxReflectors->clock(ms);
if (ms < 5U) CThread::sleep(5U);
}
m_m17Network->close();
m_dmrNetwork->close();
delete m_dmrNetwork;
delete m_m17Network;
if (m_xlxReflectors != NULL)
delete m_xlxReflectors;
::LogFinalise();
return 0;
}
bool CM172DMR::createDMRNetwork()
{
std::string address = m_conf.getDMRNetworkAddress();
m_xlxmodule = m_conf.getDMRXLXModule();
m_xlxrefl = m_conf.getDMRXLXReflector();
unsigned int port = m_conf.getDMRNetworkPort();
unsigned int local = m_conf.getDMRNetworkLocal();
std::string password = m_conf.getDMRNetworkPassword();
bool debug = m_conf.getDMRNetworkDebug();
unsigned int jitter = m_conf.getDMRNetworkJitter();
bool slot1 = false;
bool slot2 = true;
bool duplex = false;
HW_TYPE hwType = HWT_MMDVM;
m_srcHS = m_conf.getDMRId();
m_colorcode = 1U;
if (m_xlxmodule.empty()) {
m_dstid = m_conf.getDMRDstId();
m_dmrpc = m_conf.getDMRPC();
}
else {
const char *xlxmod = m_xlxmodule.c_str();
m_dstid = 4000 + xlxmod[0] - 64;
m_dmrpc = 0;
CReflector* reflector = m_xlxReflectors->find(m_xlxrefl);
if (reflector == NULL)
return false;
address = reflector->m_address;
}
if (m_srcHS > 99999999U)
m_defsrcid = m_srcHS / 100U;
else if (m_srcHS > 9999999U)
m_defsrcid = m_srcHS / 10U;
else
m_defsrcid = m_srcHS;
LogMessage("DMR Network Parameters");
LogMessage(" ID: %u", m_srcHS);
LogMessage(" Default SrcID: %u", m_defsrcid);
if (!m_xlxmodule.empty()) {
LogMessage(" XLX Reflector: %d", m_xlxrefl);
LogMessage(" XLX Module: %s (%d)", m_xlxmodule.c_str(), m_dstid);
}
else {
LogMessage(" Startup DstID: %s%u", m_dmrpc ? "" : "TG ", m_dstid);
LogMessage(" Address: %s", address.c_str());
}
LogMessage(" Port: %u", port);
if (local > 0U)
LogMessage(" Local: %u", local);
else
LogMessage(" Local: random");
LogMessage(" Jitter: %ums", jitter);
m_dmrNetwork = new CDMRNetwork(address, port, local, m_srcHS, password, duplex, VERSION, debug, slot1, slot2, hwType, jitter);
std::string options = m_conf.getDMRNetworkOptions();
if (!options.empty()) {
LogMessage(" Options: %s", options.c_str());
m_dmrNetwork->setOptions(options);
}
unsigned int rxFrequency = m_conf.getRxFrequency();
unsigned int txFrequency = m_conf.getTxFrequency();
unsigned int power = m_conf.getPower();
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();
LogMessage("Info Parameters");
LogMessage(" Callsign: %s", m_callsign.c_str());
LogMessage(" RX Frequency: %uHz", rxFrequency);
LogMessage(" TX Frequency: %uHz", txFrequency);
LogMessage(" Power: %uW", power);
LogMessage(" Latitude: %fdeg N", latitude);
LogMessage(" Longitude: %fdeg E", longitude);
LogMessage(" Height: %um", height);
LogMessage(" Location: \"%s\"", location.c_str());
LogMessage(" Description: \"%s\"", description.c_str());
LogMessage(" URL: \"%s\"", url.c_str());
m_dmrNetwork->setConfig(m_callsign, rxFrequency, txFrequency, power, m_colorcode, latitude, longitude, height, location, description, url);
bool ret = m_dmrNetwork->open();
if (!ret) {
delete m_dmrNetwork;
m_dmrNetwork = NULL;
return false;
}
m_dmrNetwork->enable(true);
return true;
}
void CM172DMR::writeXLXLink(unsigned int srcId, unsigned int dstId, CDMRNetwork* network)
{
assert(network != NULL);
unsigned int streamId = ::rand() + 1U;
CDMRData data;
data.setSlotNo(XLX_SLOT);
data.setFLCO(FLCO_USER_USER);
data.setSrcId(srcId);
data.setDstId(dstId);
data.setDataType(DT_VOICE_LC_HEADER);
data.setN(0U);
data.setStreamId(streamId);
unsigned char buffer[DMR_FRAME_LENGTH_BYTES];
CDMRLC lc;
lc.setSrcId(srcId);
lc.setDstId(dstId);
lc.setFLCO(FLCO_USER_USER);
CDMRFullLC fullLC;
fullLC.encode(lc, buffer, DT_VOICE_LC_HEADER);
CDMRSlotType slotType;
slotType.setColorCode(XLX_COLOR_CODE);
slotType.setDataType(DT_VOICE_LC_HEADER);
slotType.getData(buffer);
CSync::addDMRDataSync(buffer, true);
data.setData(buffer);
for (unsigned int i = 0U; i < 3U; i++) {
data.setSeqNo(i);
network->write(data);
}
data.setDataType(DT_TERMINATOR_WITH_LC);
fullLC.encode(lc, buffer, DT_TERMINATOR_WITH_LC);
slotType.setDataType(DT_TERMINATOR_WITH_LC);
slotType.getData(buffer);
data.setData(buffer);
for (unsigned int i = 0U; i < 2U; i++) {
data.setSeqNo(i + 3U);
network->write(data);
}
}

97
M172DMR/M172DMR.h Normal file
View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
* Copyright (C) 2020 by Doug McLain AD8DP
*
* 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(P252DMR_H)
#define P252DMR_H
#include "DMRDefines.h"
#include "ModeConv.h"
#include "DMRNetwork.h"
#include "M17Network.h"
#include "DMREmbeddedData.h"
#include "DMRLC.h"
#include "DMRFullLC.h"
#include "DMREMB.h"
#include "DMRLookup.h"
#include "Reflectors.h"
#include "UDPSocket.h"
#include "StopWatch.h"
#include "Version.h"
#include "Thread.h"
#include "Timer.h"
#include "Sync.h"
#include "Utils.h"
#include "Conf.h"
#include "Log.h"
#include "CRC.h"
#include <string>
enum TG_STATUS {
NONE,
WAITING_UNLINK,
SEND_REPLY,
SEND_PTT
};
class CM172DMR
{
public:
CM172DMR(const std::string& configFile);
~CM172DMR();
int run();
private:
std::string m_callsign;
std::string m_m17Ref;
CConf m_conf;
CDMRNetwork* m_dmrNetwork;
CM17Network* m_m17Network;
CDMRLookup* m_dmrlookup;
CModeConv m_conv;
unsigned int m_colorcode;
unsigned int m_srcHS;
unsigned int m_defsrcid;
unsigned int m_dstid;
bool m_dmrpc;
unsigned int m_dmrSrc;
unsigned int m_dmrDst;
std::string m_m17Src;
std::string m_m17Dst;
unsigned char m_dmrLastDT;
unsigned char* m_m17Frame;
unsigned int m_m17Frames;
unsigned char* m_dmrFrame;
unsigned int m_dmrFrames;
CDMREmbeddedData m_EmbeddedLC;
FLCO m_dmrflco;
bool m_dmrinfo;
std::string m_xlxmodule;
bool m_xlxConnected;
CReflectors* m_xlxReflectors;
unsigned int m_xlxrefl;
bool m_firstSync;
bool createDMRNetwork();
void writeXLXLink(unsigned int srcId, unsigned int dstId, CDMRNetwork* network);
};
#endif

48
M172DMR/M172DMR.ini Normal file
View File

@ -0,0 +1,48 @@
[Info]
RXFrequency=435000000
TXFrequency=435000000
Power=1
Latitude=0.0
Longitude=0.0
Height=0
Location=Nowhere
Description=Multi-Mode Repeater
URL=www.google.co.uk
[M17 Network]
Callsign=AD8DP D
LocalPort=32010
DstName=M17-BRO A
#DstAddress=3.138.122.152
DstAddress=192.168.1.8
DstPort=17000
GainAdjustdB=-6
Daemon=0
Debug=1
[DMR Network]
Id=3126482
#XLXFile=XLXHosts.txt
#XLXReflector=950
#XLXModule=D
StartupDstId=4003
# For TG call: StartupPC=0
StartupPC=0
Address=xlx625.wa8bro.com
Port=62030
Jitter=500
# Local=62032
Password=PASSWORD
# Options=
Debug=1
[DMR Id Lookup]
File=DMRIds.dat
Time=24
[Log]
# Logging levels, 0=No logging
DisplayLevel=1
FileLevel=1
FilePath=.
FileRoot=M172DMR

129
M172DMR/M17Network.cpp Normal file
View File

@ -0,0 +1,129 @@
/*
* Copyright (C) 2009-2014,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 "M17Network.h"
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
CM17Network::CM17Network(const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, unsigned char* callsign, bool debug) :
m_address(),
m_port(gatewayPort),
//m_socket(localAddress, localPort),
m_socket(localPort),
m_debug(debug)
{
memcpy(m_callsign, callsign, 6);
m_address = CUDPSocket::lookup(gatewayAddress);
}
CM17Network::~CM17Network()
{
}
bool CM17Network::open()
{
LogInfo("Opening M17 network connection");
return m_socket.open();
}
bool CM17Network::writeData(const unsigned char* data, unsigned int length)
{
assert(data != NULL);
assert(length > 0U);
if (m_debug)
CUtils::dump(1U, "M17 Network Data Sent", data, length);
return m_socket.write(data, length, m_address, m_port);
}
bool CM17Network::writePoll()
{
unsigned char data[10U];
memcpy(data, "PONG", 4);
memcpy(data+4, m_callsign, 6);
if (m_debug)
CUtils::dump(1U, "M17 Network Pong Sent", data, 10U);
return m_socket.write(data, 10U, m_address, m_port);
}
bool CM17Network::writeLink(char m)
{
unsigned char data[11U];
memcpy(data, "CONN", 4);
memcpy(data+4, m_callsign, 6);
data[10U] = m;
if (m_debug)
CUtils::dump(1U, "M17 Network Link Sent", data, 11U);
//LogInfo("writeLink add:port == %x, %x", m_address.s_addr, m_port);
return m_socket.write(data, 11U, m_address, m_port);
}
bool CM17Network::writeUnlink()
{
unsigned char data[10U];
memcpy(data, "DISC", 4);
memcpy(data+4, m_callsign, 6);
if (m_debug)
CUtils::dump(1U, "M17 Network Unlink Sent", data, 10U);
return m_socket.write(data, 10U, m_address, m_port);
}
unsigned int CM17Network::readData(unsigned char* data, unsigned int length)
{
assert(data != NULL);
assert(length > 0U);
in_addr address;
unsigned int port;
int len = m_socket.read(data, length, address, port);
if (len <= 0)
return 0U;
// Check if the data is for us
if (m_address.s_addr != address.s_addr || port != m_port) {
LogMessage("M17 packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, m_port, port);
return 0U;
}
if (m_debug)
CUtils::dump(1U, "M17 Network Data Received", data, len);
return len;
}
void CM17Network::close()
{
m_socket.close();
LogInfo("Closing P25 network connection");
}

47
M172DMR/M17Network.h Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2009-2014,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 M17Network_H
#define M17Network_H
#include "UDPSocket.h"
#include <cstdint>
#include <string>
class CM17Network {
public:
CM17Network(const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, unsigned char* callsign, bool debug);
~CM17Network();
bool open();
bool writeData(const unsigned char* data, unsigned int length);
unsigned int readData(unsigned char* data, unsigned int length);
bool writePoll();
bool writeLink(char m);
bool writeUnlink();
void close();
private:
in_addr m_address;
unsigned int m_port;
CUDPSocket m_socket;
bool m_debug;
unsigned char m_callsign[6];
};
#endif

41
M172DMR/MBEVocoder.cpp Normal file
View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
* Copyright (C) 2020 by Doug McLain AD8DP
*
* 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 <cstring>
#include <md380_vocoder.h>
#include "MBEVocoder.h"
const uint8_t BIT_MASK_TABLE8[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
#define WRITE_BIT8(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE8[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE8[(i)&7])
#define READ_BIT8(p,i) (p[(i)>>3] & BIT_MASK_TABLE8[(i)&7])
MBEVocoder::MBEVocoder(void)
{
}
void MBEVocoder::decode_2450(int16_t *pcm, uint8_t *ambe49)
{
md380_decode(ambe49, pcm);
}
void MBEVocoder::encode_2450(int16_t *pcm, uint8_t *ambe49)
{
md380_encode(ambe49, pcm);
}

35
M172DMR/MBEVocoder.h Normal file
View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
* Copyright (C) 2020 by Doug McLain AD8DP
*
* 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 INCLUDED_AMBE_ENCODER_H
#define INCLUDED_AMBE_ENCODER_H
#include <stdint.h>
class MBEVocoder {
public:
void decode_2450(int16_t *, uint8_t *);
void encode_2450(int16_t *, uint8_t *);
MBEVocoder(void);
private:
};
#endif /* INCLUDED_AMBE_ENCODER_H */

26
M172DMR/Makefile Normal file
View File

@ -0,0 +1,26 @@
CC ?= gcc
CXX ?= g++
CFLAGS ?= -g -O3 -Wall -std=c++0x -pthread
LIBS = -lm -lpthread -lmd380_vocoder
LDFLAGS ?= -g
OBJECTS = BPTC19696.o Conf.o CRC.o DelayBuffer.o DMRData.o DMREMB.o DMREmbeddedData.o \
DMRFullLC.o DMRLC.o DMRLookup.o DMRNetwork.o DMRSlotType.o M17Network.o Golay2087.o \
Golay24128.o Hamming.o Log.o ModeConv.o Mutex.o QR1676.o RS129.o SHA256.o StopWatch.o \
Sync.o Thread.o Timer.o UDPSocket.o Utils.o MBEVocoder.o Reflectors.o codec2/codebooks.o codec2/kiss_fft.o \
codec2/lpc.o codec2/nlp.o codec2/pack.o codec2/qbase.o codec2/quantise.o codec2/codec2.o M172DMR.o
all: M172DMR
M172DMR: $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o M172DMR -Xlinker --section-start=.firmware=0x0800C000 -Xlinker --section-start=.sram=0x20000000
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
install:
install -m 755 M172DMR /usr/local/bin/
clean:
$(RM) M172DMR *.o *.d *.bak *~

781
M172DMR/ModeConv.cpp Normal file
View File

@ -0,0 +1,781 @@
/*
* Copyright (C) 2010,2014,2016,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2016 Mathias Weyland, HB9FRV
* Copyright (C) 2018 by Andy Uribe CA6JAU
* Copyright (C) 2020 by Doug McLain AD8DP
*
* 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 "ModeConv.h"
#include "Golay24128.h"
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
#define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
#define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
const unsigned int PRNG_TABLE[] = {
0x42CC47U, 0x19D6FEU, 0x304729U, 0x6B2CD0U, 0x60BF47U, 0x39650EU, 0x7354F1U, 0xEACF60U, 0x819C9FU, 0xDE25CEU,
0xD7B745U, 0x8CC8B8U, 0x8D592BU, 0xF71257U, 0xBCA084U, 0xA5B329U, 0xEE6AFAU, 0xF7D9A7U, 0xBCC21CU, 0x4712D9U,
0x4F2922U, 0x14FA37U, 0x5D43ECU, 0x564115U, 0x299A92U, 0x20A9EBU, 0x7B707DU, 0x3BE3A4U, 0x20D95BU, 0x6B085AU,
0x5233A5U, 0x99A474U, 0xC0EDCBU, 0xCB5F12U, 0x918455U, 0xF897ECU, 0xE32E3BU, 0xAA7CC2U, 0xB1E7C9U, 0xFC561DU,
0xA70DE6U, 0x8DBE73U, 0xD4F608U, 0x57658DU, 0x0E5E56U, 0x458DABU, 0x7E15B8U, 0x376645U, 0x2DFD86U, 0x64EC3BU,
0x3F1F60U, 0x3481B4U, 0x4DA00FU, 0x067BCEU, 0x1B68B1U, 0xD19328U, 0xCA03FFU, 0xA31856U, 0xF8EB81U, 0xF9F2F8U,
0xA26067U, 0xA91BB6U, 0xF19A59U, 0x9A6148U, 0x8372B6U, 0xC8E86FU, 0x9399DCU, 0x1A0291U, 0x619142U, 0x6DE9FFU,
0x367A2CU, 0x7D2511U, 0x6484DAU, 0x2F1F0FU, 0x1E6DB4U, 0x55F6E1U, 0x0EA70AU, 0x061C96U, 0xDD0E45U, 0xB4D738U,
0xAF64ABU, 0xE47F42U, 0xFDBE9DU, 0xB684ACU, 0xFE5773U, 0xC1E4A2U, 0x8AFD0DU, 0x932ED4U, 0xD814E3U, 0x81853AU,
0x225EECU, 0x7A6945U, 0x31A112U, 0x2AB2EBU, 0x630974U, 0x785AB5U, 0x11E3CEU, 0x4A715BU, 0x402AA0U, 0x199B7DU,
0x16C05EU, 0x6F5283U, 0xA4FB10U, 0xBFA8ECU, 0xF633B7U, 0xEC4012U, 0xADD8C9U, 0xD6EB1CU, 0xDD3027U, 0x84A1FAU,
0xCF9E19U, 0xD64C80U, 0xBC4557U, 0xA7B62EU, 0x6E2DA1U, 0x311F50U, 0x38C68EU, 0x63D5BFU, 0x486E60U, 0x10BFE1U,
0x5BAD1EU, 0x4A4647U, 0x0157F0U, 0x7ACC29U, 0x73BEEAU, 0x2825D7U, 0xA0940CU, 0xFBCFF9U, 0xB05C62U, 0x892426U,
0xC6B3DDU, 0xDF3840U, 0x9449B3U, 0xCED3BEU, 0xE7804DU, 0xBC3B90U, 0xF5AA0BU, 0xE6D17EU, 0x2D43B5U, 0x345A04U,
0x5EA9DBU, 0x07A202U, 0x0C7134U, 0x45C9FDU, 0x5EDA0AU, 0x310193U, 0x6830C4U, 0x62AA3DU, 0x3B59B2U, 0xB04043U,
0xEB975CU, 0x82BCADU, 0x912E62U, 0xD8F7FBU, 0x82C489U, 0x895F54U, 0xF00FE7U, 0xFBBC2AU, 0xA2E771U, 0xE956C4U,
0xF6CD1FU, 0x3F8FEAU, 0x0534E1U, 0x4C653CU, 0x17FE8FU, 0x1C4C52U, 0x4515A1U, 0x2E86A9U, 0x3FBD56U, 0x756C87U,
0x6ED218U, 0x279179U, 0x7C0AA6U, 0xD53B17U, 0x8EE0C8U, 0x85F291U, 0xD94B36U, 0x9298EFU, 0xAB8318U, 0xE07301U,
0xBB68DFU, 0xB2CB7CU, 0xE910A5U, 0xE101D2U, 0x92BB4BU, 0x59E8B4U, 0x407175U, 0x0B026AU, 0x12989BU, 0x792944U,
0x2376EDU, 0x2EF5BAU, 0x758663U, 0x7C1ED5U, 0x078D0CU, 0x4EF6ABU, 0x5567F2U, 0x9F7C29U, 0xC68E9CU, 0xC51747U,
0xBC6422U, 0xB7EFB9U, 0xECFD44U, 0xA50497U, 0xAF178AU, 0xD68C69U, 0xD97DB5U, 0x82670EU, 0xCBB45BU, 0x508D90U,
0x190A25U, 0x63F0FEU, 0x68E3C7U, 0x317A10U, 0x3A09D9U, 0x6B926EU, 0x004237U, 0x1B79C8U, 0x53EA59U, 0x48B3B7U,
0x811166U, 0xDE4A79U, 0xF5F988U, 0xAC6057U, 0xE733FEU, 0xFF89ADU, 0xB49830U, 0x8F4BC3U, 0xC6F00EU, 0x9DA135U,
0x942FE0U, 0xC71C3BU, 0x4DC78FU, 0x3476C4U, 0x7F6C39U, 0x66BFAAU, 0x298657U, 0x725504U, 0x5B4E89U, 0x01FE72U,
0x0835A3U, 0x53269CU, 0x189D4DU, 0x01CDC2U, 0xEA763BU, 0xF3A56DU, 0xB0BCD4U, 0xE80F13U, 0xE355CAU, 0x98C47DU,
0x91AB24U, 0xCE38DBU, 0x87A35AU, 0x9CD3A5U, 0xD648F4U, 0xAF7B6FU, 0x24A292U, 0x7D3011U, 0x764B6DU, 0x2DDABEU,
0x44D123U, 0x5E22D8U, 0x1FB09DU, 0x04A926U, 0x4F5AF3U, 0x064128U, 0x3DB105U, 0x70AAD6U, 0xAA392FU, 0xA1C4B8U,
0xF8C7C0U, 0xD35D0FU, 0x8A2E9EU, 0xC1B761U, 0xDA44F0U, 0x925E8FU, 0x89CF4EU, 0xE8B4D1U, 0xB32728U, 0xB8FE7FU,
0x61DCC6U, 0x2A4701U, 0x1614D8U, 0x5DADE2U, 0x46BE37U, 0x0F44DCU, 0x54D549U, 0x5D8E32U, 0x263DAFU, 0x2C237CU,
0x75E291U, 0xBE5982U, 0xA74A7FU, 0xC493A4U, 0xDFA131U, 0x967A5AU, 0xCCCB8EU, 0xC1D835U, 0x9A02ECU, 0xF331BBU,
0xE8B812U, 0xA3EBC5U, 0xBA507CU, 0x7080ABU, 0x099BC2U, 0x02285DU, 0x59718CU, 0x50C273U, 0x0B1862U, 0x4A1F8CU,
0x70A655U, 0x3BF5C2U, 0x666FBBU, 0x6DDE68U, 0x3485C5U, 0x9F161EU, 0xC46F4BU, 0x8CFDF0U, 0x97C625U, 0xDE058EU,
0xC59CD3U, 0xAEAE20U, 0xF775BCU, 0xFC647FU, 0xBD9F02U, 0xE70C91U, 0xCC1468U, 0x11E7B7U, 0x1AFC36U, 0x435B49U,
0x080398U, 0x139027U, 0x7B63FEU, 0x607AF9U, 0x29E900U, 0x7293D6U, 0x79026FU, 0x00D930U, 0x0BEAF1U, 0xD3614EU,
0x90119FU, 0x8B8AE4U, 0xC61969U, 0xBD609AU, 0xB4F247U, 0xEFA954U, 0xE518A9U, 0xBC0362U, 0xD7D0D6U, 0xCE7E8DU,
0x856F18U, 0x1C94E3U, 0x578726U, 0x0D5F1DU, 0x24ECC0U, 0x7FF713U, 0x3E26AAU, 0x251D6DU, 0x6A8F14U, 0x53648BU,
0x19757AU, 0x40AEB4U, 0xCB9CA5U, 0x90055AU, 0x9956C3U, 0xE2ED34U, 0xAB3C7DU, 0xB126EAU, 0xFA9513U, 0xA3D2C8U,
0x886BFDU, 0xD9F836U, 0xD2A2E3U, 0x8D1359U, 0x454804U, 0x5EDBF7U, 0x37637AU, 0x2C3089U, 0x67ABD4U, 0x3E8847U,
0x3551BAU, 0x4D6331U, 0x46B8C4U, 0x1D299FU, 0x54120EU, 0x5FC0E1U, 0x86D93BU, 0xE56A0EU, 0xFBB1D5U, 0xB2B600U,
0xA94EABU, 0xE05DF6U, 0x9BE605U, 0x90B798U, 0xC92C6BU, 0xC3DE66U, 0x9AC7BDU, 0xD15448U, 0x6A3FD3U, 0x23ADA3U,
0x78346CU, 0x7147F5U, 0x2BDC02U, 0x0EAD5BU, 0x553FFCU, 0x1EA425U, 0x07D5F2U, 0x4C4ECBU, 0x554C14U, 0x3EB3F5U,
0xE4A26AU, 0xED799BU, 0xB6CA85U, 0xFFD25CU, 0xC421BFU, 0x8F3A22U, 0x96AB51U, 0xDC518CU, 0x895217U, 0x8289F2U,
0xF9B8A9U, 0xF0231CU, 0x2BF1C7U, 0x62C80AU, 0x781B39U, 0x1320E5U, 0x4AB156U, 0x41EB8FU, 0x1848E0U, 0x13D771U,
0x4886AEU, 0x203C5FU, 0x3B6F40U, 0x76F6A1U, 0xE5457EU, 0xAE1EE7U, 0xD7AC10U, 0xDCB549U, 0x8476EFU, 0x8FC536U,
0xD49DE9U, 0x9D0ED8U, 0xA63513U, 0xEFE4A6U, 0xB4DF7DU, 0x3E0D00U, 0x779693U, 0x4CA75EU, 0x0568ADU, 0x527BB0U,
0x59C34BU, 0x00109FU, 0x0A0B14U, 0x73FA61U, 0x38E0BAU, 0x23530FU, 0x6A88D4U, 0xB199DDU, 0x98322AU, 0xC260F3U,
0xCBF944U, 0x908A0DU, 0xDB11F2U, 0xC28163U, 0xADFABDU, 0xBC694CU, 0xF65243U, 0xAD83BAU, 0xA40D6DU, 0x5F7EF4U,
0x16E787U, 0x0DF44AU, 0x460EF1U, 0x5E1F24U, 0x15CC3FU, 0x6C77CAU, 0x676401U, 0x3C9CBDU, 0x359FEEU, 0x6A0413U,
0x02F590U, 0x91EE4DU, 0xDA3C3EU, 0xC305A3U, 0x889658U, 0xF14D99U, 0xFA7F86U, 0xA1E677U, 0xE981E8U, 0xF21A10U,
0xBB4BD7U, 0x80F1CEU, 0xCB6239U, 0x123BE0U, 0x1D885FU, 0x45921EU, 0x6641E1U, 0x3DE870U, 0x74BBAFU, 0x6F00C6U,
0x261055U, 0x7DCBA8U, 0x57787AU, 0x0E2167U, 0x05B28CU, 0xCC8819U, 0x975BE2U, 0xBC52B7U, 0xE5E52CU, 0xEB37C9U,
0xB20E12U, 0xF9DD2FU, 0xE8C6FCU, 0x837701U, 0xD8AD82U, 0xD1BE5AU, 0x0B0525U, 0x0244B4U, 0x79FE5BU, 0x322DCAU,
0x2B3495U, 0x60876CU, 0x79DCFBU, 0x334C12U, 0x4C7745U, 0x45A4DCU, 0x1E3F23U, 0x175FF2U, 0xC4C0D8U, 0xAFF30DU,
0xB72AF6U, 0xFCB96BU, 0xA5C338U, 0xAE5295U, 0xF54946U, 0xDCBABBU, 0x87A1A8U, 0xCF2165U, 0xD4DA9EU, 0x9FC90BU,
0x223070U, 0x6922A4U, 0x30B92FU, 0x3348D6U, 0x695B01U, 0x20C038U, 0x1BB2EFU, 0x523B06U, 0x49EC99U, 0x02D7C8U,
0x5B4777U, 0x713CA6U, 0xA8AF49U, 0xA3B650U, 0xF84586U, 0xB5DF7FU, 0xAE8CF8U, 0xC72581U, 0x9D3652U, 0x9EEDCFU,
0xC75D34U, 0xCC0671U, 0xB5B5CAU, 0xFEAC1FU, 0x677EA4U, 0x2DC5F9U, 0x26D63AU, 0x7F1F86U, 0x142855U, 0x0DF2A8U,
0x42E3B3U, 0x195872U, 0x108B8DU, 0x6AB31CU, 0x632063U, 0x307BAAU, 0xFBC83DU, 0xE201C4U, 0xA91393U, 0x90A82AU,
0xDAF9E4U, 0x816A55U, 0x88D00AU, 0xD383DBU, 0xFA3A64U, 0xA569A5U, 0xEEE2DEU, 0x76D243U, 0x3D0D90U, 0x649E6DU,
0x47E76EU, 0x1C7491U, 0x156E49U, 0x4E9DDEU, 0x0604B7U, 0x3D3720U, 0x76FDD9U, 0x6FEC06U, 0x2417B7U, 0xFD04F8U,
0xF29D29U, 0x886F92U, 0xC1744FU, 0xDAC73CU, 0x939EB1U, 0x880C63U, 0xEBE79EU, 0xB2F285U, 0xB86970U, 0xE11ABBU,
0xEA822EU, 0x311155U, 0x586AC0U, 0x43F92BU, 0x0A81F6U, 0x5412C5U, 0x5D111CU, 0x26E8CBU, 0x2D7B63U, 0x74213CU,
0x3F90CDU, 0x2E8B52U, 0x645883U, 0xDFE36CU, 0x96F375U, 0xDD0882U, 0xC40B1BU, 0x8FD6CCU, 0xB464A5U, 0xFC7F3EU,
0xA7AECBU, 0xAA9511U, 0xF10634U, 0xBA5CEFU, 0x83ED32U, 0x483681U, 0x5015DCU, 0x138D3FU, 0x48DEA2U, 0x616571U,
0x3AF40CU, 0x33AF97U, 0x681D72U, 0x2246E9U, 0x3BD7B9U, 0x506C46U, 0x0D2FDFU, 0x869338U, 0xDDC061U, 0xD45BD6U,
0xAF6A0FU, 0xE7B8C0U, 0xFC2371U, 0xBF102EU, 0xA6C9DFU, 0xEDDA40U, 0x943089U, 0x9FA1BFU, 0x459A66U, 0x0C4995U,
0x175108U, 0x7AE243U, 0x6139B6U, 0x2A2A2DU, 0x73D3D8U, 0x79C183U, 0x204A26U, 0x0B3FFDU, 0x5AA420U, 0x111613U,
0x8A4FDFU, 0xC3DC2CU, 0xF9A7B5U, 0xB034EAU, 0xEBAC5BU, 0xE0CF94U, 0xBD5465U, 0xF605FAU, 0xCFBEA3U, 0x85AC54U,
0x9E55DDU, 0xD7C62AU, 0x0CDD73U, 0x252FCDU, 0x76361CU, 0x7DF5D3U, 0x3546E2U, 0x6E5B39U, 0x67A98CU, 0x1CB247U,
0x57231AU, 0x4AD8A9U, 0x01CA74U, 0x191187U, 0xF2208AU, 0xA9AB50U, 0xA0F8A5U, 0xFB403EU, 0xF2D34BU, 0xA9A880U,
0xCB393DU, 0xD262EEU, 0x99D0B7U, 0xC04B00U, 0xCB1AC9U, 0xB0B176U, 0x39E3A7U, 0x677EF8U, 0x2ECD58U, 0x359687U,
0x7E277EU, 0x473D69U, 0x0CEEB0U, 0x55D557U, 0x5F04CEU, 0x0C8EBDU, 0x25BD60U, 0x7E64DBU, 0xB7771EU, 0xACCC05U,
0xE51CF0U, 0xBF2F2AU, 0x90F497U, 0xC9E7D4U, 0xC25F09U, 0x9B9CBAU, 0xD08767U, 0xEB320CU, 0xA36999U, 0x38FB42U,
0x7180B3U, 0x22112CU, 0x29AA45U, 0x50F9D2U, 0x1B610AU, 0x0202FDU, 0x4899E4U, 0x57080BU, 0x3E72DAU, 0x65E165U,
0x6CFA34U, 0xB70BEBU, 0xBC104AU, 0xE4E295U, 0x8F7BECU, 0x96787FU, 0xD583B2U, 0x9E9740U, 0x870C5DU, 0xECFFA6U,
0xF4E433U, 0xBF35F8U, 0xE00F8DU, 0x699C16U, 0x3265EBU, 0x1B6638U, 0x40F515U, 0x0A8DC6U, 0x131E1BU, 0x5845A0U,
0x21F670U, 0x2A6E1FU, 0x791D8EU, 0x708651U, 0x2AD7E8U, 0xE37CAFU, 0xD8EE56U, 0x97B3C1U, 0x8E0018U, 0xC51B6FU,
0x9CC9E6U, 0xB67019U, 0xEF23C8U, 0xE498F2U, 0xBF9927U, 0xF643ECU, 0xCD7051U, 0x04E902U, 0x563AFFU, 0x5D006CU,
0x04D3A1U, 0x0FCA9AU, 0x72794FU, 0x39A2B4U, 0x228231U, 0x6A19EAU, 0x714E96U, 0x18F705U, 0x4324FCU, 0xC83E3BU,
0x918D02U, 0xDADCD5U, 0xC2470CU, 0xA135B3U, 0xBABCF2U, 0xF30F4DU, 0xA8549EU, 0xA1C543U, 0xDEFF78U, 0xD42CBCU,
0x0DB747U, 0x46C6D2U, 0x5F5C89U, 0x144F60U, 0x6FA6F7U, 0x66350EU, 0x2C0A59U, 0x35DAE0U, 0x7EC12FU, 0x0D32FEU,
0x0429C1U, 0x5FB911U, 0xD642AEU, 0x895167U, 0xC3D8B0U, 0xFAAB89U, 0xB1315AU, 0xA8C0A7U, 0xE3DB24U, 0xB84879U,
0x913382U, 0xCBA317U, 0x82F8FCU, 0x994BA9U, 0x50C213U, 0x4390CEU, 0x282F5DU, 0x713E30U, 0x7FCDE3U, 0x26565EU,
0x2D0485U, 0x56BDD4U, 0x1FAE7BU, 0x0475AAU, 0x4DD555U, 0x17CE4CU, 0x9C1D9BU, 0xE52473U, 0xEEF7E4U, 0xB7CD1DU,
0xF45E42U, 0xEF87E3U, 0x87B43CU, 0x986FADU, 0xD16FD2U, 0x8AD403U, 0x8103A8U, 0xD83A75U, 0x33A826U, 0x2BF39BU,
0x604049U, 0x7B99A4U, 0x328ABFU, 0x49306AU, 0x407191U, 0x1BEA04U, 0x19D96FU, 0x4001F2U, 0x0FB201U, 0x36E9DCU,
0xFD7ADFU, 0xE64326U, 0xAF91F9U, 0xF51249U, 0xDC2B16U, 0x87F8D7U, 0xCCE668U, 0xC517B1U, 0x9E8C46U, 0x97BF5FU,
0xED6498U, 0xA67461U, 0x378FF6U, 0x788C8FU, 0x611514U, 0x0AE6F1U, 0x53FC2BU, 0x596F3EU, 0x0216C5U, 0x4B8508U,
0x507FBBU, 0x396EE6U, 0x22F535U, 0xE99688U, 0xB10F43U, 0xBA1D36U, 0xC3E2ADU, 0xC07178U, 0x9B28C3U, 0xD69A8BU,
0xCD817CU, 0x8570E5U, 0xFEEB12U, 0xF5E8CBU, 0xAC10C4U, 0x270335U, 0x7ED8EAU, 0x156B5BU, 0x0E7A14U, 0x46A0C5U,
0x5D937AU, 0x144AA3U, 0x4F79D5U, 0x6CF35CU, 0x31228FU, 0x7A1932U, 0x628E69U, 0xA9D59CU, 0x926517U, 0xDBBEE2U,
0x80ADB9U, 0x891424U, 0xD246D7U, 0xD8ED1AU, 0xA17C28U, 0xEA27F5U, 0xF3942EU, 0xB8CE8FU, 0xAB5FD0U, 0x466461U,
0x1CB7BEU, 0x152F6FU, 0x4E1CC0U, 0x05D799U, 0x1CE66EU, 0x773DF7U, 0x7EAB00U, 0x249048U, 0x6D41D7U, 0x765A26U,
0x1DA9F9U, 0x8431C8U, 0xCF0203U, 0x96C1DEU, 0x90D86DU, 0xCB6A30U, 0xA23193U, 0xB9A24EU, 0xF05B95U, 0xEB48A0U,
0xA0D27AU, 0xD8A39FU, 0xD33804U, 0x0A9B79U, 0x01C3AAU, 0x5A5437U, 0x132FD4U, 0x28BC0DU, 0x60253AU, 0x3F57E3U,
0x3CCC7CU, 0x65DD9DU, 0x4E26C2U, 0x172572U, 0xDCDDADU, 0xC64E64U, 0x8F5553U, 0x94A68AU, 0xFDBE7DU, 0xA66DE4U,
0xADD68BU, 0xF4C75AU, 0xFE0CC1U, 0x873E34U, 0xC8A72FU, 0xDBD0C2U, 0x124B10U, 0x49998DU, 0x40A8FEU, 0x3A3323U,
0x316088U, 0x68D95DU, 0x235B06U, 0x3A00B3U, 0x51B178U, 0x4AEA89U, 0x025816U, 0x59C36FU, 0xD092B8U, 0x8B2930U,
0xE43AC7U, 0xF5E2DEU, 0xBEC121U, 0xA71AF0U, 0xED8B7FU, 0x94B40EU, 0x9F66D1U, 0xD45D68U, 0xCD8CBFU, 0x8617F6U,
0x5F2545U, 0x75FC98U, 0x2EFF62U, 0x674467U, 0x7C959CU, 0x318F09U, 0x0A7CD2U, 0x4967AFU, 0x11D62CU, 0x1A8CD1U,
0x431F02U, 0x48A69DU, 0xB3E5ECU, 0xFA7623U, 0xE10E9AU, 0xA99948U, 0xB20215U, 0xD971A6U, 0x80E86BU, 0x8BDA90U,
0xD60185U, 0x9D907EU, 0x8FFBFBU, 0xE66920U, 0x7D705DU, 0x3483CEU, 0x6F9833U, 0x646BF1U, 0x1DF3E8U, 0x17E017U,
0x4E1BC6U, 0x050A79U, 0x1E8038U, 0x5773E7U, 0x2C685EU, 0xA1BD89U, 0xFB86B0U, 0xF01477U, 0xA16D8EU, 0xCAFE19U,
0xD365C1U, 0x9815AEU, 0x839E3FU, 0xCBCDC4U, 0x907611U, 0xB9E70AU, 0xE2BDE7U, 0x2B0E34U, 0x301789U, 0x7BE4DAU,
0x477707U, 0x0C2FACU, 0x558C79U, 0x5E9743U, 0x0D4496U, 0x04786DU, 0x7FABE0U, 0x3730B3U, 0x3C014AU, 0xE7DADDU,
0xEEE834U, 0x956163U, 0xDCB2FAU, 0xC78905U, 0x8D5BD4U, 0xD0427BU, 0xDBF12BU, 0xA22AB4U, 0xA93B4DU, 0xFA819AU,
0xB3D2B3U, 0x287B64U, 0x40289DU, 0x5BB206U, 0x100153U, 0x495CB8U, 0x42CF2DU, 0x3BF4D6U, 0x70248BU, 0x6ABF19U,
0x23CCF4U, 0x3C4527U, 0x75761AU, 0x8EACC1U, 0x853F44U, 0xD44EBFU, 0xDED5EEU, 0x87C751U, 0xEC3E80U, 0xF72D6FU,
0xBEB676U, 0xE557A1U, 0xEC4D59U, 0xB6BECEU, 0x9DA527U, 0x443078U, 0x0BCAE9U, 0x12D916U, 0x594087U, 0x6033E8U,
0x22A831U, 0x7948A2U, 0x70535FU, 0x2BC01CU, 0x62BBA1U, 0x592A7BU, 0x92308EU, 0x8AC395U, 0xC15A50U, 0x9809ABU,
0xB3B336U, 0xECB245U, 0xE54998U, 0xBEDA1BU, 0xF681E6U, 0xED35F5U, 0x8E2E0CU, 0x87FDD3U, 0x5CC453U, 0x1556ACU,
0x0E85FDU, 0x64AC42U, 0x3D7F8BU, 0x36447CU, 0x6FD665U, 0x640FB2U, 0x3B3C4BU, 0x52A7C4U, 0x48F7B5U, 0x014C2EU,
0x9A9FFBU, 0xD19601U, 0xA0250CU, 0xAB7FFFU, 0xF2C822U, 0xB8D1B1U, 0xA302CCU, 0xEAB907U, 0xD1E9B2U, 0x987269U,
0xC3411CU, 0xCC8897U, 0x141A42U, 0x3F61B8U, 0x66F2A1U, 0x2DCB56U, 0x3618DFU, 0x778208U, 0x2CB3F1U, 0x0468EEU,
0x5F7B1FU, 0x5693D0U, 0x0D8041U, 0x461B3EU, 0xFFECE7U, 0xB4FD50U, 0xA94798U, 0xE314CFU, 0xB88D76U, 0xB17EADU,
0xCA7508U, 0xC3E553U, 0x989EA6U, 0xDB0D3DU, 0xC396E8U, 0xA8E683U, 0x717D1EU, 0x7A0EEDU, 0x219730U, 0x288422U,
0x736ECFU, 0x1BFF14U, 0x04A4A1U, 0x4F177AU, 0x56092BU, 0x1DD884U, 0x64635DU, 0xEF70EAU, 0xA589B3U, 0xF49B54U,
0xFF50CDU, 0xA66312U, 0x8DFA62U, 0xD628FDU, 0x9F131CU, 0x8582C3U, 0xCCF9DAU, 0xF36A29U, 0xB8B2F4U, 0x618157U,
0x6A020AU, 0x335999U, 0x79E864U, 0x4272BFU, 0x03259AU, 0x189C40U, 0x51CFB5U, 0x0A752EU, 0x216463U, 0x79BF90U,
0x721C0DU, 0xAB47FEU, 0xE4D727U, 0xFDEC28U, 0x963FD9U, 0x8DA646U, 0xC594B7U, 0x9E4FE8U, 0x977E60U, 0xECA597U,
0xAF264EU, 0xB61C79U, 0xFDCDA0U, 0x65D64FU, 0x2E61DCU, 0x553881U, 0x5CAA72U, 0x0351FBU, 0x0A400CU, 0x51FB55U,
0x3BB9CAU, 0x22223AU, 0x6993B5U, 0x30C8C4U, 0x3B5B1BU, 0xE02B82U, 0xC1B075U, 0x9B23BCU, 0xD25A8BU, 0xC9C852U,
0x82A3A9U, 0xBB303CU, 0xF42977U, 0xADDA82U, 0xA64418U, 0xFC55E5U, 0xB5AEE6U, 0x0EBD3BU, 0x4765C8U, 0x4CD655U,
0x17DD2EU, 0x562EEBU, 0x6C3770U, 0x25A585U, 0x3E5EDEU, 0x754F6FU, 0x2C94A1U, 0x23A758U, 0x5A3F4FU, 0xD07C96U,
0x8BC761U, 0xC254E8U, 0xD92C97U, 0xB0BF06U, 0xEBE0D9U, 0xE25138U, 0xB8CAA7U, 0xBB98DEU, 0xE22109U, 0x896291U,
0x10F172U, 0x5BCB2FU, 0x401A94U, 0x0CA141U, 0x77B2BAU, 0x7E6BBFU, 0x255964U, 0x6E82D9U, 0x77130AU, 0x3C3877U,
0x04EAF4U, 0x4FD129U, 0x9C40DBU, 0x959BC6U, 0xCEAC2DU, 0xE774FCU, 0xBC6763U, 0xF6DC12U, 0xEB8DCDU, 0xA00664U,
0xF9F4B3U, 0xD2EF4AU, 0x895E5DU, 0x800584U, 0x5A972BU, 0x132EFBU, 0x287D84U, 0x63E615U, 0x7297CEU, 0x391D23U,
0x608E30U, 0x6AF5CDU, 0x11641EU, 0x5C5E93U, 0x4789E0U, 0x0E903DU, 0x956386U, 0xFEF053U, 0xB6E879U, 0xAD0BACU,
0xE41077U, 0xFF83CAU, 0xB47A99U, 0xCD6870U, 0xCE93E7U, 0x96823EU, 0x9D1941U, 0xC4EBD0U, 0x2BF23FU, 0x3031EEU,
0x790A71U, 0x229909U, 0x2AC1CEU, 0x717677U, 0x5AEDA0U, 0x039C99U, 0x480646U, 0x515587U, 0x1AEC3CU, 0x296F69U,
0xE13492U, 0xBA8607U, 0xB39FCCU, 0xEC4CB1U, 0xA77723U, 0x9EA7DEU, 0xD51C0DU, 0xCD0F00U, 0x86D4FBU, 0xDDF56EU,
0xF46F95U, 0x2FBCD4U, 0x268D6BU, 0x7D52B2U, 0x374165U, 0x26F9DCU, 0x4D2A9BU, 0x141163U, 0x1FD2FCU, 0x40CA2DU,
0x497952U, 0x3322D3U, 0x7AB32CU, 0xE108F5U, 0xAA5AE2U, 0xB3E31BU, 0xF8B098U, 0x812B65U, 0x8B8936U, 0xD0D08AU,
0xD94341U, 0x8A7894U, 0xE3A9AFU, 0xF8377AU, 0xB74481U, 0x6FDD0CU, 0x64EE5FU, 0x3D35A2U, 0x163731U, 0x5F8ECCU,
0x045DC7U, 0x0F4616U, 0x57B6E8U, 0x7CAD79U, 0x253E86U, 0x6EC7CFU, 0x7DD478U, 0xB426A1U, 0xCF2D76U, 0xC3BC5FU,
0x984780U, 0x935571U, 0xCACCEEU, 0x81BBBFU, 0xB82054U, 0xF371C0U, 0xE9CB3BU, 0xA05826U, 0xFB33F5U, 0x52A218U,
0x09B88BU, 0x424BF6U, 0x53D22DU, 0x198198U, 0x043A53U, 0x6F2A06U, 0x34F1BDU, 0x3DC260U, 0x664982U, 0x6FB81BU,
0x15A24CU, 0xDE71F5U, 0xC7482AU, 0x8CDFCBU, 0x9505D4U, 0xDE3405U, 0xA5EFFAU, 0xA4FC63U, 0xFE5704U, 0xB387DDU,
0xA8BC6AU, 0xC32FB2U, 0x5A7EE5U, 0x11C44CU, 0x489797U, 0x420E62U, 0x19BD79U, 0x30E6BCU, 0x6B6407U, 0x225DDAU,
0x398EA9U, 0x703534U, 0x0A64F7U, 0x09FA0AU, 0xD4C910U, 0xDF10E5U, 0x86833EU, 0xCDB99BU, 0xE67A40U, 0xBE631BU,
0xB590AEU, 0xEC8B75U, 0xA73BD0U, 0x9CE08BU, 0xD5F35EU, 0x8E0AE5U, 0x061828U, 0x5D835AU, 0x5660C7U, 0x277914U,
0x68CAE9U, 0x7190E2U, 0x3A0113U, 0x20FECCU, 0x49ED7DU, 0x127522U, 0x1B06ABU, 0x40855CU, 0x8B9E85U, 0x926FB2U,
0xF8F56AU, 0xE186A5U, 0xAA1F14U, 0xF10CCBU, 0xF0F7BAU, 0x8F6735U, 0x867CECU, 0xDC9F1FU, 0x978402U, 0x8E54F1U,
0x45EF3CU, 0x7CFC8FU, 0x3705D2U, 0x6C1248U, 0x64C8BDU, 0x3FF976U, 0x566243U, 0x4DA198U, 0x069B45U, 0x1F0AF6U,
0x5851BBU, 0x00E248U, 0xAB3BD1U, 0xF2090EU, 0xF9926FU, 0xA2C3F1U, 0xEB7800U, 0xD07B9FU, 0x98A1E6U, 0xC31021U,
0xC84BB8U, 0x91D84FU, 0x9AEC96U, 0x6337A9U, 0x288468U, 0x369FB3U, 0x774E06U, 0x6C645DU, 0x05B7A9U, 0x4E2E22U,
0x551DFFU, 0x1CC78CU, 0x47D611U, 0x4F2DF2U, 0x343E6FU, 0xBF8514U, 0xE655C1U, 0xAD5E5AU, 0xB4EDBFU, 0xDFB4E4U,
0xC1265DU, 0x80DD8BU, 0xDBC852U, 0xD25375U, 0x8920ACU, 0xA2BA53U, 0xFB0BC2U, 0x31401DU, 0x28D33CU, 0x63AAE3U,
0x18381AU, 0x11238DU, 0x4AD2E4U, 0x434933U, 0x195BABU, 0x56A058U, 0x6FB105U, 0x2C5AAEU, 0x35C97BU, 0xFED9A0U,
0xA52295U, 0x8D314EU, 0xD6ECA3U, 0x9F5E30U, 0x84456DU, 0xCFB6DEU, 0xD6AF03U, 0xBD2CE9U, 0xE556FCU, 0xEEC707U,
0xB71CD6U, 0x382F59U, 0x43B720U, 0x02E4F7U, 0x195F4EU, 0x51CC99U, 0x0AA550U, 0x013767U, 0x786CBEU, 0x73DD01U,
0x2AC6D1U, 0x61159EU, 0x7BA92FU, 0x92BAF4U, 0x896109U, 0xC0521AU, 0x9F9AF7U, 0x942924U, 0xC532B9U, 0xEFE3C2U,
0xA6D807U, 0xFD0ABCU, 0xF69369U, 0xAFA033U, 0x44738EU, 0x5D694DU, 0x17C8F0U, 0x0C93A3U, 0x45207AU, 0x1EF9C5U,
0x37EB04U, 0x6850FBU, 0x6305EAU, 0x3B9E15U, 0x782DC4U, 0x41774BU, 0x8AF633U, 0xD18DE4U, 0xD81E5DU, 0x83A69AU,
0x8AF583U, 0xF06E7CU, 0xBB5FADU, 0xA28416U, 0xE99653U, 0xF06D88U, 0x9FEC35U, 0xC4F7E6U, 0x4C059AU, 0x1F1C19U,
0x56EFC4U, 0x4D743FU, 0x24612AU, 0x3F9BD1U, 0x748814U, 0x2C13AFU, 0x27F276U, 0x5EE861U, 0x553B88U, 0x0E0A5FU,
0xC791E6U, 0xD8E2B0U, 0x907A69U, 0xABE9C6U, 0xE09217U, 0xB10168U, 0xBA48F9U, 0xE3FA26U, 0x8861CFU, 0x9230D8U,
0xDB8B21U, 0xC099B2U, 0x09644FU, 0x52F704U, 0x79AC90U, 0x201F6BU, 0x2E17BEU, 0x77C495U, 0x3CFF48U, 0x172E9BU,
0x4E9426U, 0x0D8775U, 0x145E98U, 0x5E6D03U, 0xC5F6D6U, 0xAC242DU, 0xF70D3CU, 0xFEDED2U, 0xA5C543U, 0xAE74BCU,
0xD62EE5U, 0x9D9D72U, 0x80029BU, 0xCB534CU, 0x90E175U, 0x19BAAAU, 0x6A3B6BU, 0x6280D4U, 0x39D385U, 0x724B7AU,
0x6B78E2U, 0x00A321U, 0x19101CU, 0x5248CFU, 0x0ADB30U, 0x01F0A9U, 0x5A21CEU, 0xB73A17U, 0xACC880U, 0xE55179U,
0xFE42A6U, 0xB4B987U, 0xC5AF58U, 0xCE1688U, 0x97C533U, 0x9CCE76U, 0xC73F8DU, 0x8E2510U, 0xB4B6C3U, 0x7D4FFEU,
0x665C3DU, 0x2DC7C0U, 0x70B55BU, 0x5B2C2EU, 0x025FF5U, 0x49D470U, 0x53448AU, 0x1A3FD7U, 0x09AC64U, 0x60BDBDU,
0x3B467AU, 0xB0D043U, 0xE98B9CU, 0xE33A2DU, 0x9A21E2U, 0xD1C3B3U, 0xCA5A0CU, 0x8709DDU, 0xDCB222U, 0xF5A3AAU,
0xBF79DDU, 0xA44A04U, 0xEDD193U, 0x3E006AU, 0x373B21U, 0x4CF994U, 0x47C04FU, 0x1F53DAU, 0x5488A1U, 0x4DB86CU,
0x2623DFU, 0x7D7402U, 0x70CF50U, 0x2B9EFDU, 0x232426U, 0xF8A7D3U, 0x91FEC8U, 0x8A4D39U, 0xC117F6U, 0xD0866FU,
0x9B3D18U, 0xE36EC1U, 0xE8F576U, 0xB3C5BFU, 0xBA1629U, 0xE1BD50U, 0xA8EC8FU, 0x17763EU, 0x5D45F1U, 0x049CA0U,
0x0F8F1FU, 0x5630C6U, 0x7DE225U, 0x26FB38U, 0x6F08CBU, 0x7D0316U, 0x34B28DU, 0x2F68E9U, 0xC47B72U, 0x9DC287U,
0x96915CU, 0xCF0B41U, 0x85F8A2U, 0xBAE17FU, 0xF372CCU, 0xE81991U, 0xA1894AU, 0xFAF2EBU, 0xF16134U, 0x89F845U,
0x0A8ADBU, 0x53153AU, 0x1806E5U, 0x03FF7CU, 0x6A7C0BU, 0x312692U, 0x399775U, 0x628CACU, 0x6D7FB3U, 0x34EE42U,
0x5FF49DU, 0x56073CU, 0x8D1C67U, 0x87CDBBU, 0xDEE708U, 0xB574D5U, 0xA4ADB6U, 0xEF9E2BU, 0xF605D0U, 0xBD7545U,
0xE6EE0EU, 0xCE39FBU, 0x950260U, 0xD8929DU, 0x43D9CEU, 0x086A47U, 0x31B3B1U, 0x7AA068U, 0x221ADFU, 0x294B86U,
0x72F049U, 0x73E3F8U, 0x083927U, 0x418856U, 0x5AC3C9U, 0x105020U, 0xC969B7U, 0xE2BBEEU, 0xBF2019U, 0xB41181U,
0xEFCA6AU, 0xA6FD3FU, 0xBC27A4U, 0xD53651U, 0xCE9D9AU, 0x854EA7U, 0xDC5E74U, 0xDFE5A9U, 0x26B61AU, 0x6C0D57U,
0x77DCECU, 0x3EC639U, 0x2575C3U, 0x682CD6U, 0x13AF1DU, 0x1855ECU, 0x404473U, 0x4BDF8AU, 0x12ACDDU, 0xF93754U,
0xE207A3U, 0xABD87AU, 0xF04B45U, 0xF03284U, 0xABB05BU, 0x80ABEBU, 0xD95AB4U, 0x92C10DU, 0x8FD2CEU, 0xC42833U,
0xEC3920U, 0x37C2FDU, 0x7C5106U, 0x654883U, 0x2EAAF8U, 0x37B12DU, 0x5C20B6U, 0x065B42U, 0x07C909U, 0x5C12B4U,
0x152367U, 0x2EB4FAU, 0x65CF19U, 0xFC5F40U, 0xB294FFU, 0xEBA72EU, 0xE03ED1U, 0x9B6CD0U, 0x92D70FU, 0xC944F6U,
0x801D60U, 0x9AAE19U, 0xF1F4DEU, 0xA85547U, 0xAB4EB8U, 0x729DE9U, 0x792456U, 0x223697U, 0x4BED0CU, 0x55DE71U,
0x1C03A2U, 0x07910FU, 0x4CAADCU, 0x356BA0U, 0x3E5033U, 0x67C3EEU, 0x2D9B05U, 0xB62810U, 0xFFF3EBU, 0xC4E03EU,
0x8558A5U, 0xDE0B48U, 0xD5905BU, 0x8D71A2U, 0xA26A75U, 0xFBD8ECU, 0xB08982U, 0xAB1253U, 0xE2A1ECU, 0x79FB3FU,
0x116E52U, 0x4A15C9U, 0x43861CU, 0x188FE7U, 0x537DF2U, 0x62E619U, 0x29D7C0U, 0x310C57U, 0x7A1F2EU, 0x25E5B8U,
0xAC7451U, 0xC76F86U, 0xDE9C9FU, 0x959460U, 0xCF27B1U, 0xC6FC1EU, 0xBDEDCFU, 0xF416B0U, 0xEF0429U, 0xA49FEEU,
0xBDEA17U, 0xFF7104U, 0x06A3F8U, 0x0D8A63U, 0x5219A6U, 0x5B62DDU, 0x00F348U, 0x6969B3U, 0x731A6EU, 0x38816DU,
0x61D090U, 0x6A6343U, 0x33F9FEU, 0x18B8A5U, 0xC30340U, 0x8B10DAU, 0x98E80BU, 0xD1FB74U, 0xEA20F5U, 0xA5930AU,
0xFC8E93U, 0xF75CC4U, 0xAF673DU, 0xA4E6BAU, 0xDF3D43U, 0x960F9CU, 0x0DD68DU, 0x44E572U, 0x1F7EB2U, 0x35AD09U,
0x6C9554U, 0x6746A7U, 0x365D3AU, 0x7DFCF9U, 0x64A6C4U, 0x0B351FU, 0x118CEAU, 0x58DF61U, 0x836434U, 0x8A36CFU,
0xF1AB5BU, 0xBA18A0U, 0xA343EDU, 0xE8C27EU, 0xF0F887U, 0xBB2B50U, 0xC03A69U, 0xC9C1A6U, 0x9A5317U, 0x9368C8U,
0x5CB919U, 0x26A226U, 0x2F01EFU, 0x74D919U, 0x3DCA80U, 0x2631D7U, 0x6D223EU, 0x54BAA1U, 0x1E4950U, 0x47520BU,
0x4CA79EU, 0x97BC75U, 0xBE3EA8U, 0xED479BU, 0xA4D446U, 0xBA4FF5U, 0xF13C39U, 0xE8A46AU, 0x83D7D7U, 0xDA4C0CU,
0xD1DDF9U, 0x8AA7F2U, 0xC22427U, 0x793DDCU, 0x30CE45U, 0x2B5522U, 0x6007FBU, 0x39BE6CU, 0x32AD95U, 0x42560BU,
0x4D426AU, 0x16D1B5U, 0x5F3A04U, 0x442BDBU, 0x2DF082U, 0xF6C225U, 0xFE59FCU, 0xA5880FU, 0xAEB312U, 0xF761C9U,
0x9C582CU, 0x85CBB7U, 0xCE00C3U, 0xD43118U, 0x9DAB9DU, 0xEAF866U, 0xE3437BU, 0x381288U, 0x738955U, 0x6A3BF6U,
0x2066ABU, 0x19D570U, 0x52DEC1U, 0x090E1EU, 0x00B5FFU, 0x5BE6E1U, 0x727D38U, 0x284CCFU, 0x639656U, 0xFA8531U,
0xBD3CA8U, 0xD4EF77U, 0xCFC586U, 0x841489U, 0x9C0F78U, 0xD7BCA7U, 0x8E671EU, 0xA5774DU, 0xFE8481U, 0xF79F32U,
0xAC0AEFU, 0x65F09CU, 0x5FF301U, 0x144ACAU, 0x0D193FU, 0x468224U, 0x13F0D1U, 0x18694AU, 0x63FA87U, 0x2B81F4U,
0x30106DU, 0x790A9BU, 0xE2E952U, 0x8970CDU, 0xD003BCU, 0xDB9963U, 0x838AD2U, 0x88731DU, 0xD1E064U, 0xBAFFF3U,
0xA10F2AU, 0xEC049DU, 0xBFD7D4U, 0xB7EE2BU, 0x4C7CBBU, 0x478760U, 0x1E9415U, 0x554D9EU, 0x4C7E6BU, 0x07E4B0U,
0x3D35ADU, 0x741E4EU, 0x2F8D93U, 0x26FC20U, 0x7D667DU, 0x16B586U, 0x8B8E02U, 0xC91FD9U, 0xD0456CU, 0x9BF237U,
0xC0EBCEU, 0xE92849U, 0xB29390U, 0xBBC3E7U, 0xE1787EU, 0xAA6B81U, 0x93B040U, 0xD8005FU, 0x411BAEU, 0x0AC870U,
0x51F1D1U, 0x5D328EU, 0x362837U, 0x6799E0U, 0x6C4239U, 0x37711AU, 0x3EABC7U, 0x45BA3CU, 0x0D01A9U, 0x16D6F2U,
0xDDCF17U, 0xC46D8CU, 0x8F3670U, 0xF6A723U, 0xFD5CBCU, 0xA74F5DU, 0xEAF582U, 0xF1A43BU, 0x903768U, 0x8B0CC5U,
0xC0DC16U, 0x9957CBU, 0x1324F0U, 0x4ABD25U, 0x61AECEU, 0x38545AU, 0x73C701U, 0x68FEF4U, 0x212D6FU, 0x5B3382U,
0x52C2D1U, 0x09494CU, 0x065ABFU, 0xDFA126U, 0x9CB149U, 0xA56A98U, 0xEE5927U, 0xF4C0F6U, 0xBD33B8U, 0xE62901U,
0xCFB8D6U, 0x94D32FU, 0x9F40B8U, 0xC69AF1U, 0x8CAB0EU, 0x15309FU, 0x7E6360U, 0x21DA31U, 0x2848BAU, 0x733747U,
0x72A6D4U, 0x08EDA8U, 0x435F7BU, 0x5A4CD6U, 0x119505U, 0x082658U, 0x433DE3U, 0xB8ED26U, 0xB0D6DDU, 0xEB05C8U,
0xA2BC13U, 0xA9BEEAU, 0xD6656DU, 0xDF5614U, 0x848F82U, 0xC41C5BU, 0xDF26A4U, 0x94F7A5U, 0xADCC5AU, 0x665B8BU,
0x3F1234U, 0x34A0EDU, 0x6E7BAAU, 0x076813U, 0x1CD1C4U, 0x55833DU, 0x4E1836U, 0x03A9E2U, 0x58F219U, 0x72418CU,
0x2B09F7U, 0xA89A72U, 0xF1A1A9U, 0xBA7254U, 0x81EA47U, 0xC899BAU, 0xD20279U, 0x9B13C4U, 0xC0E09FU, 0xCB7E4BU,
0xB25FF0U, 0xF98431U, 0xE4974EU, 0x2E6CD7U, 0x35FC00U, 0x5CE7A9U, 0x07147EU, 0x060D07U, 0x5D9F98U, 0x56E449U,
0x0E65A6U, 0x659EB7U, 0x7C8D49U, 0x371790U, 0x6C6623U, 0xE5FD6EU, 0x9E6EBDU, 0x921600U, 0xC985D3U, 0x82DAEEU,
0x9B7B25U, 0xD0E0F0U, 0xE1924BU, 0xAA091EU, 0xF158F5U, 0xF9E369U, 0x22F1BAU, 0x4B28C7U, 0x509B54U, 0x1B80BDU,
0x024162U, 0x497B53U, 0x01A88CU, 0x3E1B5DU, 0x7502F2U, 0x6CD12BU, 0x27EB1CU, 0x7E7AC5U, 0xDDA113U, 0x8596BAU,
0xCE5EEDU, 0xD54D14U, 0x9CF68BU, 0x87A54AU, 0xEE1C31U, 0xB58EA4U, 0xBFD55FU, 0xE66482U, 0xE93FA1U, 0x90AD7CU,
0x5B04EFU, 0x405713U, 0x09CC48U, 0x13BFEDU, 0x522736U, 0x2914E3U, 0x22CFD8U, 0x7B5E05U, 0x3061E6U, 0x29B37FU,
0x43BAA8U, 0x5849D1U, 0x91D25EU, 0xCEE0AFU, 0xC73971U, 0x9C2A40U, 0xB7919FU, 0xEF401EU, 0xA452E1U, 0xB5B9B8U,
0xFEA80FU, 0x8533D6U, 0x8C4115U, 0xD7DA28U, 0x5F6BF3U, 0x043006U, 0x4FA39DU, 0x76DBD9U, 0x394C22U, 0x20C7BFU,
0x6BB64CU, 0x312C41U, 0x187FB2U, 0x43C46FU, 0x0A55F4U, 0x192E81U, 0xD2BC4AU, 0xCBA5FBU, 0xA15624U, 0xF85DFDU,
0xF38ECBU, 0xBA3602U, 0xA125F5U, 0xCEFE6CU, 0x97CF3BU, 0x9D55C2U, 0xC4A64DU, 0x4FBFBCU, 0x1468A3U, 0x7D4352U,
0x6ED19DU, 0x270804U, 0x7D3B76U, 0x76A0ABU, 0x0FF018U, 0x0443D5U, 0x5D188EU, 0x16A93BU, 0x0932E0U, 0xC07015U,
0xFACB1EU, 0xB39AC3U, 0xE80170U, 0xE3B3ADU, 0xBAEA5EU, 0xD17956U, 0xC042A9U, 0x8A9378U, 0x912DE7U, 0xD86E86U,
0x83F559U, 0x2AC4E8U, 0x711F37U, 0x7A0D6EU, 0x26B4C9U, 0x6D6710U, 0x547CE7U, 0x1F8CFEU, 0x449720U, 0x4D3483U,
0x16EF5AU, 0x1EFE2DU, 0x6D44B4U, 0xA6174BU, 0xBF8E8AU, 0xF4FD95U, 0xED6764U, 0x86D6BBU, 0xDC8912U, 0xD10A45U,
0x8A799CU, 0x83E12AU, 0xF872F3U, 0xB10954U, 0xAA980DU, 0x6083D6U, 0x397163U, 0x3AE8B8U, 0x439BDDU, 0x481046U,
0x1302BBU, 0x5AFB68U, 0x50E875U, 0x297396U, 0x26824AU, 0x7D98F1U, 0x344BA4U, 0xAF726FU, 0xE6F5DAU, 0x9C0F01U,
0x971C38U, 0xCE85EFU, 0xC5F626U, 0x946D91U, 0xFFBDC8U, 0xE48637U, 0xAC15A6U, 0xB74C48U, 0x7EEE99U, 0x21B586U,
0x0A0677U, 0x539FA8U, 0x18CC01U, 0x007652U, 0x4B67CFU, 0x70B43CU, 0x390FF1U, 0x625ECAU, 0x6BD01FU, 0x38E3C4U,
0xB23870U, 0xCB893BU, 0x8093C6U, 0x994055U, 0xD679A8U, 0x8DAAFBU, 0xA4B176U, 0xFE018DU, 0xF7CA5CU, 0xACD963U,
0xE762B2U, 0xFE323DU, 0x1589C4U, 0x0C5A92U, 0x4F432BU, 0x17F0ECU, 0x1CAA35U, 0x673B82U, 0x6E54DBU, 0x31C724U,
0x785CA5U, 0x632C5AU, 0x29B70BU, 0x508490U, 0xDB5D6DU, 0x82CFEEU, 0x89B492U, 0xD22541U, 0xBB2EDCU, 0xA1DD27U,
0xE04F62U, 0xFB56D9U, 0xB0A50CU, 0xF9BED7U, 0xC24EFAU, 0x8F5529U, 0x55C6D0U, 0x5E3B47U, 0x07383FU, 0x2CA2F0U,
0x75D161U, 0x3E489EU, 0x25BB0FU, 0x6DA170U, 0x7630B1U, 0x174B2EU, 0x4CD8D7U, 0x470180U, 0x9E2339U, 0xD5B8FEU,
0xE9EB27U, 0xA2521DU, 0xB941C8U, 0xF0BB23U, 0xAB2AB6U, 0xA271CDU, 0xD9C250U, 0xD3DC83U, 0x8A1D6EU, 0x41A67DU,
0x58B580U, 0x3B6C5BU, 0x205ECEU, 0x6985A5U, 0x333471U, 0x3E27CAU, 0x65FD13U, 0x0CCE44U, 0x1747EDU, 0x5C143AU,
0x45AF83U, 0x8F7F54U, 0xF6643DU, 0xFDD7A2U, 0xA68E73U, 0xAF3D8CU, 0xF4E79DU, 0xB5E073U, 0x8F59AAU, 0xC40A3DU,
0x999044U, 0x922197U, 0xCB7A3AU, 0x60E9E1U, 0x3B90B4U, 0x73020FU, 0x6839DAU, 0x21FA71U, 0x3A632CU, 0x5151DFU,
0x088A43U, 0x039B80U, 0x4260FDU, 0x18F36EU, 0x33EB97U, 0xEE1848U, 0xE503C9U, 0xBCA4B6U, 0xF7FC67U, 0xEC6FD8U,
0x849C01U, 0x9F8506U, 0xD616FFU, 0x8D6C29U, 0x86FD90U, 0xFF26CFU, 0xF4150EU, 0x2C9EB1U, 0x6FEE60U, 0x74751BU,
0x39E696U, 0x429F65U, 0x4B0DB8U, 0x1056ABU, 0x1AE756U, 0x43FC9DU, 0x282F29U, 0x318172U, 0x7A90E7U, 0xE36B1CU,
0xA878D9U, 0xF2A0E2U, 0xDB133FU, 0x8008ECU, 0xC1D955U, 0xDAE292U, 0x9570EBU, 0xAC9B74U, 0xE68A85U, 0xBF514BU,
0x34635AU, 0x6FFAA5U, 0x66A93CU, 0x1D12CBU, 0x54C382U, 0x4ED915U, 0x056AECU, 0x5C2D37U, 0x779402U, 0x2607C9U,
0x2D5D1CU, 0x72ECA6U, 0xBAB7FBU, 0xA12408U, 0xC89C85U, 0xD3CF76U, 0x98542BU, 0xC177B8U, 0xCAAE45U, 0xB29CCEU,
0xB9473BU, 0xE2D660U, 0xABEDF1U, 0xA03F1EU, 0x7926C4U, 0x1A95F1U, 0x044E2AU, 0x4D49FFU, 0x56B154U, 0x1FA209U,
0x6419FAU, 0x6F4867U, 0x36D394U, 0x3C2199U, 0x653842U, 0x2EABB7U, 0x95C02CU, 0xDC525CU, 0x87CB93U, 0x8EB80AU,
0xD423FDU, 0xF152A4U, 0xAAC003U, 0xE15BDAU, 0xF82A0DU, 0xB3B134U, 0xAAB3EBU, 0xC14C0AU, 0x1B5D95U, 0x128664U,
0x49357AU, 0x002DA3U, 0x3BDE40U, 0x70C5DDU, 0x6954AEU, 0x23AE73U, 0x76ADE8U, 0x7D760DU, 0x064756U, 0x0FDCE3U,
0xD40E38U, 0x9D37F5U, 0x87E4C6U, 0xECDF1AU, 0xB54EA9U, 0xBE1470U, 0xE7B71FU, 0xEC288EU, 0xB77951U, 0xDFC3A0U,
0xC490BFU, 0x89095EU, 0x1ABA81U, 0x51E118U, 0x2853EFU, 0x234AB6U, 0x7B8910U, 0x703AC9U, 0x2B6216U, 0x62F127U,
0x59CAECU, 0x101B59U, 0x4B2082U, 0xC1F2FFU, 0x88696CU, 0xB358A1U, 0xFA9752U, 0xAD844FU, 0xA63CB4U, 0xFFEF60U,
0xF5F4EBU, 0x8C059EU, 0xC71F45U, 0xDCACF0U, 0x95772BU, 0x4E6622U, 0x67CDD5U, 0x3D9F0CU, 0x3406BBU, 0x6F75F2U,
0x24EE0DU, 0x3D7E9CU, 0x520542U, 0x4396B3U, 0x09ADBCU, 0x527C45U, 0x5BF292U, 0xA0810BU, 0xE91878U, 0xF20BB5U,
0xB9F10EU, 0xA1E0DBU, 0xEA33C0U, 0x938835U, 0x989BFEU, 0xC36342U, 0xCA6011U, 0x95FBECU, 0xFD0A6FU, 0x6E11B2U,
0x25C3C1U, 0x3CFA5CU, 0x7769A7U, 0x0EB266U, 0x058079U, 0x5E1988U, 0x167E17U, 0x0DE5EFU, 0x44B428U, 0x7F0E31U,
0x349DC6U, 0xEDC41FU, 0xE277A0U, 0xBA6DE1U, 0x99BE1EU, 0xC2178FU, 0x8B4450U, 0x90FF39U, 0xD9EFAAU, 0x823457U,
0xA88785U, 0xF1DE98U, 0xFA4D73U, 0x3377E6U, 0x68A41DU, 0x43AD48U, 0x1A1AD3U, 0x14C836U, 0x4DF1EDU, 0x0622D0U,
0x173903U, 0x7C88FEU, 0x27527DU, 0x2E41A5U, 0xF4FADAU, 0xFDBB4BU, 0x8601A4U, 0xCDD235U, 0xD4CB6AU, 0x9F7893U,
0x862304U, 0xCCB3EDU, 0xB388BAU, 0xBA5B23U, 0xE1C0DCU, 0xE8A00DU, 0x3B3F27U, 0x500CF2U, 0x48D509U, 0x034694U,
0x5A3CC7U, 0x51AD6AU, 0x0AB6B9U, 0x234544U, 0x785E57U, 0x30DE9AU, 0x2B2561U, 0x6036F4U, 0xDDCF8FU, 0x96DD5BU,
0xCF46D0U, 0xCCB729U, 0x96A4FEU, 0xDF3FC7U, 0xE44D10U, 0xADC4F9U, 0xB61366U, 0xFD2837U, 0xA4B888U, 0x8EC359U,
0x5750B6U, 0x5C49AFU, 0x07BA79U, 0x4A2080U, 0x517307U, 0x38DA7EU, 0x62C9ADU, 0x611230U, 0x38A2CBU, 0x33F98EU,
0x4A4A35U, 0x0153E0U, 0x98815BU, 0xD23A06U, 0xD929C5U, 0x80E079U, 0xEBD7AAU, 0xF20D57U, 0xBD1C4CU, 0xE6A78DU,
0xEF7472U, 0x954CE3U, 0x9CDF9CU, 0xCF8455U, 0x0437C2U, 0x1DFE3BU, 0x56EC6CU, 0x6F57D5U, 0x25061BU, 0x7E95AAU,
0x772FF5U, 0x2C7C24U, 0x05C59BU, 0x5A965AU, 0x111D21U, 0x892DBCU, 0xC2F26FU, 0x9B6192U, 0xB81891U, 0xE38B6EU,
0xEA91B6U, 0xB16221U, 0xF9FB48U, 0xC2C8DFU, 0x890226U, 0x9013F9U, 0xDBE848U, 0x02FB07U, 0x0D62D6U, 0x77906DU,
0x3E8BB0U, 0x2538C3U, 0x6C614EU, 0x77F39CU, 0x141861U, 0x4D0D7AU, 0x47968FU, 0x1EE544U, 0x157DD1U, 0xCEEEAAU,
0xA7953FU, 0xBC06D4U, 0xF57E09U, 0xABED3AU, 0xA2EEE3U, 0xD91734U, 0xD2849CU, 0x8BDEC3U, 0xC06F32U, 0xD174ADU,
0x9BA77CU, 0x201C93U, 0x690C8AU, 0x22F77DU, 0x3BF4E4U, 0x702933U, 0x4B9B5AU, 0x0380C1U, 0x585134U, 0x556AEEU,
0x0EF9CBU, 0x45A310U, 0x7C12CDU, 0xB7C97EU, 0xAFEA23U, 0xEC72C0U, 0xB7215DU, 0x9E9A8EU, 0xC50BF3U, 0xCC5068U,
0x97E28DU, 0xDDB916U, 0xC42846U, 0xAF93B9U, 0xF2D020U, 0x796CC7U, 0x223F9EU, 0x2BA429U, 0x5095F0U, 0x18473FU,
0x03DC8EU, 0x40EFD1U, 0x593620U, 0x1225BFU, 0x6BCF76U, 0x605E40U, 0xBA6599U, 0xF3B66AU, 0xE8AEF7U, 0x851DBCU,
0x9EC649U, 0xD5D5D2U, 0x8C2C27U, 0x863E7CU, 0xDFB5D9U, 0xF4C002U, 0xA55BDFU, 0xEEE9ECU, 0x75B020U, 0x3C23D3U,
0x06584AU, 0x4FCB15U, 0x1453A4U, 0x1F306BU, 0x42AB9AU, 0x09FA05U, 0x30415CU, 0x7A53ABU, 0x61AA22U, 0x2839D5U,
0xF3228CU, 0xDAD032U, 0x89C9E3U, 0x820A2CU, 0xCAB91DU, 0x91A4C6U, 0x985673U, 0xE34DB8U, 0xA8DCE5U, 0xB52756U,
0xFE358BU, 0xE6EE78U, 0x0DDF75U, 0x5654AFU, 0x5F075AU, 0x04BFC1U, 0x0D2CB4U, 0x56577FU, 0x34C6C2U, 0x2D9D11U,
0x662F48U, 0x3FB4FFU, 0x34E536U, 0x4F4E89U, 0xC61C58U, 0x988107U, 0xD132A7U, 0xCA6978U, 0x81D881U, 0xB8C296U,
0xF3114FU, 0xAA2AA8U, 0xA0FB31U, 0xF37142U, 0xDA429FU, 0x819B24U, 0x4888E1U, 0x5333FAU, 0x1AE30FU, 0x40D0D5U,
0x6F0B68U, 0x36182BU, 0x3DA0F6U, 0x646345U, 0x2F7898U, 0x14CDF3U, 0x5C9666U, 0xC704BDU, 0x8E7F4CU, 0xDDEED3U,
0xD655BAU, 0xAF062DU, 0xE49EF5U, 0xFDFD02U, 0xB7661BU, 0xA8F7F4U, 0xC18D25U, 0x9A1E9AU, 0x9305CBU, 0x48F414U,
0x43EFB5U, 0x1B1D6AU, 0x708413U, 0x698780U, 0x2A7C4DU, 0x6168BFU, 0x78F3A2U, 0x130059U, 0x0B1BCCU, 0x40CA07U,
0x1FF072U, 0x9663E9U, 0xCD9A14U, 0xE499C7U, 0xBF0AEAU, 0xF57239U, 0xECE1E4U, 0xA7BA5FU, 0xDE098FU, 0xD591E0U,
0x86E271U, 0x8F79AEU, 0xD52817U, 0x1C8350U, 0x2711A9U, 0x684C3EU, 0x71FFE7U, 0x3AE490U, 0x633619U, 0x498FE6U,
0x10DC37U, 0x1B670DU, 0x4066D8U, 0x09BC13U, 0x328FAEU, 0xFB16FDU, 0xA9C500U, 0xA2FF93U, 0xFB2C5EU, 0xF03565U,
0x8D86B0U, 0xC65D4BU, 0xDD7DCEU, 0x95E615U, 0x8EB169U, 0xE708FAU, 0xBCDB03U, 0x37C1C4U, 0x6E72FDU, 0x25232AU,
0x3DB8F3U, 0x5ECA4CU, 0x45430DU, 0x0CF0B2U, 0x57AB61U, 0x5E3ABCU, 0x210087U, 0x2BD343U, 0xF248B8U, 0xB9392DU,
0xA0A376U, 0xEBB09FU, 0x905908U, 0x99CAF1U, 0xD3F5A6U, 0xCA251FU, 0x813ED0U, 0xF2CD01U, 0xFBD63EU, 0xA046EEU,
0x29BD51U, 0x76AE98U, 0x3C274FU, 0x055476U, 0x4ECEA5U, 0x573F58U, 0x1C24DBU, 0x47B786U, 0x6ECC7DU, 0x345CE8U,
0x7D0703U, 0x66B456U, 0xAF3DECU, 0xBC6F31U, 0xD7D0A2U, 0x8EC1CFU, 0x80321CU, 0xD9A9A1U, 0xD2FB7AU, 0xA9422BU,
0xE05184U, 0xFB8A55U, 0xB22AAAU, 0xE831B3U, 0x63E264U, 0x1ADB8CU, 0x11081BU, 0x4832E2U, 0x0BA1BDU, 0x10781CU,
0x784BC3U, 0x679052U, 0x2E902DU, 0x752BFCU, 0x7EFC57U, 0x27C58AU, 0xCC57D9U, 0xD40C64U, 0x9FBFB6U, 0x84665BU,
0xCD7540U, 0xB6CF95U, 0xBF8E6EU, 0xE415FBU, 0xE62690U, 0xBFFE0DU, 0xF04DFEU, 0xC91623U, 0x028520U, 0x19BCD9U,
0x506E06U, 0x0AEDB6U, 0x23D4E9U, 0x780728U, 0x331997U, 0x3AE84EU, 0x6173B9U, 0x6840A0U, 0x129B67U, 0x598B9EU,
0xC87009U, 0x877370U, 0x9EEAEBU, 0xF5190EU, 0xAC03D4U, 0xA690C1U, 0xFDE93AU, 0xB47AF7U, 0xAF8044U, 0xC69119U,
0xDD0ACAU, 0x166977U, 0x4EF0BCU, 0x45E2C9U, 0x3C1D52U, 0x3F8E87U, 0x64D73CU, 0x296574U, 0x327E83U, 0x7A8F1AU,
0x0114EDU, 0x0A1734U, 0x53EF3BU, 0xD8FCCAU, 0x812715U, 0xEA94A4U, 0xF185EBU, 0xB95F3AU, 0xA26C85U, 0xEBB55CU,
0xB0862AU, 0x930CA3U, 0xCEDD70U, 0x85E6CDU, 0x9D7196U, 0x562A63U, 0x6D9AE8U, 0x24411DU, 0x7F5246U, 0x76EBDBU,
0x2DB928U, 0x2712E5U, 0x5E83D7U, 0x15D80AU, 0x0C6BD1U, 0x473170U, 0x54A02FU, 0xB99B9EU, 0xE34841U, 0xEAD090U,
0xB1E33FU, 0xFA2866U, 0xE31991U, 0x88C208U, 0x8154FFU, 0xDB6FB7U, 0x92BE28U, 0x89A5D9U, 0xE25606U, 0x7BCE37U,
0x30FDFCU, 0x693E21U, 0x6F2792U, 0x3495CFU, 0x5DCE6CU, 0x465DB1U, 0x0FA46AU, 0x14B75FU, 0x5F2D85U, 0x275C60U,
0x2CC7FBU, 0xF56486U, 0xFE3C55U, 0xA5ABC8U, 0xECD02BU, 0xD743F2U, 0x9FDAC5U, 0xC0A81CU, 0xC33383U, 0x9A2262U,
0xB1D93DU, 0xE8DA8DU, 0x232252U, 0x39B19BU, 0x70AAACU, 0x6B5975U, 0x024182U, 0x59921BU, 0x522974U, 0x0B38A5U,
0x01F33EU, 0x78C1CBU, 0x3758D0U, 0x242F3DU, 0xEDB4EFU, 0xB66672U, 0xBF5701U, 0xC5CCDCU, 0xCE9F77U, 0x9726A2U,
0xDCA4F9U, 0xC5FF4CU, 0xAE4E87U, 0xB51576U, 0xFDA7E9U, 0xA63C90U, 0x2F6D47U, 0x74D6CFU, 0x1BC538U, 0x0A1D21U,
0x413EDEU, 0x58E50FU, 0x127480U, 0x6B4BF1U, 0x60992EU, 0x2BA297U, 0x327340U, 0x79E809U, 0xA0DABAU, 0x8A0367U,
0xD1009DU, 0x98BB98U, 0x836A63U, 0xCE70F6U, 0xF5832DU, 0xB69850U, 0xEE29D3U, 0xE5732EU, 0xBCE0FDU, 0xB75962U,
0x4C1A13U, 0x0589DCU, 0x1EF165U, 0x5666B7U, 0x4DFDEAU, 0x268E59U, 0x7F1794U, 0x74256FU, 0x29FE7AU, 0x626F81U,
0x700404U, 0x1996DFU, 0x828FA2U, 0xCB7C31U, 0x9067CCU, 0x9B940EU, 0xE20C17U, 0xE81FE8U, 0xB1E439U, 0xFAF586U,
0xE17FC7U, 0xA88C18U, 0xD397A1U, 0x5E4276U, 0x04794FU, 0x0FEB88U, 0x5E9271U, 0x3501E6U, 0x2C9A3EU, 0x67EA51U,
0x7C61C0U, 0x34323BU, 0x6F89EEU, 0x4618F5U, 0x1D4218U, 0xD4F1CBU, 0xCFE876U, 0x841B25U, 0xB888F8U, 0xF3D053U,
0xAA7386U, 0xA168BCU, 0xF2BB69U, 0xFB8792U, 0x80541FU, 0xC8CF4CU, 0xC3FEB5U, 0x182522U, 0x1117CBU, 0x6A9E9CU,
0x234D05U, 0x3876FAU, 0x72A42BU, 0x2FBD84U, 0x240ED4U, 0x5DD54BU, 0x56C4B2U, 0x057E65U, 0x4C2D4CU, 0xD7849BU,
0xBFD762U, 0xA44DF9U, 0xEFFEACU, 0xB6A347U, 0xBD30D2U, 0xC40B29U, 0x8FDB74U, 0x9540E6U, 0xDC330BU, 0xC3BAD8U,
0x8A89E5U, 0x71533EU, 0x7AC0BBU, 0x2BB140U, 0x212A11U, 0x7838AEU, 0x13C17FU, 0x08D290U, 0x414989U, 0x1AA85EU,
0x13B2A6U, 0x494131U, 0x625AD8U, 0xBBCF87U, 0xF43516U, 0xED26E9U, 0xA6BF78U, 0x9FCC17U, 0xDD57CEU, 0x86B75DU,
0x8FACA0U, 0xD43FE3U, 0x9D445EU, 0xA6D584U, 0x6DCF71U, 0x753C6AU, 0x3EA5AFU, 0x67F654U, 0x4C4CC9U, 0x134DBAU,
0x1AB667U, 0x4125E4U, 0x097E19U, 0x12CA0AU, 0x71D1F3U, 0x78022CU, 0xA33BACU, 0xEAA953U, 0xF17A02U, 0x9B53BDU,
0xC28074U, 0xC9BB83U, 0x90299AU, 0x9BF04DU, 0xC4C3B4U, 0xAD583BU, 0xB7084AU, 0xFEB3D1U, 0x656004U, 0x2E69FEU,
0x5FDAF3U, 0x548000U, 0x0D37DDU, 0x472E4EU, 0x5CFD33U, 0x1546F8U, 0x2E164DU, 0x678D96U, 0x3CBEE3U, 0x337768U,
0xEBE5BDU, 0xC09E47U, 0x990D5EU, 0xD234A9U, 0xC9E720U, 0x887DF7U, 0xD34C0EU, 0xFB9711U, 0xA084E0U, 0xA96C2FU,
0xF27FBEU, 0xB9E4C1U, 0x001318U, 0x4B02AFU, 0x56B867U, 0x1CEB30U, 0x477289U, 0x4E8152U, 0x358AF7U, 0x3C1AACU,
0x676159U, 0x24F2C2U, 0x3C6917U, 0x57197CU, 0x8E82E1U, 0x85F112U, 0xDE68CFU, 0xD77BDDU, 0x8C9130U, 0xE400EBU,
0xFB5B5EU, 0xB0E885U, 0xA9F6D4U, 0xE2277BU, 0x9B9CA2U, 0x108F15U, 0x5A764CU, 0x0B64ABU, 0x00AF32U, 0x599CEDU,
0x72059DU, 0x29D702U, 0x60ECE3U, 0x7A7D3CU, 0x330625U, 0x0C95D6U, 0x474D0BU, 0x9E7EA8U, 0x95FDF5U, 0xCCA666U,
0x86179BU, 0xBD8D40U, 0xFCDA65U, 0xE763BFU, 0xAE304AU, 0xF58AD1U, 0xDE9B9CU, 0x86406FU, 0x8DE3F2U, 0x54B801U,
0x1B28D8U, 0x0213D7U, 0x69C026U, 0x7259B9U, 0x3A6B48U, 0x61B017U, 0x68819FU, 0x135A68U, 0x50D9B1U, 0x49E386U,
0x02325FU, 0x9A29B0U, 0xD19E23U, 0xAAC77EU, 0xA3558DU, 0xFCAE04U, 0xF5BFF3U, 0xAE04AAU, 0xC44635U, 0xDDDDC5U,
0x966C4AU, 0xCF373BU, 0xC4A4E4U, 0x1FD47DU, 0x3E4F8AU, 0x64DC43U, 0x2DA574U, 0x3637ADU, 0x7D5C56U, 0x44CFC3U,
0x0BD688U, 0x52257DU, 0x59BBE7U, 0x03AA1AU, 0x4A5119U, 0xF142C4U, 0xB89A37U, 0xB329AAU, 0xE822D1U, 0xA9D114U,
0x93C88FU, 0xDA5A7AU, 0xC1A121U, 0x8AB090U, 0xD36B5EU, 0xDC58A7U, 0xA5C0B0U, 0x2F8369U, 0x74389EU, 0x3DAB17U,
0x26D368U, 0x4F40F9U, 0x141F26U, 0x1DAEC7U, 0x473558U, 0x446721U, 0x1DDEF6U, 0x769D6EU, 0xEF0E8DU, 0xA434D0U,
0xBFE56BU, 0xF35EBEU, 0x884D45U, 0x819440U, 0xDAA69BU, 0x917D26U, 0x88ECF5U, 0xC3C788U, 0xFB150BU, 0xB02ED6U,
0x63BF24U, 0x6A6439U, 0x3153D2U, 0x188B03U, 0x43989CU, 0x0923EDU, 0x147232U, 0x5FF99BU, 0x060B4CU, 0x2D10B5U,
0x76A1A2U, 0x7FFA7BU, 0xA568D4U, 0xECD104U, 0xD7827BU, 0x9C19EAU, 0x8D6831U, 0xC6E2DCU, 0x9F71CFU, 0x950A32U,
0xEE9BE1U, 0xA3A16CU, 0xB8761FU, 0xF16FC2U, 0x6A9C79U, 0x010FACU, 0x491786U, 0x52F453U, 0x1BEF88U, 0x007C35U,
0x4B8566U, 0x32978FU, 0x316C18U, 0x697DC1U, 0x62E6BEU, 0x3B142FU, 0xD40DC0U, 0xCFCE11U, 0x86F58EU, 0xDD66F6U,
0xD53E31U, 0x8E8988U, 0xA5125FU, 0xFC6366U, 0xB7F9B9U, 0xAEAA78U, 0xE513C3U, 0xD69096U, 0x1ECB6DU, 0x4579F8U,
0x4C6033U, 0x13B34EU, 0x5888DCU, 0x615821U, 0x2AE3F2U, 0x32F0FFU, 0x792B04U, 0x220A91U, 0x0B906AU, 0xD0432BU,
0xD97294U, 0x82AD4DU, 0xC8BE9AU, 0xD90623U, 0xB2D564U, 0xEBEE9CU, 0xE02D03U, 0xBF35D2U, 0xB686ADU, 0xCCDD2CU,
0x854CD3U, 0x1EF70AU, 0x55A51DU, 0x4C1CE4U, 0x074F67U, 0x7ED49AU, 0x7476C9U, 0x2F2F75U, 0x26BCBEU, 0x75876BU,
0x1C5650U, 0x07C885U, 0x48BB7EU, 0x9022F3U, 0x9B11A0U, 0xC2CA5DU, 0xE9C8CEU, 0xA07133U, 0xFBA238U, 0xF0B9E9U,
0xA84917U, 0x835286U, 0xDAC179U, 0x913830U, 0x822B87U, 0x4BD95EU, 0x30D289U, 0x3C43A0U, 0x67B87FU, 0x6CAA8EU,
0x353311U, 0x7E4440U, 0x47DFABU, 0x0C8E3FU, 0x1634C4U, 0x5FA7D9U, 0x04CC0AU, 0xAD5DE7U, 0xF64774U, 0xBDB409U,
0xAC2DD2U, 0xE67E67U, 0xFBC5ACU, 0x90D5F9U, 0xCB0E42U, 0xC23D9FU, 0x99B67DU, 0x9047E4U, 0xEA5DB3U, 0x218E0AU,
0x38B7D5U, 0x732034U, 0x6AFA2BU, 0x21CBFAU, 0x5A1005U, 0x5B039CU, 0x01A8FBU, 0x4C7822U, 0x574395U, 0x3CD04DU,
0xA5811AU, 0xEE3BB3U, 0xB76868U, 0xBDF19DU, 0xE64286U, 0xCF1943U, 0x949BF8U, 0xDDA225U, 0xC67156U, 0x8FCACBU,
0xF59B08U, 0xF605F5U, 0x2B36EFU, 0x20EF1AU, 0x797CC1U, 0x324664U, 0x1985BFU, 0x419CE4U, 0x4A6F51U, 0x13748AU,
0x58C42FU, 0x631F74U, 0x2A0CA1U, 0x71F51AU, 0xF9E7D7U, 0xA27CA5U, 0xA99F38U, 0xD886EBU, 0x973516U, 0x8E6F1DU,
0xC5FEECU, 0xDF0133U, 0xB61282U, 0xED8ADDU, 0xE4F954U, 0xBF7AA3U, 0x74617AU, 0x6D904DU, 0x070A95U, 0x1E795AU,
0x55E0EBU, 0x0EF334U, 0x0F0845U, 0x7098CAU, 0x798313U, 0x2360E0U, 0x687BFDU, 0x71AB0EU, 0xBA10C3U, 0x830370U,
0xC8FA2DU, 0x93EDB7U, 0x9B3742U, 0xC00689U, 0xA99DBCU, 0xB25E67U, 0xF964BAU, 0xE0F509U, 0xA7AE44U, 0xFF1DB7U,
0x54C42EU, 0x0DF6F1U, 0x066D90U, 0x5D3C0EU, 0x1487FFU, 0x2F8460U, 0x675E19U, 0x3CEFDEU, 0x37B447U, 0x6E27B0U,
0x651369U, 0x9CC856U, 0xD77B97U, 0xC9604CU, 0x88B1F9U, 0x939BA2U, 0xFA4856U, 0xB1D1DDU, 0xAAE200U, 0xE33873U,
0xB829EEU, 0xB0D20DU, 0xCBC190U, 0x407AEBU, 0x19AA3EU, 0x52A1A5U, 0x4B1240U, 0x204B1BU, 0x3ED9A2U, 0x7F2274U,
0x2437ADU, 0x2DAC8AU, 0x76DF53U, 0x5D45ACU, 0x04F43DU, 0xCEBFE2U, 0xD72CC3U, 0x9C551CU, 0xE7C7E5U, 0xEEDC72U,
0xB52D1BU, 0xBCB6CCU, 0xE6A454U, 0xA95FA7U, 0x904EFAU, 0xD3A551U, 0xCA3684U, 0x01265FU, 0x5ADD6AU, 0x72CEB1U,
0x29135CU, 0x60A1CFU, 0x7BBA92U, 0x304921U, 0x2950FCU, 0x42D316U, 0x1AA903U, 0x1138F8U, 0x48E329U, 0xC7D0A6U,
0xBC48DFU, 0xFD1B08U, 0xE6A0B1U, 0xAE3366U, 0xF55AAFU, 0xFEC898U, 0x879341U, 0x8C22FEU, 0xD5392EU, 0x9EEA61U,
0x8456D0U, 0x6D450BU, 0x769EF6U, 0x3FADE5U, 0x606508U, 0x6BD6DBU, 0x3ACD46U, 0x101C3DU, 0x5927F8U, 0x02F543U,
0x096C96U, 0x505FCCU, 0xBB8C71U, 0xA296B2U, 0xE8370FU, 0xF36C5CU, 0xBADF85U, 0xE1063AU, 0xC814FBU, 0x97AF04U,
0x9CFA15U, 0xC461EAU, 0x87D23BU, 0xBE88B4U, 0x7509CCU, 0x2E721BU, 0x27E1A2U, 0x7C5965U, 0x750A7CU, 0x0F9183U,
0x44A052U, 0x5D7BE9U, 0x1669ACU, 0x0F9277U, 0x6013CAU, 0x3B0819U, 0xB3FA65U, 0xE0E3E6U, 0xA9103BU, 0xB28BC0U,
0xDB9ED5U, 0xC0642EU, 0x8B77EBU, 0xD3EC50U, 0xD80D89U, 0xA1179EU, 0xAAC477U, 0xF1F5A0U, 0x386E19U, 0x271D4FU,
0x6F8596U, 0x541639U, 0x1F6DE8U, 0x4EFE97U, 0x45B706U, 0x1C05D9U, 0x779E30U, 0x6DCF27U, 0x2474DEU, 0x3F664DU,
0xF69BB0U, 0xAD08FBU, 0x86536FU, 0xDFE094U, 0xD1E841U, 0x883B6AU, 0xC300B7U, 0xE8D164U, 0xB16BD9U, 0xF2788AU,
0xEBA167U, 0xA192FCU, 0x3A0929U, 0x53DBD2U, 0x08F2C3U, 0x01212DU, 0x5A3ABCU, 0x518B43U, 0x29D11AU, 0x62628DU,
0x7FFD64U, 0x34ACB3U, 0x6F1E8AU, 0xE64555U, 0x95C494U, 0x9D7F2BU, 0xC62C7AU, 0x8DB485U, 0x94871DU, 0xFF5CDEU,
0xE6EFE3U, 0xADB730U, 0xF524CFU, 0xFE0F56U, 0xA5DE31U, 0x48C5E8U, 0x53377FU, 0x1AAE86U, 0x01BD59U, 0x4B4678U,
0x3A50A7U, 0x31E977U, 0x683ACCU, 0x633189U, 0x38C072U, 0x71DAEFU, 0x4B493CU, 0x82B001U, 0x99A3C2U, 0xD2383FU,
0x8F4AA4U, 0xA4D3D1U, 0xFDA00AU, 0xB62B8FU, 0xACBB75U, 0xE5C028U, 0xF6539BU, 0x9F4242U, 0xC4B985U, 0x4F2FBCU,
0x167463U, 0x1CC5D2U, 0x65DE1DU, 0x2E3C4CU, 0x35A5F3U, 0x78F622U, 0x234DDDU, 0x0A5C55U, 0x408622U, 0x5BB5FBU,
0x122E6CU, 0xC1FF95U, 0xC8C4DEU, 0xB3066BU, 0xB83FB0U, 0xE0AC25U, 0xAB775EU, 0xB24793U, 0xD9DC20U, 0x828BFDU,
0x8F30AFU, 0xD46102U, 0xDCDBD9U, 0x07582CU, 0x6E0137U, 0x75B2C6U, 0x3EE809U, 0x2F7990U, 0x64C2E7U, 0x1C913EU,
0x170A89U, 0x4C3A40U, 0x45E9D6U, 0x1E42AFU, 0x571370U, 0xE889C1U, 0xA2BA0EU, 0xFB635FU, 0xF070E0U, 0xA9CF39U,
0x821DDAU, 0xD904C7U, 0x90F734U, 0x82FCE9U, 0xCB4D72U, 0xD09716U, 0x3B848DU, 0x623D78U, 0x696EA3U, 0x30F4BEU,
0x7A075DU, 0x451E80U, 0x0C8D33U, 0x17E66EU, 0x5E76B5U, 0x050D14U, 0x0E9ECBU, 0x7607BAU, 0xF57524U, 0xACEAC5U,
0xE7F91AU, 0xFC0083U, 0x9583F4U, 0xCED96DU, 0xC6688AU, 0x9D7353U, 0x92804CU, 0xCB11BDU, 0xA00B62U, 0xA9F8C3U,
0x72E398U, 0x783244U, 0x2118F7U, 0x4A8B2AU, 0x5B5249U, 0x1061D4U, 0x09FA2FU, 0x428ABAU, 0x1911F1U, 0x31C604U,
0x6AFD9FU, 0x276D62U, 0xBC2631U, 0xF795B8U, 0xCE4C4EU, 0x855F97U, 0xDDE520U, 0xD6B479U, 0x8D0FB6U, 0x8C1C07U,
0xF7C6D8U, 0xBE77A9U, 0xA53C36U, 0xEFAFDFU, 0x369648U, 0x1D4411U, 0x40DFE6U, 0x4BEE7EU, 0x103595U, 0x5902C0U,
0x43D85BU, 0x2AC9AEU, 0x316265U, 0x7AB158U, 0x23A18BU, 0x201A56U, 0xD949E5U, 0x93F2A8U, 0x882313U, 0xC139C6U,
0xDA8A3CU, 0x97D329U, 0xEC50E2U, 0xE7AA13U, 0xBFBB8CU, 0xB42075U, 0xED5322U, 0x06C8ABU, 0x1DF85CU, 0x542785U,
0x0FB4BAU, 0x0FCD7BU, 0x544FA4U, 0x7F5414U, 0x26A54BU, 0x6D3EF2U, 0x702D31U, 0x3BD7CCU, 0x13C6DFU, 0xC83D02U,
0x83AEF9U, 0x9AB77CU, 0xD15507U, 0xC84ED2U, 0xA3DF49U, 0xF9A4BDU, 0xF836F6U, 0xA3ED4BU, 0xEADC98U, 0xD14B05U,
0x9A30E6U, 0x03A0BFU, 0x4D6B00U, 0x1458D1U, 0x1FC12EU, 0x64932FU, 0x6D28F0U, 0x36BB09U, 0x7FE29FU, 0x6551E6U,
0x0E0B21U, 0x57AAB8U, 0x54B147U, 0x8D6216U, 0x86DBA9U, 0xDDC968U, 0xB412F3U, 0xAA218EU, 0xE3FC5DU, 0xF86EF0U,
0xB35523U, 0xCA945FU, 0xC1AFCCU, 0x983C11U, 0xD264FAU, 0x49D7EFU, 0x000C14U, 0x3B1FC1U, 0x7AA75AU, 0x21F4B7U,
0x2A6FA4U, 0x728E5DU, 0x5D958AU, 0x042713U, 0x4F767DU, 0x54EDACU, 0x1D5E13U, 0x8604C0U, 0xEE91ADU, 0xB5EA36U,
0xBC79E3U, 0xE77018U, 0xAC820DU, 0x9D19E6U, 0xD6283FU, 0xCEF3A8U, 0x85E0D1U, 0xDA1A47U, 0x538BAEU, 0x389079U,
0x216360U, 0x6A6B9FU, 0x30D84EU, 0x3903E1U, 0x421230U, 0x0BE94FU, 0x10FBD6U, 0x5B6011U, 0x4215E8U, 0x008EFBU,
0xF95C07U, 0xF2759CU, 0xADE659U, 0xA49D22U, 0xFF0CB7U, 0x96964CU, 0x8CE591U, 0xC77E92U, 0x9E2F6FU, 0x959CBCU,
0xCC0601U, 0xE7475AU, 0x3CFCBFU, 0x74EF25U, 0x6717F4U, 0x2E048BU, 0x15DF0AU, 0x5A6CF5U, 0x03716CU, 0x08A33BU,
0x5098C2U, 0x5B1945U, 0x20C2BCU, 0x69F063U, 0xF22972U, 0xBB1A8DU, 0xE0814DU, 0xCA52F6U, 0x936AABU, 0x98B958U,
0xC9A2C5U, 0x820306U, 0x9B593BU, 0xF4CAE0U, 0xEE7315U, 0xA7209EU, 0x7C9BCBU, 0x75C930U, 0x0E54A4U, 0x45E75FU,
0x5CBC12U, 0x173D81U, 0x0F0778U, 0x44D4AFU, 0x3FC596U, 0x363E59U, 0x65ACE8U, 0x6C9737U, 0xA346E6U, 0xD95DD9U,
0xD0FE10U, 0x8B26E6U, 0xC2357FU, 0xD9CE28U, 0x92DDC1U, 0xAB455EU, 0xE1B6AFU, 0xB8ADF4U, 0xB35861U, 0x68438AU,
0x41C157U, 0x12B864U, 0x5B2BB9U, 0x45B00AU, 0x0EC3C6U, 0x175B95U, 0x7C2828U, 0x25B3F3U, 0x2E2206U, 0x75580DU,
0x3DDBD8U, 0x86C223U, 0xCF31BAU, 0xD4AADDU, 0x9FF804U, 0xC64193U, 0xCD526AU, 0xBDA9F4U, 0xB2BD95U, 0xE92E4AU,
0xA0C5FBU, 0xBBD424U, 0xD20F7DU, 0x093DDAU, 0x01A603U, 0x5A77F0U, 0x514CEDU, 0x089E36U, 0x63A7D3U, 0x7A3448U,
0x31FF3CU, 0x2BCEE7U, 0x625462U, 0x150799U, 0x1CBC84U, 0xC7ED77U, 0x8C76AAU, 0x95C409U, 0xDF9954U, 0xE62A8FU,
0xAD213EU, 0xF6F1E1U, 0xFF4A00U, 0xA4191EU, 0x8D82C7U, 0xD7B330U, 0x9C69A9U, 0x057ACEU, 0x42C357U, 0x2B1088U,
0x303A79U, 0x7BEB76U, 0x63F087U, 0x284358U, 0x7198E1U, 0x5A88B2U, 0x017B7EU, 0x0860CDU, 0x53F510U, 0x9A0F63U,
0xA00CFEU, 0xEBB535U, 0xF2E6C0U, 0xB97DDBU, 0xEC0F2EU, 0xE796B5U, 0x9C0578U, 0xD47E0BU, 0xCFEF92U, 0x86F564U,
0x1D16ADU, 0x768F32U, 0x2FFC43U, 0x24669CU, 0x7C752DU, 0x778CE2U, 0x2E1F9BU, 0x45000CU, 0x5EF0D5U, 0x13FB62U,
0x40282BU, 0x4811D4U, 0xB38344U, 0xB8789FU, 0xE16BEAU, 0xAAB261U, 0xB38194U, 0xF81B4FU, 0xC2CA52U, 0x8BE1B1U,
0xD0726CU, 0xD903DFU, 0x829982U, 0xE94A79U, 0x7471FDU, 0x36E026U, 0x2FBA93U, 0x640DC8U, 0x3F1431U, 0x16D7B6U,
0x4D6C6FU, 0x443C18U, 0x1E8781U, 0x55947EU, 0x6C4FBFU, 0x27FFA0U, 0xBEE451U, 0xF5378FU, 0xAE0E2EU, 0xA2CD71U,
0xC9D7C8U, 0x98661FU, 0x93BDC6U, 0xC88EE5U, 0xC15438U, 0xBA45C3U, 0xF2FE56U, 0xE9290DU, 0x2230E8U, 0x3B9273U,
0x70C98FU, 0x0958DCU, 0x02A343U, 0x58B0A2U, 0x150A7DU, 0x0E5BC4U, 0x6FC897U, 0x74F33AU, 0x3F23E9U, 0x66A834U,
0xECDB0FU, 0xB542DAU, 0x9E5131U, 0xC7ABA5U, 0x8C38FEU, 0x97010BU, 0xDED290U, 0xA4CC7DU, 0xAD3D2EU, 0xF6B6B3U,
0xF9A540U, 0x205ED9U, 0x634EB6U, 0x5A9567U, 0x11A6D8U, 0x0B3F09U };
const unsigned int A_TABLE[] = { 0U, 4U, 8U, 12U, 16U, 20U, 24U, 28U, 32U, 36U, 40U, 44U,
48U, 52U, 56U, 60U, 64U, 68U, 1U, 5U, 9U, 13U, 17U, 21U };
const unsigned int B_TABLE[] = { 25U, 29U, 33U, 37U, 41U, 45U, 49U, 53U, 57U, 61U, 65U, 69U,
2U, 6U, 10U, 14U, 18U, 22U, 26U, 30U, 34U, 38U, 42U };
const unsigned int C_TABLE[] = { 46U, 50U, 54U, 58U, 62U, 66U, 70U, 3U, 7U, 11U, 15U, 19U,
23U, 27U, 31U, 35U, 39U, 43U, 47U, 51U, 55U, 59U, 63U, 67U, 71U };
const unsigned char AMBE_SILENCE[] = {0xB9U, 0xE8U, 0x81U, 0x52U, 0x61U, 0x73U, 0x00U, 0x2AU, 0x6BU};
CModeConv::CModeConv() :
m_m17N(0U),
m_dmrN(0U),
m_M17(5000U, "DMR2M17"),
m_DMR(5000U, "M172DMR"),
m_m17GainMultiplier(1),
m_m17Attenuate(false)
{
m_mbe = new MBEVocoder();
m_c2 = new CCodec2(true);
}
CModeConv::~CModeConv()
{
}
void CModeConv::setM17GainAdjDb(std::string dbstring)
{
float db = std::stof(dbstring);
float ratio = powf(10.0, (db/10.0));
if(db < 0){
ratio = 1/ratio;
m_m17Attenuate = true;
}
m_m17GainMultiplier = (uint16_t)roundf(ratio);
}
void CModeConv::putDMRHeader()
{
const uint8_t quiet[] = { 0x00u, 0x01u, 0x43u, 0x09u, 0xe4u, 0x9cu, 0x08u, 0x21u };
m_M17.addData(&TAG_HEADER, 1U);
m_M17.addData(quiet, 8U);
m_m17N += 1U;
}
void CModeConv::putDMREOT()
{
const uint8_t quiet[] = { 0x00u, 0x01u, 0x43u, 0x09u, 0xe4u, 0x9cu, 0x08u, 0x21u };
m_M17.addData(&TAG_EOT, 1U);
m_M17.addData(quiet, 8U);
m_m17N += 1U;
}
void CModeConv::putDMR(unsigned char* data)
{
assert(data != NULL);
int16_t audio[160U];
uint8_t ambe[9U];
uint8_t v_ambe[9U];
uint8_t codec2[8U];
::memset(audio, 0, sizeof(audio));
::memset(ambe, 0, sizeof(ambe));
::memset(v_ambe, 0, sizeof(v_ambe));
::memset(codec2, 0, sizeof(codec2));
decode(data, ambe, 0U);
m_mbe->decode_2450(audio, ambe);
m_c2->codec2_encode(codec2, audio);
m_M17.addData(&TAG_DATA, 1U);
m_M17.addData(codec2, 8U);
m_m17N += 1U;
data += 9U;
for (unsigned int i = 0U; i < 4U; i++)
v_ambe[i] = data[i];
v_ambe[4U] = data[4U] & 0xF0;
v_ambe[4U] |= data[10U] & 0x0F;
for (unsigned int i = 0U; i < 4U; i++)
v_ambe[i + 5U] = data[i + 11U];
decode(v_ambe, ambe, 0U);
m_mbe->decode_2450(audio, ambe);
m_c2->codec2_encode(codec2, audio);
m_M17.addData(&TAG_DATA, 1U);
m_M17.addData(codec2, 8U);
m_m17N += 1U;
data += 15U;;
decode(data, ambe, 0U);
m_mbe->decode_2450(audio, ambe);
m_c2->codec2_encode(codec2, audio);
m_M17.addData(&TAG_DATA, 1U);
m_M17.addData(codec2, 8U);
m_m17N += 1U;
}
void CModeConv::putM17Header()
{
unsigned char vch[9U];
::memset(vch, 0, 9U);
m_DMR.addData(&TAG_HEADER, 1U);
m_DMR.addData(vch, 9U);
m_dmrN += 1U;
}
void CModeConv::putM17EOT()
{
unsigned char vch[9U];
::memset(vch, 0, 9U);
unsigned int fill = 3U - (m_dmrN % 3U);
for (unsigned int i = 0U; i < fill; i++) {
m_DMR.addData(&TAG_DATA, 1U);
m_DMR.addData(AMBE_SILENCE, 9U);
m_dmrN += 1U;
}
m_DMR.addData(&TAG_EOT, 1U);
m_DMR.addData(vch, 9U);
m_dmrN += 1U;
}
void CModeConv::putM17(unsigned char* data)
{
assert(data != NULL);
int16_t audio[160U];
int16_t audio_adjusted[160U];
uint8_t ambe[9U];
uint8_t codec2[8U];
uint8_t vch[10U];
::memset(audio, 0, sizeof(audio));
::memset(ambe, 0, sizeof(ambe));
::memcpy(codec2, &data[36], 8);
m_c2->codec2_decode(audio, codec2);
for(int i = 0; i < 160; ++i){
m_m17Attenuate ? audio_adjusted[i] = audio[i] / m_m17GainMultiplier : audio[i] * m_m17GainMultiplier;
}
m_mbe->encode_2450(audio_adjusted, ambe);
encode(ambe, vch, 0U);
m_DMR.addData(&TAG_DATA, 1U);
m_DMR.addData(vch, 9U);
m_dmrN += 1U;
::memcpy(codec2, &data[44], 8);
m_c2->codec2_decode(audio, codec2);
for(int i = 0; i < 160; ++i){
m_m17Attenuate ? audio_adjusted[i] = audio[i] / m_m17GainMultiplier : audio[i] * m_m17GainMultiplier;
}
m_mbe->encode_2450(audio_adjusted, ambe);
encode(ambe, vch, 0U);
m_DMR.addData(&TAG_DATA, 1U);
m_DMR.addData(vch, 9U);
m_dmrN += 1U;
}
unsigned int CModeConv::getDMR(unsigned char* data)
{
unsigned char tmp[9U];
unsigned char tag[1U];
tag[0U] = TAG_NODATA;
if (m_dmrN >= 1U) {
m_DMR.peek(tag, 1U);
//LogMessage("CModeConv::getDMR %d:%d:%d", m_DMR.isEmpty(), m_dmrN, tag[0]);
if (tag[0U] != TAG_DATA) {
m_DMR.getData(tag, 1U);
m_DMR.getData(data, 9U);
m_dmrN -= 1U;
if(tag[0U] == TAG_EOT){
m_DMR.clear();
m_dmrN = 0;
}
return tag[0U];
}
}
if (m_dmrN >= 3U) {
m_DMR.getData(tag, 1U);
m_DMR.getData(data, 9U);
m_dmrN -= 1U;
m_DMR.getData(tag, 1U);
m_DMR.getData(tmp, 9U);
m_dmrN -= 1U;
::memcpy(data + 9U, tmp, 4U);
data[13U] = tmp[4U] & 0xF0U;
data[19U] = tmp[4U] & 0x0FU;
::memcpy(data + 20U, tmp + 5U, 4U);
m_DMR.getData(tag, 1U);
m_DMR.getData(data + 24U, 9U);
m_dmrN -= 1U;
return TAG_DATA;
}
else
return TAG_NODATA;
}
unsigned int CModeConv::getM17(unsigned char* data)
{
unsigned char tag[2U];
tag[0U] = TAG_NODATA;
tag[1U] = TAG_NODATA;
if (m_m17N >= 2U) {
m_M17.getData(tag, 1U);
m_M17.getData(data, 8U);
m_M17.getData(tag+1, 1U);
m_M17.getData(data+8, 8U);
fprintf(stderr, "getM17() m_m17N:tag1:tag2 == %d:%d:%d\n", m_m17N, tag[0U], tag[1U]);
m_m17N -= 2U;
}
return (tag[1U] == TAG_EOT) ? tag[1U] : tag[0];
}
void CModeConv::decode(const unsigned char* in, unsigned char* out, unsigned int offset) const
{
assert(in != NULL);
assert(out != NULL);
unsigned int a = 0U;
unsigned int MASK = 0x800000U;
for (unsigned int i = 0U; i < 24U; i++, MASK >>= 1) {
unsigned int aPos = A_TABLE[i];
if (READ_BIT(in, aPos))
a |= MASK;
}
unsigned int b = 0U;
MASK = 0x400000U;
for (unsigned int i = 0U; i < 23U; i++, MASK >>= 1) {
unsigned int bPos = B_TABLE[i];
if (READ_BIT(in, bPos))
b |= MASK;
}
unsigned int c = 0U;
MASK = 0x1000000U;
for (unsigned int i = 0U; i < 25U; i++, MASK >>= 1) {
unsigned int cPos = C_TABLE[i];
if (READ_BIT(in, cPos))
c |= MASK;
}
a >>= 12;
// The PRNG
b ^= (PRNG_TABLE[a] >> 1);
b >>= 11;
MASK = 0x000800U;
for (unsigned int i = 0U; i < 12U; i++, MASK >>= 1) {
unsigned int aPos = i + offset + 0U;
unsigned int bPos = i + offset + 12U;
WRITE_BIT(out, aPos, a & MASK);
WRITE_BIT(out, bPos, b & MASK);
}
MASK = 0x1000000U;
for (unsigned int i = 0U; i < 25U; i++, MASK >>= 1) {
unsigned int cPos = i + offset + 24U;
WRITE_BIT(out, cPos, c & MASK);
}
}
void CModeConv::encode(const unsigned char* in, unsigned char* out, unsigned int offset) const
{
assert(in != NULL);
assert(out != NULL);
unsigned int aOrig = 0U;
unsigned int bOrig = 0U;
unsigned int cOrig = 0U;
unsigned int MASK = 0x000800U;
for (unsigned int i = 0U; i < 12U; i++, MASK >>= 1) {
unsigned int n1 = i + offset + 0U;
unsigned int n2 = i + offset + 12U;
if (READ_BIT(in, n1))
aOrig |= MASK;
if (READ_BIT(in, n2))
bOrig |= MASK;
}
MASK = 0x1000000U;
for (unsigned int i = 0U; i < 25U; i++, MASK >>= 1) {
unsigned int n = i + offset + 24U;
if (READ_BIT(in, n))
cOrig |= MASK;
}
unsigned int a = CGolay24128::encode24128(aOrig);
// The PRNG
unsigned int p = PRNG_TABLE[aOrig] >> 1;
unsigned int b = CGolay24128::encode23127(bOrig) >> 1;
b ^= p;
MASK = 0x800000U;
for (unsigned int i = 0U; i < 24U; i++, MASK >>= 1) {
unsigned int aPos = A_TABLE[i];
WRITE_BIT(out, aPos, a & MASK);
}
MASK = 0x400000U;
for (unsigned int i = 0U; i < 23U; i++, MASK >>= 1) {
unsigned int bPos = B_TABLE[i];
WRITE_BIT(out, bPos, b & MASK);
}
MASK = 0x1000000U;
for (unsigned int i = 0U; i < 25U; i++, MASK >>= 1) {
unsigned int cPos = C_TABLE[i];
WRITE_BIT(out, cPos, cOrig & MASK);
}
}

59
M172DMR/ModeConv.h Normal file
View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2010,2014,2016,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
* Copyright (C) 2020 by Doug McLain AD8DP
*
* 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 "Defines.h"
#include "RingBuffer.h"
#include "MBEVocoder.h"
#include "codec2/codec2.h"
#if !defined(MODECONV_H)
#define MODECONV_H
class CModeConv {
public:
CModeConv();
~CModeConv();
void setM17GainAdjDb(std::string dbstring);
void putDMR(unsigned char* data);
void putDMRHeader();
void putDMREOT();
void putM17(unsigned char* data);
void putM17Header();
void putM17EOT();
unsigned int getM17(unsigned char* data);
unsigned int getDMR(unsigned char* data);
private:
unsigned int m_m17N;
unsigned int m_dmrN;
CRingBuffer<unsigned char> m_M17;
CRingBuffer<unsigned char> m_DMR;
MBEVocoder *m_mbe;
CCodec2 *m_c2;
uint16_t m_m17GainMultiplier;
bool m_m17Attenuate;
void encode(const unsigned char* in, unsigned char* out, unsigned int offset) const;
void decode(const unsigned char* in, unsigned char* out, unsigned int offset) const;
};
#endif

65
M172DMR/Mutex.cpp Normal file
View File

@ -0,0 +1,65 @@
/*
* 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 "Mutex.h"
#if defined(_WIN32) || defined(_WIN64)
CMutex::CMutex() :
m_handle()
{
m_handle = ::CreateMutex(NULL, FALSE, NULL);
}
CMutex::~CMutex()
{
::CloseHandle(m_handle);
}
void CMutex::lock()
{
::WaitForSingleObject(m_handle, INFINITE);
}
void CMutex::unlock()
{
::ReleaseMutex(m_handle);
}
#else
CMutex::CMutex() :
m_mutex(PTHREAD_MUTEX_INITIALIZER)
{
}
CMutex::~CMutex()
{
}
void CMutex::lock()
{
::pthread_mutex_lock(&m_mutex);
}
void CMutex::unlock()
{
::pthread_mutex_unlock(&m_mutex);
}
#endif

45
M172DMR/Mutex.h Normal file
View File

@ -0,0 +1,45 @@
/*
* 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(MUTEX_H)
#define MUTEX_H
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#else
#include <pthread.h>
#endif
class CMutex
{
public:
CMutex();
~CMutex();
void lock();
void unlock();
private:
#if defined(_WIN32) || defined(_WIN64)
HANDLE m_handle;
#else
pthread_mutex_t m_mutex;
#endif
};
#endif

115
M172DMR/QR1676.cpp Normal file
View File

@ -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 <cstdio>
#include <cassert>
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;
}

32
M172DMR/QR1676.h Normal file
View File

@ -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

54
M172DMR/README.md Normal file
View File

@ -0,0 +1,54 @@
# Description
This is the source code of P252DMR, a software for digital voice conversion from P25 to DMR digital mode, based on Jonathan G4KLX's [MMDVM](https://github.com/g4klx) software. Unlike the other cross mode utilities upon which this is based, this utility performs software transcoding between IMBE 4400x2800(P25) and AMBE+2 2450x1150(DMR).
You can use this software with MMDVMHost and P25Gateway, with the default UDP ports:
MMDVMHost(P25 Mode):32010 <-> 42020:P25Gateway:42010 <-> 42012:P252DMR <-> (DMR Master server)
If you want to connect directly to a XLX reflector (with DMR support), you only need to uncomment ([DMR Network] section):
XLXFile=XLXHosts.txt
XLXReflector=950
XLXModule=D
and replace XLXReflector and XLXModule according your preferences. Also, you need to configure the DMR port according the XLX reflector port, for example:
Port=62030
StartupDstId, StartupPC and Address parameters don't care in XLX mode.
This software is licenced under the GPL v2 and is intended for amateur and educational use only. Use of this software for commercial purposes is strictly forbidden.
# Building
This utility is not built with the other cross mode ulitities, and has 2 external dependencies:
imbe_vocoder https://github.com/nostar/imbe_vocoder
md380_vocoder https://github.com/nostar/md380_vocoder
With these dependencies installed, run 'make' from the source directory.
# Crosslink configuration
You can use P252DMR to link a [P25 Reflector](https://github.com/g4klx/P25Clients) to a DMR network (without using any RF link):
P25Reflector <-> P252DMR <-> any DMR Network
Install the P252DMR software at the same server where P25Reflector is located. Configure your [DMR Network] section (P252DMR.ini) as usual, depending on your preferred DMR network. Then, you only need to match the P25Reflector UDP port (Port in [Network], P25Reflector.ini) to P25 UDP port (DstPort in [P25 Network], P252DMR.ini).
For example, a common UDP port in P25Reflector.ini:
[Network]
Port=41000
Then you need to configure P252DMR.ini (example):
[P25 Network]
Callsign=CE1ABC
TG=10300
DstAddress=127.0.0.1
DstPort=41000
LocalAddress=127.0.0.1
LocalPort=41015
Daemon=0

127
M172DMR/RS129.cpp Normal file
View File

@ -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 "RS129.h"
#include <cstdio>
#include <cassert>
#include <cstring>
const unsigned int NPAR = 3U;
/* 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];
}

30
M172DMR/RS129.h Normal file
View File

@ -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

106
M172DMR/Reflectors.cpp Normal file
View File

@ -0,0 +1,106 @@
/*
* Copyright (C) 2016,2017 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 "Reflectors.h"
#include "Log.h"
#include <algorithm>
#include <functional>
#include <cstdio>
#include <cassert>
#include <cstring>
#include <cctype>
CReflectors::CReflectors(const std::string& hostsFile, unsigned int reloadTime) :
m_hostsFile(hostsFile),
m_reflectors(),
m_timer(1000U, reloadTime * 60U)
{
if (reloadTime > 0U)
m_timer.start();
}
CReflectors::~CReflectors()
{
for (std::vector<CReflector*>::iterator it = m_reflectors.begin(); it != m_reflectors.end(); ++it)
delete *it;
m_reflectors.clear();
}
bool CReflectors::load()
{
for (std::vector<CReflector*>::iterator it = m_reflectors.begin(); it != m_reflectors.end(); ++it)
delete *it;
m_reflectors.clear();
FILE* fp = ::fopen(m_hostsFile.c_str(), "rt");
if (fp != NULL) {
char buffer[100U];
while (::fgets(buffer, 100U, fp) != NULL) {
if (buffer[0U] == '#')
continue;
char* p1 = ::strtok(buffer, ";\r\n");
char* p2 = ::strtok(NULL, ";\r\n");
char* p3 = ::strtok(NULL, "\r\n");
if (p1 != NULL && p2 != NULL && p3 != NULL) {
CReflector* refl = new CReflector;
refl->m_id = (unsigned int)::atoi(p1);
refl->m_address = std::string(p2);
refl->m_startup = (unsigned int)::atoi(p3);
m_reflectors.push_back(refl);
}
}
::fclose(fp);
}
size_t size = m_reflectors.size();
LogInfo("Loaded %u XLX reflectors", size);
size = m_reflectors.size();
if (size == 0U)
return false;
return true;
}
CReflector* CReflectors::find(unsigned int id)
{
for (std::vector<CReflector*>::iterator it = m_reflectors.begin(); it != m_reflectors.end(); ++it) {
if (id == (*it)->m_id)
return *it;
}
LogMessage("Trying to find non existent XLX reflector with an id of %u", id);
return NULL;
}
void CReflectors::clock(unsigned int ms)
{
m_timer.clock(ms);
if (m_timer.isRunning() && m_timer.hasExpired()) {
load();
m_timer.start();
}
}

58
M172DMR/Reflectors.h Normal file
View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2016,2017 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(Reflectors_H)
#define Reflectors_H
#include "Timer.h"
#include <vector>
#include <string>
class CReflector {
public:
CReflector() :
m_id(0U),
m_address(),
m_startup(0U)
{
}
unsigned int m_id;
std::string m_address;
unsigned int m_startup;
};
class CReflectors {
public:
CReflectors(const std::string& hostsFile, unsigned int reloadTime);
~CReflectors();
bool load();
CReflector* find(unsigned int id);
void clock(unsigned int ms);
private:
std::string m_hostsFile;
std::vector<CReflector*> m_reflectors;
CTimer m_timer;
};
#endif

154
M172DMR/RingBuffer.h Normal file
View File

@ -0,0 +1,154 @@
/*
* Copyright (C) 2006-2009,2012,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 RingBuffer_H
#define RingBuffer_H
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
template<class T> class CRingBuffer {
public:
CRingBuffer(unsigned int length, const char* name) :
m_length(length),
m_name(name),
m_buffer(NULL),
m_iPtr(0U),
m_oPtr(0U)
{
assert(length > 0U);
assert(name != NULL);
m_buffer = new T[length];
::memset(m_buffer, 0x00, m_length * sizeof(T));
}
~CRingBuffer()
{
delete[] m_buffer;
}
bool addData(const T* buffer, unsigned int nSamples)
{
if (nSamples >= freeSpace()) {
LogError("%s buffer overflow, clearing the buffer. (%u >= %u)", m_name, nSamples, freeSpace());
clear();
return false;
}
for (unsigned int i = 0U; i < nSamples; i++) {
m_buffer[m_iPtr++] = buffer[i];
if (m_iPtr == m_length)
m_iPtr = 0U;
}
return true;
}
bool getData(T* buffer, unsigned int nSamples)
{
if (dataSize() < nSamples) {
LogError("**** Underflow in %s ring buffer, %u < %u", m_name, dataSize(), nSamples);
return false;
}
for (unsigned int i = 0U; i < nSamples; i++) {
buffer[i] = m_buffer[m_oPtr++];
if (m_oPtr == m_length)
m_oPtr = 0U;
}
return true;
}
bool peek(T* buffer, unsigned int nSamples)
{
if (dataSize() < nSamples) {
LogError("**** Underflow peek in %s ring buffer, %u < %u", m_name, dataSize(), nSamples);
return false;
}
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 true;
}
void clear()
{
m_iPtr = 0U;
m_oPtr = 0U;
::memset(m_buffer, 0x00, m_length * sizeof(T));
}
unsigned int freeSpace() const
{
unsigned int len = m_length;
if (m_oPtr > m_iPtr)
len = m_oPtr - m_iPtr;
else if (m_iPtr > m_oPtr)
len = m_length - (m_iPtr - m_oPtr);
if (len > m_length)
len = 0U;
return len;
}
unsigned int dataSize() const
{
return m_length - freeSpace();
}
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;
const char* m_name;
T* m_buffer;
unsigned int m_iPtr;
unsigned int m_oPtr;
};
#endif

373
M172DMR/SHA256.cpp Normal file
View File

@ -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 <cstdio>
#include <cstring>
#include <cassert>
#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;
}
}

73
M172DMR/SHA256.h Normal file
View File

@ -0,0 +1,73 @@
/*
* Copyright (C) 2005, 2006, 2008, 2009 Free Software Foundation, Inc.
* Copyright (C) 2011,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 SHA256_H
#define SHA256_H
#include <cstdint>
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

105
M172DMR/StopWatch.cpp Normal file
View File

@ -0,0 +1,105 @@
/*
* Copyright (C) 2015,2016,2018 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_frequencyS(),
m_frequencyMS(),
m_start()
{
::QueryPerformanceFrequency(&m_frequencyS);
m_frequencyMS.QuadPart = m_frequencyS.QuadPart / 1000ULL;
}
CStopWatch::~CStopWatch()
{
}
unsigned long long CStopWatch::time() const
{
LARGE_INTEGER now;
::QueryPerformanceCounter(&now);
return (unsigned long long)(now.QuadPart / m_frequencyMS.QuadPart);
}
unsigned long long CStopWatch::start()
{
::QueryPerformanceCounter(&m_start);
return (unsigned long long)(m_start.QuadPart / m_frequencyS.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_frequencyS.QuadPart);
}
#else
#include <cstdio>
#include <ctime>
CStopWatch::CStopWatch() :
m_startMS(0ULL)
{
}
CStopWatch::~CStopWatch()
{
}
unsigned long long CStopWatch::time() const
{
struct timeval now;
::gettimeofday(&now, NULL);
return now.tv_sec * 1000ULL + now.tv_usec / 1000ULL;
}
unsigned long long CStopWatch::start()
{
struct timespec now;
::clock_gettime(CLOCK_MONOTONIC, &now);
m_startMS = now.tv_sec * 1000ULL + now.tv_nsec / 1000000ULL;
return m_startMS;
}
unsigned int CStopWatch::elapsed()
{
struct timespec now;
::clock_gettime(CLOCK_MONOTONIC, &now);
unsigned long long nowMS = now.tv_sec * 1000ULL + now.tv_nsec / 1000000ULL;
return nowMS - m_startMS;
}
#endif

49
M172DMR/StopWatch.h Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2015,2016,2018 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 <windows.h>
#else
#include <sys/time.h>
#endif
class CStopWatch
{
public:
CStopWatch();
~CStopWatch();
unsigned long long time() const;
unsigned long long start();
unsigned int elapsed();
private:
#if defined(_WIN32) || defined(_WIN64)
LARGE_INTEGER m_frequencyS;
LARGE_INTEGER m_frequencyMS;
LARGE_INTEGER m_start;
#else
unsigned long long m_startMS;
#endif
};
#endif

53
M172DMR/Sync.cpp Normal file
View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
*
* 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 "Sync.h"
#include "DMRDefines.h"
#include <cstdio>
#include <cassert>
#include <cstring>
void CSync::addDMRDataSync(unsigned char* data, bool duplex)
{
assert(data != NULL);
if (duplex) {
for (unsigned int i = 0U; i < 7U; i++)
data[i + 13U] = (data[i + 13U] & ~SYNC_MASK[i]) | BS_SOURCED_DATA_SYNC[i];
} else {
for (unsigned int i = 0U; i < 7U; i++)
data[i + 13U] = (data[i + 13U] & ~SYNC_MASK[i]) | MS_SOURCED_DATA_SYNC[i];
}
}
void CSync::addDMRAudioSync(unsigned char* data, bool duplex)
{
assert(data != NULL);
if (duplex) {
for (unsigned int i = 0U; i < 7U; i++)
data[i + 13U] = (data[i + 13U] & ~SYNC_MASK[i]) | BS_SOURCED_AUDIO_SYNC[i];
} else {
for (unsigned int i = 0U; i < 7U; i++)
data[i + 13U] = (data[i + 13U] & ~SYNC_MASK[i]) | MS_SOURCED_AUDIO_SYNC[i];
}
}

32
M172DMR/Sync.h Normal file
View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
*
* 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(SYNC_H)
#define SYNC_H
class CSync
{
public:
static void addDMRDataSync(unsigned char* data, bool duplex);
static void addDMRAudioSync(unsigned char* data, bool duplex);
private:
};
#endif

101
M172DMR/Thread.cpp Normal file
View File

@ -0,0 +1,101 @@
/*
* 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 "Thread.h"
#if defined(_WIN32) || defined(_WIN64)
CThread::CThread() :
m_handle()
{
}
CThread::~CThread()
{
}
bool CThread::run()
{
m_handle = ::CreateThread(NULL, 0, &helper, this, 0, NULL);
return m_handle != NULL;
}
void CThread::wait()
{
::WaitForSingleObject(m_handle, INFINITE);
::CloseHandle(m_handle);
}
DWORD CThread::helper(LPVOID arg)
{
CThread* p = (CThread*)arg;
p->entry();
return 0UL;
}
void CThread::sleep(unsigned int ms)
{
::Sleep(ms);
}
#else
#include <unistd.h>
CThread::CThread() :
m_thread()
{
}
CThread::~CThread()
{
}
bool CThread::run()
{
return ::pthread_create(&m_thread, NULL, helper, this) == 0;
}
void CThread::wait()
{
::pthread_join(m_thread, NULL);
}
void* CThread::helper(void* arg)
{
CThread* p = (CThread*)arg;
p->entry();
return NULL;
}
void CThread::sleep(unsigned int ms)
{
::usleep(ms * 1000);
}
#endif

56
M172DMR/Thread.h Normal file
View File

@ -0,0 +1,56 @@
/*
* 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(THREAD_H)
#define THREAD_H
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#else
#include <pthread.h>
#endif
class CThread
{
public:
CThread();
virtual ~CThread();
virtual bool run();
virtual void entry() = 0;
virtual void wait();
static void sleep(unsigned int ms);
private:
#if defined(_WIN32) || defined(_WIN64)
HANDLE m_handle;
#else
pthread_t m_thread;
#endif
#if defined(_WIN32) || defined(_WIN64)
static DWORD __stdcall helper(LPVOID arg);
#else
static void* helper(void* arg);
#endif
};
#endif

68
M172DMR/Timer.cpp Normal file
View File

@ -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 <cstdio>
#include <cassert>
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;
}

89
M172DMR/Timer.h Normal file
View File

@ -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

262
M172DMR/UDPSocket.cpp Normal file
View File

@ -0,0 +1,262 @@
/*
* 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 <cassert>
#if !defined(_WIN32) && !defined(_WIN64)
#include <cerrno>
#include <cstring>
#endif
CUDPSocket::CUDPSocket(const std::string& address, unsigned int port) :
m_address(address),
m_port(port),
m_fd(-1)
{
assert(!address.empty());
#if defined(_WIN32) || defined(_WIN64)
WSAData data;
int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data);
if (wsaRet != 0)
LogError("Error from WSAStartup");
#endif
}
CUDPSocket::CUDPSocket(unsigned int port) :
m_address(),
m_port(port),
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
}

58
M172DMR/UDPSocket.h Normal file
View File

@ -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 <string>
#if !defined(_WIN32) && !defined(_WIN64)
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#else
#include <winsock.h>
#endif
class CUDPSocket {
public:
CUDPSocket(const std::string& address, unsigned int port = 0U);
CUDPSocket(unsigned int port = 0U);
~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

146
M172DMR/Utils.cpp Normal file
View File

@ -0,0 +1,146 @@
/*
* Copyright (C) 2009,2014,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 "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
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)
{
assert(bits != NULL);
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)
{
assert(bits != NULL);
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)
{
assert(bits != NULL);
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)
{
assert(bits != NULL);
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;
}

36
M172DMR/Utils.h Normal file
View File

@ -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 <string>
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

25
M172DMR/Version.h Normal file
View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
*
* 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 = "20180923";
#endif

View File

@ -0,0 +1,964 @@
/*
* This intermediary file and the files that used to create it are under
* The LGPL. See the file COPYING.
*/
#include "defines.h"
/* codebook/lsp1.txt */
static float codes00[] =
{
225,
250,
275,
300,
325,
350,
375,
400,
425,
450,
475,
500,
525,
550,
575,
600
};
/* codebook/lsp2.txt */
static float codes01[] =
{
325,
350,
375,
400,
425,
450,
475,
500,
525,
550,
575,
600,
625,
650,
675,
700
};
/* codebook/lsp3.txt */
static float codes02[] =
{
500,
550,
600,
650,
700,
750,
800,
850,
900,
950,
1000,
1050,
1100,
1150,
1200,
1250
};
/* codebook/lsp4.txt */
static float codes03[] =
{
700,
800,
900,
1000,
1100,
1200,
1300,
1400,
1500,
1600,
1700,
1800,
1900,
2000,
2100,
2200
};
/* codebook/lsp5.txt */
static float codes04[] =
{
950,
1050,
1150,
1250,
1350,
1450,
1550,
1650,
1750,
1850,
1950,
2050,
2150,
2250,
2350,
2450
};
/* codebook/lsp6.txt */
static float codes05[] =
{
1100,
1200,
1300,
1400,
1500,
1600,
1700,
1800,
1900,
2000,
2100,
2200,
2300,
2400,
2500,
2600
};
/* codebook/lsp7.txt */
static float codes06[] =
{
1500,
1600,
1700,
1800,
1900,
2000,
2100,
2200,
2300,
2400,
2500,
2600,
2700,
2800,
2900,
3000
};
/* codebook/lsp8.txt */
static float codes07[] =
{
2300,
2400,
2500,
2600,
2700,
2800,
2900,
3000
};
/* codebook/lsp9.txt */
static float codes08[] =
{
2500,
2600,
2700,
2800,
2900,
3000,
3100,
3200
};
/* codebook/lsp10.txt */
static float codes09[] =
{
2900,
3100,
3300,
3500
};
const struct lsp_codebook lsp_cb[] =
{
/* codebook/lsp1.txt */
{
1,
4,
16,
codes00
},
/* codebook/lsp2.txt */
{
1,
4,
16,
codes01
},
/* codebook/lsp3.txt */
{
1,
4,
16,
codes02
},
/* codebook/lsp4.txt */
{
1,
4,
16,
codes03
},
/* codebook/lsp5.txt */
{
1,
4,
16,
codes04
},
/* codebook/lsp6.txt */
{
1,
4,
16,
codes05
},
/* codebook/lsp7.txt */
{
1,
4,
16,
codes06
},
/* codebook/lsp8.txt */
{
1,
3,
8,
codes07
},
/* codebook/lsp9.txt */
{
1,
3,
8,
codes08
},
/* codebook/lsp10.txt */
{
1,
2,
4,
codes09
},
{ 0, 0, 0, 0 }
};
/* codebook/dlsp1.txt */
static float codes10[] =
{
25,
50,
75,
100,
125,
150,
175,
200,
225,
250,
275,
300,
325,
350,
375,
400,
425,
450,
475,
500,
525,
550,
575,
600,
625,
650,
675,
700,
725,
750,
775,
800
};
/* codebook/dlsp2.txt */
static float codes11[] =
{
25,
50,
75,
100,
125,
150,
175,
200,
225,
250,
275,
300,
325,
350,
375,
400,
425,
450,
475,
500,
525,
550,
575,
600,
625,
650,
675,
700,
725,
750,
775,
800
};
/* codebook/dlsp3.txt */
static float codes12[] =
{
25,
50,
75,
100,
125,
150,
175,
200,
225,
250,
275,
300,
325,
350,
375,
400,
425,
450,
475,
500,
525,
550,
575,
600,
625,
650,
675,
700,
725,
750,
775,
800
};
/* codebook/dlsp4.txt */
static float codes13[] =
{
25,
50,
75,
100,
125,
150,
175,
200,
250,
300,
350,
400,
450,
500,
550,
600,
650,
700,
750,
800,
850,
900,
950,
1000,
1050,
1100,
1150,
1200,
1250,
1300,
1350,
1400
};
/* codebook/dlsp5.txt */
static float codes14[] =
{
25,
50,
75,
100,
125,
150,
175,
200,
250,
300,
350,
400,
450,
500,
550,
600,
650,
700,
750,
800,
850,
900,
950,
1000,
1050,
1100,
1150,
1200,
1250,
1300,
1350,
1400
};
/* codebook/dlsp6.txt */
static float codes15[] =
{
25,
50,
75,
100,
125,
150,
175,
200,
250,
300,
350,
400,
450,
500,
550,
600,
650,
700,
750,
800,
850,
900,
950,
1000,
1050,
1100,
1150,
1200,
1250,
1300,
1350,
1400
};
/* codebook/dlsp7.txt */
static float codes16[] =
{
25,
50,
75,
100,
125,
150,
175,
200,
225,
250,
275,
300,
325,
350,
375,
400,
425,
450,
475,
500,
525,
550,
575,
600,
625,
650,
675,
700,
725,
750,
775,
800
};
/* codebook/dlsp8.txt */
static float codes17[] =
{
25,
50,
75,
100,
125,
150,
175,
200,
225,
250,
275,
300,
325,
350,
375,
400,
425,
450,
475,
500,
525,
550,
575,
600,
625,
650,
675,
700,
725,
750,
775,
800
};
/* codebook/dlsp9.txt */
static float codes18[] =
{
25,
50,
75,
100,
125,
150,
175,
200,
225,
250,
275,
300,
325,
350,
375,
400,
425,
450,
475,
500,
525,
550,
575,
600,
625,
650,
675,
700,
725,
750,
775,
800
};
/* codebook/dlsp10.txt */
static float codes19[] =
{
25,
50,
75,
100,
125,
150,
175,
200,
225,
250,
275,
300,
325,
350,
375,
400,
425,
450,
475,
500,
525,
550,
575,
600,
625,
650,
675,
700,
725,
750,
775,
800
};
const struct lsp_codebook lsp_cbd[] =
{
/* codebook/dlsp1.txt */
{
1,
5,
32,
codes10
},
/* codebook/dlsp2.txt */
{
1,
5,
32,
codes11
},
/* codebook/dlsp3.txt */
{
1,
5,
32,
codes12
},
/* codebook/dlsp4.txt */
{
1,
5,
32,
codes13
},
/* codebook/dlsp5.txt */
{
1,
5,
32,
codes14
},
/* codebook/dlsp6.txt */
{
1,
5,
32,
codes15
},
/* codebook/dlsp7.txt */
{
1,
5,
32,
codes16
},
/* codebook/dlsp8.txt */
{
1,
5,
32,
codes17
},
/* codebook/dlsp9.txt */
{
1,
5,
32,
codes18
},
/* codebook/dlsp10.txt */
{
1,
5,
32,
codes19
},
{ 0, 0, 0, 0 }
};
/* codebook/gecb.txt */
static float codes30[] =
{
2.71, 12.0184,
0.04675, -2.73881,
0.120993, 8.38895,
-1.58028, -0.892307,
1.19307, -1.91561,
0.187101, -3.27679,
0.332251, -7.66455,
-1.47944, 31.2461,
1.52761, 27.7095,
-0.524379, 5.25012,
0.55333, 7.4388,
-0.843451, -1.95299,
2.26389, 8.61029,
0.143143, 2.36549,
0.616506, 1.28427,
-1.71133, 22.0967,
1.00813, 17.3965,
-0.106718, 1.41891,
-0.136246, 14.2736,
-1.70909, -20.5319,
1.65787, -3.39107,
0.138049, -4.95785,
0.536729, -1.94375,
0.196307, 36.8519,
1.27248, 22.5565,
-0.670219, -1.90604,
0.382092, 6.40113,
-0.756911, -4.90102,
1.82931, 4.6138,
0.318794, 0.73683,
0.612815, -2.07505,
-0.410151, 24.7871,
1.77602, 13.1909,
0.106457, -0.104492,
0.192206, 10.1838,
-1.82442, -7.71565,
0.931346, 4.34835,
0.308813, -4.086,
0.397143, -11.8089,
-0.048715, 41.2273,
0.877342, 35.8503,
-0.759794, 0.476634,
0.978593, 7.67467,
-1.19506, 3.03883,
2.63989, -3.41106,
0.191127, 3.60351,
0.402932, 1.0843,
-2.15202, 18.1076,
1.5468, 8.32271,
-0.143089, -4.07592,
-0.150142, 5.86674,
-1.40844, -3.2507,
1.56615, -10.4132,
0.178171, -10.2267,
0.362164, -0.028556,
-0.070125, 24.3907,
0.594752, 17.4828,
-0.28698, -6.90407,
0.464818, 10.2055,
-1.00684, -14.3572,
2.32957, -3.69161,
0.335745, 2.40714,
1.01966, -3.15565,
-1.25945, 7.9919,
2.38369, 19.6806,
-0.094947, -2.41374,
0.20933, 6.66477,
-2.22103, 1.37986,
1.29239, 2.04633,
0.243626, -0.890741,
0.428773, -7.19366,
-1.11374, 41.3414,
2.6098, 31.1405,
-0.446468, 2.53419,
0.490104, 4.62757,
-1.11723, -3.24174,
1.79156, 8.41493,
0.156012, 0.183336,
0.532447, 3.15455,
-0.764484, 18.514,
0.952395, 11.7713,
-0.332567, 0.346987,
0.202165, 14.7168,
-2.12924, -15.559,
1.35358, -1.92679,
-0.010963, -16.3364,
0.399053, -2.79057,
0.750657, 31.1483,
0.655743, 24.4819,
-0.45321, -0.735879,
0.2869, 6.5467,
-0.715673, -12.3578,
1.54849, 3.87217,
0.271874, 0.802339,
0.502073, -4.85485,
-0.497037, 17.7619,
1.19116, 13.9544,
0.01563, 1.33157,
0.341867, 8.93537,
-2.31601, -5.39506,
0.75861, 1.9645,
0.24132, -3.23769,
0.267151, -11.2344,
-0.273126, 32.6248,
1.75352, 40.432,
-0.784011, 3.04576,
0.705987, 5.66118,
-1.3864, 1.35356,
2.37646, 1.67485,
0.242973, 4.73218,
0.491227, 0.354061,
-1.60676, 8.65895,
1.16711, 5.9871,
-0.137601, -12.0417,
-0.251375, 10.3972,
-1.43151, -8.90411,
0.98828, -13.209,
0.261484, -6.35497,
0.395932, -0.702529,
0.283704, 26.8996,
0.420959, 15.4418,
-0.355804, -13.7278,
0.527372, 12.3985,
-1.16956, -15.9985,
1.90669, -5.81605,
0.354492, 3.85157,
0.82576, -4.16264,
-0.49019, 13.0572,
2.25577, 13.5264,
-0.004956, -3.23713,
0.026709, 7.86645,
-1.81037, -0.451183,
1.08383, -0.18362,
0.135836, -2.26658,
0.375812, -5.51225,
-1.96644, 38.6829,
1.97799, 24.5655,
-0.704656, 6.35881,
0.480786, 7.05175,
-0.976417, -2.42273,
2.50215, 6.75935,
0.083588, 3.2588,
0.543629, 0.910013,
-1.23196, 23.0915,
0.785492, 14.807,
-0.213554, 1.688,
0.004748, 18.1718,
-1.54719, -16.1168,
1.50104, -3.28114,
0.080133, -4.63472,
0.476592, -2.18093,
0.44247, 40.304,
1.07277, 27.592,
-0.594738, -4.16681,
0.42248, 7.61609,
-0.927521, -7.27441,
1.99162, 1.29636,
0.291307, 2.39878,
0.721081, -1.95062,
-0.804256, 24.9295,
1.64839, 19.1197,
0.060852, -0.590639,
0.266085, 9.10325,
-1.9574, -2.88461,
1.11693, 2.6724,
0.35458, -2.74854,
0.330733, -14.1561,
-0.527851, 39.5756,
0.991152, 43.195,
-0.589619, 1.26919,
0.787401, 8.73071,
-1.0138, 1.02507,
2.8254, 1.89538,
0.24089, 2.74557,
0.427195, 2.54446,
-1.95311, 12.244,
1.44862, 12.0607,
-0.210492, -3.37906,
-0.056713, 10.204,
-1.65237, -5.10274,
1.29475, -12.2708,
0.111608, -8.67592,
0.326634, -1.16763,
0.021781, 31.1258,
0.455335, 21.4684,
-0.37544, -3.37121,
0.39362, 11.302,
-0.851456, -19.4149,
2.10703, -2.22886,
0.373233, 1.92406,
0.884438, -1.72058,
-0.975127, 9.84013,
2.0033, 17.3954,
-0.036915, -1.11137,
0.148456, 5.39997,
-1.91441, 4.77382,
1.44791, 0.537122,
0.194979, -1.03818,
0.495771, -9.95502,
-1.05899, 32.9471,
2.01122, 32.4544,
-0.30965, 4.71911,
0.436082, 4.63552,
-1.23711, -1.25428,
2.02274, 9.42834,
0.190342, 1.46077,
0.479017, 2.48479,
-1.07848, 16.2217,
1.20764, 9.65421,
-0.258087, -1.67236,
0.071852, 13.416,
-1.87723, -16.072,
1.28957, -4.87118,
0.067713, -13.4427,
0.435551, -4.1655,
0.46614, 30.5895,
0.904895, 21.598,
-0.518369, -2.53205,
0.337363, 5.63726,
-0.554975, -17.4005,
1.69188, 1.14574,
0.227934, 0.889297,
0.587303, -5.72973,
-0.262133, 18.6666,
1.39505, 17.0029,
-0.01909, 4.30838,
0.304235, 12.6699,
-2.07406, -6.46084,
0.920546, 1.21296,
0.284927, -1.78547,
0.209724, -16.024,
-0.636067, 31.5768,
1.34989, 34.6775,
-0.971625, 5.30086,
0.590249, 4.44971,
-1.56787, 3.60239,
2.1455, 4.51666,
0.296022, 4.12017,
0.445299, 0.868772,
-1.44193, 14.1284,
1.35575, 6.0074,
-0.012814, -7.49657,
-0.43, 8.50012,
-1.20469, -7.11326,
1.10102, -6.83682,
0.196463, -6.234,
0.436747, -1.12979,
0.141052, 22.8549,
0.290821, 18.8114,
-0.529536, -7.73251,
0.63428, 10.7898,
-1.33472, -20.3258,
1.81564, -1.90332,
0.394778, 3.79758,
0.732682, -8.18382,
-0.741244, 11.7683
};
const struct lsp_codebook ge_cb[] =
{
/* codebook/gecb.txt */
{
2,
8,
256,
codes30
},
{ 0, 0, 0, 0 }
};

1745
M172DMR/codec2/codec2.cpp Normal file

File diff suppressed because it is too large Load Diff

100
M172DMR/codec2/codec2.h Normal file
View File

@ -0,0 +1,100 @@
/*---------------------------------------------------------------------------*\
FILE........: codec2.h
AUTHOR......: David Rowe
DATE CREATED: 21 August 2010
Codec 2 fully quantised encoder and decoder functions. If you want use
Codec 2, these are the functions you need to call.
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2010 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CODEC2__
#define __CODEC2__
#include <complex>
#include "codec2_internal.h"
#include "defines.h"
#include "kiss_fft.h"
#include "nlp.h"
#include "quantise.h"
#define CODEC2_MODE_3200 0
#define CODEC2_MODE_1600 2
#ifndef CODEC2_MODE_EN_DEFAULT
#define CODEC2_MODE_EN_DEFAULT 1
#endif
#define CODEC2_RAND_MAX 32767
class CCodec2
{
public:
CCodec2(bool is_3200);
~CCodec2();
void codec2_encode(unsigned char *bits, const short *speech_in);
void codec2_decode(short *speech_out, const unsigned char *bits);
void codec2_set_mode(bool);
bool codec2_get_mode() {return (c2.mode == 3200); };
int codec2_samples_per_frame();
int codec2_bits_per_frame();
private:
// merged from other files
void sample_phase(MODEL *model, std::complex<float> filter_phase[], std::complex<float> A[]);
void phase_synth_zero_order(int n_samp, MODEL *model, float *ex_phase, std::complex<float> filter_phase[]);
void postfilter(MODEL *model, float *bg_est);
C2CONST c2const_create(int Fs, float framelength_ms);
void make_analysis_window(C2CONST *c2const, FFT_STATE *fft_fwd_cfg, float w[], float W[]);
void dft_speech(C2CONST *c2const, FFT_STATE &fft_fwd_cfg, std::complex<float> Sw[], float Sn[], float w[]);
void two_stage_pitch_refinement(C2CONST *c2const, MODEL *model, std::complex<float> Sw[]);
void estimate_amplitudes(MODEL *model, std::complex<float> Sw[], int est_phase);
float est_voicing_mbe(C2CONST *c2const, MODEL *model, std::complex<float> Sw[], float W[]);
void make_synthesis_window(C2CONST *c2const, float Pn[]);
void synthesise(int n_samp, FFTR_STATE *fftr_inv_cfg, float Sn_[], MODEL *model, float Pn[], int shift);
int codec2_rand(void);
void hs_pitch_refinement(MODEL *model, std::complex<float> Sw[], float pmin, float pmax, float pstep);
void interp_Wo(MODEL *interp, MODEL *prev, MODEL *next, float Wo_min);
void interp_Wo2(MODEL *interp, MODEL *prev, MODEL *next, float weight, float Wo_min);
float interp_energy(float prev, float next);
void interpolate_lsp_ver2(float interp[], float prev[], float next[], float weight, int order);
void analyse_one_frame(MODEL *model, const short *speech);
void synthesise_one_frame(short speech[], MODEL *model, std::complex<float> Aw[], float gain);
void codec2_encode_3200(unsigned char *bits, const short *speech);
void codec2_encode_1600(unsigned char *bits, const short *speech);
void codec2_decode_3200(short *speech, const unsigned char *bits);
void codec2_decode_1600(short *speech, const unsigned char *bits);
void ear_protection(float in_out[], int n);
void lsp_to_lpc(float *freq, float *ak, int lpcrdr);
void (CCodec2::*encode)(unsigned char *bits, const short *speech);
void (CCodec2::*decode)(short *speech, const unsigned char *bits);
Cnlp nlp;
CQuantize qt;
CODEC2 c2;
};
#endif

View File

@ -0,0 +1,67 @@
/*---------------------------------------------------------------------------*\
FILE........: codec2_internal.h
AUTHOR......: David Rowe
DATE CREATED: April 16 2012
Header file for Codec2 internal states, exposed via this header
file to assist in testing.
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2012 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CODEC2_INTERNAL__
#define __CODEC2_INTERNAL__
#include "kiss_fft.h"
using CODEC2 = struct codec2_tag {
int mode;
int Fs;
int n_samp;
int m_pitch;
int gray; /* non-zero for gray encoding */
int lpc_pf; /* LPC post filter on */
int bass_boost; /* LPC post filter bass boost */
int smoothing; /* enable smoothing for channels with errors */
float ex_phase; /* excitation model phase track */
float bg_est; /* background noise estimate for post filter */
float prev_f0_enc; /* previous frame's f0 estimate */
float prev_e_dec; /* previous frame's LPC energy */
float beta; /* LPC post filter parameters */
float gamma;
float xq_enc[2]; /* joint pitch and energy VQ states */
float xq_dec[2];
float W[FFT_ENC]; /* DFT of w[] */
float hpf_states[2]; /* high pass filter states */
float prev_lsps_dec[LPC_ORD]; /* previous frame's LSPs */
float *softdec; /* optional soft decn bits from demod */
MODEL prev_model_dec; /* previous frame's model parameters */
C2CONST c2const;
FFT_STATE fft_fwd_cfg; /* forward FFT config */
FFTR_STATE fftr_fwd_cfg; /* forward real FFT config */
FFTR_STATE fftr_inv_cfg; /* inverse FFT config */
std::vector<float> w; /* [m_pitch] time domain hamming window */
std::vector<float> Pn; /* [2*n_samp] trapezoidal synthesis window */
std::vector<float> Sn; /* [m_pitch] input speech */
std::vector<float> Sn_; /* [2*n_samp] synthesised output speech */
std::vector<float> bpf_buf; /* buffer for band pass filter */
};
#endif

127
M172DMR/codec2/defines.h Normal file
View File

@ -0,0 +1,127 @@
/*---------------------------------------------------------------------------*\
FILE........: defines.h
AUTHOR......: David Rowe
DATE CREATED: 23/4/93
Defines and structures used throughout the codec.
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2009 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __DEFINES__
#define __DEFINES__
#include <complex>
#include <vector>
/*---------------------------------------------------------------------------*\
DEFINES
\*---------------------------------------------------------------------------*/
/* General defines */
#define N_S 0.01 /* internal proc frame length in secs */
#define TW_S 0.005 /* trapezoidal synth window overlap */
#define MAX_AMP 160 /* maximum number of harmonics */
#ifndef PI
#define PI 3.141592654 /* mathematical constant */
#endif
#define TWO_PI 6.283185307 /* mathematical constant */
#define MAX_STR 2048 /* maximum string size */
#define FFT_ENC 512 /* size of FFT used for encoder */
#define FFT_DEC 512 /* size of FFT used in decoder */
#define V_THRESH 6.0 /* voicing threshold in dB */
#define LPC_ORD 10 /* LPC order */
#define LPC_ORD_LOW 6 /* LPC order for lower rates */
/* Pitch estimation defines */
#define M_PITCH_S 0.0400 /* pitch analysis window in s */
#define P_MIN_S 0.0025 /* minimum pitch period in s */
#define P_MAX_S 0.0200 /* maximum pitch period in s */
#define MAXFACTORS 32 // e.g. an fft of length 128 has 4 factors
// as far as kissfft is concerned 4*4*4*2
/*---------------------------------------------------------------------------*\
TYPEDEFS
\*---------------------------------------------------------------------------*/
/* Structure to hold constants calculated at run time based on sample rate */
using C2CONST = struct c2const_tag
{
int Fs; /* sample rate of this instance */
int n_samp; /* number of samples per 10ms frame at Fs */
int max_amp; /* maximum number of harmonics */
int m_pitch; /* pitch estimation window size in samples */
int p_min; /* minimum pitch period in samples */
int p_max; /* maximum pitch period in samples */
float Wo_min;
float Wo_max;
int nw; /* analysis window size in samples */
int tw; /* trapezoidal synthesis window overlap */
};
/* Structure to hold model parameters for one frame */
using MODEL = struct model_tag
{
float Wo; /* fundamental frequency estimate in radians */
int L; /* number of harmonics */
float A[MAX_AMP+1]; /* amplitiude of each harmonic */
float phi[MAX_AMP+1]; /* phase of each harmonic */
int voiced; /* non-zero if this frame is voiced */
};
/* describes each codebook */
struct lsp_codebook
{
int k; /* dimension of vector */
int log2m; /* number of bits in m */
int m; /* elements in codebook */
float *cb; /* The elements */
};
using FFT_STATE = struct fft_state_tag
{
int nfft;
bool inverse;
int factors[2*MAXFACTORS];
std::vector<std::complex<float>> twiddles;
};
using FFTR_STATE = struct fftr_state_tag
{
FFT_STATE substate;
std::vector<std::complex<float>> tmpbuf;
std::vector<std::complex<float>> super_twiddles;
};
extern const struct lsp_codebook lsp_cb[];
extern const struct lsp_codebook lsp_cbd[];
extern const struct lsp_codebook ge_cb[];
#endif

435
M172DMR/codec2/kiss_fft.cpp Normal file
View File

@ -0,0 +1,435 @@
/*
Copyright (c) 2003-2010, Mark Borgerding
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cstring>
#include <cassert>
#include "defines.h"
#include "kiss_fft.h"
void CKissFFT::kf_bfly2(std::complex<float> *Fout, const size_t fstride, FFT_STATE &st, int m)
{
std::complex<float> *Fout2;
std::complex<float> *tw1 = st.twiddles.data();
std::complex<float> t;
Fout2 = Fout + m;
do
{
t = *Fout2 * *tw1;
tw1 += fstride;
*Fout2 = *Fout - t;
*Fout += t;
++Fout2;
++Fout;
}
while (--m);
}
void CKissFFT::kf_bfly3(std::complex<float> * Fout, const size_t fstride, FFT_STATE &st, int m)
{
const size_t m2 = 2 * m;
std::complex<float> *tw1,*tw2;
std::complex<float> scratch[5];
std::complex<float> epi3;
epi3 = st.twiddles[fstride*m];
tw1 = tw2 = st.twiddles.data();
do
{
scratch[1] = Fout[m] * *tw1;
scratch[2] = Fout[m2] * *tw2;
scratch[3] = scratch[1] + scratch[2];
scratch[0] = scratch[1] - scratch[2];
tw1 += fstride;
tw2 += fstride*2;
Fout[m] = *Fout - (0.5f * scratch[3]);
scratch[0] *= epi3.imag();
*Fout += scratch[3];
Fout[m2].real(Fout[m].real() + scratch[0].imag());
Fout[m2].imag(Fout[m].imag() - scratch[0].real());
Fout[m].real(Fout[m].real() - scratch[0].imag());
Fout[m].imag(Fout[m].imag() + scratch[0].real());
++Fout;
}
while(--m);
}
void CKissFFT::kf_bfly4(std::complex<float> *Fout, const size_t fstride, FFT_STATE &st, int m)
{
std::complex<float> *tw1,*tw2,*tw3;
std::complex<float> scratch[6];
int k = m;
const int m2 = 2 * m;
const int m3 = 3 * m;
tw3 = tw2 = tw1 = st.twiddles.data();
do
{
scratch[0] = Fout[m] * *tw1;
scratch[1] = Fout[m2] * *tw2;
scratch[2] = Fout[m3] * *tw3;
scratch[5] = *Fout - scratch[1];
*Fout += scratch[1];
scratch[3] = scratch[0] + scratch[2];
scratch[4] = scratch[0] - scratch[2];
Fout[m2] = *Fout - scratch[3];
tw1 += fstride;
tw2 += fstride*2;
tw3 += fstride*3;
*Fout += scratch[3];
if(st.inverse)
{
Fout[m].real(scratch[5].real() - scratch[4].imag());
Fout[m].imag(scratch[5].imag() + scratch[4].real());
Fout[m3].real(scratch[5].real() + scratch[4].imag());
Fout[m3].imag(scratch[5].imag() - scratch[4].real());
}
else
{
Fout[m].real(scratch[5].real() + scratch[4].imag());
Fout[m].imag(scratch[5].imag() - scratch[4].real());
Fout[m3].real(scratch[5].real() - scratch[4].imag());
Fout[m3].imag(scratch[5].imag() + scratch[4].real());
}
++Fout;
}
while(--k);
}
void CKissFFT::kf_bfly5(std::complex<float> * Fout, const size_t fstride, FFT_STATE &st, int m)
{
std::complex<float> scratch[13];
std::complex<float> *twiddles = st.twiddles.data();
auto ya = twiddles[fstride*m];
auto yb = twiddles[fstride*2*m];
auto Fout0 = Fout;
auto Fout1 = Fout0 + m;
auto Fout2 = Fout0 + 2 * m;
auto Fout3 = Fout0 + 3 * m;
auto Fout4 = Fout0 + 4 * m;
auto tw = st.twiddles.data();
for (int u=0; u<m; ++u)
{
scratch[0] = *Fout0;
scratch[1] = *Fout1 * tw[u*fstride];
scratch[2] = *Fout2 * tw[2*u*fstride];
scratch[3] = *Fout3 * tw[3*u*fstride];
scratch[4] = *Fout4 * tw[4*u*fstride];
scratch[7] = scratch[1] + scratch[4];
scratch[10] = scratch[1] - scratch[4];
scratch[8] = scratch[2] + scratch[3];
scratch[9] = scratch[2] - scratch[3];
*Fout0 += scratch[7] + scratch[8];
scratch[5] = scratch[0] + (scratch[7] * ya.real()) + (scratch[8] * yb.real());
scratch[6].real( (scratch[10].imag() * ya.imag()) + (scratch[9].imag() * yb.imag()));
scratch[6].imag(-(scratch[10].real() * ya.imag()) - (scratch[9].real() * yb.imag()));
*Fout1 = scratch[5] - scratch[6];
*Fout4 = scratch[5] + scratch[6];
scratch[11] = scratch[0] + (scratch[7] * yb.real()) + (scratch[8] * ya.real());
scratch[12].real(-(scratch[10].imag() * yb.imag()) + (scratch[9].imag() * ya.imag()));
scratch[12].imag( (scratch[10].real() * yb.imag()) - (scratch[9].real() * ya.imag()));
*Fout2 = scratch[11] + scratch[12];
*Fout3 = scratch[11] - scratch[12];
++Fout0;
++Fout1;
++Fout2;
++Fout3;
++Fout4;
}
}
/* perform the butterfly for one stage of a mixed radix FFT */
void CKissFFT::kf_bfly_generic(std::complex<float> *Fout, const size_t fstride, FFT_STATE &st, int m, int p)
{
auto twiddles = st.twiddles.data();
std::complex<float> t;
int Norig = st.nfft;
std::vector<std::complex<float>> scratch(p);
for (int u=0; u<m; ++u)
{
int k = u;
for (int q1=0 ; q1<p ; ++q1)
{
scratch[q1] = Fout[k];
k += m;
}
k = u;
for (int q1=0 ; q1<p ; ++q1)
{
int twidx = 0;
Fout[k] = scratch[0];
for (int q=1; q<p; ++q)
{
twidx += fstride * k;
if (twidx >= Norig) twidx-=Norig;
t = scratch[q] * twiddles[twidx];
Fout[k] += t;
}
k += m;
}
}
scratch.clear();
}
void CKissFFT::kf_work(std::complex<float> *Fout, const std::complex<float> *f, const size_t fstride, int in_stride, int *factors, FFT_STATE &st)
{
auto Fout_beg = Fout;
const int p = *factors++; /* the radix */
const int m = *factors++; /* stage's fft length/p */
const std::complex<float> *Fout_end = Fout + p*m;
if (m==1)
{
do
{
*Fout = *f;
f += fstride*in_stride;
}
while( ++Fout != Fout_end );
}
else
{
do
{
// recursive call:
// DFT of size m*p performed by doing
// p instances of smaller DFTs of size m,
// each one takes a decimated version of the input
kf_work( Fout, f, fstride*p, in_stride, factors, st);
f += fstride*in_stride;
}
while( (Fout += m) != Fout_end );
}
Fout=Fout_beg;
// recombine the p smaller DFTs
switch (p)
{
case 2:
kf_bfly2(Fout,fstride,st,m);
break;
case 3:
kf_bfly3(Fout,fstride,st,m);
break;
case 4:
kf_bfly4(Fout,fstride,st,m);
break;
case 5:
kf_bfly5(Fout,fstride,st,m);
break;
default:
kf_bfly_generic(Fout,fstride,st,m,p);
break;
}
}
/* facbuf is populated by p1,m1,p2,m2, ...
where
p[i] * m[i] = m[i-1]
m0 = n */
void CKissFFT::kf_factor(int n,int * facbuf)
{
int p=4;
double floor_sqrt;
floor_sqrt = floorf( sqrtf((double)n) );
/*factor out powers of 4, powers of 2, then any remaining primes */
do
{
while (n % p)
{
switch (p)
{
case 4:
p = 2;
break;
case 2:
p = 3;
break;
default:
p += 2;
break;
}
if (p > floor_sqrt)
p = n; /* no more factors, skip to end */
}
n /= p;
*facbuf++ = p;
*facbuf++ = n;
}
while (n > 1);
}
void CKissFFT::fft_alloc(FFT_STATE &state, const int nfft, bool inverse_fft)
{
state.twiddles.resize(nfft);
state.nfft = nfft;
state.inverse = inverse_fft;
for (int i=0; i<nfft; ++i)
{
const double pi=3.141592653589793238462643383279502884197169399375105820974944;
double phase = -2.0 * pi * i / nfft;
if (state.inverse)
phase *= -1.0;
state.twiddles[i] = std::polar(1.0f, float(phase));
}
kf_factor(nfft, state.factors);
}
void CKissFFT::fft_stride(FFT_STATE &st, const std::complex<float> *fin, std::complex<float> *fout, int in_stride)
{
if (fin == fout)
{
//NOTE: this is not really an in-place FFT algorithm.
//It just performs an out-of-place FFT into a temp buffer
std::vector<std::complex<float>> tmpbuf(st.nfft);
kf_work(tmpbuf.data(), fin, true, in_stride, st.factors, st);
memcpy(fout, tmpbuf.data(), sizeof(std::complex<float>)*st.nfft);
tmpbuf.clear();
}
else
{
kf_work(fout, fin, 1, in_stride, st.factors, st);
}
}
void CKissFFT::fft(FFT_STATE &cfg, const std::complex<float> *fin, std::complex<float> *fout)
{
fft_stride(cfg, fin, fout, 1);
}
int CKissFFT::fft_next_fast_size(int n)
{
while(1)
{
int m = n;
while ( (m % 2) == 0 ) m /= 2;
while ( (m % 3) == 0 ) m /= 3;
while ( (m % 5) == 0 ) m /= 5;
if (m <= 1)
break; /* n is completely factorable by twos, threes, and fives */
n++;
}
return n;
}
void CKissFFT::fftr_alloc(FFTR_STATE &st, int nfft, const bool inverse_fft)
{
nfft >>= 1;
fft_alloc(st.substate, nfft, inverse_fft);
st.tmpbuf.resize(nfft);
st.super_twiddles.resize(nfft);
for (int i=0; i<nfft/2; ++i)
{
double phase = -3.141592653589793238462643383279502884197169399375105820974944 * (double(i+1) / nfft + .5);
if (inverse_fft)
phase *= -1.0;
st.super_twiddles[i] = std::polar(1.0f, float(phase));
}
}
void CKissFFT::fftr(FFTR_STATE &st, const float *timedata, std::complex<float> *freqdata)
{
assert(st.substate.inverse == false);
auto ncfft = st.substate.nfft;
/*perform the parallel fft of two real signals packed in real,imag*/
fft( st.substate, (const std::complex<float>*)timedata, st.tmpbuf.data());
/* The real part of the DC element of the frequency spectrum in st->tmpbuf
* contains the sum of the even-numbered elements of the input time sequence
* The imag part is the sum of the odd-numbered elements
*
* The sum of tdc.r and tdc.i is the sum of the input time sequence.
* yielding DC of input time sequence
* The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1...
* yielding Nyquist bin of input time sequence
*/
auto tdc = st.tmpbuf[0];
freqdata[0].real(tdc.real() + tdc.imag());
freqdata[ncfft].real(tdc.real() - tdc.imag());
freqdata[ncfft].imag(0.f);
freqdata[0].imag(0.f);
for (int k=1; k <= ncfft/2; ++k)
{
auto fpk = st.tmpbuf[k];
auto fpnk = std::conj(st.tmpbuf[ncfft-k]);
auto f1k = fpk + fpnk;
auto f2k = fpk - fpnk;
auto tw = f2k * st.super_twiddles[k-1];
freqdata[k] = 0.5f * (f1k + tw);
freqdata[ncfft-k].real(0.5f * (f1k.real() - tw.real()));
freqdata[ncfft-k].imag(0.5f * (tw.imag() - f1k.imag()));
}
}
void CKissFFT::fftri(FFTR_STATE &st, const std::complex<float> *freqdata, float *timedata)
{
assert(st.substate.inverse == true);
auto ncfft = st.substate.nfft;
st.tmpbuf[0].real(freqdata[0].real() + freqdata[ncfft].real());
st.tmpbuf[0].imag(freqdata[0].real() - freqdata[ncfft].real());
for (int k=1; k <= ncfft/2; ++k)
{
auto fk = freqdata[k];
auto fnkc = std::conj(freqdata[ncfft - k]);
auto fek = fk + fnkc;
auto tmp = fk - fnkc;
auto fok = tmp * st.super_twiddles[k-1];
st.tmpbuf[k] = fek + fok;
st.tmpbuf[ncfft - k] = std::conj(fek - fok);
}
fft (st.substate, st.tmpbuf.data(), (std::complex<float> *)timedata);
}

35
M172DMR/codec2/kiss_fft.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef KISS_FFT_H
#define KISS_FFT_H
#include <complex>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include "defines.h"
/* for real ffts, we need an even size */
#define kiss_fftr_next_fast_size_real(n) (kiss_fft_next_fast_size( ((n)+1) >> 1) << 1 )
class CKissFFT
{
public:
void fft_alloc(FFT_STATE &state, const int nfft, const bool inverse_fft);
void fft(FFT_STATE &cfg, const std::complex<float> *fin, std::complex<float> *fout);
void fft_stride(FFT_STATE &cfg, const std::complex<float> *fin, std::complex<float> *fout, int fin_stride);
int fft_next_fast_size(int n);
void fftr_alloc(FFTR_STATE &state, int nfft, const bool inverse_fft);
void fftr(FFTR_STATE &cfg,const float *timedata,std::complex<float> *freqdata);
void fftri(FFTR_STATE &cfg,const std::complex<float> *freqdata,float *timedata);
private:
void kf_bfly2(std::complex<float> *Fout, const size_t fstride, FFT_STATE &st, int m);
void kf_bfly3(std::complex<float> *Fout, const size_t fstride, FFT_STATE &st, int m);
void kf_bfly4(std::complex<float> *Fout, const size_t fstride, FFT_STATE &st, int m);
void kf_bfly5(std::complex<float> *Fout, const size_t fstride, FFT_STATE &st, int m);
void kf_bfly_generic(std::complex<float> *Fout, const size_t fstride, FFT_STATE &st, int m, int p);
void kf_work(std::complex<float> *Fout, const std::complex<float> *f, const size_t fstride, int in_stride, int *factors, FFT_STATE &st);
void kf_factor(int n, int *facbuf);
};
#endif

311
M172DMR/codec2/lpc.cpp Normal file
View File

@ -0,0 +1,311 @@
/*---------------------------------------------------------------------------*\
FILE........: lpc.c
AUTHOR......: David Rowe
DATE CREATED: 30 Sep 1990 (!)
Linear Prediction functions written in C.
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2009-2012 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define LPC_MAX_N 512 /* maximum no. of samples in frame */
#define PI 3.141592654 /* mathematical constant */
#define ALPHA 1.0
#define BETA 0.94
#include <assert.h>
#include <math.h>
#include "defines.h"
#include "lpc.h"
/*---------------------------------------------------------------------------*\
pre_emp()
Pre-emphasise (high pass filter with zero close to 0 Hz) a frame of
speech samples. Helps reduce dynamic range of LPC spectrum, giving
greater weight and hense a better match to low energy formants.
Should be balanced by de-emphasis of the output speech.
\*---------------------------------------------------------------------------*/
void Clpc::pre_emp(
float Sn_pre[], /* output frame of speech samples */
float Sn[], /* input frame of speech samples */
float *mem, /* Sn[-1]single sample memory */
int Nsam /* number of speech samples to use */
)
{
int i;
for(i=0; i<Nsam; i++)
{
Sn_pre[i] = Sn[i] - ALPHA * mem[0];
mem[0] = Sn[i];
}
}
/*---------------------------------------------------------------------------*\
de_emp()
De-emphasis filter (low pass filter with a pole close to 0 Hz).
\*---------------------------------------------------------------------------*/
void Clpc::de_emp(
float Sn_de[], /* output frame of speech samples */
float Sn[], /* input frame of speech samples */
float *mem, /* Sn[-1]single sample memory */
int Nsam /* number of speech samples to use */
)
{
int i;
for(i=0; i<Nsam; i++)
{
Sn_de[i] = Sn[i] + BETA * mem[0];
mem[0] = Sn_de[i];
}
}
/*---------------------------------------------------------------------------*\
hanning_window()
Hanning windows a frame of speech samples.
\*---------------------------------------------------------------------------*/
void Clpc::hanning_window(
float Sn[], /* input frame of speech samples */
float Wn[], /* output frame of windowed samples */
int Nsam /* number of samples */
)
{
int i; /* loop variable */
for(i=0; i<Nsam; i++)
Wn[i] = Sn[i]*(0.5 - 0.5*cosf(2*PI*(float)i/(Nsam-1)));
}
/*---------------------------------------------------------------------------*\
autocorrelate()
Finds the first P autocorrelation values of an array of windowed speech
samples Sn[].
\*---------------------------------------------------------------------------*/
void Clpc::autocorrelate(
float Sn[], /* frame of Nsam windowed speech samples */
float Rn[], /* array of P+1 autocorrelation coefficients */
int Nsam, /* number of windowed samples to use */
int order /* order of LPC analysis */
)
{
int i,j; /* loop variables */
for(j=0; j<order+1; j++)
{
Rn[j] = 0.0;
for(i=0; i<Nsam-j; i++)
Rn[j] += Sn[i]*Sn[i+j];
}
}
/*---------------------------------------------------------------------------*\
levinson_durbin()
Given P+1 autocorrelation coefficients, finds P Linear Prediction Coeff.
(LPCs) where P is the order of the LPC all-pole model. The Levinson-Durbin
algorithm is used, and is described in:
J. Makhoul
"Linear prediction, a tutorial review"
Proceedings of the IEEE
Vol-63, No. 4, April 1975
\*---------------------------------------------------------------------------*/
void Clpc::levinson_durbin(
float R[], /* order+1 autocorrelation coeff */
float lpcs[], /* order+1 LPC's */
int order /* order of the LPC analysis */
)
{
float a[order+1][order+1];
float sum, e, k;
int i,j; /* loop variables */
e = R[0]; /* Equation 38a, Makhoul */
for(i=1; i<=order; i++)
{
sum = 0.0;
for(j=1; j<=i-1; j++)
sum += a[i-1][j]*R[i-j];
k = -1.0*(R[i] + sum)/e; /* Equation 38b, Makhoul */
if (fabsf(k) > 1.0)
k = 0.0;
a[i][i] = k;
for(j=1; j<=i-1; j++)
a[i][j] = a[i-1][j] + k*a[i-1][i-j]; /* Equation 38c, Makhoul */
e *= (1-k*k); /* Equation 38d, Makhoul */
}
for(i=1; i<=order; i++)
lpcs[i] = a[order][i];
lpcs[0] = 1.0;
}
/*---------------------------------------------------------------------------*\
inverse_filter()
Inverse Filter, A(z). Produces an array of residual samples from an array
of input samples and linear prediction coefficients.
The filter memory is stored in the first order samples of the input array.
\*---------------------------------------------------------------------------*/
void Clpc::inverse_filter(
float Sn[], /* Nsam input samples */
float a[], /* LPCs for this frame of samples */
int Nsam, /* number of samples */
float res[], /* Nsam residual samples */
int order /* order of LPC */
)
{
int i,j; /* loop variables */
for(i=0; i<Nsam; i++)
{
res[i] = 0.0;
for(j=0; j<=order; j++)
res[i] += Sn[i-j]*a[j];
}
}
/*---------------------------------------------------------------------------*\
synthesis_filter()
C version of the Speech Synthesis Filter, 1/A(z). Given an array of
residual or excitation samples, and the the LP filter coefficients, this
function will produce an array of speech samples. This filter structure is
IIR.
The synthesis filter has memory as well, this is treated in the same way
as the memory for the inverse filter (see inverse_filter() notes above).
The difference is that the memory for the synthesis filter is stored in
the output array, wheras the memory of the inverse filter is stored in the
input array.
Note: the calling function must update the filter memory.
\*---------------------------------------------------------------------------*/
void Clpc::synthesis_filter(
float res[], /* Nsam input residual (excitation) samples */
float a[], /* LPCs for this frame of speech samples */
int Nsam, /* number of speech samples */
int order, /* LPC order */
float Sn_[] /* Nsam output synthesised speech samples */
)
{
int i,j; /* loop variables */
/* Filter Nsam samples */
for(i=0; i<Nsam; i++)
{
Sn_[i] = res[i]*a[0];
for(j=1; j<=order; j++)
Sn_[i] -= Sn_[i-j]*a[j];
}
}
/*---------------------------------------------------------------------------*\
find_aks()
This function takes a frame of samples, and determines the linear
prediction coefficients for that frame of samples.
\*---------------------------------------------------------------------------*/
void Clpc::find_aks(
float Sn[], /* Nsam samples with order sample memory */
float a[], /* order+1 LPCs with first coeff 1.0 */
int Nsam, /* number of input speech samples */
int order, /* order of the LPC analysis */
float *E /* residual energy */
)
{
float Wn[LPC_MAX_N]; /* windowed frame of Nsam speech samples */
float R[order+1]; /* order+1 autocorrelation values of Sn[] */
int i;
assert(Nsam < LPC_MAX_N);
hanning_window(Sn,Wn,Nsam);
autocorrelate(Wn,R,Nsam,order);
levinson_durbin(R,a,order);
*E = 0.0;
for(i=0; i<=order; i++)
*E += a[i]*R[i];
if (*E < 0.0)
*E = 1E-12;
}
/*---------------------------------------------------------------------------*\
weight()
Weights a vector of LPCs.
\*---------------------------------------------------------------------------*/
void Clpc::weight(
float ak[], /* vector of order+1 LPCs */
float gamma, /* weighting factor */
int order, /* num LPCs (excluding leading 1.0) */
float akw[] /* weighted vector of order+1 LPCs */
)
{
int i;
for(i=1; i<=order; i++)
akw[i] = ak[i]*powf(gamma,(float)i);
}

47
M172DMR/codec2/lpc.h Normal file
View File

@ -0,0 +1,47 @@
/*---------------------------------------------------------------------------*\
FILE........: lpc.h
AUTHOR......: David Rowe
DATE CREATED: 24/8/09
Linear Prediction functions written in C.
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2009-2012 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LPC__
#define __LPC__
#define LPC_MAX_ORDER 20
class Clpc {
public:
void autocorrelate(float Sn[], float Rn[], int Nsam, int order);
void levinson_durbin(float R[], float lpcs[], int order);
private:
void pre_emp(float Sn_pre[], float Sn[], float *mem, int Nsam);
void de_emp(float Sn_se[], float Sn[], float *mem, int Nsam);
void hanning_window(float Sn[], float Wn[], int Nsam);
void inverse_filter(float Sn[], float a[], int Nsam, float res[], int order);
void synthesis_filter(float res[], float a[], int Nsam, int order, float Sn_[]);
void find_aks(float Sn[], float a[], int Nsam, int order, float *E);
void weight(float ak[], float gamma, int order, float akw[]);
};
#endif

520
M172DMR/codec2/nlp.cpp Normal file
View File

@ -0,0 +1,520 @@
/*---------------------------------------------------------------------------*\
FILE........: nlp.c
AUTHOR......: David Rowe
DATE CREATED: 23/3/93
Non Linear Pitch (NLP) estimation functions.
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2009 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include "defines.h"
#include "nlp.h"
#include "kiss_fft.h"
extern CKissFFT kiss;
/*---------------------------------------------------------------------------*\
GLOBALS
\*---------------------------------------------------------------------------*/
/* 48 tap 600Hz low pass FIR filter coefficients */
static const float nlp_fir[] =
{
-1.0818124e-03,
-1.1008344e-03,
-9.2768838e-04,
-4.2289438e-04,
5.5034190e-04,
2.0029849e-03,
3.7058509e-03,
5.1449415e-03,
5.5924666e-03,
4.3036754e-03,
8.0284511e-04,
-4.8204610e-03,
-1.1705810e-02,
-1.8199275e-02,
-2.2065282e-02,
-2.0920610e-02,
-1.2808831e-02,
3.2204775e-03,
2.6683811e-02,
5.5520624e-02,
8.6305944e-02,
1.1480192e-01,
1.3674206e-01,
1.4867556e-01,
1.4867556e-01,
1.3674206e-01,
1.1480192e-01,
8.6305944e-02,
5.5520624e-02,
2.6683811e-02,
3.2204775e-03,
-1.2808831e-02,
-2.0920610e-02,
-2.2065282e-02,
-1.8199275e-02,
-1.1705810e-02,
-4.8204610e-03,
8.0284511e-04,
4.3036754e-03,
5.5924666e-03,
5.1449415e-03,
3.7058509e-03,
2.0029849e-03,
5.5034190e-04,
-4.2289438e-04,
-9.2768838e-04,
-1.1008344e-03,
-1.0818124e-03
};
static const float fdmdv_os_filter[]= {
-0.0008215855034550382,
-0.0007833023901802921,
0.001075563790768233,
0.001199092367787555,
-0.001765309502928316,
-0.002055372115328064,
0.002986877604154257,
0.003462567920638414,
-0.004856570111126334,
-0.005563143845031497,
0.007533613299748122,
0.008563932468880897,
-0.01126857129039911,
-0.01280782411693687,
0.01651443896361847,
0.01894875110322284,
-0.02421604439474981,
-0.02845107338464062,
0.03672973563400258,
0.04542046150312214,
-0.06189165826716491,
-0.08721876380763803,
0.1496157094199961,
0.4497962274137046,
0.4497962274137046,
0.1496157094199961,
-0.08721876380763803,
-0.0618916582671649,
0.04542046150312216,
0.03672973563400257,
-0.02845107338464062,
-0.02421604439474984,
0.01894875110322284,
0.01651443896361848,
-0.01280782411693687,
-0.0112685712903991,
0.008563932468880899,
0.007533613299748123,
-0.005563143845031501,
-0.004856570111126346,
0.003462567920638419,
0.002986877604154259,
-0.002055372115328063,
-0.001765309502928318,
0.001199092367787557,
0.001075563790768233,
-0.0007833023901802925,
-0.0008215855034550383
};
/*---------------------------------------------------------------------------*\
nlp_create()
Initialisation function for NLP pitch estimator.
\*---------------------------------------------------------------------------*/
void Cnlp::nlp_create(C2CONST *c2const)
{
int i;
int m = c2const->m_pitch;
int Fs = c2const->Fs;
assert((Fs == 8000) || (Fs == 16000));
snlp.Fs = Fs;
snlp.m = m;
/* if running at 16kHz allocate storage for decimating filter memory */
if (Fs == 16000)
{
snlp.Sn16k.resize(FDMDV_OS_TAPS_16K + c2const->n_samp);
for(i=0; i<FDMDV_OS_TAPS_16K; i++)
{
snlp.Sn16k[i] = 0.0;
}
/* most processing occurs at 8 kHz sample rate so halve m */
m /= 2;
}
assert(m <= PMAX_M);
for(i=0; i<m/DEC; i++)
{
snlp.w[i] = 0.5 - 0.5*cosf(2*PI*i/(m/DEC-1));
}
for(i=0; i<PMAX_M; i++)
snlp.sq[i] = 0.0;
snlp.mem_x = 0.0;
snlp.mem_y = 0.0;
for(i=0; i<NLP_NTAP; i++)
snlp.mem_fir[i] = 0.0;
kiss.fft_alloc(snlp.fft_cfg, PE_FFT_SIZE, false);
}
/*---------------------------------------------------------------------------*\
nlp_destroy()
Shut down function for NLP pitch estimator.
\*---------------------------------------------------------------------------*/
void Cnlp::nlp_destroy()
{
snlp.fft_cfg.twiddles.clear();
}
/*---------------------------------------------------------------------------*\
nlp()
Determines the pitch in samples using the Non Linear Pitch (NLP)
algorithm [1]. Returns the fundamental in Hz. Note that the actual
pitch estimate is for the centre of the M sample Sn[] vector, not
the current N sample input vector. This is (I think) a delay of 2.5
frames with N=80 samples. You should align further analysis using
this pitch estimate to be centred on the middle of Sn[].
Two post processors have been tried, the MBE version (as discussed
in [1]), and a post processor that checks sub-multiples. Both
suffer occasional gross pitch errors (i.e. neither are perfect). In
the presence of background noise the sub-multiple algorithm tends
towards low F0 which leads to better sounding background noise than
the MBE post processor.
A good way to test and develop the NLP pitch estimator is using the
tnlp (codec2/unittest) and the codec2/octave/plnlp.m Octave script.
A pitch tracker searching a few frames forward and backward in time
would be a useful addition.
References:
[1] http://rowetel.com/downloads/1997_rowe_phd_thesis.pdf Chapter 4
\*---------------------------------------------------------------------------*/
float Cnlp::nlp(
float Sn[], /* input speech vector */
int n, /* frames shift (no. new samples in Sn[]) */
float *pitch, /* estimated pitch period in samples at current Fs */
// std::complex<float> Sw[], /* Freq domain version of Sn[] */
// float W[], /* Freq domain window */
float *prev_f0 /* previous pitch f0 in Hz, memory for pitch tracking */
)
{
float notch; /* current notch filter output */
std::complex<float> Fw[PE_FFT_SIZE]; /* DFT of squared signal (input/output) */
float gmax;
int gmax_bin;
int m, i, j;
float best_f0;
m = snlp.m;
/* Square, notch filter at DC, and LP filter vector */
/* If running at 16 kHz decimate to 8 kHz, as NLP ws designed for
Fs = 8kHz. The decimating filter introduces about 3ms of delay,
that shouldn't be a problem as pitch changes slowly. */
if (snlp.Fs == 8000)
{
/* Square latest input samples */
for(i=m-n; i<m; i++)
{
snlp.sq[i] = Sn[i]*Sn[i];
}
}
else
{
assert(snlp.Fs == 16000);
/* re-sample at 8 KHz */
for(i=0; i<n; i++)
{
snlp.Sn16k[FDMDV_OS_TAPS_16K+i] = Sn[m-n+i];
}
m /= 2;
n /= 2;
float Sn8k[n];
fdmdv_16_to_8(Sn8k, &snlp.Sn16k[FDMDV_OS_TAPS_16K], n);
/* Square latest input samples */
for(i=m-n, j=0; i<m; i++, j++)
{
snlp.sq[i] = Sn8k[j]*Sn8k[j];
}
assert(j <= n);
}
for(i=m-n; i<m; i++) /* notch filter at DC */
{
notch = snlp.sq[i] - snlp.mem_x;
notch += COEFF*snlp.mem_y;
snlp.mem_x = snlp.sq[i];
snlp.mem_y = notch;
snlp.sq[i] = notch + 1.0; /* With 0 input vectors to codec,
kiss_fft() would take a long
time to execute when running in
real time. Problem was traced
to kiss_fft function call in
this function. Adding this small
constant fixed problem. Not
exactly sure why. */
}
for(i=m-n; i<m; i++) /* FIR filter vector */
{
for(j=0; j<NLP_NTAP-1; j++)
snlp.mem_fir[j] = snlp.mem_fir[j+1];
snlp.mem_fir[NLP_NTAP-1] = snlp.sq[i];
snlp.sq[i] = 0.0;
for(j=0; j<NLP_NTAP; j++)
snlp.sq[i] += snlp.mem_fir[j]*nlp_fir[j];
}
/* Decimate and DFT */
for(i=0; i<PE_FFT_SIZE; i++)
{
Fw[i].real(0);
Fw[i].imag(0);
}
for(i=0; i<m/DEC; i++)
{
Fw[i].real(snlp.sq[i*DEC]*snlp.w[i]);
}
// FIXME: check if this can be converted to a real fft
// since all imag inputs are 0
codec2_fft_inplace(snlp.fft_cfg, Fw);
for(i=0; i<PE_FFT_SIZE; i++)
Fw[i].real(Fw[i].real() * Fw[i].real() + Fw[i].imag() * Fw[i].imag());
/* todo: express everything in f0, as pitch in samples is dep on Fs */
int pmin = floor(SAMPLE_RATE*P_MIN_S);
int pmax = floor(SAMPLE_RATE*P_MAX_S);
/* find global peak */
gmax = 0.0;
gmax_bin = PE_FFT_SIZE*DEC/pmax;
for(i=PE_FFT_SIZE*DEC/pmax; i<=PE_FFT_SIZE*DEC/pmin; i++)
{
if (Fw[i].real() > gmax)
{
gmax = Fw[i].real();
gmax_bin = i;
}
}
best_f0 = post_process_sub_multiples(Fw, pmax, gmax, gmax_bin, prev_f0);
/* Shift samples in buffer to make room for new samples */
for(i=0; i<m-n; i++)
snlp.sq[i] = snlp.sq[i+n];
/* return pitch period in samples and F0 estimate */
*pitch = (float)snlp.Fs/best_f0;
*prev_f0 = best_f0;
return(best_f0);
}
/*---------------------------------------------------------------------------*\
post_process_sub_multiples()
Given the global maximma of Fw[] we search integer submultiples for
local maxima. If local maxima exist and they are above an
experimentally derived threshold (OK a magic number I pulled out of
the air) we choose the submultiple as the F0 estimate.
The rational for this is that the lowest frequency peak of Fw[]
should be F0, as Fw[] can be considered the autocorrelation function
of Sw[] (the speech spectrum). However sometimes due to phase
effects the lowest frequency maxima may not be the global maxima.
This works OK in practice and favours low F0 values in the presence
of background noise which means the sinusoidal codec does an OK job
of synthesising the background noise. High F0 in background noise
tends to sound more periodic introducing annoying artifacts.
\*---------------------------------------------------------------------------*/
float Cnlp::post_process_sub_multiples(std::complex<float> Fw[], int pmax, float gmax, int gmax_bin, float *prev_f0)
{
int min_bin, cmax_bin;
int mult;
float thresh, best_f0;
int b, bmin, bmax, lmax_bin;
float lmax;
int prev_f0_bin;
/* post process estimate by searching submultiples */
mult = 2;
min_bin = PE_FFT_SIZE*DEC/pmax;
cmax_bin = gmax_bin;
prev_f0_bin = *prev_f0*(PE_FFT_SIZE*DEC)/SAMPLE_RATE;
while(gmax_bin/mult >= min_bin)
{
b = gmax_bin/mult; /* determine search interval */
bmin = 0.8*b;
bmax = 1.2*b;
if (bmin < min_bin)
bmin = min_bin;
/* lower threshold to favour previous frames pitch estimate,
this is a form of pitch tracking */
if ((prev_f0_bin > bmin) && (prev_f0_bin < bmax))
thresh = CNLP*0.5*gmax;
else
thresh = CNLP*gmax;
lmax = 0;
lmax_bin = bmin;
for (b=bmin; b<=bmax; b++) /* look for maximum in interval */
if (Fw[b].real() > lmax)
{
lmax = Fw[b].real();
lmax_bin = b;
}
if (lmax > thresh)
if ((lmax > Fw[lmax_bin-1].real()) && (lmax > Fw[lmax_bin+1].real()))
{
cmax_bin = lmax_bin;
}
mult++;
}
best_f0 = (float)cmax_bin*SAMPLE_RATE/(PE_FFT_SIZE*DEC);
return best_f0;
}
/*---------------------------------------------------------------------------*\
FUNCTION....: fdmdv_16_to_8()
AUTHOR......: David Rowe
DATE CREATED: 9 May 2012
Changes the sample rate of a signal from 16 to 8 kHz.
n is the number of samples at the 8 kHz rate, there are FDMDV_OS*n
samples at the 48 kHz rate. As above however a memory of
FDMDV_OS_TAPS samples is reqd for in16k[] (see t16_8.c unit test as example).
Low pass filter the 16 kHz signal at 4 kHz using the same filter as
the upsampler, then just output every FDMDV_OS-th filtered sample.
Note: this function copied from fdmdv.c, included in nlp.c as a convenience
to avoid linking with another source file.
\*---------------------------------------------------------------------------*/
void Cnlp::fdmdv_16_to_8(float out8k[], float in16k[], int n)
{
float acc;
int i,j,k;
for(i=0, k=0; k<n; i+=FDMDV_OS, k++)
{
acc = 0.0;
for(j=0; j<FDMDV_OS_TAPS_16K; j++)
acc += fdmdv_os_filter[j]*in16k[i-j];
out8k[k] = acc;
}
/* update filter memory */
for(i=-FDMDV_OS_TAPS_16K; i<0; i++)
in16k[i] = in16k[i + n*FDMDV_OS];
}
// there is a little overhead for inplace kiss_fft but this is
// on the powerful platforms like the Raspberry or even x86 PC based ones
// not noticeable
// the reduced usage of RAM and increased performance on STM32 platforms
// should be worth it.
void Cnlp::codec2_fft_inplace(FFT_STATE &cfg, std::complex<float> *inout)
{
std::complex<float> in[512];
// decide whether to use the local stack based buffer for in
// or to allow kiss_fft to allocate RAM
// second part is just to play safe since first method
// is much faster and uses less RAM
if (cfg.nfft <= 512)
{
memcpy(in, inout, cfg.nfft*sizeof(std::complex<float>));
kiss.fft(cfg, in, inout);
}
else
{
kiss.fft(cfg, inout, inout);
}
}

87
M172DMR/codec2/nlp.h Normal file
View File

@ -0,0 +1,87 @@
/*---------------------------------------------------------------------------*\
FILE........: nlp.c
AUTHOR......: David Rowe
DATE CREATED: 23/3/93
Non Linear Pitch (NLP) estimation functions.
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2009 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __NLP__
#define __NLP__
#include <complex>
#include <vector>
#include "defines.h"
/*---------------------------------------------------------------------------*\
DEFINES
\*---------------------------------------------------------------------------*/
#define PMAX_M 320 /* maximum NLP analysis window size */
#define COEFF 0.95 /* notch filter parameter */
#define PE_FFT_SIZE 512 /* DFT size for pitch estimation */
#define DEC 5 /* decimation factor */
#define SAMPLE_RATE 8000
#define PI 3.141592654 /* mathematical constant */
//#define T 0.1 /* threshold for local minima candidate */
#define F0_MAX 500
#define CNLP 0.3 /* post processor constant */
#define NLP_NTAP 48 /* Decimation LPF order */
/* 8 to 16 kHz sample rate conversion */
#define FDMDV_OS 2 /* oversampling rate */
#define FDMDV_OS_TAPS_16K 48 /* number of OS filter taps at 16kHz */
#define FDMDV_OS_TAPS_8K (FDMDV_OS_TAPS_16K/FDMDV_OS) /* number of OS filter taps at 8kHz */
using NLP = struct nlp_tag
{
int Fs; /* sample rate in Hz */
int m;
float w[PMAX_M/DEC]; /* DFT window */
float sq[PMAX_M]; /* squared speech samples */
float mem_x,mem_y; /* memory for notch filter */
float mem_fir[NLP_NTAP]; /* decimation FIR filter memory */
FFT_STATE fft_cfg; /* kiss FFT config */
std::vector<float> Sn16k; /* Fs=16kHz input speech vector */
};
class Cnlp {
public:
void nlp_create(C2CONST *c2const);
void nlp_destroy();
float nlp(float Sn[], int n, float *pitch_samples, float *prev_f0);
void codec2_fft_inplace(FFT_STATE &cfg, std::complex<float> *inout);
private:
float post_process_sub_multiples(std::complex<float> Fw[], int pmax, float gmax, int gmax_bin, float *prev_f0);
void fdmdv_16_to_8(float out8k[], float in16k[], int n);
NLP snlp;
};
#endif

139
M172DMR/codec2/pack.cpp Normal file
View File

@ -0,0 +1,139 @@
/*
Copyright (C) 2010 Perens LLC <bruce@perens.com>
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "defines.h"
#include "quantise.h"
#include <stdio.h>
/* Compile-time constants */
/* Size of unsigned char in bits. Assumes 8 bits-per-char. */
static const unsigned int WordSize = 8;
/* Mask to pick the bit component out of bitIndex. */
static const unsigned int IndexMask = 0x7;
/* Used to pick the word component out of bitIndex. */
static const unsigned int ShiftRight = 3;
/** Pack a bit field into a bit string, encoding the field in Gray code.
*
* The output is an array of unsigned char data. The fields are efficiently
* packed into the bit string. The Gray coding is a naive attempt to reduce
* the effect of single-bit errors, we expect to do a better job as the
* codec develops.
*
* This code would be simpler if it just set one bit at a time in the string,
* but would hit the same cache line more often. I'm not sure the complexity
* gains us anything here.
*
* Although field is currently of int type rather than unsigned for
* compatibility with the rest of the code, indices are always expected to
* be >= 0.
*/
void CQuantize::pack(
unsigned char *bitArray, /* The output bit string. */
unsigned int *bitIndex, /* Index into the string in BITS, not bytes.*/
int field, /* The bit field to be packed. */
unsigned int fieldWidth /* Width of the field in BITS, not bytes. */
)
{
pack_natural_or_gray(bitArray, bitIndex, field, fieldWidth, 1);
}
void CQuantize::pack_natural_or_gray(
unsigned char *bitArray, /* The output bit string. */
unsigned int *bitIndex, /* Index into the string in BITS, not bytes.*/
int field, /* The bit field to be packed. */
unsigned int fieldWidth, /* Width of the field in BITS, not bytes. */
unsigned int gray /* non-zero for gray coding */
)
{
if (gray)
{
/* Convert the field to Gray code */
field = (field >> 1) ^ field;
}
do
{
unsigned int bI = *bitIndex;
unsigned int bitsLeft = WordSize - (bI & IndexMask);
unsigned int sliceWidth = bitsLeft < fieldWidth ? bitsLeft : fieldWidth;
unsigned int wordIndex = bI >> ShiftRight;
bitArray[wordIndex] |= ((unsigned char)((field >> (fieldWidth - sliceWidth)) << (bitsLeft - sliceWidth)));
*bitIndex = bI + sliceWidth;
fieldWidth -= sliceWidth;
}
while ( fieldWidth != 0 );
}
/** Unpack a field from a bit string, converting from Gray code to binary.
*
*/
int CQuantize::unpack(
const unsigned char *bitArray, /* The input bit string. */
unsigned int *bitIndex, /* Index into the string in BITS, not bytes.*/
unsigned int fieldWidth/* Width of the field in BITS, not bytes. */
)
{
return unpack_natural_or_gray(bitArray, bitIndex, fieldWidth, 1);
}
/** Unpack a field from a bit string, to binary, optionally using
* natural or Gray code.
*
*/
int CQuantize::unpack_natural_or_gray(
const unsigned char *bitArray, /* The input bit string. */
unsigned int *bitIndex, /* Index into the string in BITS, not bytes.*/
unsigned int fieldWidth,/* Width of the field in BITS, not bytes. */
unsigned int gray /* non-zero for Gray coding */
)
{
unsigned int field = 0;
unsigned int t;
do
{
unsigned int bI = *bitIndex;
unsigned int bitsLeft = WordSize - (bI & IndexMask);
unsigned int sliceWidth = bitsLeft < fieldWidth ? bitsLeft : fieldWidth;
field |= (((bitArray[bI >> ShiftRight] >> (bitsLeft - sliceWidth)) & ((1 << sliceWidth) - 1)) << (fieldWidth - sliceWidth));
*bitIndex = bI + sliceWidth;
fieldWidth -= sliceWidth;
}
while ( fieldWidth != 0 );
if (gray)
{
/* Convert from Gray code to binary. Works for maximum 8-bit fields. */
t = field ^ (field >> 8);
t ^= (t >> 4);
t ^= (t >> 2);
t ^= (t >> 1);
}
else
{
t = field;
}
return t;
}

247
M172DMR/codec2/qbase.cpp Normal file
View File

@ -0,0 +1,247 @@
#include <assert.h>
#include <math.h>
#include "qbase.h"
/*---------------------------------------------------------------------------*\
quantise
Quantises vec by choosing the nearest vector in codebook cb, and
returns the vector index. The squared error of the quantised vector
is added to se.
\*---------------------------------------------------------------------------*/
long CQbase::quantise(const float *cb, float vec[], float w[], int k, int m, float *se)
/* float cb[][K]; current VQ codebook */
/* float vec[]; vector to quantise */
/* float w[]; weighting vector */
/* int k; dimension of vectors */
/* int m; size of codebook */
/* float *se; accumulated squared error */
{
float e; /* current error */
long besti; /* best index so far */
float beste; /* best error so far */
long j;
int i;
float diff;
besti = 0;
beste = 1E32;
for(j=0; j<m; j++)
{
e = 0.0;
for(i=0; i<k; i++)
{
diff = cb[j*k+i]-vec[i];
e += (diff*w[i] * diff*w[i]);
}
if (e < beste)
{
beste = e;
besti = j;
}
}
*se += beste;
return(besti);
}
/*---------------------------------------------------------------------------*\
FUNCTION....: encode_WoE()
AUTHOR......: Jean-Marc Valin & David Rowe
DATE CREATED: 11 May 2012
Joint Wo and LPC energy vector quantiser developed my Jean-Marc
Valin. Returns index, and updated states xq[].
\*---------------------------------------------------------------------------*/
int CQbase::encode_WoE(MODEL *model, float e, float xq[])
{
int i, n1;
float x[2];
float err[2];
float w[2];
const float *codebook1 = ge_cb[0].cb;
int nb_entries = ge_cb[0].m;
int ndim = ge_cb[0].k;
assert((1<<WO_E_BITS) == nb_entries);
if (e < 0.0) e = 0; /* occasional small negative energies due LPC round off I guess */
x[0] = log10f((model->Wo/PI)*4000.0/50.0)/log10f(2);
x[1] = 10.0*log10f(1e-4 + e);
compute_weights2(x, xq, w);
for (i=0; i<ndim; i++)
err[i] = x[i]-ge_coeff[i]*xq[i];
n1 = find_nearest_weighted(codebook1, nb_entries, err, w, ndim);
for (i=0; i<ndim; i++)
{
xq[i] = ge_coeff[i]*xq[i] + codebook1[ndim*n1+i];
err[i] -= codebook1[ndim*n1+i];
}
//printf("enc: %f %f (%f)(%f) \n", xq[0], xq[1], e, 10.0*log10(1e-4 + e));
return n1;
}
/*---------------------------------------------------------------------------*\
FUNCTION....: decode_WoE()
AUTHOR......: Jean-Marc Valin & David Rowe
DATE CREATED: 11 May 2012
Joint Wo and LPC energy vector quantiser developed my Jean-Marc
Valin. Given index and states xq[], returns Wo & E, and updates
states xq[].
\*---------------------------------------------------------------------------*/
void CQbase::decode_WoE(C2CONST *c2const, MODEL *model, float *e, float xq[], int n1)
{
int i;
const float *codebook1 = ge_cb[0].cb;
int ndim = ge_cb[0].k;
float Wo_min = c2const->Wo_min;
float Wo_max = c2const->Wo_max;
for (i=0; i<ndim; i++)
{
xq[i] = ge_coeff[i]*xq[i] + codebook1[ndim*n1+i];
}
//printf("dec: %f %f\n", xq[0], xq[1]);
model->Wo = powf(2.0, xq[0])*(PI*50.0)/4000.0;
/* bit errors can make us go out of range leading to all sorts of
probs like seg faults */
if (model->Wo > Wo_max) model->Wo = Wo_max;
if (model->Wo < Wo_min) model->Wo = Wo_min;
model->L = PI/model->Wo; /* if we quantise Wo re-compute L */
*e = exp10f(xq[1]/10.0);
}
void CQbase::compute_weights2(const float *x, const float *xp, float *w)
{
w[0] = 30;
w[1] = 1;
if (x[1]<0)
{
w[0] *= .6;
w[1] *= .3;
}
if (x[1]<-10)
{
w[0] *= .3;
w[1] *= .3;
}
/* Higher weight if pitch is stable */
if (fabsf(x[0]-xp[0])<.2)
{
w[0] *= 2;
w[1] *= 1.5;
}
else if (fabsf(x[0]-xp[0])>.5) /* Lower if not stable */
{
w[0] *= .5;
}
/* Lower weight for low energy */
if (x[1] < xp[1]-10)
{
w[1] *= .5;
}
if (x[1] < xp[1]-20)
{
w[1] *= .5;
}
//w[0] = 30;
//w[1] = 1;
/* Square the weights because it's applied on the squared error */
w[0] *= w[0];
w[1] *= w[1];
}
int CQbase::find_nearest_weighted(const float *codebook, int nb_entries, float *x, const float *w, int ndim)
{
int i, j;
float min_dist = 1e15;
int nearest = 0;
for (i=0; i<nb_entries; i++)
{
float dist=0;
for (j=0; j<ndim; j++)
dist += w[j]*(x[j]-codebook[i*ndim+j])*(x[j]-codebook[i*ndim+j]);
if (dist<min_dist)
{
min_dist = dist;
nearest = i;
}
}
return nearest;
}
/*---------------------------------------------------------------------------*\
FUNCTION....: encode_log_Wo()
AUTHOR......: David Rowe
DATE CREATED: 22/8/2010
Encodes Wo in the log domain using a WO_LEVELS quantiser.
\*---------------------------------------------------------------------------*/
int CQbase::encode_log_Wo(C2CONST *c2const, float Wo, int bits)
{
int index, Wo_levels = 1<<bits;
float Wo_min = c2const->Wo_min;
float Wo_max = c2const->Wo_max;
float norm;
norm = (log10f(Wo) - log10f(Wo_min))/(log10f(Wo_max) - log10f(Wo_min));
index = floorf(Wo_levels * norm + 0.5);
if (index < 0 ) index = 0;
if (index > (Wo_levels-1)) index = Wo_levels-1;
return index;
}
/*---------------------------------------------------------------------------*\
FUNCTION....: decode_log_Wo()
AUTHOR......: David Rowe
DATE CREATED: 22/8/2010
Decodes Wo using a WO_LEVELS quantiser in the log domain.
\*---------------------------------------------------------------------------*/
float CQbase::decode_log_Wo(C2CONST *c2const, int index, int bits)
{
float Wo_min = c2const->Wo_min;
float Wo_max = c2const->Wo_max;
float step;
float Wo;
int Wo_levels = 1<<bits;
step = (log10f(Wo_max) - log10f(Wo_min))/Wo_levels;
Wo = log10f(Wo_min) + step*(index);
return exp10f(Wo);
}

39
M172DMR/codec2/qbase.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef QBASE_H
#define QBASE_H
#include "defines.h"
#define WO_BITS 7
#define WO_LEVELS (1<<WO_BITS)
#define WO_DT_BITS 3
#define E_BITS 5
#define E_LEVELS (1<<E_BITS)
#define E_MIN_DB -10.0
#define E_MAX_DB 40.0
#define LSP_SCALAR_INDEXES 10
#define LSPD_SCALAR_INDEXES 10
#define LSP_PRED_VQ_INDEXES 3
#define WO_E_BITS 8
#define LPCPF_GAMMA 0.5
#define LPCPF_BETA 0.2
class CQbase {
public:
int encode_WoE(MODEL *model, float e, float xq[]);
void decode_WoE(C2CONST *c2const, MODEL *model, float *e, float xq[], int n1);
int encode_log_Wo(C2CONST *c2const, float Wo, int bits);
float decode_log_Wo(C2CONST *c2const, int index, int bits);
protected:
long quantise(const float * cb, float vec[], float w[], int k, int m, float *se);
void compute_weights2(const float *x, const float *xp, float *w);
int find_nearest_weighted(const float *codebook, int nb_entries, float *x, const float *w, int ndim);
const float ge_coeff[2] = { 0.8, 0.9 };
};
#endif

897
M172DMR/codec2/quantise.cpp Normal file
View File

@ -0,0 +1,897 @@
/*---------------------------------------------------------------------------*\
FILE........: quantise.c
AUTHOR......: David Rowe
DATE CREATED: 31/5/92
Quantisation functions for the sinusoidal coder.
\*---------------------------------------------------------------------------*/
/*
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "defines.h"
#include "quantise.h"
#include "lpc.h"
#include "kiss_fft.h"
extern CKissFFT kiss;
#define LSP_DELTA1 0.01 /* grid spacing for LSP root searches */
/*---------------------------------------------------------------------------*\
FUNCTIONS
\*---------------------------------------------------------------------------*/
int CQuantize::lsp_bits(int i)
{
return lsp_cb[i].log2m;
}
int CQuantize::lspd_bits(int i)
{
return lsp_cbd[i].log2m;
}
/*---------------------------------------------------------------------------*\
encode_lspds_scalar()
Scalar/VQ LSP difference quantiser.
\*---------------------------------------------------------------------------*/
void CQuantize::encode_lspds_scalar(int indexes[], float lsp[], int order)
{
int i,k,m;
float lsp_hz[order];
float lsp__hz[order];
float dlsp[order];
float dlsp_[order];
float wt[order];
const float *cb;
float se;
for(i=0; i<order; i++)
{
wt[i] = 1.0;
}
/* convert from radians to Hz so we can use human readable
frequencies */
for(i=0; i<order; i++)
lsp_hz[i] = (4000.0/PI)*lsp[i];
wt[0] = 1.0;
for(i=0; i<order; i++)
{
/* find difference from previous qunatised lsp */
if (i)
dlsp[i] = lsp_hz[i] - lsp__hz[i-1];
else
dlsp[0] = lsp_hz[0];
k = lsp_cbd[i].k;
m = lsp_cbd[i].m;
cb = lsp_cbd[i].cb;
indexes[i] = quantise(cb, &dlsp[i], wt, k, m, &se);
dlsp_[i] = cb[indexes[i]*k];
if (i)
lsp__hz[i] = lsp__hz[i-1] + dlsp_[i];
else
lsp__hz[0] = dlsp_[0];
}
}
void CQuantize::decode_lspds_scalar( float lsp_[], int indexes[], int order)
{
int i,k;
float lsp__hz[order];
float dlsp_[order];
const float *cb;
for(i=0; i<order; i++)
{
k = lsp_cbd[i].k;
cb = lsp_cbd[i].cb;
dlsp_[i] = cb[indexes[i]*k];
if (i)
lsp__hz[i] = lsp__hz[i-1] + dlsp_[i];
else
lsp__hz[0] = dlsp_[0];
lsp_[i] = (PI/4000.0)*lsp__hz[i];
}
}
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX_ENTRIES 16384
void CQuantize::compute_weights(const float *x, float *w, int ndim)
{
int i;
w[0] = MIN(x[0], x[1]-x[0]);
for (i=1; i<ndim-1; i++)
w[i] = MIN(x[i]-x[i-1], x[i+1]-x[i]);
w[ndim-1] = MIN(x[ndim-1]-x[ndim-2], PI-x[ndim-1]);
for (i=0; i<ndim; i++)
w[i] = 1./(.01+w[i]);
}
int CQuantize::find_nearest(const float *codebook, int nb_entries, float *x, int ndim)
{
int i, j;
float min_dist = 1e15;
int nearest = 0;
for (i=0; i<nb_entries; i++)
{
float dist=0;
for (j=0; j<ndim; j++)
dist += (x[j]-codebook[i*ndim+j])*(x[j]-codebook[i*ndim+j]);
if (dist<min_dist)
{
min_dist = dist;
nearest = i;
}
}
return nearest;
}
int CQuantize::check_lsp_order(float lsp[], int order)
{
int i;
float tmp;
int swaps = 0;
for(i=1; i<order; i++)
if (lsp[i] < lsp[i-1])
{
//fprintf(stderr, "swap %d\n",i);
swaps++;
tmp = lsp[i-1];
lsp[i-1] = lsp[i]-0.1;
lsp[i] = tmp+0.1;
i = 1; /* start check again, as swap may have caused out of order */
}
return swaps;
}
/*---------------------------------------------------------------------------*\
lpc_post_filter()
Applies a post filter to the LPC synthesis filter power spectrum
Pw, which supresses the inter-formant energy.
The algorithm is from p267 (Section 8.6) of "Digital Speech",
edited by A.M. Kondoz, 1994 published by Wiley and Sons. Chapter 8
of this text is on the MBE vocoder, and this is a freq domain
adaptation of post filtering commonly used in CELP.
I used the Octave simulation lpcpf.m to get an understanding of the
algorithm.
Requires two more FFTs which is significantly more MIPs. However
it should be possible to implement this more efficiently in the
time domain. Just not sure how to handle relative time delays
between the synthesis stage and updating these coeffs. A smaller
FFT size might also be accetable to save CPU.
TODO:
[ ] sync var names between Octave and C version
[ ] doc gain normalisation
[ ] I think the first FFT is not rqd as we do the same
thing in aks_to_M2().
\*---------------------------------------------------------------------------*/
void CQuantize::lpc_post_filter(FFTR_STATE *fftr_fwd_cfg, float Pw[], float ak[], int order, float beta, float gamma, int bass_boost, float E)
{
int i;
float x[FFT_ENC]; /* input to FFTs */
std::complex<float> Ww[FFT_ENC/2+1]; /* weighting spectrum */
float Rw[FFT_ENC/2+1]; /* R = WA */
float e_before, e_after, gain;
float Pfw;
float max_Rw, min_Rw;
float coeff;
/* Determine weighting filter spectrum W(exp(jw)) ---------------*/
for(i=0; i<FFT_ENC; i++)
{
x[i] = 0.0;
}
x[0] = ak[0];
coeff = gamma;
for(i=1; i<=order; i++)
{
x[i] = ak[i] * coeff;
coeff *= gamma;
}
kiss.fftr(*fftr_fwd_cfg, x, Ww);
for(i=0; i<FFT_ENC/2; i++)
{
Ww[i].real(Ww[i].real() * Ww[i].real() + Ww[i].imag() * Ww[i].imag());
}
/* Determined combined filter R = WA ---------------------------*/
max_Rw = 0.0;
min_Rw = 1E32;
for(i=0; i<FFT_ENC/2; i++)
{
Rw[i] = sqrtf(Ww[i].real() * Pw[i]);
if (Rw[i] > max_Rw)
max_Rw = Rw[i];
if (Rw[i] < min_Rw)
min_Rw = Rw[i];
}
/* create post filter mag spectrum and apply ------------------*/
/* measure energy before post filtering */
e_before = 1E-4;
for(i=0; i<FFT_ENC/2; i++)
e_before += Pw[i];
/* apply post filter and measure energy */
e_after = 1E-4;
for(i=0; i<FFT_ENC/2; i++)
{
Pfw = powf(Rw[i], beta);
Pw[i] *= Pfw * Pfw;
e_after += Pw[i];
}
gain = e_before/e_after;
/* apply gain factor to normalise energy, and LPC Energy */
gain *= E;
for(i=0; i<FFT_ENC/2; i++)
{
Pw[i] *= gain;
}
if (bass_boost)
{
/* add 3dB to first 1 kHz to account for LP effect of PF */
for(i=0; i<FFT_ENC/8; i++)
{
Pw[i] *= 1.4*1.4;
}
}
}
/*---------------------------------------------------------------------------*\
aks_to_M2()
Transforms the linear prediction coefficients to spectral amplitude
samples. This function determines A(m) from the average energy per
band using an FFT.
\*---------------------------------------------------------------------------*/
void CQuantize::aks_to_M2(
FFTR_STATE * fftr_fwd_cfg,
float ak[], /* LPC's */
int order,
MODEL *model, /* sinusoidal model parameters for this frame */
float E, /* energy term */
float *snr, /* signal to noise ratio for this frame in dB */
int sim_pf, /* true to simulate a post filter */
int pf, /* true to enable actual LPC post filter */
int bass_boost, /* enable LPC filter 0-1kHz 3dB boost */
float beta,
float gamma, /* LPC post filter parameters */
std::complex<float> Aw[] /* output power spectrum */
)
{
int i,m; /* loop variables */
int am,bm; /* limits of current band */
float r; /* no. rads/bin */
float Em; /* energy in band */
float Am; /* spectral amplitude sample */
float signal, noise;
r = TWO_PI/(FFT_ENC);
/* Determine DFT of A(exp(jw)) --------------------------------------------*/
{
float a[FFT_ENC]; /* input to FFT for power spectrum */
for(i=0; i<FFT_ENC; i++)
{
a[i] = 0.0;
}
for(i=0; i<=order; i++)
a[i] = ak[i];
kiss.fftr(*fftr_fwd_cfg, a, Aw);
}
/* Determine power spectrum P(w) = E/(A(exp(jw))^2 ------------------------*/
float Pw[FFT_ENC/2];
for(i=0; i<FFT_ENC/2; i++)
{
Pw[i] = 1.0/(Aw[i].real() * Aw[i].real() + Aw[i].imag() * Aw[i].imag() + 1E-6);
}
if (pf)
lpc_post_filter(fftr_fwd_cfg, Pw, ak, order, beta, gamma, bass_boost, E);
else
{
for(i=0; i<FFT_ENC/2; i++)
{
Pw[i] *= E;
}
}
/* Determine magnitudes from P(w) ----------------------------------------*/
/* when used just by decoder {A} might be all zeroes so init signal
and noise to prevent log(0) errors */
signal = 1E-30;
noise = 1E-32;
for(m=1; m<=model->L; m++)
{
am = (int)((m - 0.5)*model->Wo/r + 0.5);
bm = (int)((m + 0.5)*model->Wo/r + 0.5);
// FIXME: With arm_rfft_fast_f32 we have to use this
// otherwise sometimes a to high bm is calculated
// which causes trouble later in the calculation
// chain
// it seems for some reason model->Wo is calculated somewhat too high
if (bm>FFT_ENC/2)
{
bm = FFT_ENC/2;
}
Em = 0.0;
for(i=am; i<bm; i++)
Em += Pw[i];
Am = sqrtf(Em);
signal += model->A[m]*model->A[m];
noise += (model->A[m] - Am)*(model->A[m] - Am);
/* This code significantly improves perf of LPC model, in
particular when combined with phase0. The LPC spectrum tends
to track just under the peaks of the spectral envelope, and
just above nulls. This algorithm does the reverse to
compensate - raising the amplitudes of spectral peaks, while
attenuating the null. This enhances the formants, and
supresses the energy between formants. */
if (sim_pf)
{
if (Am > model->A[m])
Am *= 0.7;
if (Am < model->A[m])
Am *= 1.4;
}
model->A[m] = Am;
}
*snr = 10.0*log10f(signal/noise);
}
/*---------------------------------------------------------------------------*\
FUNCTION....: encode_Wo()
AUTHOR......: David Rowe
DATE CREATED: 22/8/2010
Encodes Wo using a WO_LEVELS quantiser.
\*---------------------------------------------------------------------------*/
int CQuantize::encode_Wo(C2CONST *c2const, float Wo, int bits)
{
int index, Wo_levels = 1<<bits;
float Wo_min = c2const->Wo_min;
float Wo_max = c2const->Wo_max;
float norm;
norm = (Wo - Wo_min)/(Wo_max - Wo_min);
index = floorf(Wo_levels * norm + 0.5);
if (index < 0 ) index = 0;
if (index > (Wo_levels-1)) index = Wo_levels-1;
return index;
}
/*---------------------------------------------------------------------------*\
FUNCTION....: decode_Wo()
AUTHOR......: David Rowe
DATE CREATED: 22/8/2010
Decodes Wo using a WO_LEVELS quantiser.
\*---------------------------------------------------------------------------*/
float CQuantize::decode_Wo(C2CONST *c2const, int index, int bits)
{
float Wo_min = c2const->Wo_min;
float Wo_max = c2const->Wo_max;
float step;
float Wo;
int Wo_levels = 1<<bits;
step = (Wo_max - Wo_min)/Wo_levels;
Wo = Wo_min + step*(index);
return Wo;
}
/*---------------------------------------------------------------------------*\
FUNCTION....: speech_to_uq_lsps()
AUTHOR......: David Rowe
DATE CREATED: 22/8/2010
Analyse a windowed frame of time domain speech to determine LPCs
which are the converted to LSPs for quantisation and transmission
over the channel.
\*---------------------------------------------------------------------------*/
float CQuantize::speech_to_uq_lsps(float lsp[], float ak[], float Sn[], float w[], int m_pitch, int order)
{
int i, roots;
float Wn[m_pitch];
float R[order+1];
float e, E;
Clpc lpc;
e = 0.0;
for(i=0; i<m_pitch; i++)
{
Wn[i] = Sn[i]*w[i];
e += Wn[i]*Wn[i];
}
/* trap 0 energy case as LPC analysis will fail */
if (e == 0.0)
{
for(i=0; i<order; i++)
lsp[i] = (PI/order)*(float)i;
return 0.0;
}
lpc.autocorrelate(Wn, R, m_pitch, order);
lpc.levinson_durbin(R, ak, order);
E = 0.0;
for(i=0; i<=order; i++)
E += ak[i]*R[i];
/* 15 Hz BW expansion as I can't hear the difference and it may help
help occasional fails in the LSP root finding. Important to do this
after energy calculation to avoid -ve energy values.
*/
for(i=0; i<=order; i++)
ak[i] *= powf(0.994,(float)i);
roots = lpc_to_lsp(ak, order, lsp, 5, LSP_DELTA1);
if (roots != order)
{
/* if root finding fails use some benign LSP values instead */
for(i=0; i<order; i++)
lsp[i] = (PI/order)*(float)i;
}
return E;
}
/*---------------------------------------------------------------------------*\
FUNCTION....: encode_lsps_scalar()
AUTHOR......: David Rowe
DATE CREATED: 22/8/2010
Thirty-six bit sclar LSP quantiser. From a vector of unquantised
(floating point) LSPs finds the quantised LSP indexes.
\*---------------------------------------------------------------------------*/
void CQuantize::encode_lsps_scalar(int indexes[], float lsp[], int order)
{
int i,k,m;
float wt[1];
float lsp_hz[order];
const float *cb;
float se;
/* convert from radians to Hz so we can use human readable
frequencies */
for(i=0; i<order; i++)
lsp_hz[i] = (4000.0/PI)*lsp[i];
/* scalar quantisers */
wt[0] = 1.0;
for(i=0; i<order; i++)
{
k = lsp_cb[i].k;
m = lsp_cb[i].m;
cb = lsp_cb[i].cb;
indexes[i] = quantise(cb, &lsp_hz[i], wt, k, m, &se);
}
}
/*---------------------------------------------------------------------------*\
FUNCTION....: decode_lsps_scalar()
AUTHOR......: David Rowe
DATE CREATED: 22/8/2010
From a vector of quantised LSP indexes, returns the quantised
(floating point) LSPs.
\*---------------------------------------------------------------------------*/
void CQuantize::decode_lsps_scalar(float lsp[], int indexes[], int order)
{
int i,k;
float lsp_hz[order];
const float *cb;
for(i=0; i<order; i++)
{
k = lsp_cb[i].k;
cb = lsp_cb[i].cb;
lsp_hz[i] = cb[indexes[i]*k];
}
/* convert back to radians */
for(i=0; i<order; i++)
lsp[i] = (PI/4000.0)*lsp_hz[i];
}
/*---------------------------------------------------------------------------*\
FUNCTION....: bw_expand_lsps()
AUTHOR......: David Rowe
DATE CREATED: 22/8/2010
Applies Bandwidth Expansion (BW) to a vector of LSPs. Prevents any
two LSPs getting too close together after quantisation. We know
from experiment that LSP quantisation errors < 12.5Hz (25Hz step
size) are inaudible so we use that as the minimum LSP separation.
\*---------------------------------------------------------------------------*/
void CQuantize::bw_expand_lsps(float lsp[], int order, float min_sep_low, float min_sep_high)
{
int i;
for(i=1; i<4; i++)
{
if ((lsp[i] - lsp[i-1]) < min_sep_low*(PI/4000.0))
lsp[i] = lsp[i-1] + min_sep_low*(PI/4000.0);
}
/* As quantiser gaps increased, larger BW expansion was required
to prevent twinkly noises. This may need more experiment for
different quanstisers.
*/
for(i=4; i<order; i++)
{
if (lsp[i] - lsp[i-1] < min_sep_high*(PI/4000.0))
lsp[i] = lsp[i-1] + min_sep_high*(PI/4000.0);
}
}
/*---------------------------------------------------------------------------*\
FUNCTION....: apply_lpc_correction()
AUTHOR......: David Rowe
DATE CREATED: 22/8/2010
Apply first harmonic LPC correction at decoder. This helps improve
low pitch males after LPC modelling, like hts1a and morig.
\*---------------------------------------------------------------------------*/
void CQuantize::apply_lpc_correction(MODEL *model)
{
if (model->Wo < (PI*150.0/4000))
{
model->A[1] *= 0.032;
}
}
/*---------------------------------------------------------------------------*\
FUNCTION....: encode_energy()
AUTHOR......: David Rowe
DATE CREATED: 22/8/2010
Encodes LPC energy using an E_LEVELS quantiser.
\*---------------------------------------------------------------------------*/
int CQuantize::encode_energy(float e, int bits)
{
int index, e_levels = 1<<bits;
float e_min = E_MIN_DB;
float e_max = E_MAX_DB;
float norm;
e = 10.0*log10f(e);
norm = (e - e_min)/(e_max - e_min);
index = floorf(e_levels * norm + 0.5);
if (index < 0 ) index = 0;
if (index > (e_levels-1)) index = e_levels-1;
return index;
}
/*---------------------------------------------------------------------------*\
FUNCTION....: decode_energy()
AUTHOR......: David Rowe
DATE CREATED: 22/8/2010
Decodes energy using a E_LEVELS quantiser.
\*---------------------------------------------------------------------------*/
float CQuantize::decode_energy(int index, int bits)
{
float e_min = E_MIN_DB;
float e_max = E_MAX_DB;
float step;
float e;
int e_levels = 1<<bits;
step = (e_max - e_min)/e_levels;
e = e_min + step*(index);
e = exp10f(e/10.0);
return e;
}
/*---------------------------------------------------------------------------*\
FUNCTION....: lpc_to_lsp()
AUTHOR......: David Rowe
DATE CREATED: 24/2/93
This function converts LPC coefficients to LSP coefficients.
\*---------------------------------------------------------------------------*/
int CQuantize::lpc_to_lsp(float *a, int order, float *freq, int nb, float delta)
/* float *a lpc coefficients */
/* int order order of LPC coefficients (10) */
/* float *freq LSP frequencies in radians */
/* int nb number of sub-intervals (4) */
/* float delta grid spacing interval (0.02) */
{
float psuml,psumr,psumm,temp_xr,xl,xr,xm = 0;
float temp_psumr;
int i,j,m,flag,k;
float *px; /* ptrs of respective P'(z) & Q'(z) */
float *qx;
float *p;
float *q;
float *pt; /* ptr used for cheb_poly_eval()
whether P' or Q' */
int roots=0; /* number of roots found */
float Q[order + 1];
float P[order + 1];
flag = 1;
m = order/2; /* order of P'(z) & Q'(z) polynimials */
/* Allocate memory space for polynomials */
/* determine P'(z)'s and Q'(z)'s coefficients where
P'(z) = P(z)/(1 + z^(-1)) and Q'(z) = Q(z)/(1-z^(-1)) */
px = P; /* initilaise ptrs */
qx = Q;
p = px;
q = qx;
*px++ = 1.0;
*qx++ = 1.0;
for(i=1; i<=m; i++)
{
*px++ = a[i]+a[order+1-i]-*p++;
*qx++ = a[i]-a[order+1-i]+*q++;
}
px = P;
qx = Q;
for(i=0; i<m; i++)
{
*px = 2**px;
*qx = 2**qx;
px++;
qx++;
}
px = P; /* re-initialise ptrs */
qx = Q;
/* Search for a zero in P'(z) polynomial first and then alternate to Q'(z).
Keep alternating between the two polynomials as each zero is found */
xr = 0; /* initialise xr to zero */
xl = 1.0; /* start at point xl = 1 */
for(j=0; j<order; j++)
{
if(j%2) /* determines whether P' or Q' is eval. */
pt = qx;
else
pt = px;
psuml = cheb_poly_eva(pt,xl,order); /* evals poly. at xl */
flag = 1;
while(flag && (xr >= -1.0))
{
xr = xl - delta ; /* interval spacing */
psumr = cheb_poly_eva(pt,xr,order);/* poly(xl-delta_x) */
temp_psumr = psumr;
temp_xr = xr;
/* if no sign change increment xr and re-evaluate
poly(xr). Repeat til sign change. if a sign change has
occurred the interval is bisected and then checked again
for a sign change which determines in which interval the
zero lies in. If there is no sign change between poly(xm)
and poly(xl) set interval between xm and xr else set
interval between xl and xr and repeat till root is located
within the specified limits */
if(((psumr*psuml)<0.0) || (psumr == 0.0))
{
roots++;
psumm=psuml;
for(k=0; k<=nb; k++)
{
xm = (xl+xr)/2; /* bisect the interval */
psumm=cheb_poly_eva(pt,xm,order);
if(psumm*psuml>0.)
{
psuml=psumm;
xl=xm;
}
else
{
psumr=psumm;
xr=xm;
}
}
/* once zero is found, reset initial interval to xr */
freq[j] = (xm);
xl = xm;
flag = 0; /* reset flag for next search */
}
else
{
psuml=temp_psumr;
xl=temp_xr;
}
}
}
/* convert from x domain to radians */
for(i=0; i<order; i++)
{
freq[i] = acosf(freq[i]);
}
return(roots);
}
/*---------------------------------------------------------------------------*\
FUNCTION....: cheb_poly_eva()
AUTHOR......: David Rowe
DATE CREATED: 24/2/93
This function evalutes a series of chebyshev polynomials
FIXME: performing memory allocation at run time is very inefficient,
replace with stack variables of MAX_P size.
\*---------------------------------------------------------------------------*/
float CQuantize::cheb_poly_eva(float *coef,float x,int order)
/* float coef[] coefficients of the polynomial to be evaluated */
/* float x the point where polynomial is to be evaluated */
/* int order order of the polynomial */
{
int i;
float *t,*u,*v,sum;
float T[(order / 2) + 1];
/* Initialise pointers */
t = T; /* T[i-2] */
*t++ = 1.0;
u = t--; /* T[i-1] */
*u++ = x;
v = u--; /* T[i] */
/* Evaluate chebyshev series formulation using iterative approach */
for(i=2; i<=order/2; i++)
*v++ = (2*x)*(*u++) - *t++; /* T[i] = 2*x*T[i-1] - T[i-2] */
sum=0.0; /* initialise sum to zero */
t = T; /* reset pointer */
/* Evaluate polynomial and return value also free memory space */
for(i=0; i<=order/2; i++)
sum+=coef[(order/2)-i]**t++;
return sum;
}

68
M172DMR/codec2/quantise.h Normal file
View File

@ -0,0 +1,68 @@
/*---------------------------------------------------------------------------*\
FILE........: quantise.h
AUTHOR......: David Rowe
DATE CREATED: 31/5/92
Quantisation functions for the sinusoidal coder.
\*---------------------------------------------------------------------------*/
/*
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __QUANTISE__
#define __QUANTISE__
#include <complex>
#include "qbase.h"
class CQuantize : public CQbase {
public:
void aks_to_M2(FFTR_STATE *fftr_fwd_cfg, float ak[], int order, MODEL *model, float E, float *snr, int sim_pf, int pf, int bass_boost, float beta, float gamma, std::complex<float> Aw[]);
int encode_Wo(C2CONST *c2const, float Wo, int bits);
float decode_Wo(C2CONST *c2const, int index, int bits);
void encode_lsps_scalar(int indexes[], float lsp[], int order);
void decode_lsps_scalar(float lsp[], int indexes[], int order);
void encode_lspds_scalar(int indexes[], float lsp[], int order);
void decode_lspds_scalar(float lsp[], int indexes[], int order);
int encode_energy(float e, int bits);
float decode_energy(int index, int bits);
void pack(unsigned char * bits, unsigned int *nbit, int index, unsigned int index_bits);
void pack_natural_or_gray(unsigned char * bits, unsigned int *nbit, int index, unsigned int index_bits, unsigned int gray);
int unpack(const unsigned char * bits, unsigned int *nbit, unsigned int index_bits);
int unpack_natural_or_gray(const unsigned char * bits, unsigned int *nbit, unsigned int index_bits, unsigned int gray);
int lsp_bits(int i);
int lspd_bits(int i);
void apply_lpc_correction(MODEL *model);
float speech_to_uq_lsps(float lsp[], float ak[], float Sn[], float w[], int m_pitch, int order);
int check_lsp_order(float lsp[], int lpc_order);
void bw_expand_lsps(float lsp[], int order, float min_sep_low, float min_sep_high);
private:
void compute_weights(const float *x, float *w, int ndim);
int find_nearest(const float *codebook, int nb_entries, float *x, int ndim);
void lpc_post_filter(FFTR_STATE *fftr_fwd_cfg, float Pw[], float ak[], int order, float beta, float gamma, int bass_boost, float E);
int lpc_to_lsp (float *a, int lpcrdr, float *freq, int nb, float delta);
float cheb_poly_eva(float *coef,float x,int order);
};
#endif

201
M172DMR/typedefs.h Normal file
View File

@ -0,0 +1,201 @@
/*
* Project 25 IMBE Encoder/Decoder Fixed-Point implementation
* Developed by Pavel Yazev E-mail: pyazev@gmail.com
* Version 1.0 (c) Copyright 2009
*
* This 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 3, or (at your option)
* any later version.
*
* The software 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; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Boston, MA
* 02110-1301, USA.
*/
/*
********************************************************************************
*
* GSM AMR speech codec Version 7.4.0 January 31, 2000
*
********************************************************************************
*
* File : typedefs.h
* Description : Definition of platform independent data
* types and constants
*
*
* The following platform independent data types and corresponding
* preprocessor (#define) constants are defined:
*
* defined type meaning corresponding constants
* ----------------------------------------------------------
* Char character (none)
* Bool boolean true, false
* Word8 8-bit signed minWord8, maxWord8
* UWord8 8-bit unsigned minUWord8, maxUWord8
* Word16 16-bit signed minWord16, maxWord16
* UWord16 16-bit unsigned minUWord16, maxUWord16
* Word32 32-bit signed minWord32, maxWord32
* UWord32 32-bit unsigned minUWord32, maxUWord32
* Float floating point minFloat, maxFloat
*
*
* The following compile switches are #defined:
*
* PLATFORM string indicating platform progam is compiled on
* possible values: "OSF", "PC", "SUN"
*
* OSF only defined if the current platform is an Alpha
* PC only defined if the current platform is a PC
* SUN only defined if the current platform is a Sun
*
* LSBFIRST is defined if the byte order on this platform is
* "least significant byte first" -> defined on DEC Alpha
* and PC, undefined on Sun
*
********************************************************************************
*/
#ifndef typedefs_h
#define typedefs_h "$Id $"
/*
********************************************************************************
* INCLUDE FILES
********************************************************************************
*/
#include <float.h>
#include <limits.h>
#define __MSDOS__
/*
********************************************************************************
* DEFINITION OF CONSTANTS
********************************************************************************
*/
/*
********* define char type
*/
typedef char Char;
/*
********* define 8 bit signed/unsigned types & constants
*/
#if SCHAR_MAX == 127
typedef signed char Word8;
#define minWord8 SCHAR_MIN
#define maxWord8 SCHAR_MAX
typedef unsigned char UWord8;
#define minUWord8 0
#define maxUWord8 UCHAR_MAX
#else
#error cannot find 8-bit type
#endif
/*
********* define 16 bit signed/unsigned types & constants
*/
#if INT_MAX == 32767
typedef int Word16;
#define minWord16 INT_MIN
#define maxWord16 INT_MAX
typedef unsigned int UWord16;
#define minUWord16 0
#define maxUWord16 UINT_MAX
#elif SHRT_MAX == 32767
typedef short Word16;
#define minWord16 SHRT_MIN
#define maxWord16 SHRT_MAX
typedef unsigned short UWord16;
#define minUWord16 0
#define maxUWord16 USHRT_MAX
#else
#error cannot find 16-bit type
#endif
/*
********* define 32 bit signed/unsigned types & constants
*/
#if INT_MAX == 2147483647
typedef int Word32;
#define minWord32 INT_MIN
#define maxWord32 INT_MAX
typedef unsigned int UWord32;
#define minUWord32 0
#define maxUWord32 UINT_MAX
#elif LONG_MAX == 2147483647
typedef long Word32;
#define minWord32 LONG_MIN
#define maxWord32 LONG_MAX
typedef unsigned long UWord32;
#define minUWord32 0
#define maxUWord32 ULONG_MAX
#else
#error cannot find 32-bit type
#endif
/*
********* define floating point type & constants
*/
/* use "#if 0" below if Float should be double;
use "#if 1" below if Float should be float
*/
#if 0
typedef float Float;
#define maxFloat FLT_MAX
#define minFloat FLT_MIN
#else
typedef double Float;
#define maxFloat DBL_MAX
#define minFloat DBL_MIN
#endif
/*
********* define complex type
*/
typedef struct {
Float r; /* real part */
Float i; /* imaginary part */
} CPX;
/*
********* define boolean type
*/
typedef int Bool;
#define false 0
#define true 1
/*
********* Check current platform
*/
#if defined(__MSDOS__)
#define PC
#define PLATFORM "PC"
#define LSBFIRST
#elif defined(__osf__)
#define OSF
#define PLATFORM "OSF"
#define LSBFIRST
#elif defined(__sun__) || defined(__sun)
#define SUN
#define PLATFORM "SUN"
#undef LSBFIRST
#elif defined(linux) && defined(i386)
#define PC
#define PLATFORM "PC"
#define LSBFIRST
#else
#error "can't determine architecture; adapt typedefs.h to your platform"
#endif
#endif

View File

@ -43,6 +43,7 @@ public:
std::string getP25TGListFile() const;
bool getP25NetworkDebug() const;
// The Info section
unsigned int getRxFrequency() const;
unsigned int getTxFrequency() const;
@ -127,6 +128,7 @@ private:
std::string m_p25TGListFile;
bool m_p25NetworkDebug;
unsigned int m_logDisplayLevel;
unsigned int m_logFileLevel;
std::string m_logFilePath;

View File

@ -106,7 +106,7 @@ const char* DEFAULT_INI_FILE = "/etc/P252DMR.ini";
const char* HEADER1 = "This software is for use on amateur radio networks only,";
const char* HEADER2 = "it is to be used for educational purposes only. Its use on";
const char* HEADER3 = "commercial networks is strictly prohibited.";
const char* HEADER4 = "Copyright(C) 2018 by CA6JAU, G4KLX and others";
const char* HEADER4 = "Copyright(C) 2018 by AD8DP, CA6JAU, G4KLX and others";
#include <functional>
#include <algorithm>

View File

@ -49,7 +49,7 @@ const char* DEFAULT_INI_FILE = "/etc/YSF2DMR.ini";
const char* HEADER1 = "This software is for use on amateur radio networks only,";
const char* HEADER2 = "it is to be used for educational purposes only. Its use on";
const char* HEADER3 = "commercial networks is strictly prohibited.";
const char* HEADER4 = "Copyright(C) 2018,2019 by CA6JAU, EA7EE, G4KLX and others";
const char* HEADER4 = "Copyright(C) 2018,2019 by CA6JAU, EA7EE, G4KLX, AD8DP and others";
#include <functional>
#include <algorithm>