diff --git a/DVMHost.vcxproj b/DVMHost.vcxproj
index 6764ea0a..aa6f0524 100644
--- a/DVMHost.vcxproj
+++ b/DVMHost.vcxproj
@@ -202,6 +202,7 @@
+
@@ -272,6 +273,7 @@
+
diff --git a/DVMHost.vcxproj.filters b/DVMHost.vcxproj.filters
index f39f91b1..c34140d2 100644
--- a/DVMHost.vcxproj.filters
+++ b/DVMHost.vcxproj.filters
@@ -326,6 +326,9 @@
Header Files\network
+
+ Header Files\p25\data
+
@@ -523,6 +526,9 @@
Source Files\network
+
+ Source Files\p25\data
+
diff --git a/Makefile b/Makefile
index 659505b1..edd12a62 100644
--- a/Makefile
+++ b/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 \
diff --git a/Makefile.arm b/Makefile.arm
index eb6789e8..31a7e406 100644
--- a/Makefile.arm
+++ b/Makefile.arm
@@ -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 \
diff --git a/Makefile.rpi-arm b/Makefile.rpi-arm
index ecfec395..4a6a50eb 100644
--- a/Makefile.rpi-arm
+++ b/Makefile.rpi-arm
@@ -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 \
diff --git a/config.yml b/config.yml
index 66bf89b1..a773a86f 100644
--- a/config.yml
+++ b/config.yml
@@ -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
diff --git a/dmr/Control.cpp b/dmr/Control.cpp
index 7a7ec585..da54ddd7 100644
--- a/dmr/Control.cpp
+++ b/dmr/Control.cpp
@@ -57,12 +57,13 @@ using namespace dmr;
///
///
///
+///
/// Flag indicating whether DMR debug is enabled.
/// Flag indicating whether DMR verbose logging is enabled.
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);
}
///
@@ -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;
diff --git a/dmr/Control.h b/dmr/Control.h
index a3ce4863..3f433796 100644
--- a/dmr/Control.h
+++ b/dmr/Control.h
@@ -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);
/// Finalizes a instance of the Control class.
~Control();
@@ -95,6 +95,7 @@ namespace dmr
lookups::RadioIdLookup* m_ridLookup;
lookups::TalkgroupIdLookup* m_tidLookup;
+ bool m_dumpCSBKData;
bool m_verbose;
bool m_debug;
};
diff --git a/dmr/DataPacket.cpp b/dmr/DataPacket.cpp
index 43f08ac4..bf73eaf8 100644
--- a/dmr/DataPacket.cpp
+++ b/dmr/DataPacket.cpp
@@ -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)
/// Instance of the BaseNetwork class.
///
///
+///
/// Flag indicating whether DMR debug is enabled.
/// Flag indicating whether DMR verbose logging is enabled.
-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)
{
diff --git a/dmr/DataPacket.h b/dmr/DataPacket.h
index 178fc06a..ed28bd9d 100644
--- a/dmr/DataPacket.h
+++ b/dmr/DataPacket.h
@@ -81,11 +81,12 @@ namespace dmr
bool m_dumpDataPacket;
bool m_repeatDataPacket;
+ bool m_dumpCSBKData;
bool m_verbose;
bool m_debug;
/// Initializes a new instance of the DataPacket class.
- 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);
/// Finalizes a instance of the DataPacket class.
~DataPacket();
diff --git a/dmr/Slot.cpp b/dmr/Slot.cpp
index d7a1c1d8..e2e12417 100644
--- a/dmr/Slot.cpp
+++ b/dmr/Slot.cpp
@@ -85,10 +85,11 @@ bool Slot::m_voice2 = true;
/// Amount of time to hang on the last talkgroup mode from RF.
///
///
+///
/// Flag indicating whether DMR debug is enabled.
/// Flag indicating whether DMR verbose logging is enabled.
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);
}
///
@@ -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);
diff --git a/dmr/Slot.h b/dmr/Slot.h
index 349f38b3..002310a4 100644
--- a/dmr/Slot.h
+++ b/dmr/Slot.h
@@ -64,7 +64,7 @@ namespace dmr
public:
/// Initializes a new instance of the Slot class.
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);
/// Finalizes a instance of the Slot class.
~Slot();
@@ -130,6 +130,7 @@ namespace dmr
uint32_t m_aveRSSI;
uint32_t m_rssiCount;
+ bool m_dumpCSBKData;
bool m_verbose;
bool m_debug;
diff --git a/dmr/lc/CSBK.cpp b/dmr/lc/CSBK.cpp
index ec436a17..7eb29851 100644
--- a/dmr/lc/CSBK.cpp
+++ b/dmr/lc/CSBK.cpp
@@ -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;
///
/// Initializes a new instance of the CSBK class.
///
-///
-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);
diff --git a/dmr/lc/CSBK.h b/dmr/lc/CSBK.h
index b9fcfb26..1e0f9c78 100644
--- a/dmr/lc/CSBK.h
+++ b/dmr/lc/CSBK.h
@@ -46,7 +46,7 @@ namespace dmr
class HOST_SW_API CSBK {
public:
/// Initializes a new instance of the CSBK class.
- CSBK(bool debug);
+ CSBK();
/// Finalizes a instance of the CSBK class.
~CSBK();
@@ -56,6 +56,9 @@ namespace dmr
void encode(uint8_t* bytes) const;
public:
+ ///
+ __PROPERTY(bool, verbose, Verbose);
+
// Generic fields
/// CSBK opcode.
__PROPERTY(uint8_t, CSBKO, CSBKO);
@@ -82,7 +85,6 @@ namespace dmr
private:
uint8_t* m_data;
- bool m_debug;
};
} // namespace lc
} // namespace dmr
diff --git a/edac/CRC.cpp b/edac/CRC.cpp
index 44564f63..17546b5f 100644
--- a/edac/CRC.cpp
+++ b/edac/CRC.cpp
@@ -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);
}
diff --git a/host/Host.cpp b/host/Host.cpp
index f36588bd..888f585f 100644
--- a/host/Host.cpp
+++ b/host/Host.cpp
@@ -268,6 +268,7 @@ int Host::run()
bool embeddedLCOnly = dmrProtocol["embeddedLCOnly"].as(false);
bool dmrDumpDataPacket = dmrProtocol["dumpDataPacket"].as(false);
bool dmrRepeatDataPacket = dmrProtocol["repeatDataPacket"].as(true);
+ bool dmrDumpCsbkData = dmrProtocol["dumpCsbkData"].as(false);
bool dumpTAData = dmrProtocol["dumpTAData"].as(true);
uint32_t callHang = dmrProtocol["callHang"].as(3U);
uint32_t txHang = dmrProtocol["txHang"].as(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(false);
bool p25DumpDataPacket = p25Protocol["dumpDataPacket"].as(false);
bool p25RepeatDataPacket = p25Protocol["repeatDataPacket"].as(true);
+ bool p25DumpTsbkData = p25Protocol["dumpTsbkData"].as(false);
uint32_t callHang = p25Protocol["callHang"].as(3U);
uint32_t p25QueueSize = p25Protocol["queueSize"].as(8192U);
bool p25Verbose = p25Protocol["verbose"].as(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(7U);
int rxDCOffset = modemConf["rxDCOffset"].as(0);
int txDCOffset = modemConf["txDCOffset"].as(0);
+ int dmrSymLevel3Adj = modemConf["dmrSymLvl3Adj"].as(0);
+ int dmrSymLevel1Adj = modemConf["dmrSymLvl1Adj"].as(0);
+ int p25SymLevel3Adj = modemConf["p25SymLvl3Adj"].as(0);
+ int p25SymLevel1Adj = modemConf["p25SymLvl1Adj"].as(0);
float rxLevel = modemConf["rxLevel"].as(50.0F);
float cwIdTXLevel = modemConf["cwIdTxLevel"].as(50.0F);
float dmrTXLevel = modemConf["dmrTxLevel"].as(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);
diff --git a/host/calibrate/HostCal.cpp b/host/calibrate/HostCal.cpp
index 8875ce51..b8aadaf6 100644
--- a/host/calibrate/HostCal.cpp
+++ b/host/calibrate/HostCal.cpp
@@ -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(50.0F);
m_txLevel = modemConf["txLevel"].as(50.0F);
+ m_dmrSymLevel3Adj = modemConf["dmrSymLvl3Adj"].as(0);
+ m_dmrSymLevel1Adj = modemConf["dmrSymLvl1Adj"].as(0);
+ m_p25SymLevel3Adj = modemConf["p25SymLvl3Adj"].as(0);
+ m_p25SymLevel1Adj = modemConf["p25SymLvl1Adj"].as(0);
+
m_fdmaPreamble = (uint8_t)modemConf["fdmaPreamble"].as(80U);
m_dmrRxDelay = (uint8_t)modemConf["dmrRxDelay"].as(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");
}
///
@@ -736,6 +780,94 @@ bool HostCal::setRXDCOffset(int incr)
return true;
}
+///
+/// Helper to change the DMR Symbol Level 3 adjust.
+///
+/// Amount to change.
+/// True, if setting was applied, otherwise false.
+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;
+}
+
+///
+/// Helper to change the DMR Symbol Level 1 adjust.
+///
+/// Amount to change.
+/// True, if setting was applied, otherwise false.
+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;
+}
+
+///
+/// Helper to change the P25 Symbol Level 3 adjust.
+///
+/// Amount to change.
+/// True, if setting was applied, otherwise false.
+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;
+}
+
+///
+/// Helper to change the P25 Symbol Level 1 adjust.
+///
+/// Amount to change.
+/// True, if setting was applied, otherwise false.
+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;
+}
+
///
/// Helper to toggle modem transmit mode.
///
@@ -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;
+}
+
+///
+/// Write symbol level adjustments to the modem DSP.
+///
+/// True, if level adjustments are written, otherwise false.
+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()
///
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());
diff --git a/host/calibrate/HostCal.h b/host/calibrate/HostCal.h
index 0a4990e3..2a9a9c45 100644
--- a/host/calibrate/HostCal.h
+++ b/host/calibrate/HostCal.h
@@ -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:
/// Helper to toggle modem transmit mode.
bool setTransmit();
+ /// Helper to change the DMR Symbol Level 3 adjust.
+ bool setDMRSymLevel3Adj(int incr);
+ /// Helper to change the DMR Symbol Level 1 adjust.
+ bool setDMRSymLevel1Adj(int incr);
+ /// Helper to change the P25 Symbol Level 3 adjust.
+ bool setP25SymLevel3Adj(int incr);
+ /// Helper to change the P25 Symbol Level 1 adjust.
+ bool setP25SymLevel1Adj(int incr);
+
/// Initializes the modem DSP.
bool initModem();
/// Read data frames from the modem DSP.
@@ -135,6 +149,8 @@ private:
bool writeConfig();
/// Write configuration to the modem DSP.
bool writeConfig(uint8_t modeOverride);
+ /// Write symbol level adjustments to the modem DSP.
+ bool writeSymbolAdjust();
///
void sleep(uint32_t ms);
diff --git a/modem/Modem.cpp b/modem/Modem.cpp
index 377c3eea..f10035ec 100644
--- a/modem/Modem.cpp
+++ b/modem/Modem.cpp
@@ -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;
}
+///
+/// Sets the modem DSP Symbol adjustment levels
+///
+///
+///
+///
+///
+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;
+}
+
///
/// Sets the modem DSP DMR color code.
///
@@ -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;
}
+///
+/// Write symbol level adjustments to the modem DSP.
+///
+/// True, if level adjustments are written, otherwise false.
+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;
+}
+
///
///
///
diff --git a/modem/Modem.h b/modem/Modem.h
index 2f366713..0c52a8fe 100644
--- a/modem/Modem.h
+++ b/modem/Modem.h
@@ -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);
/// Sets the modem DSP RF deviation levels.
virtual void setLevels(float rxLevel, float cwIdTXLevel, float dmrTXLevel, float p25TXLevel);
+ /// Sets the modem DSP Symbol adjustment levels.
+ virtual void setSymbolAdjust(int dmrSymLevel3Adj, int dmrSymLevel1Adj, int p25SymLevel3Adj, int p25SymLevel1Adj);
/// Sets the modem DSP DMR color code.
virtual void setDMRColorCode(uint32_t colorCode);
/// Sets the modem DSP P25 NAC.
@@ -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();
/// Write configuration to the modem DSP.
bool writeConfig();
+ /// Write symbol level adjustments to the modem DSP.
+ bool writeSymbolAdjust();
///
void printDebug();
diff --git a/modem/NullModem.h b/modem/NullModem.h
index 2185f7e0..cb22662b 100644
--- a/modem/NullModem.h
+++ b/modem/NullModem.h
@@ -55,6 +55,8 @@ namespace modem
virtual void setModeParams(bool dmrEnabled, bool p25Enabled) { return; }
/// Sets the modem DSP RF deviation levels.
virtual void setLevels(float rxLevel, float cwIdTXLevel, float dmrTXLevel, float p25TXLevel) { return; }
+ /// Sets the modem DSP Symbol adjustment levels
+ virtual void setSymbolAdjust(int dmrSymLevel3Adj, int dmrSymLevel1Adj, int p25SymLevel3Adj, int p25SymLevel1Adj) { return; }
/// Sets the modem DSP DMR color code.
virtual void setDMRColorCode(uint32_t colorCode) { return; }
/// Sets the modem DSP RF receive deviation levels.
diff --git a/p25/Control.cpp b/p25/Control.cpp
index 0d5b7079..e1f2ff6f 100644
--- a/p25/Control.cpp
+++ b/p25/Control.cpp
@@ -74,12 +74,13 @@ const uint32_t MAX_PREAMBLE_TDU_CNT = 64U;
/// Instance of the CRSSIInterpolator class.
///
///
+///
/// Flag indicating whether P25 debug is enabled.
/// Flag indicating whether P25 verbose logging is enabled.
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);
}
///
@@ -208,6 +210,7 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s
}
m_voiceOnControl = p25Protocol["voiceOnControl"].as(false);
+ m_ackTSBKRequests = control["ackRequests"].as(true);
m_voice->m_silenceThreshold = p25Protocol["silenceThreshold"].as(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);
diff --git a/p25/Control.h b/p25/Control.h
index 55c369ae..9d1c51a1 100644
--- a/p25/Control.h
+++ b/p25/Control.h
@@ -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);
/// Finalizes a instance of the Control class.
~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;
diff --git a/p25/DataPacket.cpp b/p25/DataPacket.cpp
index 3c5fa37a..d356adc6 100644
--- a/p25/DataPacket.cpp
+++ b/p25/DataPacket.cpp
@@ -40,6 +40,7 @@
#include "Utils.h"
using namespace p25;
+using namespace p25::data;
#include
#include
@@ -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,19 +273,21 @@ 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 (!hasLLIdFNEReg(llId)) {
- // update dynamic FNE registration table entry
- m_fneRegTable[llId] = ipAddr;
-
- if (m_verbose) {
- LogMessage(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_RSP_ACCPT (Registration Response Accept), llId = %u, ipAddr = %u", llId, ipAddr);
- }
- writeRF_PDU_Reg_Response(PDU_REG_TYPE_RSP_ACCPT, llId, ipAddr);
- }
- else {
+ 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;
+
+ if (m_verbose) {
+ LogMessage(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_RSP_ACCPT (Registration Response Accept), llId = %u, ipAddr = %u", llId, ipAddr);
+ }
+ writeRF_PDU_Reg_Response(PDU_REG_TYPE_RSP_ACCPT, llId, ipAddr);
+ }
+ }
}
break;
case PDU_REG_TYPE_REQ_DISCNCT:
@@ -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,11 +326,11 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
writeRF_PDU(); // re-generate PDU and send it on
}
+
+ ::ActivityLog("P25", true, "end RF data transmission");
break;
}
- ::ActivityLog("P25", true, "end RF data transmission");
-
m_rfDataHeader.reset();
m_rfDataBlockCnt = 0U;
m_rfPDUCount = 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);
diff --git a/p25/DataPacket.h b/p25/DataPacket.h
index 174cfd82..41682ce3 100644
--- a/p25/DataPacket.h
+++ b/p25/DataPacket.h
@@ -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"
diff --git a/p25/P25Defines.h b/p25/P25Defines.h
index 82e960bb..8c74c642 100644
--- a/p25/P25Defines.h
+++ b/p25/P25Defines.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;
diff --git a/p25/TrunkPacket.cpp b/p25/TrunkPacket.cpp
index 0b8d005f..ca2ec31e 100644
--- a/p25/TrunkPacket.cpp
+++ b/p25/TrunkPacket.cpp
@@ -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
///
/// Instance of the Control class.
/// Instance of the BaseNetwork class.
+///
/// Flag indicating whether P25 debug is enabled.
/// Flag indicating whether P25 verbose logging is enabled.
-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)
///
///
///
-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 {
diff --git a/p25/TrunkPacket.h b/p25/TrunkPacket.h
index d50c785c..ad374248 100644
--- a/p25/TrunkPacket.h
+++ b/p25/TrunkPacket.h
@@ -183,7 +183,7 @@ namespace p25
bool m_debug;
/// Initializes a new instance of the TrunkPacket class.
- TrunkPacket(Control* p25, network::BaseNetwork* network, bool debug, bool verbose);
+ TrunkPacket(Control* p25, network::BaseNetwork* network, bool dumpTSBKData, bool debug, bool verbose);
/// Finalizes a instance of the TrunkPacket class.
~TrunkPacket();
@@ -213,7 +213,7 @@ namespace p25
/// Helper to write a unit to unit answer request packet.
void writeRF_TSDU_UU_Ans_Req(uint32_t srcId, uint32_t dstId);
/// Helper to write a acknowledge packet.
- 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);
/// Helper to write a deny packet.
void writeRF_TSDU_Deny(uint8_t reason, uint8_t service);
/// Helper to write a group affiliation response packet.
diff --git a/p25/data/DataBlock.cpp b/p25/data/DataBlock.cpp
index 83af467b..bb64e6bb 100644
--- a/p25/data/DataBlock.cpp
+++ b/p25/data/DataBlock.cpp
@@ -177,6 +177,13 @@ void DataBlock::encode(uint8_t* data)
}
/// Sets the data format.
+///
+void DataBlock::setFormat(const uint8_t fmt)
+{
+ m_fmt = fmt;
+}
+
+/// Sets the data format from the data header.
///
void DataBlock::setFormat(const DataHeader header)
{
diff --git a/p25/data/DataBlock.h b/p25/data/DataBlock.h
index 07f3900c..98799612 100644
--- a/p25/data/DataBlock.h
+++ b/p25/data/DataBlock.h
@@ -58,6 +58,8 @@ namespace p25
void encode(uint8_t* data);
/// Sets the data format.
+ void setFormat(const uint8_t fmt);
+ /// Sets the data format from the data header.
void setFormat(const DataHeader header);
/// Gets the data format.
uint8_t getFormat() const;
diff --git a/p25/data/DataRspHeader.cpp b/p25/data/DataRspHeader.cpp
new file mode 100644
index 00000000..c755c0fa
--- /dev/null
+++ b/p25/data/DataRspHeader.cpp
@@ -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
+#include
+#include
+
+// ---------------------------------------------------------------------------
+// Public Class Members
+// ---------------------------------------------------------------------------
+///
+/// Initializes a new instance of the DataRspHeader class.
+///
+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 */
+}
+
+///
+/// Finalizes a instance of the DataRspHeader class.
+///
+DataRspHeader::~DataRspHeader()
+{
+ /* stub */
+}
+
+///
+/// Decodes P25 PDU data response header.
+///
+///
+/// True, if PDU data response header was decoded, otherwise false.
+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;
+}
+
+///
+/// Encodes P25 PDU data response header.
+///
+///
+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);
+}
+
+///
+/// Helper to reset data values to defaults.
+///
+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;
+}
+
+/// Gets the total number of data octets.
+///
+uint32_t DataRspHeader::getDataOctets() const
+{
+ return m_dataOctets;
+}
+
+/** Common Data */
+/// Sets the total number of blocks to follow this header.
+///
+void DataRspHeader::setBlocksToFollow(uint8_t blocksToFollow)
+{
+ m_blocksToFollow = blocksToFollow;
+
+ // recalculate count of data octets
+ m_dataOctets = 16 * m_blocksToFollow - 4;
+}
+
+/// Gets the total number of blocks to follow this header.
+///
+uint8_t DataRspHeader::getBlocksToFollow() const
+{
+ return m_blocksToFollow;
+}
diff --git a/p25/data/DataRspHeader.h b/p25/data/DataRspHeader.h
new file mode 100644
index 00000000..ea565f2b
--- /dev/null
+++ b/p25/data/DataRspHeader.h
@@ -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
+
+namespace p25
+{
+ namespace data
+ {
+ // ---------------------------------------------------------------------------
+ // Class Declaration
+ // Represents the data response header for PDU P25 packets.
+ // ---------------------------------------------------------------------------
+
+ class HOST_SW_API DataRspHeader {
+ public:
+ /// Initializes a new instance of the DataRspHeader class.
+ DataRspHeader();
+ /// Finalizes a instance of the DataRspHeader class.
+ ~DataRspHeader();
+
+ /// Decodes P25 PDU data response header.
+ bool decode(const uint8_t* data);
+ /// Encodes P25 PDU data response header.
+ void encode(uint8_t* data);
+
+ /// Helper to reset data values to defaults.
+ void reset();
+
+ /// Gets the total number of data octets.
+ uint32_t getDataOctets() const;
+
+ /** Common Data */
+ /// Sets the total number of blocks to follow this header.
+ void setBlocksToFollow(uint8_t blocksToFollow);
+ /// Gets the total number of blocks to follow this header.
+ uint8_t getBlocksToFollow() const;
+
+ public:
+ /// Flag indicating if this is an outbound data packet.
+ __PROPERTY(bool, outbound, Outbound);
+ /// Response class.
+ __PROPERTY(uint8_t, rspClass, Class);
+ /// Response type.
+ __PROPERTY(uint8_t, rspType, Type);
+ /// Response status.
+ __PROPERTY(uint8_t, rspStatus, Status);
+ /// Manufacturer ID.
+ __PROPERTY(uint8_t, mfId, MFId);
+ /// Logical link ID.
+ __PROPERTY(uint32_t, llId, LLId);
+ /// Source Logical link ID.
+ __PROPERTY(uint32_t, srcLlId, SrcLLId);
+ /// Flag indicating whether or not this response packet is to extended addressing.
+ __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__
diff --git a/p25/lc/TDULC.cpp b/p25/lc/TDULC.cpp
index a89215d0..95e13bbd 100644
--- a/p25/lc/TDULC.cpp
+++ b/p25/lc/TDULC.cpp
@@ -50,6 +50,7 @@ using namespace p25;
/// Initializes a new instance of the TDULC class.
///
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);
diff --git a/p25/lc/TDULC.h b/p25/lc/TDULC.h
index de0335ea..0ec3cd60 100644
--- a/p25/lc/TDULC.h
+++ b/p25/lc/TDULC.h
@@ -80,6 +80,9 @@ namespace p25
void setNetActive(bool netActive);
public:
+ ///
+ __PROPERTY(bool, verbose, Verbose);
+
/** Common Data */
/// Flag indicating the link control data is protected.
__PROPERTY(bool, protect, Protect);
diff --git a/p25/lc/TSBK.cpp b/p25/lc/TSBK.cpp
index 08badb8f..7225a2d7 100644
--- a/p25/lc/TSBK.cpp
+++ b/p25/lc/TSBK.cpp
@@ -49,6 +49,7 @@ using namespace p25;
/// Initializes a new instance of the TSBK class.
///
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 = (tsbkValue << 32) + m_dstId; // Target Radio Address
+ 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;
diff --git a/p25/lc/TSBK.h b/p25/lc/TSBK.h
index a70589ae..03ddb775 100644
--- a/p25/lc/TSBK.h
+++ b/p25/lc/TSBK.h
@@ -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:
+ ///
+ __PROPERTY(bool, verbose, Verbose);
+
/** Common Data */
/// Flag indicating the link control data is protected.
__PROPERTY(bool, protect, Protect);
@@ -104,6 +107,8 @@ namespace p25
__PROPERTY(bool, lastBlock, LastBlock);
/// Flag indicating this TSBK contains additional information.
__PROPERTY(bool, aivFlag, AIV);
+ /// Flag indicating this TSBK contains extended addressing.
+ __PROPERTY(bool, extendedAddrFlag, EX);
/// Service type.
__PROPERTY(uint8_t, service, Service);