clarify buffer overflow message; refactor DMR and P25 code for clarity regarding how the queue buffer for the digital protocols works; refactor P25 control channel processing code into the p25::Control class clock function proper; refactor the naming of the network write functions (the naming was confusing); refactor how queue sizes are calculated, instead of using raw bytes an input, use the number of desired frames as input from config.yml; adjust the internal intermediate modem buffer sizes; implement a short delay between P25 CC packets of ~5ms, this is to give the processor time to handle packets inbetween generating CC data frames; refactor PDU handling, we no longer instantly transmit the registration response, instead following spec transmit it ~1 second after the request; implement and use the modem write immediate support for P25 PDUs and some P25 TSDUs; in the case of P25 voice call late entry, ensure we perform the appropriate queue clear and state resets; refactor and consolidate how the end of frame data is written after a voice call on P25;

pull/12/head
Bryan Biedenkapp 4 years ago
parent 64d0c770a8
commit 66e6787f33

@ -128,8 +128,6 @@ const uint32_t REMOTE_MODEM_PORT = 3334;
const uint32_t TRAFFIC_DEFAULT_PORT = 62031; const uint32_t TRAFFIC_DEFAULT_PORT = 62031;
const uint32_t RCON_DEFAULT_PORT = 9990; const uint32_t RCON_DEFAULT_PORT = 9990;
const uint32_t QUEUE_RESIZE_SIZE = 500;
const uint8_t BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; const uint8_t BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
enum HOST_STATE { enum HOST_STATE {

@ -73,7 +73,6 @@ bool g_killed = false;
bool g_fireDMRBeacon = false; bool g_fireDMRBeacon = false;
bool g_fireP25Control = false; bool g_fireP25Control = false;
bool g_interruptP25Control = false;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Global Functions // Global Functions

@ -49,7 +49,6 @@ extern bool g_killed;
extern bool g_fireDMRBeacon; extern bool g_fireDMRBeacon;
extern bool g_fireP25Control; extern bool g_fireP25Control;
extern bool g_interruptP25Control;
extern HOST_SW_API void fatal(const char* msg, ...); extern HOST_SW_API void fatal(const char* msg, ...);

@ -76,7 +76,7 @@ public:
bool addData(const T* buffer, uint32_t length) bool addData(const T* buffer, uint32_t length)
{ {
if (length >= freeSpace()) { if (length >= freeSpace()) {
LogError(LOG_HOST, "%s buffer overflow, clearing the buffer. (%u >= %u)", m_name, length, freeSpace()); LogError(LOG_HOST, "[%s] buffer overflow, clearing the buffer. (%u >= %u)", m_name, length, freeSpace());
clear(); clear();
return false; return false;
} }

@ -43,7 +43,7 @@ protocols:
callHang: 5 callHang: 5
txHang: 8 txHang: 8
silenceThreshold: 21 silenceThreshold: 21
queueSize: 5000 queueSize: 31
verbose: true verbose: true
debug: false debug: false
p25: p25:
@ -73,7 +73,7 @@ protocols:
sndcpGrant: false sndcpGrant: false
silenceThreshold: 124 silenceThreshold: 124
disableNetworkHDU: false disableNetworkHDU: false
queueSize: 5000 queueSize: 12
verbose: true verbose: true
debug: false debug: false
system: system:

@ -217,53 +217,44 @@ bool Control::processWakeup(const uint8_t* data)
} }
/// <summary> /// <summary>
/// Process a data frame for slot 1, from the RF interface. /// Process a data frame for slot, from the RF interface.
/// </summary> /// </summary>
/// <param name="data">DMR data frame buffer.</param> /// <param name="data">DMR data frame buffer.</param>
/// <param name="len">Length of data frame buffer.</param> /// <param name="len">Length of data frame buffer.</param>
/// <returns>True, if data frame was processed, otherwise false.</returns> /// <returns>True, if data frame was processed, otherwise false.</returns>
bool Control::processFrame1(uint8_t *data, uint32_t len) bool Control::processFrame(uint32_t slotNo, uint8_t *data, uint32_t len)
{ {
assert(data != NULL); assert(data != NULL);
switch (slotNo) {
case 1U:
return m_slot1->processFrame(data, len); return m_slot1->processFrame(data, len);
} case 2U:
/// <summary>
/// Get a frame data for slot 1, from data ring buffer.
/// </summary>
/// <param name="data">Buffer to put retrieved DMR data frame data.</param>
/// <returns>Length of data retrieved from DMR ring buffer.</returns>
uint32_t Control::getFrame1(uint8_t* data)
{
assert(data != NULL);
return m_slot1->getFrame(data);
}
/// <summary>
/// Process a data frame for slot 2, from the RF interface.
/// </summary>
/// <param name="data">DMR data frame buffer.</param>
/// <param name="len">Length of data frame buffer.</param>
/// <returns>True, if data frame was processed, otherwise false.</returns>
bool Control::processFrame2(uint8_t *data, uint32_t len)
{
assert(data != NULL);
return m_slot2->processFrame(data, len); return m_slot2->processFrame(data, len);
default:
LogError(LOG_NET, "DMR, invalid slot, slotNo = %u", slotNo);
return false;
}
} }
/// <summary> /// <summary>
/// Get a frame data for slot 2, from data ring buffer. /// Get a data frame for slot, from data ring buffer.
/// </summary> /// </summary>
/// <param name="data">Buffer to put retrieved DMR data frame data.</param> /// <param name="data">Buffer to put retrieved DMR data frame data.</param>
/// <returns>Length of data retrieved from DMR ring buffer.</returns> /// <returns>Length of data retrieved from DMR ring buffer.</returns>
uint32_t Control::getFrame2(uint8_t *data) uint32_t Control::getFrame(uint32_t slotNo, uint8_t* data)
{ {
assert(data != NULL); assert(data != NULL);
switch (slotNo) {
case 1U:
return m_slot1->getFrame(data);
case 2U:
return m_slot2->getFrame(data); return m_slot2->getFrame(data);
default:
LogError(LOG_NET, "DMR, invalid slot, slotNo = %u", slotNo);
return 0U;
}
} }
/// <summary> /// <summary>

@ -76,14 +76,10 @@ namespace dmr
/// <summary>Helper to process wakeup frames from the RF interface.</summary> /// <summary>Helper to process wakeup frames from the RF interface.</summary>
bool processWakeup(const uint8_t* data); bool processWakeup(const uint8_t* data);
/// <summary>Process a data frame for slot 1, from the RF interface.</summary> /// <summary>Process a data frame for slot, from the RF interface.</summary>
bool processFrame1(uint8_t* data, uint32_t len); bool processFrame(uint32_t slotNo, uint8_t* data, uint32_t len);
/// <summary>Get a frame data for slot 1, from data ring buffer.</summary> /// <summary>Get a data frame for slot, from data ring buffer.</summary>
uint32_t getFrame1(uint8_t* data); uint32_t getFrame(uint32_t slotNo, uint8_t* data);
/// <summary>Process a data frame for slot 2, from the RF interface.</summary>
bool processFrame2(uint8_t* data, uint32_t len);
/// <summary>Get a frame data for slot 2, from data ring buffer.</summary>
uint32_t getFrame2(uint8_t* data);
/// <summary>Updates the processor.</summary> /// <summary>Updates the processor.</summary>
void clock(); void clock();

@ -67,6 +67,7 @@ using namespace dmr;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Process DMR data frame from the RF interface. /// Process DMR data frame from the RF interface.
/// </summary> /// </summary>
@ -135,9 +136,9 @@ bool ControlPacket::process(uint8_t* data, uint32_t len)
data[1U] = 0x00U; data[1U] = 0x00U;
if (m_slot->m_duplex) if (m_slot->m_duplex)
m_slot->writeQueueRF(data); m_slot->addFrame(data);
m_slot->writeNetworkRF(data, DT_CSBK, gi ? FLCO_GROUP : FLCO_PRIVATE, srcId, dstId); m_slot->writeNetwork(data, DT_CSBK, gi ? FLCO_GROUP : FLCO_PRIVATE, srcId, dstId);
if (m_verbose) { if (m_verbose) {
switch (csbko) { switch (csbko) {
@ -283,11 +284,11 @@ void ControlPacket::processNetwork(const data::Data & dmrData)
// Convert the Data Sync to be from the BS or MS as needed // Convert the Data Sync to be from the BS or MS as needed
Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
m_slot->writeQueueNet(data); m_slot->addFrame(data, true);
} }
} }
else else
m_slot->writeQueueNet(data); m_slot->addFrame(data, true);
if (m_verbose) { if (m_verbose) {
switch (csbko) { switch (csbko) {
@ -425,7 +426,7 @@ void ControlPacket::writeRF_Ext_Func(uint32_t func, uint32_t arg, uint32_t dstId
data[1U] = 0x00U; data[1U] = 0x00U;
if (m_slot->m_duplex) if (m_slot->m_duplex)
m_slot->writeQueueRF(data); m_slot->addFrame(data);
} }
/// <summary> /// <summary>
@ -473,12 +474,13 @@ void ControlPacket::writeRF_Call_Alrt(uint32_t srcId, uint32_t dstId)
data[1U] = 0x00U; data[1U] = 0x00U;
if (m_slot->m_duplex) if (m_slot->m_duplex)
m_slot->writeQueueRF(data); m_slot->addFrame(data);
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Initializes a new instance of the ControlPacket class. /// Initializes a new instance of the ControlPacket class.
/// </summary> /// </summary>
@ -540,7 +542,7 @@ void ControlPacket::writeRF_TSCC_Aloha()
data[1U] = 0x00U; data[1U] = 0x00U;
if (m_slot->m_duplex) if (m_slot->m_duplex)
m_slot->writeQueueRF(data); m_slot->addFrame(data);
} }
/// <summary> /// <summary>
@ -587,7 +589,7 @@ void ControlPacket::writeRF_TSCC_Bcast_Ann_Wd(uint32_t channelNo, bool annWd)
data[1U] = 0x00U; data[1U] = 0x00U;
if (m_slot->m_duplex) if (m_slot->m_duplex)
m_slot->writeQueueRF(data); m_slot->addFrame(data);
} }
/// <summary> /// <summary>
@ -628,5 +630,5 @@ void ControlPacket::writeRF_TSCC_Bcast_Sys_Parm()
data[1U] = 0x00U; data[1U] = 0x00U;
if (m_slot->m_duplex) if (m_slot->m_duplex)
m_slot->writeQueueRF(data); m_slot->addFrame(data);
} }

@ -48,6 +48,7 @@ using namespace dmr;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Macros // Macros
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Don't process RF frames if the network isn't in a idle state. // Don't process RF frames if the network isn't in a idle state.
#define CHECK_TRAFFIC_COLLISION(_DST_ID) \ #define CHECK_TRAFFIC_COLLISION(_DST_ID) \
if (m_slot->m_netState != RS_NET_IDLE && _DST_ID == m_slot->m_netLastDstId) { \ if (m_slot->m_netState != RS_NET_IDLE && _DST_ID == m_slot->m_netLastDstId) { \
@ -65,6 +66,7 @@ using namespace dmr;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Process DMR data frame from the RF interface. /// Process DMR data frame from the RF interface.
/// </summary> /// </summary>
@ -100,11 +102,11 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
data[0U] = modem::TAG_EOT; data[0U] = modem::TAG_EOT;
data[1U] = 0x00U; data[1U] = 0x00U;
m_slot->writeNetworkRF(data, DT_TERMINATOR_WITH_LC); m_slot->writeNetwork(data, DT_TERMINATOR_WITH_LC);
if (m_slot->m_duplex) { if (m_slot->m_duplex) {
for (uint32_t i = 0U; i < m_slot->m_hangCount; i++) for (uint32_t i = 0U; i < m_slot->m_hangCount; i++)
m_slot->writeQueueRF(data); m_slot->addFrame(data);
} }
} }
@ -184,9 +186,9 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
data[1U] = 0x00U; data[1U] = 0x00U;
if (m_slot->m_duplex && m_repeatDataPacket) if (m_slot->m_duplex && m_repeatDataPacket)
m_slot->writeQueueRF(data); m_slot->addFrame(data);
m_slot->writeNetworkRF(data, DT_DATA_HEADER); m_slot->writeNetwork(data, DT_DATA_HEADER);
m_slot->m_rfState = RS_RF_DATA; m_slot->m_rfState = RS_RF_DATA;
m_slot->m_rfLastDstId = dstId; m_slot->m_rfLastDstId = dstId;
@ -263,10 +265,10 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
// convert the Data Sync to be from the BS or MS as needed // convert the Data Sync to be from the BS or MS as needed
Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
m_slot->writeNetworkRF(data, dataType); m_slot->writeNetwork(data, dataType);
if (m_slot->m_duplex && m_repeatDataPacket) { if (m_slot->m_duplex && m_repeatDataPacket) {
m_slot->writeQueueRF(data); m_slot->addFrame(data);
} }
if (m_slot->m_rfFrames == 0U) { if (m_slot->m_rfFrames == 0U) {
@ -330,11 +332,11 @@ void DataPacket::processNetwork(const data::Data& dmrData)
if (m_slot->m_duplex) { if (m_slot->m_duplex) {
for (uint32_t i = 0U; i < m_slot->m_hangCount; i++) for (uint32_t i = 0U; i < m_slot->m_hangCount; i++)
m_slot->writeQueueNet(data); m_slot->addFrame(data, true);
} }
else { else {
for (uint32_t i = 0U; i < 3U; i++) for (uint32_t i = 0U; i < 3U; i++)
m_slot->writeQueueNet(data); m_slot->addFrame(data, true);
} }
} }
@ -387,10 +389,10 @@ void DataPacket::processNetwork(const data::Data& dmrData)
data[1U] = 0x00U; data[1U] = 0x00U;
// Put a small delay into starting transmission // Put a small delay into starting transmission
m_slot->writeQueueNet(m_slot->m_idle); m_slot->addFrame(m_slot->m_idle, true);
m_slot->writeQueueNet(m_slot->m_idle); m_slot->addFrame(m_slot->m_idle, true);
m_slot->writeQueueNet(data); m_slot->addFrame(data, true);
m_slot->m_netState = RS_NET_DATA; m_slot->m_netState = RS_NET_DATA;
m_slot->m_netLastDstId = dstId; m_slot->m_netLastDstId = dstId;
@ -469,7 +471,7 @@ void DataPacket::processNetwork(const data::Data& dmrData)
data[0U] = m_slot->m_netFrames == 0U ? modem::TAG_EOT : modem::TAG_DATA; data[0U] = m_slot->m_netFrames == 0U ? modem::TAG_EOT : modem::TAG_DATA;
data[1U] = 0x00U; data[1U] = 0x00U;
m_slot->writeQueueNet(data); m_slot->addFrame(data, true);
if (m_verbose) { if (m_verbose) {
if (dataType == DT_RATE_12_DATA) { if (dataType == DT_RATE_12_DATA) {
@ -502,6 +504,7 @@ void DataPacket::processNetwork(const data::Data& dmrData)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Initializes a new instance of the DataPacket class. /// Initializes a new instance of the DataPacket class.
/// </summary> /// </summary>

@ -93,6 +93,7 @@ uint16_t Slot::m_tsccCnt = 0U;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Initializes a new instance of the Slot class. /// Initializes a new instance of the Slot class.
/// </summary> /// </summary>
@ -108,7 +109,7 @@ uint16_t Slot::m_tsccCnt = 0U;
Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSize, bool dumpDataPacket, bool repeatDataPacket, Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSize, bool dumpDataPacket, bool repeatDataPacket,
bool dumpCSBKData, bool debug, bool verbose) : bool dumpCSBKData, bool debug, bool verbose) :
m_slotNo(slotNo), m_slotNo(slotNo),
m_queue(queueSize, "DMR Slot"), m_queue(queueSize, "DMR Slot Frame"),
m_rfState(RS_RF_LISTENING), m_rfState(RS_RF_LISTENING),
m_rfLastDstId(0U), m_rfLastDstId(0U),
m_netState(RS_NET_IDLE), m_netState(RS_NET_IDLE),
@ -621,50 +622,35 @@ void Slot::setSiteData(uint32_t netId, uint8_t siteId, uint8_t channelId, uint32
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Write data processed from RF to the data ring buffer. /// Add data frame to the data ring buffer.
/// </summary> /// </summary>
/// <param name="data"></param> /// <param name="data"></param>
void Slot::writeQueueRF(const uint8_t *data) /// <param name="net"></param>
void Slot::addFrame(const uint8_t *data, bool net)
{ {
assert(data != NULL); assert(data != NULL);
if (!net) {
if (m_netState != RS_NET_IDLE) if (m_netState != RS_NET_IDLE)
return; return;
}
uint8_t len = DMR_FRAME_LENGTH_BYTES + 2U; uint8_t len = DMR_FRAME_LENGTH_BYTES + 2U;
uint32_t space = m_queue.freeSpace(); uint32_t space = m_queue.freeSpace();
if (space < (len + 1U)) { if (space < (len + 1U)) {
if (!net) {
uint32_t queueLen = m_queue.length(); uint32_t queueLen = m_queue.length();
m_queue.resize(queueLen + QUEUE_RESIZE_SIZE); m_queue.resize(queueLen + (DMR_FRAME_LENGTH_BYTES + 2U));
LogError(LOG_DMR, "Slot %u, overflow in the DMR slot queue; queue free is %u, needed %u; resized was %u is %u", m_slotNo, space, len, queueLen, m_queue.length()); LogError(LOG_DMR, "Slot %u, overflow in the DMR slot queue; queue free is %u, needed %u; resized was %u is %u", m_slotNo, space, len, queueLen, m_queue.length());
return; return;
} }
else {
if (m_debug) {
Utils::symbols("!!! *Tx DMR", data + 2U, len - 2U);
}
m_queue.addData(&len, 1U);
m_queue.addData(data, len);
}
/// <summary>
/// Write data processed from the network to the data ring buffer.
/// </summary>
/// <param name="data"></param>
void Slot::writeQueueNet(const uint8_t *data)
{
assert(data != NULL);
uint8_t len = DMR_FRAME_LENGTH_BYTES + 2U;
uint32_t space = m_queue.freeSpace();
if (space < (len + 1U)) {
LogError(LOG_DMR, "Slot %u, overflow in the DMR slot queue while writing network data; queue free is %u, needed %u", m_slotNo, space, len); LogError(LOG_DMR, "Slot %u, overflow in the DMR slot queue while writing network data; queue free is %u, needed %u", m_slotNo, space, len);
return; return;
} }
}
if (m_debug) { if (m_debug) {
Utils::symbols("!!! *Tx DMR", data + 2U, len - 2U); Utils::symbols("!!! *Tx DMR", data + 2U, len - 2U);
@ -675,21 +661,21 @@ void Slot::writeQueueNet(const uint8_t *data)
} }
/// <summary> /// <summary>
/// Write data processed from RF to the network. /// Write data frame to the network.
/// </summary> /// </summary>
/// <param name="data"></param> /// <param name="data"></param>
/// <param name="dataType"></param> /// <param name="dataType"></param>
/// <param name="errors"></param> /// <param name="errors"></param>
void Slot::writeNetworkRF(const uint8_t* data, uint8_t dataType, uint8_t errors) void Slot::writeNetwork(const uint8_t* data, uint8_t dataType, uint8_t errors)
{ {
assert(data != NULL); assert(data != NULL);
assert(m_rfLC != NULL); assert(m_rfLC != NULL);
writeNetworkRF(data, dataType, m_rfLC->getFLCO(), m_rfLC->getSrcId(), m_rfLC->getDstId(), errors); writeNetwork(data, dataType, m_rfLC->getFLCO(), m_rfLC->getSrcId(), m_rfLC->getDstId(), errors);
} }
/// <summary> /// <summary>
/// Write data processed from RF to the network. /// Write data frame to the network.
/// </summary> /// </summary>
/// <param name="data"></param> /// <param name="data"></param>
/// <param name="dataType"></param> /// <param name="dataType"></param>
@ -697,7 +683,7 @@ void Slot::writeNetworkRF(const uint8_t* data, uint8_t dataType, uint8_t errors)
/// <param name="srcId"></param> /// <param name="srcId"></param>
/// <param name="dstId"></param> /// <param name="dstId"></param>
/// <param name="errors"></param> /// <param name="errors"></param>
void Slot::writeNetworkRF(const uint8_t* data, uint8_t dataType, uint8_t flco, uint32_t srcId, void Slot::writeNetwork(const uint8_t* data, uint8_t dataType, uint8_t flco, uint32_t srcId,
uint32_t dstId, uint8_t errors) uint32_t dstId, uint8_t errors)
{ {
assert(data != NULL); assert(data != NULL);
@ -760,7 +746,7 @@ void Slot::writeEndRF(bool writeEnd)
data[1U] = 0x00U; data[1U] = 0x00U;
for (uint32_t i = 0U; i < m_hangCount; i++) for (uint32_t i = 0U; i < m_hangCount; i++)
writeQueueRF(data); addFrame(data);
} }
} }
@ -818,11 +804,11 @@ void Slot::writeEndNet(bool writeEnd)
if (m_duplex) { if (m_duplex) {
for (uint32_t i = 0U; i < m_hangCount; i++) for (uint32_t i = 0U; i < m_hangCount; i++)
writeQueueNet(data); addFrame(data, true);
} }
else { else {
for (uint32_t i = 0U; i < 3U; i++) for (uint32_t i = 0U; i < 3U; i++)
writeQueueNet(data); addFrame(data, true);
} }
} }

@ -54,6 +54,7 @@ namespace dmr
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Prototypes // Class Prototypes
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
class HOST_SW_API VoicePacket; class HOST_SW_API VoicePacket;
class HOST_SW_API DataPacket; class HOST_SW_API DataPacket;
class HOST_SW_API ControlPacket; class HOST_SW_API ControlPacket;
@ -76,7 +77,7 @@ namespace dmr
/// <summary>Process a data frame from the RF interface.</summary> /// <summary>Process a data frame from the RF interface.</summary>
bool processFrame(uint8_t* data, uint32_t len); bool processFrame(uint8_t* data, uint32_t len);
/// <summary>Get frame data from data ring buffer.</summary> /// <summary>Get data frame from data ring buffer.</summary>
uint32_t getFrame(uint8_t* data); uint32_t getFrame(uint8_t* data);
/// <summary>Process a data frames from the network.</summary> /// <summary>Process a data frames from the network.</summary>
@ -208,14 +209,13 @@ namespace dmr
static uint16_t m_tsccCnt; static uint16_t m_tsccCnt;
/// <summary>Write data processed from RF to the data ring buffer.</summary> /// <summary>Add data frame to the data ring buffer.</summary>
void writeQueueRF(const uint8_t* data); void addFrame(const uint8_t* data, bool net = false);
/// <summary>Write data processed from the network to the data ring buffer.</summary>
void writeQueueNet(const uint8_t* data); /// <summary>Write data frame to the network.</summary>
/// <summary>Write data processed from RF to the network.</summary> void writeNetwork(const uint8_t* data, uint8_t dataType, uint8_t errors = 0U);
void writeNetworkRF(const uint8_t* data, uint8_t dataType, uint8_t errors = 0U); /// <summary>Write data frame to the network.</summary>
/// <summary>Write data processed from RF to the network.</summary> void writeNetwork(const uint8_t* data, uint8_t dataType, uint8_t flco, uint32_t srcId,
void writeNetworkRF(const uint8_t* data, uint8_t dataType, uint8_t flco, uint32_t srcId,
uint32_t dstId, uint8_t errors = 0U); uint32_t dstId, uint8_t errors = 0U);
/// <summary>Helper to write RF end of frame data.</summary> /// <summary>Helper to write RF end of frame data.</summary>

@ -49,6 +49,7 @@ using namespace dmr;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Macros // Macros
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
#define CHECK_TRAFFIC_COLLISION_DELLC(_DST_ID) \ #define CHECK_TRAFFIC_COLLISION_DELLC(_DST_ID) \
if (m_slot->m_netState != RS_NET_IDLE && _DST_ID == m_slot->m_netLastDstId) { \ if (m_slot->m_netState != RS_NET_IDLE && _DST_ID == m_slot->m_netLastDstId) { \
LogWarning(LOG_RF, "DMR Slot %u, Traffic collision detect, preempting new RF traffic to existing network traffic!", m_slot->m_slotNo); \ LogWarning(LOG_RF, "DMR Slot %u, Traffic collision detect, preempting new RF traffic to existing network traffic!", m_slot->m_slotNo); \
@ -67,6 +68,7 @@ using namespace dmr;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Process DMR voice frame from the RF interface. /// Process DMR voice frame from the RF interface.
/// </summary> /// </summary>
@ -183,10 +185,10 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo); m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo);
for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++)
m_slot->writeQueueRF(data); m_slot->addFrame(data);
} }
m_slot->writeNetworkRF(data, DT_VOICE_LC_HEADER); m_slot->writeNetwork(data, DT_VOICE_LC_HEADER);
m_slot->m_rfState = RS_RF_AUDIO; m_slot->m_rfState = RS_RF_AUDIO;
m_slot->m_rfLastDstId = dstId; m_slot->m_rfLastDstId = dstId;
@ -229,9 +231,9 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
data[1U] = 0x00U; data[1U] = 0x00U;
if (m_slot->m_duplex) if (m_slot->m_duplex)
m_slot->writeQueueRF(data); m_slot->addFrame(data);
m_slot->writeNetworkRF(data, DT_VOICE_PI_HEADER); m_slot->writeNetwork(data, DT_VOICE_PI_HEADER);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_RF, DMR_DT_VOICE_PI_HEADER ", slot = %u, algId = %u, kId = %u, dstId = %u", m_slot->m_slotNo, lc->getAlgId(), lc->getKId(), lc->getDstId()); LogMessage(LOG_RF, DMR_DT_VOICE_PI_HEADER ", slot = %u, algId = %u, kId = %u, dstId = %u", m_slot->m_slotNo, lc->getAlgId(), lc->getKId(), lc->getDstId());
@ -284,9 +286,9 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
data[1U] = 0x00U; data[1U] = 0x00U;
if (m_slot->m_duplex) if (m_slot->m_duplex)
m_slot->writeQueueRF(data); m_slot->addFrame(data);
m_slot->writeNetworkRF(data, DT_VOICE_SYNC, errors); m_slot->writeNetwork(data, DT_VOICE_SYNC, errors);
return true; return true;
} }
@ -438,7 +440,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
data[0U] = modem::TAG_DATA; data[0U] = modem::TAG_DATA;
data[1U] = 0x00U; data[1U] = 0x00U;
m_slot->writeNetworkRF(data, DT_VOICE, errors); m_slot->writeNetwork(data, DT_VOICE, errors);
if (m_embeddedLCOnly) { if (m_embeddedLCOnly) {
// Only send the previously received LC // Only send the previously received LC
@ -451,7 +453,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
} }
if (m_slot->m_duplex) if (m_slot->m_duplex)
m_slot->writeQueueRF(data); m_slot->addFrame(data);
return true; return true;
} }
@ -539,10 +541,10 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo); m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo);
for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++)
m_slot->writeQueueRF(start); m_slot->addFrame(start);
} }
m_slot->writeNetworkRF(start, DT_VOICE_LC_HEADER); m_slot->writeNetwork(start, DT_VOICE_LC_HEADER);
m_rfN = data[1U] & 0x0FU; m_rfN = data[1U] & 0x0FU;
@ -589,9 +591,9 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
data[1U] = 0x00U; data[1U] = 0x00U;
if (m_slot->m_duplex) if (m_slot->m_duplex)
m_slot->writeQueueRF(data); m_slot->addFrame(data);
m_slot->writeNetworkRF(data, DT_VOICE, errors); m_slot->writeNetwork(data, DT_VOICE, errors);
m_slot->m_rfState = RS_RF_AUDIO; m_slot->m_rfState = RS_RF_AUDIO;
@ -690,15 +692,15 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
} }
for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++) for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++)
m_slot->writeQueueNet(m_slot->m_idle); m_slot->addFrame(m_slot->m_idle, true);
if (m_slot->m_duplex) { if (m_slot->m_duplex) {
for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++)
m_slot->writeQueueNet(data); m_slot->addFrame(data, true);
} }
else { else {
for (uint32_t i = 0U; i < NO_HEADERS_SIMPLEX; i++) for (uint32_t i = 0U; i < NO_HEADERS_SIMPLEX; i++)
m_slot->writeQueueNet(data); m_slot->addFrame(data, true);
} }
m_slot->m_netState = RS_NET_AUDIO; m_slot->m_netState = RS_NET_AUDIO;
@ -734,7 +736,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
} }
for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++) for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++)
m_slot->writeQueueNet(m_slot->m_idle); m_slot->addFrame(m_slot->m_idle, true);
// Create a dummy start frame // Create a dummy start frame
uint8_t start[DMR_FRAME_LENGTH_BYTES + 2U]; uint8_t start[DMR_FRAME_LENGTH_BYTES + 2U];
@ -754,11 +756,11 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
if (m_slot->m_duplex) { if (m_slot->m_duplex) {
for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++)
m_slot->writeQueueRF(start); m_slot->addFrame(start);
} }
else { else {
for (uint32_t i = 0U; i < NO_HEADERS_SIMPLEX; i++) for (uint32_t i = 0U; i < NO_HEADERS_SIMPLEX; i++)
m_slot->writeQueueRF(start); m_slot->addFrame(start);
} }
m_slot->m_netFrames = 0U; m_slot->m_netFrames = 0U;
@ -800,7 +802,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
data[0U] = modem::TAG_DATA; data[0U] = modem::TAG_DATA;
data[1U] = 0x00U; data[1U] = 0x00U;
m_slot->writeQueueNet(data); m_slot->addFrame(data, true);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_NET, DMR_DT_VOICE_PI_HEADER ", slot = %u, algId = %u, kId = %u, dstId = %u", m_slot->m_slotNo, lc->getAlgId(), lc->getKId(), lc->getDstId()); LogMessage(LOG_NET, DMR_DT_VOICE_PI_HEADER ", slot = %u, algId = %u, kId = %u, dstId = %u", m_slot->m_slotNo, lc->getAlgId(), lc->getKId(), lc->getDstId());
@ -831,7 +833,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
} }
for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++) for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++)
m_slot->writeQueueNet(m_slot->m_idle); m_slot->addFrame(m_slot->m_idle, true);
// Create a dummy start frame // Create a dummy start frame
uint8_t start[DMR_FRAME_LENGTH_BYTES + 2U]; uint8_t start[DMR_FRAME_LENGTH_BYTES + 2U];
@ -851,11 +853,11 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
if (m_slot->m_duplex) { if (m_slot->m_duplex) {
for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++)
m_slot->writeQueueRF(start); m_slot->addFrame(start);
} }
else { else {
for (uint32_t i = 0U; i < NO_HEADERS_SIMPLEX; i++) for (uint32_t i = 0U; i < NO_HEADERS_SIMPLEX; i++)
m_slot->writeQueueRF(start); m_slot->addFrame(start);
} }
m_slot->m_netFrames = 0U; m_slot->m_netFrames = 0U;
@ -902,7 +904,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
} }
if (!m_slot->m_netTimeout) if (!m_slot->m_netTimeout)
m_slot->writeQueueNet(data); m_slot->addFrame(data, true);
m_netEmbeddedReadN = (m_netEmbeddedReadN + 1U) % 2U; m_netEmbeddedReadN = (m_netEmbeddedReadN + 1U) % 2U;
m_netEmbeddedWriteN = (m_netEmbeddedWriteN + 1U) % 2U; m_netEmbeddedWriteN = (m_netEmbeddedWriteN + 1U) % 2U;
@ -1035,7 +1037,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
if (insertSilence(data, dmrData.getN())) { if (insertSilence(data, dmrData.getN())) {
if (!m_slot->m_netTimeout) if (!m_slot->m_netTimeout)
m_slot->writeQueueNet(data); m_slot->addFrame(data, true);
} }
m_slot->m_packetTimer.start(); m_slot->m_packetTimer.start();
@ -1055,6 +1057,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Initializes a new instance of the VoicePacket class. /// Initializes a new instance of the VoicePacket class.
/// </summary> /// </summary>
@ -1255,7 +1258,7 @@ void VoicePacket::insertSilence(uint32_t count)
emb.encode(data + 2U); emb.encode(data + 2U);
} }
m_slot->writeQueueNet(data); m_slot->addFrame(data, true);
m_netN = n; m_netN = n;

@ -95,8 +95,6 @@ Host::Host(const std::string& confFile) :
m_cwIdTimer(1000U), m_cwIdTimer(1000U),
m_dmrEnabled(false), m_dmrEnabled(false),
m_p25Enabled(false), m_p25Enabled(false),
m_p25CtrlChannel(false),
m_dmrCtrlChannel(false),
m_duplex(false), m_duplex(false),
m_fixedMode(false), m_fixedMode(false),
m_timeout(180U), m_timeout(180U),
@ -120,7 +118,10 @@ Host::Host(const std::string& confFile) :
m_tidLookup(NULL), m_tidLookup(NULL),
m_dmrBeacons(false), m_dmrBeacons(false),
m_dmrTSCCData(false), m_dmrTSCCData(false),
m_controlData(false), m_dmrCtrlChannel(false),
m_p25CCData(false),
m_p25CtrlChannel(false),
m_p25CtrlBroadcast(false),
m_remoteControl(NULL) m_remoteControl(NULL)
{ {
UDPSocket::startup(); UDPSocket::startup();
@ -357,10 +358,25 @@ int Host::run()
bool dumpTAData = dmrProtocol["dumpTAData"].as<bool>(true); bool dumpTAData = dmrProtocol["dumpTAData"].as<bool>(true);
uint32_t callHang = dmrProtocol["callHang"].as<uint32_t>(3U); uint32_t callHang = dmrProtocol["callHang"].as<uint32_t>(3U);
uint32_t txHang = dmrProtocol["txHang"].as<uint32_t>(4U); uint32_t txHang = dmrProtocol["txHang"].as<uint32_t>(4U);
uint32_t dmrQueueSize = dmrProtocol["queueSize"].as<uint32_t>(5120U); uint32_t queueSize = dmrProtocol["queueSize"].as<uint32_t>(31U);
bool dmrVerbose = dmrProtocol["verbose"].as<bool>(true); bool dmrVerbose = dmrProtocol["verbose"].as<bool>(true);
bool dmrDebug = dmrProtocol["debug"].as<bool>(false); bool dmrDebug = dmrProtocol["debug"].as<bool>(false);
// clamp queue size to no less then 24 and no greater the 100
if (queueSize <= 24U) {
LogWarning(LOG_HOST, "DMR queue size must be greater then 24 frames, defaulting to 24 frames!");
queueSize = 24U;
}
if (queueSize > 100U) {
LogWarning(LOG_HOST, "DMR queue size must be less then 100 frames, defaulting to 100 frames!");
queueSize = 100U;
}
if (queueSize > 60U) {
LogWarning(LOG_HOST, "DMR queue size is excessive, >60 frames!");
}
uint32_t queueSizeBytes = queueSize * (dmr::DMR_FRAME_LENGTH_BYTES * 5U);
uint32_t jitter = m_conf["network"]["jitter"].as<uint32_t>(360U); uint32_t jitter = m_conf["network"]["jitter"].as<uint32_t>(360U);
if (txHang > m_rfModeHang) if (txHang > m_rfModeHang)
@ -378,7 +394,7 @@ int Host::run()
LogInfo(" Dump CSBK Data: %s", dmrDumpCsbkData ? "yes" : "no"); LogInfo(" Dump CSBK Data: %s", dmrDumpCsbkData ? "yes" : "no");
LogInfo(" Call Hang: %us", callHang); LogInfo(" Call Hang: %us", callHang);
LogInfo(" TX Hang: %us", txHang); LogInfo(" TX Hang: %us", txHang);
LogInfo(" Queue Size: %u", dmrQueueSize); LogInfo(" Queue Size: %u (%u bytes)", queueSize, queueSizeBytes);
LogInfo(" Roaming Beacons: %s", m_dmrBeacons ? "yes" : "no"); LogInfo(" Roaming Beacons: %s", m_dmrBeacons ? "yes" : "no");
if (m_dmrBeacons) { if (m_dmrBeacons) {
@ -403,13 +419,19 @@ int Host::run()
if (dmrCtrlChannel) { if (dmrCtrlChannel) {
m_dmrCtrlChannel = dmrCtrlChannel; m_dmrCtrlChannel = dmrCtrlChannel;
} }
g_fireDMRBeacon = true;
} }
dmr = new dmr::Control(m_dmrColorCode, callHang, dmrQueueSize, embeddedLCOnly, dumpTAData, m_timeout, m_rfTalkgroupHang, dmr = new dmr::Control(m_dmrColorCode, callHang, queueSizeBytes, embeddedLCOnly, dumpTAData, m_timeout, m_rfTalkgroupHang,
m_modem, m_network, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, jitter, dmrDumpDataPacket, dmrRepeatDataPacket, m_modem, m_network, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, jitter, dmrDumpDataPacket, dmrRepeatDataPacket,
dmrDumpCsbkData, dmrDebug, dmrVerbose); dmrDumpCsbkData, dmrDebug, dmrVerbose);
dmr->setOptions(m_conf, m_dmrNetId, m_siteId, m_channelId, m_channelNo, true); dmr->setOptions(m_conf, m_dmrNetId, m_siteId, m_channelId, m_channelNo, true);
if (dmrCtrlChannel) {
dmr->setCCRunning(true);
}
m_dmrTXTimer.setTimeout(txHang); m_dmrTXTimer.setTimeout(txHang);
if (dmrVerbose) { if (dmrVerbose) {
@ -421,8 +443,8 @@ int Host::run()
} }
// initialize P25 // initialize P25
Timer p25CCIntervalTimer(1000U); Timer p25BcastIntervalTimer(1000U);
Timer p25CCDurationTimer(1000U); Timer p25BcastDurationTimer(1000U);
p25::Control* p25 = NULL; p25::Control* p25 = NULL;
LogInfo("P25 Parameters"); LogInfo("P25 Parameters");
@ -430,34 +452,47 @@ int Host::run()
if (m_p25Enabled) { if (m_p25Enabled) {
yaml::Node p25Protocol = protocolConf["p25"]; yaml::Node p25Protocol = protocolConf["p25"];
uint32_t tduPreambleCount = p25Protocol["tduPreambleCount"].as<uint32_t>(8U); uint32_t tduPreambleCount = p25Protocol["tduPreambleCount"].as<uint32_t>(8U);
m_controlData = p25Protocol["control"]["enable"].as<bool>(false); m_p25CCData = p25Protocol["control"]["enable"].as<bool>(false);
bool p25CtrlChannel = p25Protocol["control"]["dedicated"].as<bool>(false); bool p25CtrlChannel = p25Protocol["control"]["dedicated"].as<bool>(false);
bool p25CtrlBroadcast = p25Protocol["control"]["broadcast"].as<bool>(true); bool p25CtrlBroadcast = p25Protocol["control"]["broadcast"].as<bool>(true);
bool p25DumpDataPacket = p25Protocol["dumpDataPacket"].as<bool>(false); bool p25DumpDataPacket = p25Protocol["dumpDataPacket"].as<bool>(false);
bool p25RepeatDataPacket = p25Protocol["repeatDataPacket"].as<bool>(true); bool p25RepeatDataPacket = p25Protocol["repeatDataPacket"].as<bool>(true);
bool p25DumpTsbkData = p25Protocol["dumpTsbkData"].as<bool>(false); bool p25DumpTsbkData = p25Protocol["dumpTsbkData"].as<bool>(false);
uint32_t callHang = p25Protocol["callHang"].as<uint32_t>(3U); uint32_t callHang = p25Protocol["callHang"].as<uint32_t>(3U);
uint32_t p25QueueSize = p25Protocol["queueSize"].as<uint32_t>(8192U); uint16_t queueSize = p25Protocol["queueSize"].as<uint16_t>(12U);
bool p25Verbose = p25Protocol["verbose"].as<bool>(true); bool p25Verbose = p25Protocol["verbose"].as<bool>(true);
bool p25Debug = p25Protocol["debug"].as<bool>(false); bool p25Debug = p25Protocol["debug"].as<bool>(false);
// clamp queue size to no less then 5 and no greater the 100 frames
if (queueSize <= 10U) {
LogWarning(LOG_HOST, "P25 queue size must be greater then 10 frames, defaulting to 10 frames!");
queueSize = 10U;
}
if (queueSize > 100U) {
LogWarning(LOG_HOST, "P25 queue size must be less then 100 frames, defaulting to 100 frames!");
queueSize = 100U;
}
if (queueSize > 30U) {
LogWarning(LOG_HOST, "P25 queue size is excessive, >30 frames!");
}
uint32_t queueSizeBytes = queueSize * p25::P25_LDU_FRAME_LENGTH_BYTES;
LogInfo(" TDU Preamble before Voice: %u", tduPreambleCount); LogInfo(" TDU Preamble before Voice: %u", tduPreambleCount);
LogInfo(" Dump Packet Data: %s", p25DumpDataPacket ? "yes" : "no"); LogInfo(" Dump Packet Data: %s", p25DumpDataPacket ? "yes" : "no");
LogInfo(" Repeat Packet Data: %s", p25RepeatDataPacket ? "yes" : "no"); LogInfo(" Repeat Packet Data: %s", p25RepeatDataPacket ? "yes" : "no");
LogInfo(" Dump TSBK Data: %s", p25DumpTsbkData ? "yes" : "no"); LogInfo(" Dump TSBK Data: %s", p25DumpTsbkData ? "yes" : "no");
LogInfo(" Call Hang: %us", callHang); LogInfo(" Call Hang: %us", callHang);
LogInfo(" Queue Size: %u", p25QueueSize); LogInfo(" Queue Size: %u (%u bytes)", queueSize, queueSizeBytes);
LogInfo(" Control: %s", m_controlData ? "yes" : "no"); LogInfo(" Control: %s", m_p25CCData ? "yes" : "no");
uint32_t p25ControlBcstInterval = p25Protocol["control"]["interval"].as<uint32_t>(300U); uint32_t p25ControlBcstInterval = p25Protocol["control"]["interval"].as<uint32_t>(300U);
uint32_t p25ControlBcstDuration = p25Protocol["control"]["duration"].as<uint32_t>(1U); uint32_t p25ControlBcstDuration = p25Protocol["control"]["duration"].as<uint32_t>(1U);
if (m_controlData) { if (m_p25CCData) {
LogInfo(" Control Broadcast: %s", p25CtrlBroadcast ? "yes" : "no"); LogInfo(" Control Broadcast: %s", p25CtrlBroadcast ? "yes" : "no");
LogInfo(" Control Channel: %s", p25CtrlChannel ? "yes" : "no"); LogInfo(" Control Channel: %s", p25CtrlChannel ? "yes" : "no");
if (p25CtrlChannel) { if (p25CtrlChannel) {
p25ControlBcstInterval = 30U;
p25ControlBcstDuration = 120U;
m_p25CtrlChannel = p25CtrlChannel; m_p25CtrlChannel = p25CtrlChannel;
} }
else { else {
@ -465,28 +500,27 @@ int Host::run()
LogInfo(" Control Broadcast Duration: %us", p25ControlBcstDuration); LogInfo(" Control Broadcast Duration: %us", p25ControlBcstDuration);
} }
m_p25CtrlBroadcast = p25CtrlBroadcast; p25BcastDurationTimer.setTimeout(p25ControlBcstDuration);
p25CCIntervalTimer.setTimeout(p25ControlBcstInterval);
p25CCIntervalTimer.start();
p25CCDurationTimer.setTimeout(p25ControlBcstDuration); p25BcastIntervalTimer.setTimeout(p25ControlBcstInterval);
p25BcastIntervalTimer.start();
m_p25CtrlBroadcast = p25CtrlBroadcast;
if (p25CtrlBroadcast) { if (p25CtrlBroadcast) {
g_fireP25Control = true; g_fireP25Control = true;
g_interruptP25Control = false;
}
else {
g_fireP25Control = false;
g_interruptP25Control = false;
} }
} }
p25 = new p25::Control(m_p25NAC, callHang, p25QueueSize, m_modem, m_network, m_timeout, m_rfTalkgroupHang, p25 = new p25::Control(m_p25NAC, callHang, queueSizeBytes, m_modem, m_network, m_timeout, m_rfTalkgroupHang,
p25ControlBcstInterval, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, p25DumpDataPacket, p25RepeatDataPacket, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, p25DumpDataPacket, p25RepeatDataPacket,
p25DumpTsbkData, p25Debug, p25Verbose); p25DumpTsbkData, p25Debug, p25Verbose);
p25->setOptions(m_conf, m_cwCallsign, m_voiceChNo, m_p25PatchSuperGroup, m_p25NetId, m_p25SysId, m_p25RfssId, p25->setOptions(m_conf, m_cwCallsign, m_voiceChNo, m_p25PatchSuperGroup, m_p25NetId, m_p25SysId, m_p25RfssId,
m_siteId, m_channelId, m_channelNo, true); m_siteId, m_channelId, m_channelNo, true);
if (p25CtrlChannel) {
p25->setCCRunning(true);
}
if (p25Verbose) { if (p25Verbose) {
LogInfo(" Verbose: yes"); LogInfo(" Verbose: yes");
} }
@ -513,7 +547,7 @@ int Host::run()
} }
#endif #endif
// P25 control channel checks // P25 CC checks
if (m_dmrEnabled && m_p25CtrlChannel) { if (m_dmrEnabled && m_p25CtrlChannel) {
::LogError(LOG_HOST, "Cannot have DMR enabled when using dedicated P25 control!"); ::LogError(LOG_HOST, "Cannot have DMR enabled when using dedicated P25 control!");
g_killed = true; g_killed = true;
@ -523,7 +557,7 @@ int Host::run()
::LogWarning(LOG_HOST, "Fixed mode should be enabled when using dedicated P25 control!"); ::LogWarning(LOG_HOST, "Fixed mode should be enabled when using dedicated P25 control!");
} }
if (!m_duplex && m_controlData) { if (!m_duplex && m_p25CCData) {
::LogError(LOG_HOST, "Cannot have P25 control and simplex mode at the same time."); ::LogError(LOG_HOST, "Cannot have P25 control and simplex mode at the same time.");
g_killed = true; g_killed = true;
} }
@ -544,7 +578,7 @@ int Host::run()
} }
// DMR beacon checks // DMR beacon checks
if (m_dmrBeacons && m_controlData) { if (m_dmrBeacons && m_p25CCData) {
::LogError(LOG_HOST, "Cannot have DMR roaming becaons and P25 control at the same time."); ::LogError(LOG_HOST, "Cannot have DMR roaming becaons and P25 control at the same time.");
g_killed = true; g_killed = true;
} }
@ -618,13 +652,9 @@ int Host::run()
// Macro to interrupt a running P25 control channel transmission // Macro to interrupt a running P25 control channel transmission
#define INTERRUPT_P25_CONTROL \ #define INTERRUPT_P25_CONTROL \
if (p25 != NULL) { \ if (p25 != NULL) { \
if (g_interruptP25Control) { \ p25->setCCHalted(true); \
p25CCDurationTimer.stop(); \ if (p25BcastDurationTimer.isRunning() && !p25BcastDurationTimer.isPaused()) { \
if (p25CCDurationTimer.isRunning() && !p25CCDurationTimer.hasExpired()) { \ p25BcastDurationTimer.pause(); \
LogDebug(LOG_HOST, "traffic interrupts P25 CC, g_interruptP25Control = %u", g_interruptP25Control); \
m_modem->clearP25Data(); \
p25->reset(); \
} \
} \ } \
} }
@ -695,33 +725,37 @@ int Host::run()
// to the modem // to the modem
ret = m_modem->hasDMRSpace1(); ret = m_modem->hasDMRSpace1();
if (ret) { if (ret) {
len = dmr->getFrame1(data); len = dmr->getFrame(1U, data);
if (len > 0U) { if (len > 0U) {
// if the state is idle; set to DMR, start mode timer and start DMR idle frames
if (m_state == STATE_IDLE) { if (m_state == STATE_IDLE) {
m_modeTimer.setTimeout(m_netModeHang); m_modeTimer.setTimeout(m_netModeHang);
setState(STATE_DMR); setState(STATE_DMR);
START_DMR_DUPLEX_IDLE(true); START_DMR_DUPLEX_IDLE(true);
} }
// if the state is DMR; start DMR idle frames and write DMR slot 1 data
if (m_state == STATE_DMR) { if (m_state == STATE_DMR) {
START_DMR_DUPLEX_IDLE(true); START_DMR_DUPLEX_IDLE(true);
m_modem->writeDMRData1(data, len); m_modem->writeDMRData1(data, len);
// if there is no DMR CC running; run the interrupt macro to stop
// any running DMR beacon
if (!dmr->getCCRunning()) { if (!dmr->getCCRunning()) {
INTERRUPT_DMR_BEACON; INTERRUPT_DMR_BEACON;
} }
if (g_interruptP25Control && p25CCDurationTimer.isRunning()) { // if there is a P25 CC running; halt the CC
p25CCDurationTimer.pause(); if (p25 != NULL) {
if (p25->getCCRunning() && !p25->getCCHalted()) {
p25->setCCHalted(true);
INTERRUPT_P25_CONTROL;
}
} }
m_modeTimer.start(); m_modeTimer.start();
} }
/*
else if (m_state != HOST_STATE_LOCKOUT) {
LogWarning(LOG_HOST, "DMR data received, state = %u", m_state);
}
*/
} }
} }
@ -730,33 +764,35 @@ int Host::run()
// to the modem // to the modem
ret = m_modem->hasDMRSpace2(); ret = m_modem->hasDMRSpace2();
if (ret) { if (ret) {
len = dmr->getFrame2(data); len = dmr->getFrame(2U, data);
if (len > 0U) { if (len > 0U) {
// if the state is idle; set to DMR, start mode timer and start DMR idle frames
if (m_state == STATE_IDLE) { if (m_state == STATE_IDLE) {
m_modeTimer.setTimeout(m_netModeHang); m_modeTimer.setTimeout(m_netModeHang);
setState(STATE_DMR); setState(STATE_DMR);
START_DMR_DUPLEX_IDLE(true); START_DMR_DUPLEX_IDLE(true);
} }
// if the state is DMR; start DMR idle frames and write DMR slot 2 data
if (m_state == STATE_DMR) { if (m_state == STATE_DMR) {
START_DMR_DUPLEX_IDLE(true); START_DMR_DUPLEX_IDLE(true);
m_modem->writeDMRData2(data, len); m_modem->writeDMRData2(data, len);
// if there is no DMR CC running; run the interrupt macro to stop
// any running DMR beacon
if (!dmr->getCCRunning()) { if (!dmr->getCCRunning()) {
INTERRUPT_DMR_BEACON; INTERRUPT_DMR_BEACON;
} }
if (g_interruptP25Control && p25CCDurationTimer.isRunning()) { // if there is a P25 CC running; halt the CC
p25CCDurationTimer.pause(); if (p25->getCCRunning() && !p25->getCCHalted()) {
p25->setCCHalted(true);
INTERRUPT_P25_CONTROL;
} }
m_modeTimer.start(); m_modeTimer.start();
} }
/*
else if (m_state != HOST_STATE_LOCKOUT) {
LogWarning(LOG_HOST, "DMR data received, state = %u", m_state);
}
*/
} }
} }
} }
@ -770,38 +806,31 @@ int Host::run()
if (ret) { if (ret) {
len = p25->getFrame(data); len = p25->getFrame(data);
if (len > 0U) { if (len > 0U) {
// if the state is idle; set to P25 and start mode timer
if (m_state == STATE_IDLE) { if (m_state == STATE_IDLE) {
m_modeTimer.setTimeout(m_netModeHang); m_modeTimer.setTimeout(m_netModeHang);
setState(STATE_P25); setState(STATE_P25);
} }
// if the state is P25; write P25 data
if (m_state == STATE_P25) { if (m_state == STATE_P25) {
m_modem->writeP25Data(data, len); m_modem->writeP25Data(data, len);
INTERRUPT_DMR_BEACON; INTERRUPT_DMR_BEACON;
if (g_interruptP25Control && p25CCDurationTimer.isRunning()) {
p25CCDurationTimer.pause();
}
m_modeTimer.start(); m_modeTimer.start();
} }
/*
else if (m_state != HOST_STATE_LOCKOUT) {
LogWarning(LOG_HOST, "P25 data received, state = %u", m_state);
}
*/
} }
else { else {
// if we have no P25 data, and we're either idle or P25 state, check if we
// need to be starting the CC running flag or writing end of voice call data
if (m_state == STATE_IDLE || m_state == STATE_P25) { if (m_state == STATE_IDLE || m_state == STATE_P25) {
// P25 control data, if control data is being transmitted if (p25->getCCHalted()) {
if (p25CCDurationTimer.isRunning() && !p25CCDurationTimer.hasExpired()) { p25->setCCHalted(false);
p25->setCCRunning(true);
p25->writeControlRF();
} }
// P25 status data, tail on idle // write end of voice if necessary
ret = p25->writeEndRF(); ret = p25->writeRF_VoiceEnd();
if (ret) { if (ret) {
if (m_state == STATE_IDLE) { if (m_state == STATE_IDLE) {
m_modeTimer.setTimeout(m_netModeHang); m_modeTimer.setTimeout(m_netModeHang);
@ -817,13 +846,12 @@ int Host::run()
// if the modem is in duplex -- handle P25 CC burst control // if the modem is in duplex -- handle P25 CC burst control
if (m_duplex) { if (m_duplex) {
if (p25CCDurationTimer.isPaused() && !g_interruptP25Control) { if (p25BcastDurationTimer.isPaused() && !p25->getCCHalted()) {
LogDebug(LOG_HOST, "traffic complete, resume P25 CC, g_interruptP25Control = %u", g_interruptP25Control); p25BcastDurationTimer.resume();
p25CCDurationTimer.resume();
} }
if (g_interruptP25Control) { if (p25->getCCHalted()) {
g_fireP25Control = true; p25->setCCHalted(false);
} }
if (g_fireP25Control) { if (g_fireP25Control) {
@ -859,6 +887,7 @@ int Host::run()
if (ret) { if (ret) {
m_modeTimer.setTimeout(m_rfModeHang); m_modeTimer.setTimeout(m_rfModeHang);
setState(STATE_DMR); setState(STATE_DMR);
START_DMR_DUPLEX_IDLE(true); START_DMR_DUPLEX_IDLE(true);
INTERRUPT_DMR_BEACON; INTERRUPT_DMR_BEACON;
@ -871,10 +900,10 @@ int Host::run()
setState(STATE_DMR); setState(STATE_DMR);
START_DMR_DUPLEX_IDLE(true); START_DMR_DUPLEX_IDLE(true);
dmr->processFrame1(data, len); dmr->processFrame(1U, data, len);
INTERRUPT_DMR_BEACON; INTERRUPT_DMR_BEACON;
p25CCDurationTimer.stop(); p25BcastDurationTimer.stop();
} }
} }
else if (m_state == STATE_DMR) { else if (m_state == STATE_DMR) {
@ -889,7 +918,7 @@ int Host::run()
} }
else { else {
// process slot 1 frames // process slot 1 frames
bool ret = dmr->processFrame1(data, len); bool ret = dmr->processFrame(1U, data, len);
if (ret) { if (ret) {
INTERRUPT_DMR_BEACON; INTERRUPT_DMR_BEACON;
INTERRUPT_P25_CONTROL; INTERRUPT_P25_CONTROL;
@ -928,7 +957,7 @@ int Host::run()
setState(STATE_DMR); setState(STATE_DMR);
START_DMR_DUPLEX_IDLE(true); START_DMR_DUPLEX_IDLE(true);
dmr->processFrame2(data, len); dmr->processFrame(2U, data, len);
INTERRUPT_DMR_BEACON; INTERRUPT_DMR_BEACON;
INTERRUPT_P25_CONTROL; INTERRUPT_P25_CONTROL;
@ -946,7 +975,7 @@ int Host::run()
} }
else { else {
// process slot 2 frames // process slot 2 frames
bool ret = dmr->processFrame2(data, len); bool ret = dmr->processFrame(2U, data, len);
if (ret) { if (ret) {
INTERRUPT_DMR_BEACON; INTERRUPT_DMR_BEACON;
INTERRUPT_P25_CONTROL; INTERRUPT_P25_CONTROL;
@ -970,6 +999,7 @@ int Host::run()
len = m_modem->readP25Data(data); len = m_modem->readP25Data(data);
if (len > 0U) { if (len > 0U) {
if (m_state == STATE_IDLE) { if (m_state == STATE_IDLE) {
// process P25 frames
bool ret = p25->processFrame(data, len); bool ret = p25->processFrame(data, len);
if (ret) { if (ret) {
m_modeTimer.setTimeout(m_rfModeHang); m_modeTimer.setTimeout(m_rfModeHang);
@ -979,7 +1009,7 @@ int Host::run()
INTERRUPT_P25_CONTROL; INTERRUPT_P25_CONTROL;
} }
else { else {
ret = p25->writeEndRF(); ret = p25->writeRF_VoiceEnd();
if (ret) { if (ret) {
INTERRUPT_DMR_BEACON; INTERRUPT_DMR_BEACON;
@ -994,13 +1024,12 @@ int Host::run()
// if the modem is in duplex -- handle P25 CC burst control // if the modem is in duplex -- handle P25 CC burst control
if (m_duplex) { if (m_duplex) {
if (p25CCDurationTimer.isPaused() && !g_interruptP25Control) { if (p25BcastDurationTimer.isPaused() && !p25->getCCHalted()) {
LogDebug(LOG_HOST, "traffic complete, resume P25 CC, g_interruptP25Control = %u", g_interruptP25Control); p25BcastDurationTimer.resume();
p25CCDurationTimer.resume();
} }
if (g_interruptP25Control) { if (p25->getCCHalted()) {
g_fireP25Control = true; p25->setCCHalted(false);
} }
if (g_fireP25Control) { if (g_fireP25Control) {
@ -1008,20 +1037,20 @@ int Host::run()
} }
} }
else { else {
p25CCDurationTimer.stop(); p25BcastDurationTimer.stop();
g_interruptP25Control = false;
} }
} }
} }
} }
else if (m_state == STATE_P25) { else if (m_state == STATE_P25) {
// process P25 frames
bool ret = p25->processFrame(data, len); bool ret = p25->processFrame(data, len);
if (ret) { if (ret) {
m_modeTimer.start(); m_modeTimer.start();
INTERRUPT_P25_CONTROL; INTERRUPT_P25_CONTROL;
} }
else { else {
ret = p25->writeEndRF(); ret = p25->writeRF_VoiceEnd();
if (ret) { if (ret) {
m_modeTimer.start(); m_modeTimer.start();
} }
@ -1061,11 +1090,11 @@ int Host::run()
m_cwIdTimer.clock(ms); m_cwIdTimer.clock(ms);
if (m_cwIdTimer.isRunning() && m_cwIdTimer.hasExpired()) { if (m_cwIdTimer.isRunning() && m_cwIdTimer.hasExpired()) {
if (!m_modem->hasTX() && !m_p25CtrlChannel && !m_dmrCtrlChannel) { if (!m_modem->hasTX() && !m_p25CtrlChannel && !m_dmrCtrlChannel) {
if (dmrBeaconDurationTimer.isRunning() || p25CCDurationTimer.isRunning()) { if (dmrBeaconDurationTimer.isRunning() || p25BcastDurationTimer.isRunning()) {
LogDebug(LOG_HOST, "CW, beacon or CC timer running, ceasing"); LogDebug(LOG_HOST, "CW, beacon or CC timer running, ceasing");
dmrBeaconDurationTimer.stop(); dmrBeaconDurationTimer.stop();
p25CCDurationTimer.stop(); p25BcastDurationTimer.stop();
} }
LogDebug(LOG_HOST, "CW, start transmitting"); LogDebug(LOG_HOST, "CW, start transmitting");
@ -1135,7 +1164,12 @@ int Host::run()
} }
g_fireDMRBeacon = false; g_fireDMRBeacon = false;
if (m_dmrTSCCData) {
LogDebug(LOG_HOST, "DMR, start CC broadcast");
}
else {
LogDebug(LOG_HOST, "DMR, roaming beacon burst"); LogDebug(LOG_HOST, "DMR, roaming beacon burst");
}
dmrBeaconIntervalTimer.start(); dmrBeaconIntervalTimer.start();
dmrBeaconDurationTimer.start(); dmrBeaconDurationTimer.start();
} }
@ -1168,11 +1202,12 @@ int Host::run()
/** P25 */ /** P25 */
if (p25 != NULL) { if (p25 != NULL) {
if (m_controlData) { if (m_p25CCData) {
p25CCIntervalTimer.clock(ms); p25BcastIntervalTimer.clock(ms);
if (m_p25CtrlBroadcast) { if (!m_p25CtrlChannel && m_p25CtrlBroadcast) {
if ((p25CCIntervalTimer.isRunning() && p25CCIntervalTimer.hasExpired()) || g_fireP25Control) { // clock and check P25 CC broadcast interval timer
if ((p25BcastIntervalTimer.isRunning() && p25BcastIntervalTimer.hasExpired()) || g_fireP25Control) {
if ((m_state == STATE_IDLE || m_state == STATE_P25) && !m_modem->hasTX()) { if ((m_state == STATE_IDLE || m_state == STATE_P25) && !m_modem->hasTX()) {
if (m_modeTimer.isRunning()) { if (m_modeTimer.isRunning()) {
m_modeTimer.stop(); m_modeTimer.stop();
@ -1181,11 +1216,6 @@ int Host::run()
if (m_state != STATE_P25) if (m_state != STATE_P25)
setState(STATE_P25); setState(STATE_P25);
if (g_interruptP25Control) {
g_interruptP25Control = false;
LogDebug(LOG_HOST, "traffic complete, restart P25 CC broadcast, g_interruptP25Control = %u", g_interruptP25Control);
}
p25->writeAdjSSNetwork(); p25->writeAdjSSNetwork();
p25->setCCRunning(true); p25->setCCRunning(true);
@ -1195,25 +1225,25 @@ int Host::run()
} }
g_fireP25Control = false; g_fireP25Control = false;
p25CCIntervalTimer.start(); p25BcastIntervalTimer.start();
p25CCDurationTimer.start(); p25BcastDurationTimer.start();
// if the CC is continuous -- clock one cycle into the duration timer // if the CC is continuous -- clock one cycle into the duration timer
if (m_p25CtrlChannel) { if (m_p25CtrlChannel) {
p25CCDurationTimer.clock(ms); p25BcastDurationTimer.clock(ms);
} }
} }
} }
// if the CC is continuous -- we don't clock the CC duration timer (which results in the CC if (p25BcastDurationTimer.isPaused()) {
// broadcast running infinitely until stopped) p25BcastDurationTimer.resume();
if (!m_p25CtrlChannel) { }
// clock and check P25 CC duration timer
p25CCDurationTimer.clock(ms); // clock and check P25 CC broadcast duration timer
if (p25CCDurationTimer.isRunning() && p25CCDurationTimer.hasExpired()) { p25BcastDurationTimer.clock(ms);
p25CCDurationTimer.stop(); if (p25BcastDurationTimer.isRunning() && p25BcastDurationTimer.hasExpired()) {
p25BcastDurationTimer.stop();
p25->writeControlEndRF();
p25->setCCRunning(false); p25->setCCRunning(false);
if (m_state == STATE_P25 && !m_modeTimer.isRunning()) { if (m_state == STATE_P25 && !m_modeTimer.isRunning()) {
@ -1221,19 +1251,14 @@ int Host::run()
m_modeTimer.start(); m_modeTimer.start();
} }
} }
if (p25CCDurationTimer.isPaused()) {
p25CCDurationTimer.resume();
}
}
} }
else { else {
// simply use the P25 CC interval timer in a non-broadcast state to transmit adjacent site data over // simply use the P25 CC interval timer in a non-broadcast state to transmit adjacent site data over
// the network // the network
if (p25CCIntervalTimer.isRunning() && p25CCIntervalTimer.hasExpired()) { if (p25BcastIntervalTimer.isRunning() && p25BcastIntervalTimer.hasExpired()) {
if ((m_state == STATE_IDLE || m_state == STATE_P25) && !m_modem->hasTX()) { if ((m_state == STATE_IDLE || m_state == STATE_P25) && !m_modem->hasTX()) {
p25->writeAdjSSNetwork(); p25->writeAdjSSNetwork();
p25CCIntervalTimer.start(); p25BcastIntervalTimer.start();
} }
} }
} }
@ -1241,16 +1266,31 @@ int Host::run()
} }
if (g_killed) { if (g_killed) {
if (dmr != NULL) {
if (m_dmrCtrlChannel) {
if (!hasTxShutdown) {
m_modem->clearDMRData1();
m_modem->clearDMRData2();
}
dmr->setCCRunning(false);
dmrBeaconDurationTimer.stop();
dmrBeaconIntervalTimer.stop();
}
}
if (p25 != NULL) { if (p25 != NULL) {
if (m_p25CtrlChannel && !hasTxShutdown) { if (m_p25CtrlChannel) {
if (!hasTxShutdown) {
m_modem->clearP25Data(); m_modem->clearP25Data();
p25->reset(); p25->reset();
}
p25->writeControlEndRF();
p25->setCCRunning(false); p25->setCCRunning(false);
p25CCDurationTimer.stop(); p25BcastDurationTimer.stop();
p25CCIntervalTimer.stop(); p25BcastIntervalTimer.stop();
} }
} }

@ -83,10 +83,6 @@ private:
bool m_dmrEnabled; bool m_dmrEnabled;
bool m_p25Enabled; bool m_p25Enabled;
bool m_p25CtrlChannel;
bool m_p25CtrlBroadcast;
bool m_dmrCtrlChannel;
bool m_duplex; bool m_duplex;
bool m_fixedMode; bool m_fixedMode;
bool m_useDFSI; bool m_useDFSI;
@ -118,7 +114,10 @@ private:
bool m_dmrBeacons; bool m_dmrBeacons;
bool m_dmrTSCCData; bool m_dmrTSCCData;
bool m_controlData; bool m_dmrCtrlChannel;
bool m_p25CCData;
bool m_p25CtrlChannel;
bool m_p25CtrlBroadcast;
uint8_t m_siteId; uint8_t m_siteId;
uint32_t m_dmrNetId; uint32_t m_dmrNetId;

@ -160,12 +160,12 @@ Modem::Modem(port::IModemPort* port, bool duplex, bool rxInvert, bool txInvert,
m_openPortHandler(NULL), m_openPortHandler(NULL),
m_closePortHandler(NULL), m_closePortHandler(NULL),
m_rspHandler(NULL), m_rspHandler(NULL),
m_rxDMRData1(1110U, "Modem RX DMR1"), m_rxDMRData1(1089U, "Modem RX DMR1"), // 1089 bytes = 33 DMR Frames
m_rxDMRData2(1110U, "Modem RX DMR2"), m_rxDMRData2(1089U, "Modem RX DMR2"),
m_txDMRData1(740U, "Modem TX DMR1"), m_txDMRData1(792U, "Modem TX DMR1"), // 792 bytes = 24 DMR Frames
m_txDMRData2(740U, "Modem TX DMR2"), m_txDMRData2(792U, "Modem TX DMR2"),
m_rxP25Data(6000U, "Modem RX P25"), m_rxP25Data(6048U, "Modem RX P25"), // 6048 bytes = 28 P25 Frames
m_txP25Data(864U, "Modem TX P25"), m_txP25Data(864U, "Modem TX P25"), // 864 = 4 P25 Frames
m_useDFSI(false), m_useDFSI(false),
m_statusTimer(1000U, 0U, 250U), m_statusTimer(1000U, 0U, 250U),
m_inactivityTimer(1000U, 4U), m_inactivityTimer(1000U, 4U),

@ -306,7 +306,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25)
else if (rcom == RCD_P25_CC_CMD) { else if (rcom == RCD_P25_CC_CMD) {
// Command is in the form of: "p25-cc" // Command is in the form of: "p25-cc"
if (p25 != NULL) { if (p25 != NULL) {
if (host->m_controlData) { if (host->m_p25CCData) {
g_fireP25Control = true; g_fireP25Control = true;
} }
else { else {
@ -321,7 +321,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25)
// Command is in the form of: "p25-cc-fallback 0/1" // Command is in the form of: "p25-cc-fallback 0/1"
uint8_t fallback = getArgUInt8(args, 0U); uint8_t fallback = getArgUInt8(args, 0U);
if (p25 != NULL) { if (p25 != NULL) {
if (host->m_controlData) { if (host->m_p25CCData) {
p25->trunk()->setConvFallback((fallback == 1U) ? true : false); p25->trunk()->setConvFallback((fallback == 1U) ? true : false);
} }
else { else {
@ -727,7 +727,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25)
else if (rcom == RCD_P25_CC_DEDICATED_CMD) { else if (rcom == RCD_P25_CC_DEDICATED_CMD) {
// Command is in the form of: "p25-cc-dedicated" // Command is in the form of: "p25-cc-dedicated"
if (p25 != NULL) { if (p25 != NULL) {
if (host->m_controlData) { if (host->m_p25CCData) {
if (dmr != NULL) { if (dmr != NULL) {
LogError(LOG_RCON, CMD_FAILED_STR "Can't enable P25 control channel while DMR is enabled!"); LogError(LOG_RCON, CMD_FAILED_STR "Can't enable P25 control channel while DMR is enabled!");
} }
@ -735,7 +735,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25)
host->m_p25CtrlChannel = !host->m_p25CtrlChannel; host->m_p25CtrlChannel = !host->m_p25CtrlChannel;
host->m_p25CtrlBroadcast = true; host->m_p25CtrlBroadcast = true;
g_fireP25Control = true; g_fireP25Control = true;
g_interruptP25Control = false; p25->setCCHalted(false);
LogInfoEx(LOG_RCON, "P25 CC is %s", host->m_p25CtrlChannel ? "enabled" : "disabled"); LogInfoEx(LOG_RCON, "P25 CC is %s", host->m_p25CtrlChannel ? "enabled" : "disabled");
} }
@ -751,16 +751,16 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25)
else if (rcom == RCD_P25_CC_BCAST_CMD) { else if (rcom == RCD_P25_CC_BCAST_CMD) {
// Command is in the form of: "p25-cc-bcast" // Command is in the form of: "p25-cc-bcast"
if (p25 != NULL) { if (p25 != NULL) {
if (host->m_controlData) { if (host->m_p25CCData) {
host->m_p25CtrlBroadcast = !host->m_p25CtrlBroadcast; host->m_p25CtrlBroadcast = !host->m_p25CtrlBroadcast;
if (!host->m_p25CtrlBroadcast) { if (!host->m_p25CtrlBroadcast) {
g_fireP25Control = false; g_fireP25Control = false;
g_interruptP25Control = true; p25->setCCHalted(true);
} }
else { else {
g_fireP25Control = true; g_fireP25Control = true;
g_interruptP25Control = false; p25->setCCHalted(false);
} }
LogInfoEx(LOG_RCON, "P25 CC broadcast is %s", host->m_p25CtrlBroadcast ? "enabled" : "disabled"); LogInfoEx(LOG_RCON, "P25 CC broadcast is %s", host->m_p25CtrlBroadcast ? "enabled" : "disabled");

@ -59,6 +59,7 @@ const uint32_t MAX_PREAMBLE_TDU_CNT = 64U;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Initializes a new instance of the Control class. /// Initializes a new instance of the Control class.
/// </summary> /// </summary>
@ -69,7 +70,6 @@ const uint32_t MAX_PREAMBLE_TDU_CNT = 64U;
/// <param name="network">Instance of the BaseNetwork class.</param> /// <param name="network">Instance of the BaseNetwork class.</param>
/// <param name="timeout">Transmit timeout.</param> /// <param name="timeout">Transmit timeout.</param>
/// <param name="tgHang">Amount of time to hang on the last talkgroup mode from RF.</param> /// <param name="tgHang">Amount of time to hang on the last talkgroup mode from RF.</param>
/// <param name="ccBcstInterval">Control Channel Broadcast Interval.</param>
/// <param name="duplex">Flag indicating full-duplex operation.</param> /// <param name="duplex">Flag indicating full-duplex operation.</param>
/// <param name="ridLookup">Instance of the RadioIdLookup class.</param> /// <param name="ridLookup">Instance of the RadioIdLookup class.</param>
/// <param name="tidLookup">Instance of the TalkgroupIdLookup class.</param> /// <param name="tidLookup">Instance of the TalkgroupIdLookup class.</param>
@ -81,7 +81,7 @@ const uint32_t MAX_PREAMBLE_TDU_CNT = 64U;
/// <param name="debug">Flag indicating whether P25 debug is enabled.</param> /// <param name="debug">Flag indicating whether P25 debug is enabled.</param>
/// <param name="verbose">Flag indicating whether P25 verbose logging 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, 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, uint32_t timeout, uint32_t tgHang, bool duplex, lookups::RadioIdLookup* ridLookup,
lookups::TalkgroupIdLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper, lookups::TalkgroupIdLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper,
bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose) : bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose) :
m_voice(NULL), m_voice(NULL),
@ -105,19 +105,20 @@ Control::Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Mod
m_ridLookup(ridLookup), m_ridLookup(ridLookup),
m_tidLookup(tidLookup), m_tidLookup(tidLookup),
m_idenEntry(), m_idenEntry(),
m_queue(queueSize, "P25 Control"), m_queue(queueSize, "P25 Frame"),
m_rfState(RS_RF_LISTENING), m_rfState(RS_RF_LISTENING),
m_rfLastDstId(0U), m_rfLastDstId(0U),
m_netState(RS_NET_IDLE), m_netState(RS_NET_IDLE),
m_netLastDstId(0U), m_netLastDstId(0U),
m_tailOnIdle(false), m_tailOnIdle(false),
m_ccOnIdle(false),
m_ccRunning(false), m_ccRunning(false),
m_ccBcstInterval(ccBcstInterval), m_ccPrevRunning(false),
m_ccHalted(false),
m_rfTimeout(1000U, timeout), m_rfTimeout(1000U, timeout),
m_rfTGHang(1000U, tgHang), m_rfTGHang(1000U, tgHang),
m_netTimeout(1000U, timeout), m_netTimeout(1000U, timeout),
m_networkWatchdog(1000U, 0U, 1500U), m_networkWatchdog(1000U, 0U, 1500U),
m_ccPacketInterval(1000U, 0U, 5U),
m_hangCount(3U * 8U), m_hangCount(3U * 8U),
m_tduPreambleCount(8U), m_tduPreambleCount(8U),
m_ccFrameCnt(0U), m_ccFrameCnt(0U),
@ -130,6 +131,7 @@ Control::Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Mod
m_minRSSI(0U), m_minRSSI(0U),
m_aveRSSI(0U), m_aveRSSI(0U),
m_rssiCount(0U), m_rssiCount(0U),
m_writeImmediate(false),
m_verbose(verbose), m_verbose(verbose),
m_debug(debug) m_debug(debug)
{ {
@ -184,6 +186,7 @@ Control::~Control()
void Control::reset() void Control::reset()
{ {
m_rfState = RS_RF_LISTENING; m_rfState = RS_RF_LISTENING;
m_ccHalted = false;
if (m_voice != NULL) { if (m_voice != NULL) {
m_voice->resetRF(); m_voice->resetRF();
@ -293,6 +296,9 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s
m_trunk->m_voiceChTable.push_back(*it); m_trunk->m_voiceChTable.push_back(*it);
} }
uint32_t ccBcstInterval = p25Protocol["control"]["interval"].as<uint32_t>(300U);
m_trunk->m_adjSiteUpdateInterval += ccBcstInterval;
if (printOptions) { if (printOptions) {
LogInfo(" Silence Threshold: %u (%.1f%%)", m_voice->m_silenceThreshold, float(m_voice->m_silenceThreshold) / 12.33F); LogInfo(" Silence Threshold: %u (%.1f%%)", m_voice->m_silenceThreshold, float(m_voice->m_silenceThreshold) / 12.33F);
@ -342,15 +348,6 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s
} }
} }
/// <summary>
/// Sets a flag indicating whether the P25 control channel is running.
/// </summary>
/// <param name="ccRunning"></param>
void Control::setCCRunning(bool ccRunning)
{
m_ccRunning = ccRunning;
}
/// <summary> /// <summary>
/// Process a data frame from the RF interface. /// Process a data frame from the RF interface.
/// </summary> /// </summary>
@ -388,7 +385,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len)
writeRF_TDU(false); writeRF_TDU(false);
m_voice->m_lastDUID = P25_DUID_TDU; m_voice->m_lastDUID = P25_DUID_TDU;
m_voice->writeNetworkRF(data + 2U, P25_DUID_TDU); m_voice->writeNetwork(data + 2U, P25_DUID_TDU);
m_rfState = RS_RF_LISTENING; m_rfState = RS_RF_LISTENING;
m_rfLastDstId = 0U; m_rfLastDstId = 0U;
@ -496,7 +493,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len)
// are we interrupting a running CC? // are we interrupting a running CC?
if (m_ccRunning) { if (m_ccRunning) {
if (duid != P25_DUID_TSDU) { if (duid != P25_DUID_TSDU) {
g_interruptP25Control = true; m_ccHalted = true;
} }
} }
@ -557,7 +554,6 @@ uint32_t Control::getFrame(uint8_t* data)
uint8_t len = 0U; uint8_t len = 0U;
m_queue.getData(&len, 1U); m_queue.getData(&len, 1U);
m_queue.getData(data, len); m_queue.getData(data, len);
return len; return len;
@ -572,81 +568,36 @@ void Control::writeAdjSSNetwork()
} }
/// <summary> /// <summary>
/// Helper to write control channel frame data. /// Helper to write end of voice call frame data.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
bool Control::writeControlRF() bool Control::writeRF_VoiceEnd()
{ {
if (!m_control) {
return false;
}
const uint8_t maxSeq = 8U;
if (m_ccSeq == maxSeq) {
m_ccSeq = 0U;
}
if (m_ccFrameCnt == 254U) {
m_trunk->writeAdjSSNetwork();
m_ccFrameCnt = 0U;
}
if (m_netState == RS_NET_IDLE && m_rfState == RS_RF_LISTENING) { if (m_netState == RS_NET_IDLE && m_rfState == RS_RF_LISTENING) {
m_trunk->writeRF_ControlData(m_ccFrameCnt, m_ccSeq, true); if (m_tailOnIdle) {
bool ret = false;
m_ccSeq++;
if (m_ccSeq == maxSeq) {
m_ccFrameCnt++;
}
return true;
}
return false;
}
/// <summary>
/// Helper to write end of control channel frame data.
/// </summary>
/// <returns></returns>
bool Control::writeControlEndRF()
{
if (!m_control) {
return false;
}
if (m_netState == RS_NET_IDLE && m_rfState == RS_RF_LISTENING) { if (m_netState == RS_NET_IDLE && m_rfState == RS_RF_LISTENING) {
for (uint32_t i = 0; i < TSBK_PCH_CCH_CNT; i++) { m_voice->writeRF_EndOfVoice();
m_trunk->queueRF_TSBK_Ctrl(TSBK_OSP_MOT_PSH_CCH);
}
writeRF_Nulls(); // this should have been cleared by writeRF_EndOfVoice; but if it hasn't clear it
return true; // to prevent badness
if (m_voice->m_hadVoice) {
m_voice->m_hadVoice = false;
} }
return false; m_tailOnIdle = false;
if (m_network != NULL)
m_network->resetP25();
ret = true;
} }
/// <summary>
/// Helper to write end of frame data.
/// </summary>
/// <returns></returns>
bool Control::writeEndRF()
{
if (m_netState == RS_NET_IDLE && m_rfState == RS_RF_LISTENING) {
if (m_tailOnIdle) {
bool ret = m_voice->writeEndRF();
if (!m_control && m_duplex) { if (!m_control && m_duplex) {
writeRF_Nulls(); writeRF_Nulls();
} }
return ret; return ret;
} }
if (m_ccOnIdle) {
g_fireP25Control = true;
m_ccOnIdle = false;
}
} }
return false; return false;
@ -669,6 +620,40 @@ void Control::clock(uint32_t ms)
} }
} }
// if we have control enabled; do clocking to generate a CC data stream
if (m_control) {
if (m_ccRunning && !m_ccPacketInterval.isRunning()) {
m_ccPacketInterval.start();
}
if (m_ccHalted) {
if (!m_ccRunning) {
m_ccHalted = false;
m_ccPrevRunning = m_ccRunning;
}
}
else {
m_ccPacketInterval.clock(ms);
if (!m_ccPacketInterval.isRunning()) {
m_ccPacketInterval.start();
}
if (m_ccPacketInterval.isRunning() && m_ccPacketInterval.hasExpired()) {
if (m_ccRunning) {
writeRF_ControlData();
}
m_ccPacketInterval.start();
}
}
if (m_ccPrevRunning && !m_ccRunning) {
writeRF_ControlEnd();
m_ccPrevRunning = m_ccRunning;
}
}
// handle timeouts and hang timers
m_rfTimeout.clock(ms); m_rfTimeout.clock(ms);
m_netTimeout.clock(ms); m_netTimeout.clock(ms);
@ -718,6 +703,7 @@ void Control::clock(uint32_t ms)
} }
} }
// reset states if we're in a rejected state
if (m_rfState == RS_RF_REJECTED) { if (m_rfState == RS_RF_REJECTED) {
m_queue.clear(); m_queue.clear();
@ -735,8 +721,15 @@ void Control::clock(uint32_t ms)
m_rfState = RS_RF_LISTENING; m_rfState = RS_RF_LISTENING;
} }
// clock data and trunking
if (m_data != NULL) {
m_data->clock(ms);
}
if (m_trunk != NULL) {
m_trunk->clock(ms); m_trunk->clock(ms);
} }
}
/// <summary> /// <summary>
/// Helper to change the debug and verbose state. /// Helper to change the debug and verbose state.
@ -752,54 +745,43 @@ void Control::setDebugVerbose(bool debug, bool verbose)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Write data processed from RF to the data ring buffer. /// Add data frame to the data ring buffer.
/// </summary> /// </summary>
/// <param name="data"></param> /// <param name="data"></param>
/// <param name="length"></param> /// <param name="length"></param>
void Control::writeQueueRF(const uint8_t* data, uint32_t length) /// <param name="net"></param>
void Control::addFrame(const uint8_t* data, uint32_t length, bool net)
{ {
assert(data != NULL); assert(data != NULL);
if (!net) {
if (m_rfTimeout.isRunning() && m_rfTimeout.hasExpired()) if (m_rfTimeout.isRunning() && m_rfTimeout.hasExpired())
return; return;
} else {
uint32_t space = m_queue.freeSpace(); if (m_netTimeout.isRunning() && m_netTimeout.hasExpired())
if (space < (length + 1U)) {
uint32_t queueLen = m_queue.length();
m_queue.resize(queueLen + QUEUE_RESIZE_SIZE);
LogError(LOG_P25, "overflow in the P25 RF queue; queue resized was %u is %u", queueLen, m_queue.length());
return; return;
} }
if (m_debug) { if (m_writeImmediate && m_modem->hasP25Space() && m_modem->getState() == modem::STATE_P25) {
Utils::symbols("!!! *Tx P25", data + 2U, length - 2U); m_writeImmediate = false;
} m_modem->writeP25Data(data, length, true);
uint8_t len = length;
m_queue.addData(&len, 1U);
m_queue.addData(data, len);
} }
else {
/// <summary>
/// Write data processed from the network to the data ring buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
void Control::writeQueueNet(const uint8_t* data, uint32_t length)
{
assert(data != NULL);
if (m_netTimeout.isRunning() && m_netTimeout.hasExpired())
return;
uint32_t space = m_queue.freeSpace(); uint32_t space = m_queue.freeSpace();
if (space < (length + 1U)) { if (space < (length + 1U)) {
LogError(LOG_P25, "network overflow in the P25 RF queue"); if (!net) {
uint32_t queueLen = m_queue.length();
m_queue.resize(queueLen + P25_LDU_FRAME_LENGTH_BYTES);
LogError(LOG_P25, "overflow in the P25 queue while writing data; queue free is %u, needed %u; resized was %u is %u", space, length, queueLen, m_queue.length());
return;
}
else {
LogError(LOG_P25, "overflow in the P25 queue while writing network data; queue free is %u, needed %u", space, length);
return; return;
} }
}
if (m_debug) { if (m_debug) {
Utils::symbols("!!! *Tx P25", data + 2U, length - 2U); Utils::symbols("!!! *Tx P25", data + 2U, length - 2U);
@ -807,9 +789,9 @@ void Control::writeQueueNet(const uint8_t* data, uint32_t length)
uint8_t len = length; uint8_t len = length;
m_queue.addData(&len, 1U); m_queue.addData(&len, 1U);
m_queue.addData(data, len); m_queue.addData(data, len);
} }
}
#if ENABLE_DFSI_SUPPORT #if ENABLE_DFSI_SUPPORT
/// <summary> /// <summary>
@ -1031,6 +1013,70 @@ void Control::processNetwork()
delete data; delete data;
} }
/// <summary>
/// Helper to write control channel frame data.
/// </summary>
/// <returns></returns>
bool Control::writeRF_ControlData()
{
if (!m_control)
return false;
if (m_ccFrameCnt == 254U) {
m_trunk->writeAdjSSNetwork();
m_ccFrameCnt = 0U;
}
// don't add any frames if the queue is full
uint8_t len = (P25_TSDU_TRIPLE_FRAME_LENGTH_BYTES * 2U) + 2U;
uint32_t space = m_queue.freeSpace();
if (space < (len + 1U)) {
return false;
}
const uint8_t maxSeq = 8U;
if (m_ccSeq == maxSeq) {
m_ccSeq = 0U;
}
if (m_netState == RS_NET_IDLE && m_rfState == RS_RF_LISTENING) {
m_trunk->writeRF_ControlData(m_ccFrameCnt, m_ccSeq, true);
m_ccSeq++;
if (m_ccSeq == maxSeq) {
m_ccFrameCnt++;
}
return true;
}
return false;
}
/// <summary>
/// Helper to write end of control channel frame data.
/// </summary>
/// <returns></returns>
bool Control::writeRF_ControlEnd()
{
if (!m_control)
return false;
m_queue.clear();
m_ccPacketInterval.stop();
if (m_netState == RS_NET_IDLE && m_rfState == RS_RF_LISTENING) {
for (uint32_t i = 0; i < TSBK_PCH_CCH_CNT; i++) {
m_trunk->queueRF_TSBK_Ctrl(TSBK_OSP_MOT_PSH_CCH);
}
writeRF_Nulls();
return true;
}
return false;
}
/// <summary> /// <summary>
/// Helper to write data nulls. /// Helper to write data nulls.
/// </summary> /// </summary>
@ -1049,7 +1095,7 @@ void Control::writeRF_Nulls()
LogDebug(LOG_P25, "writeRF_Nulls()"); LogDebug(LOG_P25, "writeRF_Nulls()");
} }
writeQueueRF(data, NULLS_LENGTH_BYTES + 2U); addFrame(data, NULLS_LENGTH_BYTES + 2U);
} }
/// <summary> /// <summary>
@ -1110,13 +1156,13 @@ void Control::writeRF_TDU(bool noNetwork)
addBusyBits(data + 2U, P25_TDU_FRAME_LENGTH_BITS, true, true); addBusyBits(data + 2U, P25_TDU_FRAME_LENGTH_BITS, true, true);
if (!noNetwork) if (!noNetwork)
m_voice->writeNetworkRF(data + 2U, P25_DUID_TDU); m_voice->writeNetwork(data + 2U, P25_DUID_TDU);
if (m_duplex) { if (m_duplex) {
data[0U] = modem::TAG_EOT; data[0U] = modem::TAG_EOT;
data[1U] = 0x00U; data[1U] = 0x00U;
writeQueueRF(data, P25_TDU_FRAME_LENGTH_BYTES + 2U); addFrame(data, P25_TDU_FRAME_LENGTH_BYTES + 2U);
} }
} }

@ -55,6 +55,7 @@ namespace p25
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Prototypes // Class Prototypes
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
class HOST_SW_API VoicePacket; class HOST_SW_API VoicePacket;
namespace dfsi { class HOST_SW_API DFSIVoicePacket; } namespace dfsi { class HOST_SW_API DFSIVoicePacket; }
class HOST_SW_API DataPacket; class HOST_SW_API DataPacket;
@ -70,7 +71,7 @@ namespace p25
public: public:
/// <summary>Initializes a new instance of the Control class.</summary> /// <summary>Initializes a new instance of the Control class.</summary>
Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Modem* modem, network::BaseNetwork* network, 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, uint32_t timeout, uint32_t tgHang, bool duplex, lookups::RadioIdLookup* ridLookup,
lookups::TalkgroupIdLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper, lookups::TalkgroupIdLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper,
bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose); bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose);
/// <summary>Finalizes a instance of the Control class.</summary> /// <summary>Finalizes a instance of the Control class.</summary>
@ -83,8 +84,15 @@ namespace p25
void setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector<uint32_t> voiceChNo, void setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector<uint32_t> voiceChNo,
uint32_t pSuperGroup, uint32_t netId, uint32_t sysId, uint8_t rfssId, uint8_t siteId, uint32_t pSuperGroup, uint32_t netId, uint32_t sysId, uint8_t rfssId, uint8_t siteId,
uint8_t channelId, uint32_t channelNo, bool printOptions); uint8_t channelId, uint32_t channelNo, bool printOptions);
/// <summary>Gets a flag indicating whether the P25 control channel is running.</summary>
bool getCCRunning() { return m_ccRunning; }
/// <summary>Sets a flag indicating whether the P25 control channel is running.</summary> /// <summary>Sets a flag indicating whether the P25 control channel is running.</summary>
void setCCRunning(bool ccRunning); void setCCRunning(bool ccRunning) { m_ccPrevRunning = m_ccRunning; m_ccRunning = ccRunning; }
/// <summary>Gets a flag indicating whether the P25 control channel is running.</summary>
bool getCCHalted() { return m_ccHalted; }
/// <summary>Sets a flag indicating whether the P25 control channel is halted.</summary>
void setCCHalted(bool ccHalted) { m_ccHalted = ccHalted; }
/// <summary>Process a data frame from the RF interface.</summary> /// <summary>Process a data frame from the RF interface.</summary>
bool processFrame(uint8_t* data, uint32_t len); bool processFrame(uint8_t* data, uint32_t len);
@ -94,12 +102,8 @@ namespace p25
/// <summary>Helper to write P25 adjacent site information to the network.</summary> /// <summary>Helper to write P25 adjacent site information to the network.</summary>
void writeAdjSSNetwork(); void writeAdjSSNetwork();
/// <summary>Helper to write control channel frame data.</summary> /// <summary>Helper to write end of voice call frame data.</summary>
bool writeControlRF(); bool writeRF_VoiceEnd();
/// <summary>Helper to write end of control channel frame data.</summary>
bool writeControlEndRF();
/// <summary>Helper to write end of frame data.</summary>
bool writeEndRF();
/// <summary>Updates the processor by the passed number of milliseconds.</summary> /// <summary>Updates the processor by the passed number of milliseconds.</summary>
void clock(uint32_t ms); void clock(uint32_t ms);
@ -154,15 +158,17 @@ namespace p25
uint32_t m_netLastDstId; uint32_t m_netLastDstId;
bool m_tailOnIdle; bool m_tailOnIdle;
bool m_ccOnIdle;
bool m_ccRunning; bool m_ccRunning;
uint32_t m_ccBcstInterval; bool m_ccPrevRunning;
bool m_ccHalted;
Timer m_rfTimeout; Timer m_rfTimeout;
Timer m_rfTGHang; Timer m_rfTGHang;
Timer m_netTimeout; Timer m_netTimeout;
Timer m_networkWatchdog; Timer m_networkWatchdog;
Timer m_ccPacketInterval;
uint32_t m_hangCount; uint32_t m_hangCount;
uint32_t m_tduPreambleCount; uint32_t m_tduPreambleCount;
@ -180,13 +186,13 @@ namespace p25
uint32_t m_aveRSSI; uint32_t m_aveRSSI;
uint32_t m_rssiCount; uint32_t m_rssiCount;
bool m_writeImmediate; // This is essentially a "latch" that will auto-reset after a writeRF_Queue() call.
bool m_verbose; bool m_verbose;
bool m_debug; bool m_debug;
/// <summary>Write data processed from RF to the data ring buffer.</summary> /// <summary>Add data frame to the data ring buffer.</summary>
void writeQueueRF(const uint8_t* data, uint32_t length); void addFrame(const uint8_t* data, uint32_t length, bool net = false);
/// <summary>Write data processed from the network to the data ring buffer.</summary>
void writeQueueNet(const uint8_t* data, uint32_t length);
#if ENABLE_DFSI_SUPPORT #if ENABLE_DFSI_SUPPORT
/// <summary>Process a DFSI data frame from the RF interface.</summary> /// <summary>Process a DFSI data frame from the RF interface.</summary>
@ -196,6 +202,11 @@ namespace p25
/// <summary>Process a data frames from the network.</summary> /// <summary>Process a data frames from the network.</summary>
void processNetwork(); void processNetwork();
/// <summary>Helper to write control channel frame data.</summary>
bool writeRF_ControlData();
/// <summary>Helper to write end of control channel frame data.</summary>
bool writeRF_ControlEnd();
/// <summary>Helper to write data nulls.</summary> /// <summary>Helper to write data nulls.</summary>
void writeRF_Nulls(); void writeRF_Nulls();
/// <summary>Helper to write TDU preamble packet burst.</summary> /// <summary>Helper to write TDU preamble packet burst.</summary>

@ -47,9 +47,16 @@ using namespace p25::data;
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint32_t CONN_WAIT_TIMEOUT = 1U;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Resets the data states for the RF interface. /// Resets the data states for the RF interface.
/// </summary> /// </summary>
@ -86,7 +93,7 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
// are we interrupting a running CC? // are we interrupting a running CC?
if (m_p25->m_ccRunning) { if (m_p25->m_ccRunning) {
g_interruptP25Control = true; m_p25->m_ccHalted = true;
} }
// handle individual DUIDs // handle individual DUIDs
@ -227,7 +234,7 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
} }
} }
writeNetworkRF(m_rfDataBlockCnt, m_pduUserData + dataOffset, (m_rfDataHeader.getFormat() == PDU_FMT_CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES); writeNetwork(m_rfDataBlockCnt, m_pduUserData + dataOffset, (m_rfDataHeader.getFormat() == PDU_FMT_CONFIRMED) ? P25_PDU_CONFIRMED_DATA_LENGTH_BYTES : P25_PDU_UNCONFIRMED_LENGTH_BYTES);
m_rfDataBlockCnt++; m_rfDataBlockCnt++;
} }
else { else {
@ -273,34 +280,30 @@ bool DataPacket::process(uint8_t* data, uint32_t len)
ulong64_t ipAddr = (m_pduUserData[8U] << 24) + (m_pduUserData[9U] << 16) + ulong64_t ipAddr = (m_pduUserData[8U] << 24) + (m_pduUserData[9U] << 16) +
(m_pduUserData[10U] << 8) + m_pduUserData[11U]; (m_pduUserData[10U] << 8) + m_pduUserData[11U];
if (m_verbose) { if (m_rfDataHeader.getAckNeeded()) {
LogMessage(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_REQ_CNCT (Registration Request Connect), llId = %u, ipAddr = %s", llId, __IP_FROM_ULONG(ipAddr).c_str()); m_p25->m_writeImmediate = true;
}
writeRF_PDU_Ack_Response(PDU_ACK_CLASS_ACK, PDU_ACK_TYPE_ACK, llId); writeRF_PDU_Ack_Response(PDU_ACK_CLASS_ACK, PDU_ACK_TYPE_ACK, llId);
if (!acl::AccessControl::validateSrcId(llId)) {
LogWarning(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_RSP_DENY (Registration Response Deny), llId = %u, ipAddr = %s", llId, __IP_FROM_ULONG(ipAddr).c_str());
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) { if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_RSP_ACCPT (Registration Response Accept), llId = %u, ipAddr = %s", llId, __IP_FROM_ULONG(ipAddr).c_str()); LogMessage(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_REQ_CNCT (Registration Request Connect), llId = %u, ipAddr = %s", llId, __IP_FROM_ULONG(ipAddr).c_str());
} }
writeRF_PDU_Reg_Response(PDU_REG_TYPE_RSP_ACCPT, llId, ipAddr); m_connQueueTable[llId] = ipAddr;
}
m_connTimerTable[llId] = Timer(1000U, CONN_WAIT_TIMEOUT);
m_connTimerTable[llId].start();
} }
break; break;
case PDU_REG_TYPE_REQ_DISCNCT: case PDU_REG_TYPE_REQ_DISCNCT:
{ {
uint32_t llId = (m_pduUserData[1U] << 16) + (m_pduUserData[2U] << 8) + m_pduUserData[3U]; uint32_t llId = (m_pduUserData[1U] << 16) + (m_pduUserData[2U] << 8) + m_pduUserData[3U];
if (m_rfDataHeader.getAckNeeded()) {
m_p25->m_writeImmediate = true;
writeRF_PDU_Ack_Response(PDU_ACK_CLASS_ACK, PDU_ACK_TYPE_ACK, llId);
}
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_REQ_DISCNCT (Registration Request Disconnect), llId = %u", llId); LogMessage(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_REQ_DISCNCT (Registration Request Disconnect), llId = %u", llId);
} }
@ -502,9 +505,54 @@ bool DataPacket::hasLLIdFNEReg(uint32_t llId) const
} }
} }
/// <summary>
/// Updates the processor by the passed number of milliseconds.
/// </summary>
/// <param name="ms"></param>
void DataPacket::clock(uint32_t ms)
{
// clock all the connect timers
std::vector<uint32_t> connToClear = std::vector<uint32_t>();
for (auto it = m_connQueueTable.begin(); it != m_connQueueTable.end(); ++it) {
uint32_t llId = it->first;
m_connTimerTable[llId].clock(ms);
if (m_connTimerTable[llId].isRunning() && m_connTimerTable[llId].hasExpired()) {
connToClear.push_back(llId);
}
}
// handle PDU connection registration
for (auto it = connToClear.begin(); it != connToClear.end(); ++it) {
uint32_t llId = *it;
uint64_t ipAddr = m_connQueueTable[llId];
m_p25->m_writeImmediate = true;
if (!acl::AccessControl::validateSrcId(llId)) {
LogWarning(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_RSP_DENY (Registration Response Deny), llId = %u, ipAddr = %s", llId, __IP_FROM_ULONG(ipAddr).c_str());
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 = %s", llId, __IP_FROM_ULONG(ipAddr).c_str());
}
writeRF_PDU_Reg_Response(PDU_REG_TYPE_RSP_ACCPT, llId, ipAddr);
}
m_connQueueTable.erase(llId);
}
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Initializes a new instance of the DataPacket class. /// Initializes a new instance of the DataPacket class.
/// </summary> /// </summary>
@ -537,6 +585,8 @@ DataPacket::DataPacket(Control* p25, network::BaseNetwork* network, bool dumpPDU
m_pduUserData(NULL), m_pduUserData(NULL),
m_pduUserDataLength(0U), m_pduUserDataLength(0U),
m_fneRegTable(), m_fneRegTable(),
m_connQueueTable(),
m_connTimerTable(),
m_dumpPDUData(dumpPDUData), m_dumpPDUData(dumpPDUData),
m_repeatPDU(repeatPDU), m_repeatPDU(repeatPDU),
m_verbose(verbose), m_verbose(verbose),
@ -556,6 +606,8 @@ DataPacket::DataPacket(Control* p25, network::BaseNetwork* network, bool dumpPDU
::memset(m_pduUserData, 0x00U, P25_MAX_PDU_COUNT * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U); ::memset(m_pduUserData, 0x00U, P25_MAX_PDU_COUNT * P25_PDU_CONFIRMED_LENGTH_BYTES + 2U);
m_fneRegTable.clear(); m_fneRegTable.clear();
m_connQueueTable.clear();
m_connTimerTable.clear();
} }
/// <summary> /// <summary>
@ -574,7 +626,7 @@ DataPacket::~DataPacket()
/// <param name="currentBlock"></param> /// <param name="currentBlock"></param>
/// <param name="data"></param> /// <param name="data"></param>
/// <param name="len"></param> /// <param name="len"></param>
void DataPacket::writeNetworkRF(const uint8_t currentBlock, const uint8_t *data, uint32_t len) void DataPacket::writeNetwork(const uint8_t currentBlock, const uint8_t *data, uint32_t len)
{ {
assert(data != NULL); assert(data != NULL);
@ -624,7 +676,8 @@ void DataPacket::writeRF_PDU(const uint8_t* pdu, uint32_t bitLength, bool noNull
if (m_p25->m_duplex) { if (m_p25->m_duplex) {
data[0U] = modem::TAG_DATA; data[0U] = modem::TAG_DATA;
data[1U] = 0x00U; data[1U] = 0x00U;
m_p25->writeQueueRF(data, newByteLength + 2U);
m_p25->addFrame(data, newByteLength + 2U);
} }
// add trailing null pad; only if control data isn't being transmitted // add trailing null pad; only if control data isn't being transmitted
@ -805,7 +858,8 @@ void DataPacket::writeRF_PDU_Reg_Response(uint8_t regType, uint32_t llId, ulong6
/// <param name="ackClass"></param> /// <param name="ackClass"></param>
/// <param name="ackType"></param> /// <param name="ackType"></param>
/// <param name="llId"></param> /// <param name="llId"></param>
void DataPacket::writeRF_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, uint32_t llId) /// <param name="noNulls"></param>
void DataPacket::writeRF_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, uint32_t llId, bool noNulls)
{ {
if (ackClass == PDU_ACK_CLASS_ACK && ackType != PDU_ACK_TYPE_ACK) if (ackClass == PDU_ACK_CLASS_ACK && ackType != PDU_ACK_TYPE_ACK)
return; return;
@ -839,6 +893,6 @@ void DataPacket::writeRF_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, uin
rspHeader.encode(block); rspHeader.encode(block);
Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS); Utils::setBitRange(block, data, offset, P25_PDU_FEC_LENGTH_BITS);
writeRF_PDU(data, bitLength, true); writeRF_PDU(data, bitLength, noNulls);
delete[] data; delete[] data;
} }

@ -38,6 +38,7 @@
#include "p25/lc/LC.h" #include "p25/lc/LC.h"
#include "p25/Control.h" #include "p25/Control.h"
#include "network/BaseNetwork.h" #include "network/BaseNetwork.h"
#include "Timer.h"
#include <cstdio> #include <cstdio>
#include <string> #include <string>
@ -48,6 +49,7 @@ namespace p25
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Prototypes // Class Prototypes
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
class HOST_SW_API Control; class HOST_SW_API Control;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -68,6 +70,9 @@ namespace p25
/// <summary>Helper to check if a logical link ID has registered with data services.</summary> /// <summary>Helper to check if a logical link ID has registered with data services.</summary>
bool hasLLIdFNEReg(uint32_t llId) const; bool hasLLIdFNEReg(uint32_t llId) const;
/// <summary>Updates the processor by the passed number of milliseconds.</summary>
void clock(uint32_t ms);
private: private:
friend class Control; friend class Control;
Control* m_p25; Control* m_p25;
@ -99,6 +104,9 @@ namespace p25
std::unordered_map<uint32_t, ulong64_t> m_fneRegTable; std::unordered_map<uint32_t, ulong64_t> m_fneRegTable;
std::unordered_map<uint32_t, ulong64_t> m_connQueueTable;
std::unordered_map<uint32_t, Timer> m_connTimerTable;
bool m_dumpPDUData; bool m_dumpPDUData;
bool m_repeatPDU; bool m_repeatPDU;
@ -111,7 +119,7 @@ namespace p25
~DataPacket(); ~DataPacket();
/// <summary>Write data processed from RF to the network.</summary> /// <summary>Write data processed from RF to the network.</summary>
void writeNetworkRF(const uint8_t currentBlock, const uint8_t* data, uint32_t len); void writeNetwork(const uint8_t currentBlock, const uint8_t* data, uint32_t len);
/// <summary>Helper to write a P25 PDU packet.</summary> /// <summary>Helper to write a P25 PDU packet.</summary>
void writeRF_PDU(const uint8_t* pdu, uint32_t bitLength, bool noNulls = false); void writeRF_PDU(const uint8_t* pdu, uint32_t bitLength, bool noNulls = false);
@ -122,7 +130,7 @@ namespace p25
/// <summary>Helper to write a PDU registration response.</summary> /// <summary>Helper to write a PDU registration response.</summary>
void writeRF_PDU_Reg_Response(uint8_t regType, uint32_t llId, ulong64_t ipAddr); void writeRF_PDU_Reg_Response(uint8_t regType, uint32_t llId, ulong64_t ipAddr);
/// <summary>Helper to write a PDU acknowledge response.</summary> /// <summary>Helper to write a PDU acknowledge response.</summary>
void writeRF_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, uint32_t llId); void writeRF_PDU_Ack_Response(uint8_t ackClass, uint8_t ackType, uint32_t llId, bool noNulls = false);
}; };
} // namespace p25 } // namespace p25

@ -44,6 +44,7 @@ using namespace p25::data;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Macros // Macros
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Make sure control data is supported. // Make sure control data is supported.
#define IS_SUPPORT_CONTROL_CHECK(_PCKT_STR, _PCKT, _SRCID) \ #define IS_SUPPORT_CONTROL_CHECK(_PCKT_STR, _PCKT, _SRCID) \
if (!m_p25->m_control) { \ if (!m_p25->m_control) { \
@ -136,6 +137,7 @@ const uint8_t CONV_FALLBACK_PACKET_DELAY = 8U;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Resets the data states for the RF interface. /// Resets the data states for the RF interface.
/// </summary> /// </summary>
@ -1290,6 +1292,7 @@ void TrunkPacket::setTSBKVerbose(bool verbose)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Protected Class Members // Protected Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Initializes a new instance of the TrunkPacket class. /// Initializes a new instance of the TrunkPacket class.
/// </summary> /// </summary>
@ -1353,7 +1356,7 @@ TrunkPacket::TrunkPacket(Control* p25, network::BaseNetwork* network, bool dumpT
m_grantChTable.clear(); m_grantChTable.clear();
m_grantTimers.clear(); m_grantTimers.clear();
m_adjSiteUpdateInterval = ADJ_SITE_TIMER_TIMEOUT + m_p25->m_ccBcstInterval; m_adjSiteUpdateInterval = ADJ_SITE_TIMER_TIMEOUT;
m_adjSiteUpdateTimer.setTimeout(m_adjSiteUpdateInterval); m_adjSiteUpdateTimer.setTimeout(m_adjSiteUpdateInterval);
m_adjSiteUpdateTimer.start(); m_adjSiteUpdateTimer.start();
} }
@ -1548,7 +1551,7 @@ void TrunkPacket::writeRF_TDULC(lc::TDULC lc, bool noNetwork)
data[0U] = modem::TAG_EOT; data[0U] = modem::TAG_EOT;
data[1U] = 0x00U; data[1U] = 0x00U;
m_p25->writeQueueRF(data, P25_TDULC_FRAME_LENGTH_BYTES + 2U); m_p25->addFrame(data, P25_TDULC_FRAME_LENGTH_BYTES + 2U);
} }
//if (m_verbose) { //if (m_verbose) {
@ -1669,7 +1672,7 @@ void TrunkPacket::writeRF_TSDU_SBF(bool noNetwork, bool clearBeforeWrite, bool f
data[0U] = modem::TAG_DATA; data[0U] = modem::TAG_DATA;
data[1U] = 0x00U; data[1U] = 0x00U;
m_p25->writeQueueRF(data, P25_TSDU_FRAME_LENGTH_BYTES + 2U); m_p25->addFrame(data, P25_TSDU_FRAME_LENGTH_BYTES + 2U);
} }
} }
@ -1768,7 +1771,7 @@ void TrunkPacket::writeRF_TSDU_MBF(bool clearBeforeWrite)
m_p25->m_queue.clear(); m_p25->m_queue.clear();
} }
m_p25->writeQueueRF(data, P25_TSDU_TRIPLE_FRAME_LENGTH_BYTES + 2U); m_p25->addFrame(data, P25_TSDU_TRIPLE_FRAME_LENGTH_BYTES + 2U);
::memset(m_rfMBF, 0x00U, P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U); ::memset(m_rfMBF, 0x00U, P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U);
m_mbfCnt = 0U; m_mbfCnt = 0U;
@ -2161,6 +2164,7 @@ bool TrunkPacket::writeRF_TSDU_Grant(bool grp, bool skip, bool net, bool skipNet
// transmit group grant // transmit group grant
m_rfTSBK.setLCO(TSBK_IOSP_GRP_VCH); m_rfTSBK.setLCO(TSBK_IOSP_GRP_VCH);
m_p25->m_writeImmediate = true;
writeRF_TSDU_SBF(false, true, net); writeRF_TSDU_SBF(false, true, net);
} }
else { else {
@ -2175,6 +2179,7 @@ bool TrunkPacket::writeRF_TSDU_Grant(bool grp, bool skip, bool net, bool skipNet
// transmit private grant // transmit private grant
m_rfTSBK.setLCO(TSBK_IOSP_UU_VCH); m_rfTSBK.setLCO(TSBK_IOSP_UU_VCH);
m_p25->m_writeImmediate = true;
writeRF_TSDU_SBF(false, true, net); writeRF_TSDU_SBF(false, true, net);
} }
@ -2652,7 +2657,7 @@ void TrunkPacket::writeNet_TDULC(lc::TDULC lc)
// Add busy bits // Add busy bits
m_p25->addBusyBits(buffer + 2U, P25_TDULC_FRAME_LENGTH_BITS, true, true); m_p25->addBusyBits(buffer + 2U, P25_TDULC_FRAME_LENGTH_BITS, true, true);
m_p25->writeQueueNet(buffer, P25_TDULC_FRAME_LENGTH_BYTES + 2U); m_p25->addFrame(buffer, P25_TDULC_FRAME_LENGTH_BYTES + 2U, true);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_NET, P25_TDULC_STR ", lc = $%02X, srcId = %u", lc.getLCO(), lc.getSrcId()); LogMessage(LOG_NET, P25_TDULC_STR ", lc = $%02X, srcId = %u", lc.getLCO(), lc.getSrcId());
@ -2702,7 +2707,7 @@ void TrunkPacket::writeNet_TSDU()
// Set first busy bits to 1,1 // Set first busy bits to 1,1
m_p25->setBusyBits(buffer + 2U, P25_SS0_START, true, true); m_p25->setBusyBits(buffer + 2U, P25_SS0_START, true, true);
m_p25->writeQueueNet(buffer, P25_TSDU_FRAME_LENGTH_BYTES + 2U); m_p25->addFrame(buffer, P25_TSDU_FRAME_LENGTH_BYTES + 2U, true);
if (m_network != NULL) if (m_network != NULL)
m_network->resetP25(); m_network->resetP25();

@ -56,6 +56,7 @@ const uint32_t VOC_LDU1_COUNT = 3U;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Resets the data states for the RF interface. /// Resets the data states for the RF interface.
/// </summary> /// </summary>
@ -123,7 +124,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
// are we interrupting a running CC? // are we interrupting a running CC?
if (m_p25->m_ccRunning) { if (m_p25->m_ccRunning) {
g_interruptP25Control = true; m_p25->m_ccHalted = true;
} }
if (m_p25->m_rfState != RS_RF_LISTENING) { if (m_p25->m_rfState != RS_RF_LISTENING) {
@ -138,8 +139,10 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
if (duid == P25_DUID_HDU) { if (duid == P25_DUID_HDU) {
m_lastDUID = P25_DUID_HDU; m_lastDUID = P25_DUID_HDU;
if (m_p25->m_rfState == RS_RF_LISTENING && m_p25->m_ccRunning) { if (m_p25->m_rfState == RS_RF_LISTENING) {
if (!m_p25->m_dedicatedControl) {
m_p25->m_modem->clearP25Data(); m_p25->m_modem->clearP25Data();
}
m_p25->m_queue.clear(); m_p25->m_queue.clear();
resetRF(); resetRF();
resetNet(); resetNet();
@ -196,6 +199,16 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
m_lastDUID = P25_DUID_LDU1; m_lastDUID = P25_DUID_LDU1;
if (m_p25->m_rfState == RS_RF_LISTENING) { if (m_p25->m_rfState == RS_RF_LISTENING) {
// if this is a late entry call, clear states
if (m_rfLastHDU.getDstId() == 0U) {
if (!m_p25->m_dedicatedControl) {
m_p25->m_modem->clearP25Data();
}
m_p25->m_queue.clear();
resetRF();
resetNet();
}
if (m_p25->m_control) { if (m_p25->m_control) {
if (!m_p25->m_ccRunning && m_p25->m_voiceOnControl) { if (!m_p25->m_ccRunning && m_p25->m_voiceOnControl) {
m_p25->m_trunk->writeRF_ControlData(255U, 0U, false); m_p25->m_trunk->writeRF_ControlData(255U, 0U, false);
@ -350,7 +363,6 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
// single-channel trunking or voice on control support? // single-channel trunking or voice on control support?
if (m_p25->m_control && m_p25->m_voiceOnControl) { if (m_p25->m_control && m_p25->m_voiceOnControl) {
m_p25->m_ccRunning = false; // otherwise the grant will be bundled with other packets
m_p25->m_trunk->writeRF_TSDU_Grant(group, true); m_p25->m_trunk->writeRF_TSDU_Grant(group, true);
} }
@ -390,12 +402,13 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
// Add busy bits // Add busy bits
m_p25->addBusyBits(buffer + 2U, P25_HDU_FRAME_LENGTH_BITS, false, true); m_p25->addBusyBits(buffer + 2U, P25_HDU_FRAME_LENGTH_BITS, false, true);
writeNetworkRF(buffer, P25_DUID_HDU); writeNetwork(buffer, P25_DUID_HDU);
if (m_p25->m_duplex) { if (m_p25->m_duplex) {
buffer[0U] = modem::TAG_DATA; buffer[0U] = modem::TAG_DATA;
buffer[1U] = 0x00U; buffer[1U] = 0x00U;
m_p25->writeQueueRF(buffer, P25_HDU_FRAME_LENGTH_BYTES + 2U);
m_p25->addFrame(buffer, P25_HDU_FRAME_LENGTH_BYTES + 2U);
} }
if (m_verbose) { if (m_verbose) {
@ -515,12 +528,13 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
// Add busy bits // Add busy bits
m_p25->addBusyBits(data + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true); m_p25->addBusyBits(data + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true);
writeNetworkRF(data + 2U, P25_DUID_LDU1); writeNetwork(data + 2U, P25_DUID_LDU1);
if (m_p25->m_duplex) { if (m_p25->m_duplex) {
data[0U] = modem::TAG_DATA; data[0U] = modem::TAG_DATA;
data[1U] = 0x00U; data[1U] = 0x00U;
m_p25->writeQueueRF(data, P25_LDU_FRAME_LENGTH_BYTES + 2U);
m_p25->addFrame(data, P25_LDU_FRAME_LENGTH_BYTES + 2U);
} }
if (m_verbose) { if (m_verbose) {
@ -593,12 +607,13 @@ bool VoicePacket::process(uint8_t* data, uint32_t len)
// Add busy bits // Add busy bits
m_p25->addBusyBits(data + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true); m_p25->addBusyBits(data + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true);
writeNetworkRF(data + 2U, P25_DUID_LDU2); writeNetwork(data + 2U, P25_DUID_LDU2);
if (m_p25->m_duplex) { if (m_p25->m_duplex) {
data[0U] = modem::TAG_DATA; data[0U] = modem::TAG_DATA;
data[1U] = 0x00U; data[1U] = 0x00U;
m_p25->writeQueueRF(data, P25_LDU_FRAME_LENGTH_BYTES + 2U);
m_p25->addFrame(data, P25_LDU_FRAME_LENGTH_BYTES + 2U);
} }
if (m_verbose) { if (m_verbose) {
@ -729,12 +744,7 @@ bool VoicePacket::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, d
if (m_p25->m_netState == RS_NET_IDLE) { if (m_p25->m_netState == RS_NET_IDLE) {
// are we interrupting a running CC? // are we interrupting a running CC?
if (m_p25->m_ccRunning) { if (m_p25->m_ccRunning) {
g_interruptP25Control = true; m_p25->m_ccHalted = true;
}
// single-channel trunking or voice on control support?
if (m_p25->m_control && m_p25->m_voiceOnControl) {
m_p25->m_ccRunning = false; // otherwise the grant will be bundled with other packets
} }
} }
@ -833,40 +843,10 @@ bool VoicePacket::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, d
return true; return true;
} }
/// <summary>
/// Helper to write end of frame data.
/// </summary>
/// <returns></returns>
bool VoicePacket::writeEndRF()
{
if (m_p25->m_netState == RS_NET_IDLE && m_p25->m_rfState == RS_RF_LISTENING) {
writeRF_EndOfVoice();
// this should have been cleared by writeRF_EndOfVoice; but if it hasn't clear it
// to prevent badness
if (m_hadVoice) {
m_hadVoice = false;
}
if (m_p25->m_control && !m_p25->m_ccRunning) {
m_p25->m_trunk->writeRF_ControlData(255U, 0U, false);
m_p25->writeControlEndRF();
}
m_p25->m_tailOnIdle = false;
if (m_network != NULL)
m_network->resetP25();
return true;
}
return false;
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Protected Class Members // Protected Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Initializes a new instance of the VoicePacket class. /// Initializes a new instance of the VoicePacket class.
/// </summary> /// </summary>
@ -929,7 +909,7 @@ VoicePacket::~VoicePacket()
/// </summary> /// </summary>
/// <param name="data"></param> /// <param name="data"></param>
/// <param name="duid"></param> /// <param name="duid"></param>
void VoicePacket::writeNetworkRF(const uint8_t *data, uint8_t duid) void VoicePacket::writeNetwork(const uint8_t *data, uint8_t duid)
{ {
assert(data != NULL); assert(data != NULL);
@ -1004,7 +984,7 @@ void VoicePacket::writeNet_TDU()
// Add busy bits // Add busy bits
m_p25->addBusyBits(buffer + 2U, P25_TDU_FRAME_LENGTH_BITS, true, true); m_p25->addBusyBits(buffer + 2U, P25_TDU_FRAME_LENGTH_BITS, true, true);
m_p25->writeQueueNet(buffer, P25_TDU_FRAME_LENGTH_BYTES + 2U); m_p25->addFrame(buffer, P25_TDU_FRAME_LENGTH_BYTES + 2U, true);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_NET, P25_TDU_STR ", srcId = %u", m_netLC.getSrcId()); LogMessage(LOG_NET, P25_TDU_STR ", srcId = %u", m_netLC.getSrcId());
@ -1188,7 +1168,6 @@ void VoicePacket::writeNet_LDU1()
// single-channel trunking or voice on control support? // single-channel trunking or voice on control support?
if (m_p25->m_control && m_p25->m_voiceOnControl) { if (m_p25->m_control && m_p25->m_voiceOnControl) {
m_p25->m_ccRunning = false; // otherwise the grant will be bundled with other packets
if (!m_p25->m_trunk->writeRF_TSDU_Grant(group, false, true)) { if (!m_p25->m_trunk->writeRF_TSDU_Grant(group, false, true)) {
if (m_network != NULL) if (m_network != NULL)
m_network->resetP25(); m_network->resetP25();
@ -1241,7 +1220,8 @@ void VoicePacket::writeNet_LDU1()
buffer[0U] = modem::TAG_DATA; buffer[0U] = modem::TAG_DATA;
buffer[1U] = 0x00U; buffer[1U] = 0x00U;
m_p25->writeQueueNet(buffer, P25_HDU_FRAME_LENGTH_BYTES + 2U);
m_p25->addFrame(buffer, P25_HDU_FRAME_LENGTH_BYTES + 2U, true);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_NET, P25_HDU_STR ", dstId = %u, algo = $%02X, kid = $%04X", m_netLC.getDstId(), m_netLC.getAlgId(), m_netLC.getKId()); LogMessage(LOG_NET, P25_HDU_STR ", dstId = %u, algo = $%02X, kid = $%04X", m_netLC.getDstId(), m_netLC.getAlgId(), m_netLC.getKId());
@ -1299,7 +1279,8 @@ void VoicePacket::writeNet_LDU1()
buffer[0U] = modem::TAG_DATA; buffer[0U] = modem::TAG_DATA;
buffer[1U] = 0x00U; buffer[1U] = 0x00U;
m_p25->writeQueueNet(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U);
m_p25->addFrame(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U, true);
if (m_verbose) { if (m_verbose) {
uint32_t loss = 0; uint32_t loss = 0;
@ -1403,7 +1384,8 @@ void VoicePacket::writeNet_LDU2()
buffer[0U] = modem::TAG_DATA; buffer[0U] = modem::TAG_DATA;
buffer[1U] = 0x00U; buffer[1U] = 0x00U;
m_p25->writeQueueNet(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U);
m_p25->addFrame(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U, true);
if (m_verbose) { if (m_verbose) {
uint32_t loss = 0; uint32_t loss = 0;

@ -68,9 +68,6 @@ namespace p25
/// <summary>Process a data frame from the network.</summary> /// <summary>Process a data frame from the network.</summary>
virtual bool processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::LowSpeedData& lsd, uint8_t& duid); virtual bool processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::LowSpeedData& lsd, uint8_t& duid);
/// <summary>Helper to write end of frame data.</summary>
bool writeEndRF();
protected: protected:
friend class TrunkPacket; friend class TrunkPacket;
friend class Control; friend class Control;
@ -121,7 +118,7 @@ namespace p25
virtual ~VoicePacket(); virtual ~VoicePacket();
/// <summary>Write data processed from RF to the network.</summary> /// <summary>Write data processed from RF to the network.</summary>
void writeNetworkRF(const uint8_t* data, uint8_t duid); void writeNetwork(const uint8_t* data, uint8_t duid);
/// <summary>Helper to write end of voice frame data.</summary> /// <summary>Helper to write end of voice frame data.</summary>
void writeRF_EndOfVoice(); void writeRF_EndOfVoice();

@ -38,6 +38,7 @@ using namespace p25::dfsi;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Resets the data states for the RF interface. /// Resets the data states for the RF interface.
/// </summary> /// </summary>
@ -94,6 +95,7 @@ bool DFSITrunkPacket::process(uint8_t* data, uint32_t len, bool preDecoded)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Protected Class Members // Protected Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Initializes a new instance of the DFSITrunkPacket class. /// Initializes a new instance of the DFSITrunkPacket class.
/// </summary> /// </summary>
@ -189,7 +191,7 @@ void DFSITrunkPacket::writeRF_TSDU_SBF(bool noNetwork, bool clearBeforeWrite, bo
data[0U] = modem::TAG_DATA; data[0U] = modem::TAG_DATA;
data[1U] = 0x00U; data[1U] = 0x00U;
m_p25->writeQueueRF(data, P25_DFSI_TSBK_FRAME_LENGTH_BYTES + 2U); m_p25->addFrame(data, P25_DFSI_TSBK_FRAME_LENGTH_BYTES + 2U);
writeRF_DSFI_Stop(P25_DFSI_TYPE_TSBK); writeRF_DSFI_Stop(P25_DFSI_TYPE_TSBK);
} }
@ -209,7 +211,7 @@ void DFSITrunkPacket::writeNet_TSDU()
m_netDFSILC.tsbk(m_netTSBK); m_netDFSILC.tsbk(m_netTSBK);
m_netDFSILC.encodeTSBK(buffer + 2U); m_netDFSILC.encodeTSBK(buffer + 2U);
m_p25->writeQueueNet(buffer, P25_DFSI_TSBK_FRAME_LENGTH_BYTES + 2U); m_p25->addFrame(buffer, P25_DFSI_TSBK_FRAME_LENGTH_BYTES + 2U, true);
if (m_network != NULL) if (m_network != NULL)
m_network->resetP25(); m_network->resetP25();
@ -235,7 +237,7 @@ void DFSITrunkPacket::writeRF_DFSI_Start(uint8_t type)
buffer[0U] = modem::TAG_DATA; buffer[0U] = modem::TAG_DATA;
buffer[1U] = 0x00U; buffer[1U] = 0x00U;
m_p25->writeQueueRF(buffer, P25_DFSI_SS_FRAME_LENGTH_BYTES + 2U); m_p25->addFrame(buffer, P25_DFSI_SS_FRAME_LENGTH_BYTES + 2U);
} }
/// <suimmary> /// <suimmary>
@ -260,6 +262,6 @@ void DFSITrunkPacket::writeRF_DSFI_Stop(uint8_t type)
// for whatever reason this is almost always sent twice // for whatever reason this is almost always sent twice
for (uint8_t i = 0; i < 2;i ++) { for (uint8_t i = 0; i < 2;i ++) {
m_p25->writeQueueRF(buffer, P25_DFSI_SS_FRAME_LENGTH_BYTES + 2U); m_p25->addFrame(buffer, P25_DFSI_SS_FRAME_LENGTH_BYTES + 2U);
} }
} }

@ -50,6 +50,7 @@ const uint32_t VOC_LDU1_COUNT = 3U;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Resets the data states for the RF interface. /// Resets the data states for the RF interface.
/// </summary> /// </summary>
@ -90,7 +91,7 @@ bool DFSIVoicePacket::process(uint8_t* data, uint32_t len)
uint8_t frameType = m_rfDFSILC.getFrameType(); uint8_t frameType = m_rfDFSILC.getFrameType();
if (frameType == P25_DFSI_VHDR2) { if (frameType == P25_DFSI_VHDR2) {
if (m_p25->m_rfState == RS_RF_LISTENING && m_p25->m_ccRunning) { if (m_p25->m_rfState == RS_RF_LISTENING && m_p25->m_ccRunning) {
m_p25->m_modem->clearP25Data(); //m_p25->m_modem->clearP25Data();
m_p25->m_queue.clear(); m_p25->m_queue.clear();
resetRF(); resetRF();
resetNet(); resetNet();
@ -333,7 +334,6 @@ bool DFSIVoicePacket::process(uint8_t* data, uint32_t len)
// single-channel trunking or voice on control support? // single-channel trunking or voice on control support?
if (m_p25->m_control && m_p25->m_voiceOnControl) { if (m_p25->m_control && m_p25->m_voiceOnControl) {
m_p25->m_ccRunning = false; // otherwise the grant will be bundled with other packets
m_p25->m_trunk->writeRF_TSDU_Grant(group, true); m_p25->m_trunk->writeRF_TSDU_Grant(group, true);
} }
@ -373,7 +373,7 @@ bool DFSIVoicePacket::process(uint8_t* data, uint32_t len)
// Add busy bits // Add busy bits
m_p25->addBusyBits(buffer + 2U, P25_HDU_FRAME_LENGTH_BITS, false, true); m_p25->addBusyBits(buffer + 2U, P25_HDU_FRAME_LENGTH_BITS, false, true);
writeNetworkRF(buffer, P25_DUID_HDU); writeNetwork(buffer, P25_DUID_HDU);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_RF, P25_HDU_STR " DFSI, dstId = %u, algo = $%02X, kid = $%04X", m_rfLC.getDstId(), m_rfLC.getAlgId(), m_rfLC.getKId()); LogMessage(LOG_RF, P25_HDU_STR " DFSI, dstId = %u, algo = $%02X, kid = $%04X", m_rfLC.getDstId(), m_rfLC.getAlgId(), m_rfLC.getKId());
@ -448,7 +448,7 @@ bool DFSIVoicePacket::process(uint8_t* data, uint32_t len)
// Add busy bits // Add busy bits
m_p25->addBusyBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true); m_p25->addBusyBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true);
writeNetworkRF(buffer + 2U, P25_DUID_LDU1); writeNetwork(buffer + 2U, P25_DUID_LDU1);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_RF, P25_LDU1_STR " DFSI, audio, srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u", LogMessage(LOG_RF, P25_LDU1_STR " DFSI, audio, srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u",
@ -540,7 +540,7 @@ bool DFSIVoicePacket::process(uint8_t* data, uint32_t len)
// Add busy bits // Add busy bits
m_p25->addBusyBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true); m_p25->addBusyBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true);
writeNetworkRF(buffer + 2U, P25_DUID_LDU2); writeNetwork(buffer + 2U, P25_DUID_LDU2);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_RF, P25_LDU2_STR " DFSI, audio, algo = $%02X, kid = $%04X", LogMessage(LOG_RF, P25_LDU2_STR " DFSI, audio, algo = $%02X, kid = $%04X",
@ -571,7 +571,7 @@ bool DFSIVoicePacket::process(uint8_t* data, uint32_t len)
// Add busy bits // Add busy bits
m_p25->addBusyBits(data + 2U, P25_TDU_FRAME_LENGTH_BITS, true, true); m_p25->addBusyBits(data + 2U, P25_TDU_FRAME_LENGTH_BITS, true, true);
writeNetworkRF(data + 2U, P25_DUID_TDU); writeNetwork(data + 2U, P25_DUID_TDU);
m_lastDUID = P25_DUID_TDU; m_lastDUID = P25_DUID_TDU;
@ -675,12 +675,7 @@ bool DFSIVoicePacket::processNetwork(uint8_t* data, uint32_t len, lc::LC& contro
if (m_p25->m_netState == RS_NET_IDLE) { if (m_p25->m_netState == RS_NET_IDLE) {
// are we interrupting a running CC? // are we interrupting a running CC?
if (m_p25->m_ccRunning) { if (m_p25->m_ccRunning) {
g_interruptP25Control = true; m_p25->m_ccHalted = true;
}
// single-channel trunking or voice on control support?
if (m_p25->m_control && m_p25->m_voiceOnControl) {
m_p25->m_ccRunning = false; // otherwise the grant will be bundled with other packets
} }
} }
@ -782,6 +777,7 @@ bool DFSIVoicePacket::processNetwork(uint8_t* data, uint32_t len, lc::LC& contro
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Protected Class Members // Protected Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Initializes a new instance of the DFSIVoicePacket class. /// Initializes a new instance of the DFSIVoicePacket class.
/// </summary> /// </summary>
@ -990,7 +986,6 @@ void DFSIVoicePacket::writeNet_LDU1()
// single-channel trunking or voice on control support? // single-channel trunking or voice on control support?
if (m_p25->m_control && m_p25->m_voiceOnControl) { if (m_p25->m_control && m_p25->m_voiceOnControl) {
m_p25->m_ccRunning = false; // otherwise the grant will be bundled with other packets
if (!m_p25->m_trunk->writeRF_TSDU_Grant(group, false, true)) { if (!m_p25->m_trunk->writeRF_TSDU_Grant(group, false, true)) {
if (m_network != NULL) if (m_network != NULL)
m_network->resetP25(); m_network->resetP25();
@ -1039,7 +1034,7 @@ void DFSIVoicePacket::writeNet_LDU1()
buffer[0U] = modem::TAG_DATA; buffer[0U] = modem::TAG_DATA;
buffer[1U] = 0x00U; buffer[1U] = 0x00U;
m_p25->writeQueueNet(buffer, P25_DFSI_VHDR1_FRAME_LENGTH_BYTES + 2U); m_p25->addFrame(buffer, P25_DFSI_VHDR1_FRAME_LENGTH_BYTES + 2U, true);
// Generate Voice Header 2 // Generate Voice Header 2
m_netDFSILC.setFrameType(P25_DFSI_VHDR2); m_netDFSILC.setFrameType(P25_DFSI_VHDR2);
@ -1047,7 +1042,7 @@ void DFSIVoicePacket::writeNet_LDU1()
buffer[0U] = modem::TAG_DATA; buffer[0U] = modem::TAG_DATA;
buffer[1U] = 0x00U; buffer[1U] = 0x00U;
m_p25->writeQueueNet(buffer, P25_DFSI_VHDR2_FRAME_LENGTH_BYTES + 2U); m_p25->addFrame(buffer, P25_DFSI_VHDR2_FRAME_LENGTH_BYTES + 2U, true);
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_NET, P25_HDU_STR " DFSI, dstId = %u, algo = $%02X, kid = $%04X", m_netLC.getDstId(), m_netLC.getAlgId(), m_netLC.getKId()); LogMessage(LOG_NET, P25_HDU_STR " DFSI, dstId = %u, algo = $%02X, kid = $%04X", m_netLC.getDstId(), m_netLC.getAlgId(), m_netLC.getKId());
@ -1124,7 +1119,8 @@ void DFSIVoicePacket::writeNet_LDU1()
buffer[0U] = modem::TAG_DATA; buffer[0U] = modem::TAG_DATA;
buffer[1U] = 0x00U; buffer[1U] = 0x00U;
m_p25->writeQueueNet(buffer, len + 2U);
m_p25->addFrame(buffer, len + 2U, true);
} }
if (m_verbose) { if (m_verbose) {
@ -1239,7 +1235,8 @@ void DFSIVoicePacket::writeNet_LDU2()
buffer[0U] = modem::TAG_DATA; buffer[0U] = modem::TAG_DATA;
buffer[1U] = 0x00U; buffer[1U] = 0x00U;
m_p25->writeQueueNet(buffer, len + 2U);
m_p25->addFrame(buffer, len + 2U, true);
} }
if (m_verbose) { if (m_verbose) {

Loading…
Cancel
Save

Powered by TurnKey Linux.