add support to selectively do a hex dump of the TSBK, TDULC or DMR CSBK bytes for encode/decode; add appropriate P25 PDU response packet; correct CRC-9 table and implementation (although I believe its still wrong); add support to adjust engineering parameters for symbol levels; add support to selectively disable P25 ACK responses for some TSBKs;

pull/1/head
Bryan Biedenkapp 5 years ago
parent 91158f84da
commit 4b3e8de18f

@ -202,6 +202,7 @@
<ClInclude Include="p25\acl\AccessControl.h" />
<ClInclude Include="p25\data\DataBlock.h" />
<ClInclude Include="p25\data\DataHeader.h" />
<ClInclude Include="p25\data\DataRspHeader.h" />
<ClInclude Include="p25\data\LowSpeedData.h" />
<ClInclude Include="p25\edac\Trellis.h" />
<ClInclude Include="p25\lc\LC.h" />
@ -272,6 +273,7 @@
<ClCompile Include="p25\acl\AccessControl.cpp" />
<ClCompile Include="p25\data\DataBlock.cpp" />
<ClCompile Include="p25\data\DataHeader.cpp" />
<ClCompile Include="p25\data\DataRspHeader.cpp" />
<ClCompile Include="p25\data\LowSpeedData.cpp" />
<ClCompile Include="p25\edac\Trellis.cpp" />
<ClCompile Include="p25\lc\LC.cpp" />

@ -326,6 +326,9 @@
<ClInclude Include="network\BaseNetwork.h">
<Filter>Header Files\network</Filter>
</ClInclude>
<ClInclude Include="p25\data\DataRspHeader.h">
<Filter>Header Files\p25\data</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Log.cpp">
@ -523,6 +526,9 @@
<ClCompile Include="network\BaseNetwork.cpp">
<Filter>Source Files\network</Filter>
</ClCompile>
<ClCompile Include="p25\data\DataRspHeader.cpp">
<Filter>Source Files\p25\data</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="Makefile" />

@ -39,6 +39,7 @@ OBJECTS = \
p25/acl/AccessControl.o \
p25/data/DataBlock.o \
p25/data/DataHeader.o \
p25/data/DataRspHeader.o \
p25/data/LowSpeedData.o \
p25/edac/Trellis.o \
p25/lc/LC.o \

@ -39,6 +39,7 @@ OBJECTS = \
p25/acl/AccessControl.o \
p25/data/DataBlock.o \
p25/data/DataHeader.o \
p25/data/DataRspHeader.o \
p25/data/LowSpeedData.o \
p25/edac/Trellis.o \
p25/lc/LC.o \

@ -39,6 +39,7 @@ OBJECTS = \
p25/acl/AccessControl.o \
p25/data/DataBlock.o \
p25/data/DataHeader.o \
p25/data/DataRspHeader.o \
p25/data/LowSpeedData.o \
p25/edac/Trellis.o \
p25/lc/LC.o \

@ -30,6 +30,7 @@ protocols:
dumpTAData: true
dumpDataPacket: false
repeatDataPacket: true
dumpCsbkData: false
callHang: 5
txHang: 8
queueSize: 10000
@ -40,6 +41,7 @@ protocols:
tduPreambleCount: 6
control:
enable: false
ackRequests: true
continuous: false
interval: 60
duration: 1
@ -51,6 +53,7 @@ protocols:
verifyReg: false
dumpDataPacket: false
repeatDataPacket: true
dumpTsbkData: false
callHang: 5
noStatusAck: false
noMessageAck: true

@ -57,12 +57,13 @@ using namespace dmr;
/// <param name="jitter"></param>
/// <param name="dumpDataPacket"></param>
/// <param name="repeatDataPacket"></param>
/// <param name="dumpCSBKData"></param>
/// <param name="debug">Flag indicating whether DMR debug is enabled.</param>
/// <param name="verbose">Flag indicating whether DMR verbose logging is enabled.</param>
Control::Control(uint32_t colorCode, uint32_t callHang, uint32_t queueSize, bool embeddedLCOnly,
bool dumpTAData, uint32_t timeout, uint32_t tgHang, modem::Modem* modem, network::BaseNetwork* network, bool duplex,
lookups::RadioIdLookup* ridLookup, lookups::TalkgroupIdLookup* tidLookup, lookups::RSSIInterpolator* rssi,
uint32_t jitter, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose) :
uint32_t jitter, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose) :
m_colorCode(colorCode),
m_modem(modem),
m_network(network),
@ -70,6 +71,7 @@ Control::Control(uint32_t colorCode, uint32_t callHang, uint32_t queueSize, bool
m_slot2(NULL),
m_ridLookup(ridLookup),
m_tidLookup(tidLookup),
m_dumpCSBKData(dumpCSBKData),
m_verbose(verbose),
m_debug(debug)
{
@ -81,8 +83,8 @@ Control::Control(uint32_t colorCode, uint32_t callHang, uint32_t queueSize, bool
acl::AccessControl::init(m_ridLookup, m_tidLookup);
Slot::init(colorCode, embeddedLCOnly, dumpTAData, callHang, modem, network, duplex, m_ridLookup, m_tidLookup, rssi, jitter);
m_slot1 = new Slot(1U, timeout, tgHang, queueSize, dumpDataPacket, repeatDataPacket, debug, verbose);
m_slot2 = new Slot(2U, timeout, tgHang, queueSize, dumpDataPacket, repeatDataPacket, debug, verbose);
m_slot1 = new Slot(1U, timeout, tgHang, queueSize, dumpDataPacket, repeatDataPacket, dumpCSBKData, debug, verbose);
m_slot2 = new Slot(2U, timeout, tgHang, queueSize, dumpDataPacket, repeatDataPacket, dumpCSBKData, debug, verbose);
}
/// <summary>
@ -108,7 +110,9 @@ bool Control::processWakeup(const uint8_t* data)
return false;
// generate a new CSBK and check validity
lc::CSBK csbk = lc::CSBK(m_debug);
lc::CSBK csbk = lc::CSBK();
csbk.setVerbose(m_dumpCSBKData);
bool valid = csbk.decode(data + 2U);
if (!valid)
return false;

@ -59,7 +59,7 @@ namespace dmr
Control(uint32_t colorCode, uint32_t callHang, uint32_t queueSize, bool embeddedLCOnly,
bool dumpTAData, uint32_t timeout, uint32_t tgHang, modem::Modem* modem, network::BaseNetwork* network, bool duplex,
lookups::RadioIdLookup* ridLookup, lookups::TalkgroupIdLookup* tidLookup, lookups::RSSIInterpolator* rssi,
uint32_t jitter, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose);
uint32_t jitter, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose);
/// <summary>Finalizes a instance of the Control class.</summary>
~Control();
@ -95,6 +95,7 @@ namespace dmr
lookups::RadioIdLookup* m_ridLookup;
lookups::TalkgroupIdLookup* m_tidLookup;
bool m_dumpCSBKData;
bool m_verbose;
bool m_debug;
};

@ -273,7 +273,9 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
}
else if (dataType == DT_CSBK) {
// generate a new CSBK and check validity
lc::CSBK csbk = lc::CSBK(m_debug);
lc::CSBK csbk = lc::CSBK();
csbk.setVerbose(m_dumpCSBKData);
bool valid = csbk.decode(data + 2U);
if (!valid)
return false;
@ -778,7 +780,9 @@ void DataPacket::processNetwork(const data::Data& dmrData)
writeEndNet();
}
else if (dataType == DT_CSBK) {
lc::CSBK csbk = lc::CSBK(m_debug);
lc::CSBK csbk = lc::CSBK();
csbk.setVerbose(m_dumpCSBKData);
bool valid = csbk.decode(data + 2U);
if (!valid) {
LogError(LOG_NET, "DMR Slot %u, DT_CSBK, unable to decode the network CSBK", m_slot->m_slotNo);
@ -1055,9 +1059,10 @@ void DataPacket::processNetwork(const data::Data& dmrData)
/// <param name="network">Instance of the BaseNetwork class.</param>
/// <param name="dumpDataPacket"></param>
/// <param name="repeatDataPacket"></param>
/// <param name="dumpCSBKData"></param>
/// <param name="debug">Flag indicating whether DMR debug is enabled.</param>
/// <param name="verbose">Flag indicating whether DMR verbose logging is enabled.</param>
DataPacket::DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose) :
DataPacket::DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose) :
m_slot(slot),
m_rfLC(NULL),
m_netLC(NULL),
@ -1065,6 +1070,7 @@ DataPacket::DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataP
m_pduDataOffset(0U),
m_dumpDataPacket(dumpDataPacket),
m_repeatDataPacket(repeatDataPacket),
m_dumpCSBKData(dumpCSBKData),
m_verbose(verbose),
m_debug(debug)
{

@ -81,11 +81,12 @@ namespace dmr
bool m_dumpDataPacket;
bool m_repeatDataPacket;
bool m_dumpCSBKData;
bool m_verbose;
bool m_debug;
/// <summary>Initializes a new instance of the DataPacket class.</summary>
DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose);
DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose);
/// <summary>Finalizes a instance of the DataPacket class.</summary>
~DataPacket();

@ -85,10 +85,11 @@ bool Slot::m_voice2 = true;
/// <param name="tgHang">Amount of time to hang on the last talkgroup mode from RF.</param>
/// <param name="dumpDataPacket"></param>
/// <param name="repeatDataPacket"></param>
/// <param name="dumpCSBKData"></param>
/// <param name="debug">Flag indicating whether DMR debug is enabled.</param>
/// <param name="verbose">Flag indicating whether DMR verbose logging is enabled.</param>
Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSize, bool dumpDataPacket, bool repeatDataPacket,
bool debug, bool verbose) :
bool dumpCSBKData, bool debug, bool verbose) :
m_slotNo(slotNo),
m_queue(queueSize, "DMR Slot"),
m_rfState(RS_RF_LISTENING),
@ -118,13 +119,14 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz
m_minRSSI(0U),
m_aveRSSI(0U),
m_rssiCount(0U),
m_dumpCSBKData(dumpCSBKData),
m_verbose(verbose),
m_debug(debug)
{
m_interval.start();
m_voice = new VoicePacket(this, m_network, m_embeddedLCOnly, m_dumpTAData, debug, verbose);
m_data = new DataPacket(this, m_network, dumpDataPacket, repeatDataPacket, debug, verbose);
m_data = new DataPacket(this, m_network, dumpDataPacket, repeatDataPacket, dumpCSBKData, debug, verbose);
}
/// <summary>
@ -561,7 +563,8 @@ void Slot::writeRF_Ext_Func(uint32_t func, uint32_t arg, uint32_t dstId)
slotType.setColorCode(m_colorCode);
slotType.setDataType(DT_CSBK);
lc::CSBK csbk = lc::CSBK(false);
lc::CSBK csbk = lc::CSBK();
csbk.setVerbose(m_dumpCSBKData);
csbk.setCSBKO(CSBKO_EXT_FNCT);
csbk.setFID(FID_DMRA);
@ -609,7 +612,8 @@ void Slot::writeRF_Call_Alrt(uint32_t srcId, uint32_t dstId)
slotType.setColorCode(m_colorCode);
slotType.setDataType(DT_CSBK);
lc::CSBK csbk = lc::CSBK(false);
lc::CSBK csbk = lc::CSBK();
csbk.setVerbose(m_dumpCSBKData);
csbk.setCSBKO(CSBKO_CALL_ALRT);
csbk.setFID(FID_DMRA);

@ -64,7 +64,7 @@ namespace dmr
public:
/// <summary>Initializes a new instance of the Slot class.</summary>
Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSize, bool dumpDataPacket, bool repeatDataPacket,
bool debug, bool verbose);
bool dumpCSBKData, bool debug, bool verbose);
/// <summary>Finalizes a instance of the Slot class.</summary>
~Slot();
@ -130,6 +130,7 @@ namespace dmr
uint32_t m_aveRSSI;
uint32_t m_rssiCount;
bool m_dumpCSBKData;
bool m_verbose;
bool m_debug;

@ -32,6 +32,7 @@
#include "dmr/lc/CSBK.h"
#include "edac/BPTC19696.h"
#include "edac/CRC.h"
#include "Log.h"
#include "Utils.h"
using namespace dmr::lc;
@ -46,8 +47,8 @@ using namespace dmr;
/// <summary>
/// Initializes a new instance of the CSBK class.
/// </summary>
/// <param name="debug"></param>
CSBK::CSBK(bool debug) :
CSBK::CSBK() :
m_verbose(false),
m_CSBKO(CSBKO_NONE),
m_FID(0x00U),
m_bsId(0U),
@ -56,8 +57,7 @@ CSBK::CSBK(bool debug) :
m_dstId(0U),
m_dataContent(false),
m_CBF(0U),
m_data(NULL),
m_debug(debug)
m_data(NULL)
{
m_data = new uint8_t[12U];
}
@ -95,6 +95,10 @@ bool CSBK::decode(const uint8_t* bytes)
m_data[10U] ^= CSBK_CRC_MASK[0U];
m_data[11U] ^= CSBK_CRC_MASK[1U];
if (m_verbose) {
Utils::dump(2U, "Decoded CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES);
}
m_CSBKO = m_data[0U] & 0x3FU;
m_FID = m_data[1U];
@ -105,8 +109,6 @@ bool CSBK::decode(const uint8_t* bytes)
m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
m_dataContent = false;
m_CBF = 0U;
if (m_debug)
Utils::dump(1U, "Downlink Activate CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES);
break;
case CSBKO_UU_V_REQ:
@ -115,8 +117,6 @@ bool CSBK::decode(const uint8_t* bytes)
m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
m_dataContent = false;
m_CBF = 0U;
if (m_debug)
Utils::dump(1U, "Unit to Unit Service Request CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES);
break;
case CSBKO_UU_ANS_RSP:
@ -125,8 +125,6 @@ bool CSBK::decode(const uint8_t* bytes)
m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
m_dataContent = false;
m_CBF = 0U;
if (m_debug)
Utils::dump(1U, "Unit to Unit Service Answer Response CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES);
break;
case CSBKO_PRECCSBK:
@ -135,8 +133,6 @@ bool CSBK::decode(const uint8_t* bytes)
m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
m_dataContent = (m_data[2U] & 0x80U) == 0x80U;
m_CBF = m_data[3U];
if (m_debug)
Utils::dump(1U, "Preamble CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES);
break;
case CSBKO_CALL_ALRT:
@ -145,8 +141,6 @@ bool CSBK::decode(const uint8_t* bytes)
m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
m_dataContent = (m_data[2U] & 0x80U) == 0x80U;
m_CBF = m_data[3U];
if (m_debug)
Utils::dump(1U, "Call Alert CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES);
break;
case CSBKO_ACK_RSP:
@ -155,8 +149,6 @@ bool CSBK::decode(const uint8_t* bytes)
m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
m_dataContent = (m_data[2U] & 0x80U) == 0x80U;
m_CBF = m_data[3U];
if (m_debug)
Utils::dump(1U, "ACK Response CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES);
break;
case CSBKO_EXT_FNCT:
@ -165,8 +157,6 @@ bool CSBK::decode(const uint8_t* bytes)
m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
m_dataContent = (m_data[2U] & 0x80U) == 0x80U;
m_CBF = m_data[3U];
if (m_debug)
Utils::dump(1U, "Extended Function CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES);
break;
case CSBKO_NACK_RSP:
@ -175,8 +165,6 @@ bool CSBK::decode(const uint8_t* bytes)
m_dstId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
m_dataContent = false;
m_CBF = 0U;
if (m_debug)
Utils::dump(1U, "Negative Acknowledge Response CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES);
break;
default:
@ -185,8 +173,7 @@ bool CSBK::decode(const uint8_t* bytes)
m_dstId = 0U;
m_dataContent = false;
m_CBF = 0U;
if (m_debug)
Utils::dump("Unhandled CSBK type", m_data, DMR_LC_HEADER_LENGTH_BYTES);
LogError(LOG_DMR, "unknown CSBK type, csbko = $%02X", m_CSBKO);
return true;
}
@ -241,6 +228,10 @@ void CSBK::encode(uint8_t* bytes) const
m_data[10U] ^= CSBK_CRC_MASK[0U];
m_data[11U] ^= CSBK_CRC_MASK[1U];
if (m_verbose) {
Utils::dump(2U, "Encoded CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES);
}
// encode BPTC (196,96) FEC
edac::BPTC19696 bptc;
bptc.encode(m_data, bytes);

@ -46,7 +46,7 @@ namespace dmr
class HOST_SW_API CSBK {
public:
/// <summary>Initializes a new instance of the CSBK class.</summary>
CSBK(bool debug);
CSBK();
/// <summary>Finalizes a instance of the CSBK class.</summary>
~CSBK();
@ -56,6 +56,9 @@ namespace dmr
void encode(uint8_t* bytes) const;
public:
/// <summary></summary>
__PROPERTY(bool, verbose, Verbose);
// Generic fields
/// <summary>CSBK opcode.</summary>
__PROPERTY(uint8_t, CSBKO, CSBKO);
@ -82,7 +85,6 @@ namespace dmr
private:
uint8_t* m_data;
bool m_debug;
};
} // namespace lc
} // namespace dmr

@ -68,38 +68,38 @@ const uint8_t CRC8_TABLE[] = {
0xFA, 0xFD, 0xF4, 0xF3, 0x01 };
const uint16_t CRC9_TABLE[] = {
0x000, 0x259, 0x2B2, 0x0EB, 0x23D, 0x064, 0x08F, 0x2D6,
0x27A, 0x023, 0x0C8, 0x291, 0x047, 0x21E, 0x2F5, 0x0AC,
0x0F4, 0x2AD, 0x246, 0x01F, 0x2C9, 0x090, 0x07B, 0x222,
0x28E, 0x0D7, 0x03C, 0x265, 0x0B3, 0x2EA, 0x201, 0x058,
0x2B1, 0x0E8, 0x003, 0x25A, 0x08C, 0x2D5, 0x23E, 0x067,
0x0CB, 0x292, 0x279, 0x020, 0x2F6, 0x0AF, 0x044, 0x21D,
0x245, 0x01C, 0x0F7, 0x2AE, 0x078, 0x221, 0x2CA, 0x093,
0x03F, 0x266, 0x28D, 0x0D4, 0x202, 0x05B, 0x0B0, 0x2E9,
0x03B, 0x262, 0x289, 0x0D0, 0x206, 0x05F, 0x0B4, 0x2ED,
0x241, 0x018, 0x0F3, 0x2AA, 0x07C, 0x225, 0x2CE, 0x097,
0x0CF, 0x296, 0x27D, 0x024, 0x2F2, 0x0AB, 0x040, 0x219,
0x2B5, 0x0EC, 0x007, 0x25E, 0x088, 0x2D1, 0x23A, 0x063,
0x28A, 0x0D3, 0x038, 0x261, 0x0B7, 0x2EE, 0x205, 0x05C,
0x0F0, 0x2A9, 0x242, 0x01B, 0x2CD, 0x094, 0x07F, 0x226,
0x27E, 0x027, 0x0CC, 0x295, 0x043, 0x21A, 0x2F1, 0x0A8,
0x004, 0x25D, 0x2B6, 0x0EF, 0x239, 0x060, 0x08B, 0x2D2,
0x276, 0x02F, 0x0C4, 0x29D, 0x04B, 0x212, 0x2F9, 0x0A0,
0x00C, 0x255, 0x2BE, 0x0E7, 0x231, 0x068, 0x083, 0x2DA,
0x282, 0x0DB, 0x030, 0x269, 0x0BF, 0x2E6, 0x20D, 0x054,
0x0F8, 0x2A1, 0x24A, 0x013, 0x2C5, 0x09C, 0x077, 0x22E,
0x0C7, 0x29E, 0x275, 0x02C, 0x2FA, 0x0A3, 0x048, 0x211,
0x2BD, 0x0E4, 0x00F, 0x256, 0x080, 0x2D9, 0x232, 0x06B,
0x033, 0x26A, 0x281, 0x0D8, 0x20E, 0x057, 0x0BC, 0x2E5,
0x249, 0x010, 0x0FB, 0x2A2, 0x074, 0x22D, 0x2C6, 0x09F,
0x24D, 0x014, 0x0FF, 0x2A6, 0x070, 0x229, 0x2C2, 0x09B,
0x037, 0x26E, 0x285, 0x0DC, 0x20A, 0x053, 0x0B8, 0x2E1,
0x2B9, 0x0E0, 0x00B, 0x252, 0x084, 0x2DD, 0x236, 0x06F,
0x0C3, 0x29A, 0x271, 0x028, 0x2FE, 0x0A7, 0x04C, 0x215,
0x0FC, 0x2A5, 0x24E, 0x017, 0x2C1, 0x098, 0x073, 0x22A,
0x286, 0x0DF, 0x034, 0x26D, 0x0BB, 0x2E2, 0x209, 0x050,
0x008, 0x251, 0x2BA, 0x0E3, 0x235, 0x06C, 0x087, 0x2DE,
0x272, 0x02B, 0x0C0, 0x299, 0x04F, 0x216, 0x2FD, 0x0A4 };
0x000, 0x059, 0x0B2, 0x0EB, 0x164, 0x13D, 0x1D6, 0x18F,
0x091, 0x0C8, 0x023, 0x07A, 0x1F5, 0x1AC, 0x147, 0x11E,
0x122, 0x17B, 0x190, 0x1C9, 0x046, 0x01F, 0x0F4, 0x0AD,
0x1B3, 0x1EA, 0x101, 0x158, 0x0d7, 0x08E, 0x065, 0x03C,
0x01D, 0x044, 0x0AF, 0x0F6, 0x179, 0x120, 0x1CB, 0x192,
0x08C, 0x0D5, 0x03E, 0x067, 0x1E8, 0x1B1, 0x15A, 0x103,
0x13F, 0x166, 0x18D, 0x1D4, 0x05B, 0x002, 0x0E9, 0x0B0,
0x1AE, 0x1F7, 0x11C, 0x145, 0x0CA, 0x093, 0x078, 0x021,
0x03A, 0x063, 0x088, 0x0D1, 0x15E, 0x107, 0x1EC, 0x1B5,
0x0AB, 0x0F2, 0x019, 0x040, 0x1CF, 0x196, 0x17D, 0x124,
0x118, 0x141, 0x1AA, 0x1F3, 0x07C, 0x025, 0x0CE, 0x097,
0x189, 0x1D0, 0x13B, 0x162, 0x0ED, 0x0B4, 0x05F, 0x006,
0x027, 0x07E, 0x095, 0x0CC, 0x143, 0x11A, 0x1F1, 0x1A8,
0x0B6, 0x0EF, 0x004, 0x05D, 0x1D2, 0x18B, 0x160, 0x139,
0x105, 0x15C, 0x1B7, 0x1EE, 0x061, 0x038, 0x0D3, 0x08A,
0x194, 0x1CD, 0x126, 0x17F, 0x0F0, 0x0A9, 0x042, 0x01B,
0x074, 0x02D, 0x0C6, 0x09F, 0x110, 0x149, 0x1A2, 0x1FB,
0x0E5, 0x0BC, 0x057, 0x00E, 0x181, 0x1D8, 0x133, 0x16A,
0x156, 0x10F, 0x1E4, 0x1BD, 0x032, 0x06B, 0x080, 0x0D9,
0x1C7, 0x19E, 0x175, 0x12C, 0x0A3, 0x0FA, 0x011, 0x048,
0x069, 0x030, 0x0DB, 0x082, 0x10D, 0x154, 0x1BF, 0x1E6,
0x0F8, 0x0A1, 0x04A, 0x013, 0x19C, 0x1C5, 0x12E, 0x177,
0x14B, 0x112, 0x1F9, 0x1A0, 0x02F, 0x076, 0x09D, 0x0C4,
0x1DA, 0x183, 0x168, 0x131, 0x0BE, 0x0E7, 0x00C, 0x055,
0x04E, 0x017, 0x0FC, 0x0A5, 0x12A, 0x173, 0x198, 0x1C1,
0x0DF, 0x086, 0x06D, 0x034, 0x1BB, 0x1E2, 0x109, 0x150,
0x16C, 0x135, 0x1DE, 0x187, 0x008, 0x051, 0x0BA, 0x0E3,
0x1FD, 0x1A4, 0x14F, 0x116, 0x099, 0x0C0, 0x02B, 0x072,
0x053, 0x00A, 0x0E1, 0x0B8, 0x137, 0x16E, 0x185, 0x1DC,
0x0C2, 0x09B, 0x070, 0x029, 0x1A6, 0x1FF, 0x114, 0x14D,
0x171, 0x128, 0x1C3, 0x19A, 0x015, 0x04C, 0x0A7, 0x0FE,
0x1E0, 0x1B9, 0x152, 0x10B, 0x084, 0x0DD, 0x036, 0x06F };
const uint16_t CCITT16_TABLE1[] = {
0x0000U, 0x1189U, 0x2312U, 0x329bU, 0x4624U, 0x57adU, 0x6536U, 0x74bfU,
@ -434,9 +434,20 @@ uint16_t CRC::crc9(const uint8_t* in, uint32_t length)
assert(in != NULL);
uint16_t crc = 0U;
uint16_t tmp = 0U;
for (uint32_t i = 0U; i < length; i++)
crc = CRC9_TABLE[(crc ^ in[i]) & 0xFF];
while (length--)
{
tmp = (crc >> 1) & 0xFF;
crc = crc << 8;
crc = crc ^ CRC9_TABLE[tmp ^ (*in >> 8)];
return crc;
tmp = (crc >> 1) & 0xFF;
crc = crc << 8;
crc = crc ^ CRC9_TABLE[tmp ^ (*in & 0xFF)];
in++;
}
return ((~crc) & 0x1FF);
}

@ -268,6 +268,7 @@ int Host::run()
bool embeddedLCOnly = dmrProtocol["embeddedLCOnly"].as<bool>(false);
bool dmrDumpDataPacket = dmrProtocol["dumpDataPacket"].as<bool>(false);
bool dmrRepeatDataPacket = dmrProtocol["repeatDataPacket"].as<bool>(true);
bool dmrDumpCsbkData = dmrProtocol["dumpCsbkData"].as<bool>(false);
bool dumpTAData = dmrProtocol["dumpTAData"].as<bool>(true);
uint32_t callHang = dmrProtocol["callHang"].as<uint32_t>(3U);
uint32_t txHang = dmrProtocol["txHang"].as<uint32_t>(4U);
@ -289,6 +290,7 @@ int Host::run()
LogInfo(" Dump Talker Alias Data: %s", dumpTAData ? "yes" : "no");
LogInfo(" Dump Packet Data: %s", dmrDumpDataPacket ? "yes" : "no");
LogInfo(" Repeat Packet Data: %s", dmrRepeatDataPacket ? "yes" : "no");
LogInfo(" Dump CSBK Data: %s", dmrDumpCsbkData ? "yes" : "no");
LogInfo(" Call Hang: %us", callHang);
LogInfo(" TX Hang: %us", txHang);
LogInfo(" Queue Size: %u", dmrQueueSize);
@ -310,7 +312,8 @@ int Host::run()
}
dmr = new dmr::Control(m_dmrColorCode, callHang, dmrQueueSize, embeddedLCOnly, dumpTAData, m_timeout, m_rfTalkgroupHang,
m_modem, m_network, m_duplex, m_ridLookup, m_tidLookup, rssi, jitter, dmrDumpDataPacket, dmrRepeatDataPacket, dmrDebug, dmrVerbose);
m_modem, m_network, m_duplex, m_ridLookup, m_tidLookup, rssi, jitter, dmrDumpDataPacket, dmrRepeatDataPacket,
dmrDumpCsbkData, dmrDebug, dmrVerbose);
m_dmrTXTimer.setTimeout(txHang);
if (dmrVerbose) {
@ -335,6 +338,7 @@ int Host::run()
bool controlBcstContinuous = p25Protocol["control"]["continuous"].as<bool>(false);
bool p25DumpDataPacket = p25Protocol["dumpDataPacket"].as<bool>(false);
bool p25RepeatDataPacket = p25Protocol["repeatDataPacket"].as<bool>(true);
bool p25DumpTsbkData = p25Protocol["dumpTsbkData"].as<bool>(false);
uint32_t callHang = p25Protocol["callHang"].as<uint32_t>(3U);
uint32_t p25QueueSize = p25Protocol["queueSize"].as<uint32_t>(8192U);
bool p25Verbose = p25Protocol["verbose"].as<bool>(true);
@ -343,6 +347,7 @@ int Host::run()
LogInfo(" TDU Preamble before Voice: %u", tduPreambleCount);
LogInfo(" Dump Packet Data: %s", p25DumpDataPacket ? "yes" : "no");
LogInfo(" Repeat Packet Data: %s", p25RepeatDataPacket ? "yes" : "no");
LogInfo(" Dump TSBK Data: %s", p25DumpTsbkData ? "yes" : "no");
LogInfo(" Call Hang: %us", callHang);
LogInfo(" Queue Size: %u", p25QueueSize);
@ -372,7 +377,8 @@ int Host::run()
}
p25 = new p25::Control(m_p25NAC, callHang, p25QueueSize, m_modem, m_network, m_timeout, m_rfTalkgroupHang,
p25ControlBcstInterval, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, p25DumpDataPacket, p25RepeatDataPacket, p25Debug, p25Verbose);
p25ControlBcstInterval, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, p25DumpDataPacket, p25RepeatDataPacket,
p25DumpTsbkData, p25Debug, p25Verbose);
p25->setOptions(m_conf, m_cwCallsign, m_voiceChNo, m_p25PatchSuperGroup, m_p25NetId, m_p25SysId, m_p25RfssId,
m_p25SiteId, m_channelId, m_channelNo, true);
@ -1202,6 +1208,10 @@ bool Host::createModem()
uint8_t dmrRxDelay = (uint8_t)modemConf["dmrRxDelay"].as<uint32_t>(7U);
int rxDCOffset = modemConf["rxDCOffset"].as<int>(0);
int txDCOffset = modemConf["txDCOffset"].as<int>(0);
int dmrSymLevel3Adj = modemConf["dmrSymLvl3Adj"].as<int>(0);
int dmrSymLevel1Adj = modemConf["dmrSymLvl1Adj"].as<int>(0);
int p25SymLevel3Adj = modemConf["p25SymLvl3Adj"].as<int>(0);
int p25SymLevel1Adj = modemConf["p25SymLvl1Adj"].as<int>(0);
float rxLevel = modemConf["rxLevel"].as<float>(50.0F);
float cwIdTXLevel = modemConf["cwIdTxLevel"].as<float>(50.0F);
float dmrTXLevel = modemConf["dmrTxLevel"].as<float>(50.0F);
@ -1243,6 +1253,7 @@ bool Host::createModem()
m_modem = Modem::createModem(port, m_duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, fdmaPreamble, dmrRxDelay, packetPlayoutTime, disableOFlowReset, trace, debug);
m_modem->setModeParams(m_dmrEnabled, m_p25Enabled);
m_modem->setLevels(rxLevel, cwIdTXLevel, dmrTXLevel, p25TXLevel);
m_modem->setSymbolAdjust(dmrSymLevel3Adj, dmrSymLevel1Adj, p25SymLevel3Adj, p25SymLevel1Adj);
m_modem->setDCOffsetParams(txDCOffset, rxDCOffset);
m_modem->setDMRColorCode(m_dmrColorCode);
m_modem->setP25NAC(m_p25NAC);

@ -148,6 +148,10 @@ HostCal::HostCal(const std::string& confFile) :
m_p25Rx1K(false),
m_txDCOffset(0),
m_rxDCOffset(0),
m_dmrSymLevel3Adj(0),
m_dmrSymLevel1Adj(0),
m_p25SymLevel3Adj(0),
m_p25SymLevel1Adj(0),
m_fdmaPreamble(80U),
m_dmrRxDelay(7U),
m_debug(false),
@ -235,6 +239,11 @@ int HostCal::run()
m_rxLevel = modemConf["rxLevel"].as<float>(50.0F);
m_txLevel = modemConf["txLevel"].as<float>(50.0F);
m_dmrSymLevel3Adj = modemConf["dmrSymLvl3Adj"].as<int>(0);
m_dmrSymLevel1Adj = modemConf["dmrSymLvl1Adj"].as<int>(0);
m_p25SymLevel3Adj = modemConf["p25SymLvl3Adj"].as<int>(0);
m_p25SymLevel1Adj = modemConf["p25SymLvl1Adj"].as<int>(0);
m_fdmaPreamble = (uint8_t)modemConf["fdmaPreamble"].as<uint32_t>(80U);
m_dmrRxDelay = (uint8_t)modemConf["dmrRxDelay"].as<uint32_t>(7U);
@ -307,6 +316,32 @@ int HostCal::run()
setTXDCOffset(1);
break;
case '-':
setDMRSymLevel3Adj(-1);
break;
case '=':
setDMRSymLevel3Adj(1);
break;
case '_':
setDMRSymLevel1Adj(-1);
break;
case '+':
setDMRSymLevel1Adj(1);
break;
case '[':
setP25SymLevel3Adj(-1);
break;
case ']':
setP25SymLevel3Adj(1);
break;
case '{':
setP25SymLevel1Adj(-1);
break;
case '}':
setP25SymLevel1Adj(1);
break;
case '1':
{
m_duplex = true;
@ -626,6 +661,15 @@ void HostCal::displayHelp()
LogMessage(LOG_CAL, " b %s", P25_FEC_STR);
LogMessage(LOG_CAL, " j %s", P25_FEC_1K_STR);
LogMessage(LOG_CAL, " x %s", RSSI_CAL_STR);
LogMessage(LOG_CAL, "Engineering Commands:");
LogMessage(LOG_CAL, " -/= Increase/Decrease DMR +/- 3 Symbol Level");
LogMessage(LOG_CAL, " _/+ Increase/Decrease DMR +/- 1 Symbol Level");
LogMessage(LOG_CAL, " [/] Increase/Decrease P25 +/- 3 Symbol Level");
LogMessage(LOG_CAL, " {/} Increase/Decrease P25 +/- 1 Symbol Level");
LogMessage(LOG_CAL, " 1 Transmit DMR/P25 +3 Symbols Only");
LogMessage(LOG_CAL, " 2 Transmit DMR/P25 +1 Symbols Only");
LogMessage(LOG_CAL, " 3 Transmit DMR/P25 -1 Symbols Only");
LogMessage(LOG_CAL, " 4 Transmit DMR/P25 +1 Symbols Only");
}
/// <summary>
@ -736,6 +780,94 @@ bool HostCal::setRXDCOffset(int incr)
return true;
}
/// <summary>
/// Helper to change the DMR Symbol Level 3 adjust.
/// </summary>
/// <param name="incr">Amount to change.</param>
/// <returns>True, if setting was applied, otherwise false.</returns>
bool HostCal::setDMRSymLevel3Adj(int incr)
{
if (incr > 0 && m_dmrSymLevel3Adj < 127) {
m_dmrSymLevel3Adj++;
LogMessage(LOG_CAL, " - DMR Symbol Level +/- 3 Adjust: %d", m_dmrSymLevel3Adj);
return writeSymbolAdjust();
}
if (incr < 0 && m_dmrSymLevel3Adj > -127) {
m_dmrSymLevel3Adj--;
LogMessage(LOG_CAL, " - DMR Symbol Level +/- 3 Adjust: %d", m_dmrSymLevel3Adj);
return writeSymbolAdjust();
}
return true;
}
/// <summary>
/// Helper to change the DMR Symbol Level 1 adjust.
/// </summary>
/// <param name="incr">Amount to change.</param>
/// <returns>True, if setting was applied, otherwise false.</returns>
bool HostCal::setDMRSymLevel1Adj(int incr)
{
if (incr > 0 && m_dmrSymLevel1Adj < 127) {
m_dmrSymLevel1Adj++;
LogMessage(LOG_CAL, " - DMR Symbol Level +/- 1 Adjust: %d", m_dmrSymLevel1Adj);
return writeSymbolAdjust();
}
if (incr < 0 && m_dmrSymLevel1Adj > -127) {
m_dmrSymLevel1Adj--;
LogMessage(LOG_CAL, " - DMR Symbol Level +/- 1 Adjust: %d", m_dmrSymLevel1Adj);
return writeSymbolAdjust();
}
return true;
}
/// <summary>
/// Helper to change the P25 Symbol Level 3 adjust.
/// </summary>
/// <param name="incr">Amount to change.</param>
/// <returns>True, if setting was applied, otherwise false.</returns>
bool HostCal::setP25SymLevel3Adj(int incr)
{
if (incr > 0 && m_p25SymLevel3Adj < 127) {
m_p25SymLevel3Adj++;
LogMessage(LOG_CAL, " - P25 Symbol Level +/- 3 Adjust: %d", m_p25SymLevel3Adj);
return writeSymbolAdjust();
}
if (incr < 0 && m_p25SymLevel3Adj > -127) {
m_p25SymLevel3Adj--;
LogMessage(LOG_CAL, " - P25 Symbol Level +/- 3 Adjust: %d", m_p25SymLevel3Adj);
return writeSymbolAdjust();
}
return true;
}
/// <summary>
/// Helper to change the P25 Symbol Level 1 adjust.
/// </summary>
/// <param name="incr">Amount to change.</param>
/// <returns>True, if setting was applied, otherwise false.</returns>
bool HostCal::setP25SymLevel1Adj(int incr)
{
if (incr > 0 && m_p25SymLevel1Adj < 127) {
m_p25SymLevel1Adj++;
LogMessage(LOG_CAL, " - P25 Symbol Level +/- 1 Adjust: %d", m_p25SymLevel1Adj);
return writeSymbolAdjust();
}
if (incr < 0 && m_p25SymLevel1Adj > -127) {
m_p25SymLevel1Adj--;
LogMessage(LOG_CAL, " - P25 Symbol Level +/- 1 Adjust: %d", m_p25SymLevel1Adj);
return writeSymbolAdjust();
}
return true;
}
/// <summary>
/// Helper to toggle modem transmit mode.
/// </summary>
@ -1432,7 +1564,7 @@ bool HostCal::writeConfig(uint8_t modeOverride)
uint8_t buffer[50U];
buffer[0U] = DVM_FRAME_START;
buffer[1U] = 16U;
buffer[1U] = 17U;
buffer[2U] = CMD_SET_CONFIG;
buffer[3U] = 0x00U;
@ -1491,7 +1623,52 @@ bool HostCal::writeConfig(uint8_t modeOverride)
m_conf["system"]["modem"]["rxDCOffset"] = __INT_STR(m_rxDCOffset);
buffer[17U] = (uint8_t)(m_rxDCOffset + 128);
int ret = m_serial.write(buffer, 16U);
int ret = m_serial.write(buffer, 17U);
if (ret <= 0)
return false;
sleep(10U);
ret = readModem(buffer, 50U);
if (ret <= 0)
return false;
if (buffer[2U] == CMD_NAK) {
LogError(LOG_CAL, "Got a NAK from the modem");
return false;
}
if (buffer[2U] != CMD_ACK) {
Utils::dump("Invalid response", buffer, ret);
return false;
}
return true;
}
/// <summary>
/// Write symbol level adjustments to the modem DSP.
/// </summary>
/// <returns>True, if level adjustments are written, otherwise false.</returns>
bool HostCal::writeSymbolAdjust()
{
uint8_t buffer[10U];
buffer[0U] = DVM_FRAME_START;
buffer[1U] = 7U;
buffer[2U] = CMD_SET_SYMLVLADJ;
m_conf["system"]["modem"]["dmrSymLvl3Adj"] = __INT_STR(m_dmrSymLevel3Adj);
buffer[3U] = (uint8_t)(m_dmrSymLevel3Adj + 128);
m_conf["system"]["modem"]["dmrSymLvl1Adj"] = __INT_STR(m_dmrSymLevel1Adj);
buffer[4U] = (uint8_t)(m_dmrSymLevel1Adj + 128);
m_conf["system"]["modem"]["p25SymLvl3Adj"] = __INT_STR(m_p25SymLevel3Adj);
buffer[5U] = (uint8_t)(m_p25SymLevel3Adj + 128);
m_conf["system"]["modem"]["p25SymLvl1Adj"] = __INT_STR(m_p25SymLevel1Adj);
buffer[6U] = (uint8_t)(m_p25SymLevel1Adj + 128);
int ret = m_serial.write(buffer, 7U);
if (ret <= 0)
return false;
@ -1571,9 +1748,12 @@ void HostCal::timerStop()
/// </summary>
void HostCal::printStatus()
{
LogMessage(LOG_CAL, " - PTT Invert: %s, RX Invert: %s, TX Invert: %s, DC Blocker: %s, RX Level: %.1f%%, TX Level: %.1f%%, TX DC Offset: %d, RX DC Offset: %d",
m_pttInvert ? "yes" : "no", m_rxInvert ? "yes" : "no", m_txInvert ? "yes" : "no", m_dcBlocker ? "yes" : "no",
LogMessage(LOG_CAL, " - PTT Invert: %s, RX Invert: %s, TX Invert: %s, DC Blocker: %s",
m_pttInvert ? "yes" : "no", m_rxInvert ? "yes" : "no", m_txInvert ? "yes" : "no", m_dcBlocker ? "yes" : "no");
LogMessage(LOG_CAL, " - RX Level: %.1f%%, TX Level: %.1f%%, TX DC Offset: %d, RX DC Offset: %d",
m_rxLevel, m_txLevel, m_txDCOffset, m_rxDCOffset);
LogMessage(LOG_CAL, " - DMR Symbol +/- 3 Level Adj.: %d, DMR Symbol +/- 1 Level Adj.: %d, P25 Symbol +/- 3 Level Adj.: %d, P25 Symbol +/- 1 Level Adj.: %d",
m_dmrSymLevel3Adj, m_dmrSymLevel1Adj, m_p25SymLevel3Adj, m_p25SymLevel1Adj);
LogMessage(LOG_CAL, " - FDMA Preambles: %u (%.1fms), DMR Rx Delay: %u (%.1fms)", m_fdmaPreamble, float(m_fdmaPreamble) * 0.2083F, m_dmrRxDelay, float(m_dmrRxDelay) * 0.0416666F);
LogMessage(LOG_CAL, " - Operating Mode: %s", m_modeStr.c_str());

@ -86,6 +86,11 @@ private:
int m_txDCOffset;
int m_rxDCOffset;
int m_dmrSymLevel3Adj;
int m_dmrSymLevel1Adj;
int m_p25SymLevel3Adj;
int m_p25SymLevel1Adj;
uint8_t m_fdmaPreamble;
uint8_t m_dmrRxDelay;
@ -117,6 +122,15 @@ private:
/// <summary>Helper to toggle modem transmit mode.</summary>
bool setTransmit();
/// <summary>Helper to change the DMR Symbol Level 3 adjust.</summary>
bool setDMRSymLevel3Adj(int incr);
/// <summary>Helper to change the DMR Symbol Level 1 adjust.</summary>
bool setDMRSymLevel1Adj(int incr);
/// <summary>Helper to change the P25 Symbol Level 3 adjust.</summary>
bool setP25SymLevel3Adj(int incr);
/// <summary>Helper to change the P25 Symbol Level 1 adjust.</summary>
bool setP25SymLevel1Adj(int incr);
/// <summary>Initializes the modem DSP.</summary>
bool initModem();
/// <summary>Read data frames from the modem DSP.</summary>
@ -135,6 +149,8 @@ private:
bool writeConfig();
/// <summary>Write configuration to the modem DSP.</summary>
bool writeConfig(uint8_t modeOverride);
/// <summary>Write symbol level adjustments to the modem DSP.</summary>
bool writeSymbolAdjust();
/// <summary></summary>
void sleep(uint32_t ms);

@ -94,6 +94,10 @@ Modem::Modem(const std::string& port, bool duplex, bool rxInvert, bool txInvert,
m_p25Enabled(false),
m_rxDCOffset(0),
m_txDCOffset(0),
m_dmrSymLevel3Adj(0),
m_dmrSymLevel1Adj(0),
m_p25SymLevel3Adj(0),
m_p25SymLevel1Adj(0),
m_adcOverFlowCount(0U),
m_dacOverFlowCount(0U),
m_serial(port, SERIAL_115200, true),
@ -167,6 +171,40 @@ void Modem::setLevels(float rxLevel, float cwIdTXLevel, float dmrTXLevel, float
m_p25TXLevel = p25TXLevel;
}
/// <summary>
/// Sets the modem DSP Symbol adjustment levels
/// </summary>
/// <param name="dmrSymLevel3Adj"></param>
/// <param name="dmrSymLevel1Adj"></param>
/// <param name="p25SymLevel3Adj"></param>
/// <param name="p25SymLevel1Adj"></param>
void Modem::setSymbolAdjust(int dmrSymLevel3Adj, int dmrSymLevel1Adj, int p25SymLevel3Adj, int p25SymLevel1Adj)
{
m_dmrSymLevel3Adj = dmrSymLevel3Adj;
if (dmrSymLevel3Adj > 128)
m_dmrSymLevel3Adj = 0;
if (dmrSymLevel3Adj < -128)
m_dmrSymLevel3Adj = 0;
m_dmrSymLevel1Adj = dmrSymLevel1Adj;
if (dmrSymLevel1Adj > 128)
m_dmrSymLevel1Adj = 0;
if (dmrSymLevel1Adj < -128)
m_dmrSymLevel1Adj = 0;
m_p25SymLevel3Adj = p25SymLevel3Adj;
if (p25SymLevel3Adj > 128)
m_p25SymLevel3Adj = 0;
if (p25SymLevel3Adj < -128)
m_p25SymLevel3Adj = 0;
m_p25SymLevel1Adj = p25SymLevel1Adj;
if (p25SymLevel1Adj > 128)
m_p25SymLevel1Adj = 0;
if (p25SymLevel1Adj < -128)
m_p25SymLevel1Adj = 0;
}
/// <summary>
/// Sets the modem DSP DMR color code.
/// </summary>
@ -263,6 +301,8 @@ bool Modem::open()
return false;
}
writeSymbolAdjust();
m_statusTimer.start();
m_error = false;
@ -1125,7 +1165,7 @@ bool Modem::writeConfig()
uint8_t buffer[20U];
buffer[0U] = DVM_FRAME_START;
buffer[1U] = 16U;
buffer[1U] = 17U;
buffer[2U] = CMD_SET_CONFIG;
buffer[3U] = 0x00U;
@ -1177,10 +1217,10 @@ bool Modem::writeConfig()
buffer[16U] = (uint8_t)(m_txDCOffset + 128);
buffer[17U] = (uint8_t)(m_rxDCOffset + 128);
// Utils::dump(1U, "Written", buffer, 16U);
// Utils::dump(1U, "Written", buffer, 17U);
int ret = m_serial.write(buffer, 16U);
if (ret != 16)
int ret = m_serial.write(buffer, 17U);
if (ret != 17)
return false;
uint32_t count = 0U;
@ -1210,6 +1250,55 @@ bool Modem::writeConfig()
return true;
}
/// <summary>
/// Write symbol level adjustments to the modem DSP.
/// </summary>
/// <returns>True, if level adjustments are written, otherwise false.</returns>
bool Modem::writeSymbolAdjust()
{
uint8_t buffer[10U];
buffer[0U] = DVM_FRAME_START;
buffer[1U] = 7U;
buffer[2U] = CMD_SET_SYMLVLADJ;
buffer[3U] = (uint8_t)(m_dmrSymLevel3Adj + 128);
buffer[4U] = (uint8_t)(m_dmrSymLevel1Adj + 128);
buffer[5U] = (uint8_t)(m_p25SymLevel3Adj + 128);
buffer[6U] = (uint8_t)(m_p25SymLevel1Adj + 128);
int ret = m_serial.write(buffer, 7U);
if (ret <= 0)
return false;
uint32_t count = 0U;
RESP_TYPE_DVM resp;
do {
Thread::sleep(10U);
resp = getResponse();
if (resp == RTM_OK && m_buffer[2U] != CMD_ACK && m_buffer[2U] != CMD_NAK) {
count++;
if (count >= MAX_RESPONSES) {
LogError(LOG_MODEM, "No response, SET_SYMLVLADJ command");
return false;
}
}
} while (resp == RTM_OK && m_buffer[2U] != CMD_ACK && m_buffer[2U] != CMD_NAK);
// Utils::dump(1U, "Response", m_buffer, m_length);
if (resp == RTM_OK && m_buffer[2U] == CMD_NAK) {
LogError(LOG_MODEM, "NAK to the SET_SYMLVLADJ command from the modem");
return false;
}
m_playoutTimer.start();
return true;
}
/// <summary>
///
/// </summary>

@ -93,6 +93,7 @@ namespace modem
CMD_SET_CONFIG = 0x02U,
CMD_SET_MODE = 0x03U,
CMD_SET_SYMLVLADJ = 0x04U,
CMD_SET_RXLEVEL = 0x05U,
CMD_CAL_DATA = 0x08U,
@ -175,6 +176,8 @@ namespace modem
virtual void setModeParams(bool dmrEnabled, bool p25Enabled);
/// <summary>Sets the modem DSP RF deviation levels.</summary>
virtual void setLevels(float rxLevel, float cwIdTXLevel, float dmrTXLevel, float p25TXLevel);
/// <summary>Sets the modem DSP Symbol adjustment levels.</summary>
virtual void setSymbolAdjust(int dmrSymLevel3Adj, int dmrSymLevel1Adj, int p25SymLevel3Adj, int p25SymLevel1Adj);
/// <summary>Sets the modem DSP DMR color code.</summary>
virtual void setDMRColorCode(uint32_t colorCode);
/// <summary>Sets the modem DSP P25 NAC.</summary>
@ -285,6 +288,11 @@ namespace modem
int m_rxDCOffset;
int m_txDCOffset;
int m_dmrSymLevel3Adj;
int m_dmrSymLevel1Adj;
int m_p25SymLevel3Adj;
int m_p25SymLevel1Adj;
uint32_t m_adcOverFlowCount;
uint32_t m_dacOverFlowCount;
@ -319,6 +327,8 @@ namespace modem
bool getStatus();
/// <summary>Write configuration to the modem DSP.</summary>
bool writeConfig();
/// <summary>Write symbol level adjustments to the modem DSP.</summary>
bool writeSymbolAdjust();
/// <summary></summary>
void printDebug();

@ -55,6 +55,8 @@ namespace modem
virtual void setModeParams(bool dmrEnabled, bool p25Enabled) { return; }
/// <summary>Sets the modem DSP RF deviation levels.</summary>
virtual void setLevels(float rxLevel, float cwIdTXLevel, float dmrTXLevel, float p25TXLevel) { return; }
/// <summary>Sets the modem DSP Symbol adjustment levels</summary>
virtual void setSymbolAdjust(int dmrSymLevel3Adj, int dmrSymLevel1Adj, int p25SymLevel3Adj, int p25SymLevel1Adj) { return; }
/// <summary>Sets the modem DSP DMR color code.</summary>
virtual void setDMRColorCode(uint32_t colorCode) { return; }
/// <summary>Sets the modem DSP RF receive deviation levels.</summary>

@ -74,12 +74,13 @@ const uint32_t MAX_PREAMBLE_TDU_CNT = 64U;
/// <param name="rssi">Instance of the CRSSIInterpolator class.</param>
/// <param name="dumpPDUData"></param>
/// <param name="repeatPDU"></param>
/// <param name="dumpTSBKData"></param>
/// <param name="debug">Flag indicating whether P25 debug is enabled.</param>
/// <param name="verbose">Flag indicating whether P25 verbose logging is enabled.</param>
Control::Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Modem* modem, network::BaseNetwork* network,
uint32_t timeout, uint32_t tgHang, uint32_t ccBcstInterval, bool duplex, lookups::RadioIdLookup* ridLookup,
lookups::TalkgroupIdLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper,
bool dumpPDUData, bool repeatPDU, bool debug, bool verbose) :
bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose) :
m_voice(NULL),
m_data(NULL),
m_trunk(NULL),
@ -94,6 +95,7 @@ Control::Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Mod
m_control(false),
m_continuousControl(false),
m_voiceOnControl(false),
m_ackTSBKRequests(true),
m_idenTable(idenTable),
m_ridLookup(ridLookup),
m_tidLookup(tidLookup),
@ -135,7 +137,7 @@ Control::Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Mod
m_voice = new VoicePacket(this, network, debug, verbose);
m_data = new DataPacket(this, network, dumpPDUData, repeatPDU, debug, verbose);
m_trunk = new TrunkPacket(this, network, debug, verbose);
m_trunk = new TrunkPacket(this, network, dumpTSBKData, debug, verbose);
}
/// <summary>
@ -208,6 +210,7 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s
}
m_voiceOnControl = p25Protocol["voiceOnControl"].as<bool>(false);
m_ackTSBKRequests = control["ackRequests"].as<bool>(true);
m_voice->m_silenceThreshold = p25Protocol["silenceThreshold"].as<uint32_t>(p25::DEFAULT_SILENCE_THRESHOLD);
@ -224,6 +227,7 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s
if (m_control) {
LogInfo(" Voice on Control: %s", m_voiceOnControl ? "yes" : "no");
LogInfo(" Ack Requests: %s", m_ackTSBKRequests ? "yes" : "no");
}
LogInfo(" Inhibit Illegal: %s", m_inhibitIllegal ? "yes" : "no");
@ -823,7 +827,15 @@ void Control::addBusyBits(uint8_t* data, uint32_t length, bool b1, bool b2)
{
assert(data != NULL);
// insert the "10" (Unknown, use for inbound or outbound) status bits
for (uint32_t ss0Pos = P25_SS0_START; ss0Pos < length; ss0Pos += P25_SS_INCREMENT) {
uint32_t ss1Pos = ss0Pos + 1U;
WRITE_BIT(data, ss0Pos, true); // 1
WRITE_BIT(data, ss1Pos, false); // 0
}
// interleave the requested status bits (every other)
for (uint32_t ss0Pos = P25_SS0_START; ss0Pos < length; ss0Pos += (P25_SS_INCREMENT * 2)) {
uint32_t ss1Pos = ss0Pos + 1U;
WRITE_BIT(data, ss0Pos, b1);
WRITE_BIT(data, ss1Pos, b2);

@ -69,7 +69,7 @@ namespace p25
Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Modem* modem, network::BaseNetwork* network,
uint32_t timeout, uint32_t tgHang, uint32_t ccBcstInterval, bool duplex, lookups::RadioIdLookup* ridLookup,
lookups::TalkgroupIdLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper,
bool dumpPDUData, bool repeatPDU, bool debug, bool verbose);
bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose);
/// <summary>Finalizes a instance of the Control class.</summary>
~Control();
@ -128,6 +128,7 @@ namespace p25
bool m_control;
bool m_continuousControl;
bool m_voiceOnControl;
bool m_ackTSBKRequests;
lookups::IdenTableLookup* m_idenTable;
lookups::RadioIdLookup* m_ridLookup;

@ -40,6 +40,7 @@
#include "Utils.h"
using namespace p25;
using namespace p25::data;
#include <cassert>
#include <cstdio>
@ -163,8 +164,6 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
}
writeNetworkRF(P25_DT_DATA_HEADER, buffer, P25_PDU_FEC_LENGTH_BYTES);
::ActivityLog("P25", true, "received RF data transmission from %u to %u, %u blocks", m_rfDataHeader.getLLId(), m_rfDataHeader.getLLId(), m_rfDataHeader.getBlocksToFollow());
}
if (m_p25->m_rfState == RS_RF_DATA) {
@ -274,6 +273,11 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
LogMessage(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_REQ_CNCT (Registration Request Connect), llId = %u, ipAddr = %u", llId, ipAddr);
}
if (!acl::AccessControl::validateSrcId(llId)) {
LogWarning(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_RSP_DENY (Registration Response Deny), llId = %u, ipAddr = %u", llId, ipAddr);
writeRF_PDU_Reg_Response(PDU_REG_TYPE_RSP_DENY, llId, ipAddr);
}
else {
if (!hasLLIdFNEReg(llId)) {
// update dynamic FNE registration table entry
m_fneRegTable[llId] = ipAddr;
@ -283,9 +287,6 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
}
writeRF_PDU_Reg_Response(PDU_REG_TYPE_RSP_ACCPT, llId, ipAddr);
}
else {
LogWarning(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_RSP_DENY (Registration Response Deny), llId = %u, ipAddr = %u", llId, ipAddr);
writeRF_PDU_Reg_Response(PDU_REG_TYPE_RSP_DENY, llId, ipAddr);
}
}
break;
@ -316,6 +317,8 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
}
break;
default:
::ActivityLog("P25", true, "received RF data transmission from %u to %u, %u blocks", m_rfDataHeader.getLLId(), m_rfDataHeader.getLLId(), m_rfDataHeader.getBlocksToFollow());
if (m_repeatPDU) {
if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", repeating PDU, llId = %u", (m_rfDataHeader.getSAP() == PDU_SAP_EXT_ADDR) ? m_rfSecondHeader.getLLId() : m_rfDataHeader.getLLId());
@ -323,10 +326,10 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
writeRF_PDU(); // re-generate PDU and send it on
}
break;
}
::ActivityLog("P25", true, "end RF data transmission");
break;
}
m_rfDataHeader.reset();
m_rfDataBlockCnt = 0U;
@ -696,23 +699,16 @@ void DataPacket::writeRF_PDU_Reg_Response(uint8_t regType, uint32_t llId, ulong6
uint8_t buffer[P25_PDU_FEC_LENGTH_BYTES];
::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES);
m_rfDataHeader.reset();
// set header fields
m_rfDataHeader.setAckNeeded(true);
m_rfDataHeader.setOutbound(true);
m_rfDataHeader.setSAP(PDU_SAP_REG);
m_rfDataHeader.setLLId(llId);
m_rfDataHeader.setFullMessage(true);
m_rfDataHeader.setBlocksToFollow(1U);
m_rfDataHeader.setPadCount(0U);
m_rfDataHeader.setSync(false);
m_rfDataHeader.setN(0U);
m_rfDataHeader.setSeqNo(8U);
m_rfDataHeader.setHeaderOffset(0U);
DataRspHeader rspHeader = DataRspHeader();
rspHeader.setOutbound(true);
rspHeader.setClass(PDU_ACK_CLASS_ACK);
rspHeader.setType(PDU_ACK_TYPE_ACK);
rspHeader.setLLId(llId);
rspHeader.setSrcLLId(P25_WUID_FNE);
rspHeader.setBlocksToFollow(1U);
// Generate the PDU header and 1/2 rate Trellis
m_rfDataHeader.encode(buffer);
rspHeader.encode(buffer);
Utils::setBitRange(buffer, m_rfPDU, offset, P25_PDU_FEC_LENGTH_BITS);
offset += P25_PDU_FEC_LENGTH_BITS;
@ -733,7 +729,7 @@ void DataPacket::writeRF_PDU_Reg_Response(uint8_t regType, uint32_t llId, ulong6
edac::CRC::addCRC32(buffer, P25_PDU_CONFIRMED_LENGTH_BYTES);
// Generate the PDU data
m_rfData[0].setFormat(m_rfDataHeader);
m_rfData[0].setFormat(PDU_FMT_RSP);
m_rfData[0].setSerialNo(0U);
m_rfData[0].setHalfRateTrellis(true);
m_rfData[0].setData(buffer);

@ -34,6 +34,7 @@
#include "Defines.h"
#include "p25/data/DataBlock.h"
#include "p25/data/DataHeader.h"
#include "p25/data/DataRspHeader.h"
#include "p25/data/LowSpeedData.h"
#include "p25/lc/LC.h"
#include "p25/Control.h"

@ -201,6 +201,22 @@ namespace p25
const uint8_t PDU_SAP_UNENC_KMM = 0x28U;
const uint8_t PDU_SAP_ENC_KMM = 0x29U;
// PDU ACK Class
const uint8_t PDU_ACK_CLASS_ACK = 0x00U;
const uint8_t PDU_ACK_CLASS_NACK = 0x01U;
const uint8_t PDU_ACK_CLASS_ACK_RETRY = 0x02U;
// PDU ACK Type(s)
const uint8_t PDU_ACK_TYPE_ACK = 0x01U;
const uint8_t PDU_ACK_TYPE_NACK_ILLEGAL = 0x00U; // Illegal Format
const uint8_t PDU_ACK_TYPE_NACK_PACKET_CRC = 0x01U; // Packet CRC
const uint8_t PDU_ACK_TYPE_NACK_MEMORY_FULL = 0x02U; // Memory Full
const uint8_t PDU_ACK_TYPE_NACK_SEQ = 0x03U; // Out of logical sequence FSN
const uint8_t PDU_ACK_TYPE_NACK_UNDELIVERABLE = 0x04U;// Undeliverable
const uint8_t PDU_ACK_TYPE_NACK_OUT_OF_SEQ = 0x05U; // Out of sequence, N(S) != V(R) or V(R) + 1
const uint8_t PDU_ACK_TYPE_NACK_INVL_USER = 0x06U; // Invalid User disallowed by the system
// PDU Registration Type(s)
const uint8_t PDU_REG_TYPE_REQ_CNCT = 0x00U;
const uint8_t PDU_REG_TYPE_REQ_DISCNCT = 0x01U;

@ -377,7 +377,9 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len)
m_rfTSBK.getResponse(), srcId, dstId);
}
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_UU_ANS, true);
if (m_p25->m_ackTSBKRequests) {
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_UU_ANS, false, true);
}
if (m_rfTSBK.getResponse() == P25_ANS_RSP_PROCEED) {
writeRF_TSDU_Grant(false, false);
@ -401,14 +403,16 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len)
m_rfTSBK.getResponse(), srcId);
}
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_TELE_INT_ANS, true);
if (m_p25->m_ackTSBKRequests) {
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_TELE_INT_ANS, false, true);
}
if (m_rfTSBK.getResponse() == P25_ANS_RSP_PROCEED) {
//writeRF_TSDU_Grant(false);
writeRF_TSDU_Deny(P25_DENY_RSN_SYS_UNSUPPORTED_SVC, TSBK_IOSP_TELE_INT_ANS);
}
else if (m_rfTSBK.getResponse() == P25_ANS_RSP_DENY) {
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_TELE_INT_ANS, true);
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_TELE_INT_ANS, false, true);
}
else if (m_rfTSBK.getResponse() == P25_ANS_RSP_WAIT) {
writeRF_TSDU_Queue(P25_QUE_RSN_TGT_UNIT_QUEUED, TSBK_IOSP_TELE_INT_ANS);
@ -429,7 +433,7 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len)
::ActivityLog("P25", true, "received status update from %u", srcId);
if (!m_noStatusAck) {
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_STS_UPDT, false);
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_STS_UPDT, false, false);
}
if (m_statusCmdEnable) {
@ -448,7 +452,7 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len)
}
if (!m_noMessageAck) {
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_MSG_UPDT, false);
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_MSG_UPDT, false, false);
}
::ActivityLog("P25", true, "received message update from %u", srcId);
@ -510,7 +514,7 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len)
::ActivityLog("P25", true, "received cancel service request from %u", srcId);
writeRF_TSDU_ACK_FNE(srcId, TSBK_ISP_CAN_SRV_REQ, true);
writeRF_TSDU_ACK_FNE(srcId, TSBK_ISP_CAN_SRV_REQ, false, true);
break;
case TSBK_IOSP_EXT_FNCT:
if (m_verbose) {
@ -552,7 +556,9 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len)
LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_IOSP_GRP_AFF (Group Affiliation Request), srcId = %u, dstId = %u", srcId, dstId);
}
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_GRP_AFF, true);
if (m_p25->m_ackTSBKRequests) {
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_GRP_AFF, true, true);
}
writeRF_TSDU_Grp_Aff_Rsp(srcId, dstId);
break;
case TSBK_ISP_GRP_AFF_Q_RSP:
@ -578,7 +584,9 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len)
dstId = P25_WUID_SYS;
}
writeRF_TSDU_ACK_FNE(srcId, TSBK_ISP_U_DEREG_REQ, true);
if (m_p25->m_ackTSBKRequests) {
writeRF_TSDU_ACK_FNE(srcId, TSBK_ISP_U_DEREG_REQ, true, true);
}
writeRF_TSDU_U_Dereg_Ack(srcId);
break;
case TSBK_IOSP_U_REG:
@ -589,7 +597,9 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len)
LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_ISP_U_REG_REQ (Unit Registration Request), srcId = %u", srcId);
}
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_U_REG, true);
if (m_p25->m_ackTSBKRequests) {
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_U_REG, true, true);
}
writeRF_TSDU_U_Reg_Rsp(srcId);
break;
case TSBK_ISP_LOC_REG_REQ:
@ -1211,9 +1221,10 @@ void TrunkPacket::writeRF_TSDU_Mot_Patch(uint32_t group1, uint32_t group2, uint3
/// </summary>
/// <param name="p25">Instance of the Control class.</param>
/// <param name="network">Instance of the BaseNetwork class.</param>
/// <param name="dumpTSBKData"></param>
/// <param name="debug">Flag indicating whether P25 debug is enabled.</param>
/// <param name="verbose">Flag indicating whether P25 verbose logging is enabled.</param>
TrunkPacket::TrunkPacket(Control* p25, network::BaseNetwork* network, bool debug, bool verbose) :
TrunkPacket::TrunkPacket(Control* p25, network::BaseNetwork* network, bool dumpTSBKData, bool debug, bool verbose) :
m_p25(p25),
m_network(network),
m_patchSuperGroup(0xFFFFU),
@ -1260,9 +1271,13 @@ TrunkPacket::TrunkPacket(Control* p25, network::BaseNetwork* network, bool debug
m_netTSBK.setSiteData(m_siteData);
m_rfTSBK.setCallsign("CHANGEME");
m_netTSBK.setCallsign("CHANGEME");
m_rfTSBK.setVerbose(dumpTSBKData);
m_netTSBK.setVerbose(dumpTSBKData);
m_rfTDULC.setSiteData(m_siteData);
m_netTDULC.setSiteData(m_siteData);
m_rfTDULC.setVerbose(dumpTSBKData);
m_netTDULC.setVerbose(dumpTSBKData);
m_voiceChTable.clear();
@ -1534,7 +1549,7 @@ void TrunkPacket::writeRF_TSDU_SBF(bool noNetwork, bool clearBeforeWrite)
}
// Add busy bits
m_p25->addBusyBits(data + 2U, P25_TSDU_FRAME_LENGTH_BITS, true, false);
m_p25->addBusyBits(data + 2U, P25_TSDU_FRAME_LENGTH_BITS, false, true);
// Set first busy bits to 1,1
m_p25->setBusyBits(data + 2U, P25_SS0_START, true, true);
@ -1637,7 +1652,7 @@ void TrunkPacket::writeRF_TSDU_MBF(bool clearBeforeWrite)
P25Utils::encode(tsdu, data + 2U, 114U, 720U);
// Add busy bits
m_p25->addBusyBits(data + 2U, P25_TSDU_TRIPLE_FRAME_LENGTH_BITS, true, false);
m_p25->addBusyBits(data + 2U, P25_TSDU_TRIPLE_FRAME_LENGTH_BITS, false, true);
// Add idle bits
addIdleBits(data + 2U, P25_TSDU_TRIPLE_FRAME_LENGTH_BITS, true, true);
@ -1959,7 +1974,7 @@ void TrunkPacket::writeRF_TSDU_UU_Ans_Req(uint32_t srcId, uint32_t dstId)
/// <param name="srcId"></param>
/// <param name="service"></param>
/// <param name="noNetwork"></param>
void TrunkPacket::writeRF_TSDU_ACK_FNE(uint32_t srcId, uint32_t service, bool noNetwork)
void TrunkPacket::writeRF_TSDU_ACK_FNE(uint32_t srcId, uint32_t service, bool extended, bool noNetwork)
{
uint8_t lco = m_rfTSBK.getLCO();
uint8_t mfId = m_rfTSBK.getMFId();
@ -1968,9 +1983,14 @@ void TrunkPacket::writeRF_TSDU_ACK_FNE(uint32_t srcId, uint32_t service, bool no
m_rfTSBK.setMFId(P25_MFG_STANDARD);
m_rfTSBK.setService(service);
if (extended) {
m_rfTSBK.setAIV(true);
m_rfTSBK.setEX(true);
}
if (m_verbose) {
LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_IOSP_ACK_RSP (Acknowledge Response), AIV = %u, serviceType = $%02X, srcId = %u",
m_rfTSBK.getAIV(), m_rfTSBK.getService(), srcId);
LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_IOSP_ACK_RSP (Acknowledge Response), AIV = %u, EX = %u, serviceType = $%02X, srcId = %u",
m_rfTSBK.getAIV(), m_rfTSBK.getEX(), m_rfTSBK.getService(), srcId);
}
writeRF_TSDU_SBF(noNetwork);
@ -2086,9 +2106,7 @@ void TrunkPacket::writeRF_TSDU_U_Reg_Rsp(uint32_t srcId)
}
}
// because Motorola -- we'll set both Source and Destination to the Source ID
// for the U_REG_RSP apparently should have the SUID set this way...
m_rfTSBK.setSrcId(srcId/*P25_WUID_REG*/);
m_rfTSBK.setSrcId(srcId);
m_rfTSBK.setDstId(srcId);
writeRF_TSDU_SBF(true);
@ -2176,7 +2194,7 @@ void TrunkPacket::writeNet_TSDU_From_RF(uint8_t* data)
m_rfTSBK.encode(data, true);
// Add busy bits
m_p25->addBusyBits(data, P25_TSDU_FRAME_LENGTH_BYTES, true, false);
m_p25->addBusyBits(data, P25_TSDU_FRAME_LENGTH_BYTES, false, true);
// Set first busy bits to 1,1
m_p25->setBusyBits(data, P25_SS0_START, true, true);
@ -2255,7 +2273,7 @@ void TrunkPacket::writeNet_TSDU()
m_netTSBK.encode(buffer + 2U, true);
// Add busy bits
m_p25->addBusyBits(buffer + 2U, P25_TSDU_FRAME_LENGTH_BYTES, true, false);
m_p25->addBusyBits(buffer + 2U, P25_TSDU_FRAME_LENGTH_BYTES, false, true);
// Set first busy bits to 1,1
m_p25->setBusyBits(buffer + 2U, P25_SS0_START, true, true);
@ -2360,7 +2378,7 @@ bool TrunkPacket::processStatusCommand(uint32_t srcId, uint32_t dstId)
resetStatusCommand();
}
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_CALL_ALRT, false);
writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_CALL_ALRT, false, false);
return true;
}
else {

@ -183,7 +183,7 @@ namespace p25
bool m_debug;
/// <summary>Initializes a new instance of the TrunkPacket class.</summary>
TrunkPacket(Control* p25, network::BaseNetwork* network, bool debug, bool verbose);
TrunkPacket(Control* p25, network::BaseNetwork* network, bool dumpTSBKData, bool debug, bool verbose);
/// <summary>Finalizes a instance of the TrunkPacket class.</summary>
~TrunkPacket();
@ -213,7 +213,7 @@ namespace p25
/// <summary>Helper to write a unit to unit answer request packet.</summary>
void writeRF_TSDU_UU_Ans_Req(uint32_t srcId, uint32_t dstId);
/// <summary>Helper to write a acknowledge packet.</summary>
void writeRF_TSDU_ACK_FNE(uint32_t srcId, uint32_t service, bool noNetwork);
void writeRF_TSDU_ACK_FNE(uint32_t srcId, uint32_t service, bool extended, bool noNetwork);
/// <summary>Helper to write a deny packet.</summary>
void writeRF_TSDU_Deny(uint8_t reason, uint8_t service);
/// <summary>Helper to write a group affiliation response packet.</summary>

@ -177,6 +177,13 @@ void DataBlock::encode(uint8_t* data)
}
/// <summary>Sets the data format.</summary>
/// <param name="fmt"></param>
void DataBlock::setFormat(const uint8_t fmt)
{
m_fmt = fmt;
}
/// <summary>Sets the data format from the data header.</summary>
/// <param name="header"></param>
void DataBlock::setFormat(const DataHeader header)
{

@ -58,6 +58,8 @@ namespace p25
void encode(uint8_t* data);
/// <summary>Sets the data format.</summary>
void setFormat(const uint8_t fmt);
/// <summary>Sets the data format from the data header.</summary>
void setFormat(const DataHeader header);
/// <summary>Gets the data format.</summary>
uint8_t getFormat() const;

@ -0,0 +1,196 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
//
// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2020 by Bryan Biedenkapp N2PLL
*
* 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 "p25/P25Defines.h"
#include "p25/data/DataRspHeader.h"
#include "edac/CRC.h"
#include "Utils.h"
using namespace p25::data;
using namespace p25;
#include <cstdio>
#include <cassert>
#include <cstring>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the DataRspHeader class.
/// </summary>
DataRspHeader::DataRspHeader() :
m_outbound(true),
m_rspClass(PDU_ACK_CLASS_NACK),
m_rspType(PDU_ACK_TYPE_NACK_ILLEGAL),
m_rspStatus(0U),
m_mfId(P25_MFG_STANDARD),
m_llId(0U),
m_srcLlId(0U),
m_extended(true),
m_trellis(),
m_blocksToFollow(0U),
m_dataOctets(0U)
{
/* stub */
}
/// <summary>
/// Finalizes a instance of the DataRspHeader class.
/// </summary>
DataRspHeader::~DataRspHeader()
{
/* stub */
}
/// <summary>
/// Decodes P25 PDU data response header.
/// </summary>
/// <param name="data"></param>
/// <returns>True, if PDU data response header was decoded, otherwise false.</returns>
bool DataRspHeader::decode(const uint8_t* data)
{
assert(data != NULL);
uint8_t header[P25_PDU_HEADER_LENGTH_BYTES];
::memset(header, 0x00U, P25_PDU_HEADER_LENGTH_BYTES);
// decode 1/2 rate Trellis & check CRC-CCITT 16
bool valid = m_trellis.decode12(data, header);
if (valid)
valid = edac::CRC::checkCCITT162(header, P25_PDU_HEADER_LENGTH_BYTES);
if (!valid) {
return false;
}
// Utils::dump(1U, "PDU Response Header Data", data, P25_PDU_HEADER_LENGTH_BYTES);
m_outbound = (header[0U] & 0x20U) == 0x20U; // Inbound/Outbound
m_rspClass = (header[1U] >> 6) & 0x03U; // Response Class
m_rspType = (header[1U] >> 3) & 0x07U; // Response Type
m_rspStatus = header[1U] & 0x07U; // Response Status
m_mfId = header[2U]; // Mfg Id.
m_llId = (header[3U] << 16) + (header[4U] << 8) + header[5U]; // Logical Link ID
m_extended = (header[6U] & 0x80U) == 0x80U; // Extended Addressing
m_blocksToFollow = header[6U] & 0x7FU; // Block Frames to Follow
m_srcLlId = (header[7U] << 16) + (header[8U] << 8) + header[9U]; // Source Logical Link ID
return true;
}
/// <summary>
/// Encodes P25 PDU data response header.
/// </summary>
/// <param name="data"></param>
void DataRspHeader::encode(uint8_t * data)
{
assert(data != NULL);
uint8_t header[P25_PDU_HEADER_LENGTH_BYTES];
::memset(header, 0x00U, P25_PDU_HEADER_LENGTH_BYTES);
header[0U] = PDU_FMT_RSP;
header[0U] = (m_outbound ? 0x20U : 0x00U) + // Inbound/Outbound
(PDU_FMT_RSP & 0x1FU); // Packet Format
header[1U] = ((m_rspClass & 0x03U) << 6) + // Response Class
((m_rspType & 0x07U) << 3) + // Response Type
((m_rspStatus & 0x07U)); // Response Status
header[2U] = m_mfId; // Mfg Id.
header[3U] = (m_llId >> 16) & 0xFFU; // Logical Link ID
header[4U] = (m_llId >> 8) & 0xFFU;
header[5U] = (m_llId >> 0) & 0xFFU;
header[6U] = (m_extended ? 0x80U : 0x00U) + // Extended Addressing
(m_blocksToFollow & 0x7FU); // Blocks Frames to Follow
header[7U] = (m_srcLlId >> 16) & 0xFFU; // Source Logical Link ID
header[8U] = (m_srcLlId >> 8) & 0xFFU;
header[9U] = (m_srcLlId >> 0) & 0xFFU;
// compute CRC-CCITT 16
edac::CRC::addCCITT162(header, P25_PDU_HEADER_LENGTH_BYTES);
// encode 1/2 rate Trellis
m_trellis.encode12(header, data);
}
/// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void DataRspHeader::reset()
{
m_outbound = false;
m_rspClass = PDU_ACK_CLASS_NACK;
m_rspType = PDU_ACK_TYPE_NACK_ILLEGAL;
m_rspStatus = 0U;
m_mfId = P25_MFG_STANDARD;
m_llId = 0U;
m_srcLlId = 0U;
m_extended = true;
m_blocksToFollow = 0U;
m_dataOctets = 0U;
}
/// <summary>Gets the total number of data octets.</summary>
/// <returns></returns>
uint32_t DataRspHeader::getDataOctets() const
{
return m_dataOctets;
}
/** Common Data */
/// <summary>Sets the total number of blocks to follow this header.</summary>
/// <param name="blocksToFollow"></param>
void DataRspHeader::setBlocksToFollow(uint8_t blocksToFollow)
{
m_blocksToFollow = blocksToFollow;
// recalculate count of data octets
m_dataOctets = 16 * m_blocksToFollow - 4;
}
/// <summary>Gets the total number of blocks to follow this header.</summary>
/// <returns></returns>
uint8_t DataRspHeader::getBlocksToFollow() const
{
return m_blocksToFollow;
}

@ -0,0 +1,98 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
//
// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2020 by Bryan Biedenkapp N2PLL
*
* 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(__P25_DATA__DATA_RSP_HEADER_H__)
#define __P25_DATA__DATA_RSP_HEADER_H__
#include "Defines.h"
#include "p25/edac/Trellis.h"
#include <string>
namespace p25
{
namespace data
{
// ---------------------------------------------------------------------------
// Class Declaration
// Represents the data response header for PDU P25 packets.
// ---------------------------------------------------------------------------
class HOST_SW_API DataRspHeader {
public:
/// <summary>Initializes a new instance of the DataRspHeader class.</summary>
DataRspHeader();
/// <summary>Finalizes a instance of the DataRspHeader class.</summary>
~DataRspHeader();
/// <summary>Decodes P25 PDU data response header.</summary>
bool decode(const uint8_t* data);
/// <summary>Encodes P25 PDU data response header.</summary>
void encode(uint8_t* data);
/// <summary>Helper to reset data values to defaults.</summary>
void reset();
/// <summary>Gets the total number of data octets.</summary>
uint32_t getDataOctets() const;
/** Common Data */
/// <summary>Sets the total number of blocks to follow this header.</summary>
void setBlocksToFollow(uint8_t blocksToFollow);
/// <summary>Gets the total number of blocks to follow this header.</summary>
uint8_t getBlocksToFollow() const;
public:
/// <summary>Flag indicating if this is an outbound data packet.</summary>
__PROPERTY(bool, outbound, Outbound);
/// <summary>Response class.</summary>
__PROPERTY(uint8_t, rspClass, Class);
/// <summary>Response type.</summary>
__PROPERTY(uint8_t, rspType, Type);
/// <summary>Response status.</summary>
__PROPERTY(uint8_t, rspStatus, Status);
/// <summary>Manufacturer ID.</summary>
__PROPERTY(uint8_t, mfId, MFId);
/// <summary>Logical link ID.</summary>
__PROPERTY(uint32_t, llId, LLId);
/// <summary>Source Logical link ID.</summary>
__PROPERTY(uint32_t, srcLlId, SrcLLId);
/// <summary>Flag indicating whether or not this response packet is to extended addressing.</summary>
__PROPERTY(bool, extended, Extended);
private:
edac::Trellis m_trellis;
uint8_t m_blocksToFollow;
uint32_t m_dataOctets;
};
} // namespace data
} // namespace p25
#endif // __P25_DATA__DATA_RSP_HEADER_H__

@ -50,6 +50,7 @@ using namespace p25;
/// Initializes a new instance of the TDULC class.
/// </summary>
TDULC::TDULC() :
m_verbose(false),
m_protect(false),
m_lco(LC_GROUP),
m_mfId(P25_MFG_STANDARD),
@ -107,7 +108,9 @@ bool TDULC::decode(const uint8_t* data)
return false;
}
// Utils::dump(2U, "TDULC", rs, P25_TDULC_LENGTH_BYTES);
if (m_verbose) {
Utils::dump(2U, "Decoded TDULC", rs, P25_TDULC_LENGTH_BYTES);
}
return decodeLC(rs);
}
@ -126,7 +129,9 @@ void TDULC::encode(uint8_t * data)
encodeLC(rs);
// Utils::dump(2U, "TDULC", rs, P25_TDULC_LENGTH_BYTES);
if (m_verbose) {
Utils::dump(2U, "Encoded TDULC", rs, P25_TDULC_LENGTH_BYTES);
}
// encode RS (24,12,13) FEC
m_rs.encode241213(rs);

@ -80,6 +80,9 @@ namespace p25
void setNetActive(bool netActive);
public:
/// <summary></summary>
__PROPERTY(bool, verbose, Verbose);
/** Common Data */
/// <summary>Flag indicating the link control data is protected.</summary>
__PROPERTY(bool, protect, Protect);

@ -49,6 +49,7 @@ using namespace p25;
/// Initializes a new instance of the TSBK class.
/// </summary>
TSBK::TSBK() :
m_verbose(false),
m_protect(false),
m_lco(LC_GROUP),
m_mfId(P25_MFG_STANDARD),
@ -56,6 +57,7 @@ TSBK::TSBK() :
m_dstId(0U),
m_lastBlock(false),
m_aivFlag(true),
m_extendedAddrFlag(false),
m_emergency(false),
m_encrypted(false),
m_priority(4U),
@ -122,7 +124,9 @@ bool TSBK::decode(const uint8_t* data)
return false;
}
// Utils::dump(2U, "TSDU TSBK", tsbk, P25_TSBK_LENGTH_BYTES);
if (m_verbose) {
Utils::dump(2U, "Decoded TSBK", tsbk, P25_TSBK_LENGTH_BYTES);
}
m_lco = tsbk[0U] & 0x3F; // LCO
m_lastBlock = (tsbk[0U] & 0x80U) == 0x80U; // Protect Flag
@ -357,7 +361,14 @@ void TSBK::encode(uint8_t * data, bool singleBlock)
case TSBK_IOSP_ACK_RSP:
tsbkValue = (m_service & 0x3F); // Service Type
tsbkValue |= (m_aivFlag) ? 0x80U : 0x00U; // Additional Info. Valid Flag
tsbkValue |= (m_extendedAddrFlag) ? 0x40U : 0x00U; // Extended Addressing Flag
if (m_aivFlag && m_extendedAddrFlag) {
tsbkValue = (tsbkValue << 20) + m_siteData.netId(); // Network ID
tsbkValue = (tsbkValue << 12) + m_siteData.sysId(); // System ID
}
else {
tsbkValue = (tsbkValue << 32) + m_dstId; // Target Radio Address
}
tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address
break;
case TSBK_IOSP_EXT_FNCT:
@ -688,7 +699,9 @@ void TSBK::encode(uint8_t * data, bool singleBlock)
// compute CRC-CCITT 16
edac::CRC::addCCITT162(tsbk, P25_TSBK_LENGTH_BYTES);
// Utils::dump(2U, "TSBK", tsbk, P25_TSBK_LENGTH_BYTES);
if (m_verbose) {
Utils::dump(2U, "Encoded TSBK", tsbk, P25_TSBK_LENGTH_BYTES);
}
uint8_t raw[P25_TSBK_FEC_LENGTH_BYTES];
::memset(raw, 0x00U, P25_TSBK_FEC_LENGTH_BYTES);
@ -724,6 +737,7 @@ void TSBK::reset()
m_lastBlock = true;
m_aivFlag = true;
m_extendedAddrFlag = false;
m_service = 0U;
m_response = P25_RSP_ACCEPT;

@ -12,7 +12,7 @@
//
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2017-2019 by Bryan Biedenkapp N2PLL
* Copyright (C) 2017-2020 by Bryan Biedenkapp N2PLL
*
* 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
@ -87,6 +87,9 @@ namespace p25
void setSiteChCnt(uint8_t chCnt);
public:
/// <summary></summary>
__PROPERTY(bool, verbose, Verbose);
/** Common Data */
/// <summary>Flag indicating the link control data is protected.</summary>
__PROPERTY(bool, protect, Protect);
@ -104,6 +107,8 @@ namespace p25
__PROPERTY(bool, lastBlock, LastBlock);
/// <summary>Flag indicating this TSBK contains additional information.</summary>
__PROPERTY(bool, aivFlag, AIV);
/// <summary>Flag indicating this TSBK contains extended addressing.</summary>
__PROPERTY(bool, extendedAddrFlag, EX);
/// <summary>Service type.</summary>
__PROPERTY(uint8_t, service, Service);

Loading…
Cancel
Save

Powered by TurnKey Linux.