diff --git a/configs/config.example.yml b/configs/config.example.yml
index d9e0e06b..be6f6f2f 100644
--- a/configs/config.example.yml
+++ b/configs/config.example.yml
@@ -9,6 +9,7 @@ daemon: true
#
# Logging Configuration
+#
# Logging Levels:
# 1 - Debug
# 2 - Message
@@ -392,6 +393,17 @@ system:
# DMR/P25/NXDN Site ID.
siteId: 1
+ #
+ # CW ID Configuration
+ #
+ cwId:
+ # Flag indicating whether or not CWID is enabled.
+ enable: true
+ # Amount of time between CWID transmissions. (minutes)
+ time: 15
+ # Callsign to be transmitted.
+ callsign: ABCD123
+
#
# Modem Configuration
#
@@ -525,17 +537,6 @@ system:
# Flag indicating whether or not debug logging is enabled.
debug: false
- #
- # CW ID Configuration
- #
- cwId:
- # Flag indicating whether or not CWID is enabled.
- enable: true
- # Amount of time between CWID transmissions. (minutes)
- time: 15
- # Callsign to be transmitted.
- callsign: ABCD123
-
#
# Channel Identity Table Configuration
#
diff --git a/src/network/BaseNetwork.cpp b/src/network/BaseNetwork.cpp
index f93112ae..84eb00a8 100644
--- a/src/network/BaseNetwork.cpp
+++ b/src/network/BaseNetwork.cpp
@@ -645,6 +645,8 @@ UInt8Array BaseNetwork::createDMR_Message(uint32_t& length, const uint32_t strea
uint32_t slotNo = data.getSlotNo();
+ buffer[14U] = 0U; // Control Bits
+
// Individual slot disabling
if (slotNo == 1U && !m_slot1)
return nullptr;
@@ -685,52 +687,54 @@ UInt8Array BaseNetwork::createDMR_Message(uint32_t& length, const uint32_t strea
///
/// Creates an P25 frame message header.
///
-///
+///
///
///
///
///
-void BaseNetwork::createP25_MessageHdr(uint8_t* data, uint8_t duid, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
+void BaseNetwork::createP25_MessageHdr(uint8_t* buffer, uint8_t duid, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
uint8_t frameType)
{
- assert(data != nullptr);
+ assert(buffer != nullptr);
// construct P25 message header
- ::memcpy(data + 0U, TAG_P25_DATA, 4U);
+ ::memcpy(buffer + 0U, TAG_P25_DATA, 4U);
- data[4U] = control.getLCO(); // LCO
+ buffer[4U] = control.getLCO(); // LCO
uint32_t srcId = control.getSrcId(); // Source Address
- __SET_UINT16(srcId, data, 5U);
+ __SET_UINT16(srcId, buffer, 5U);
uint32_t dstId = control.getDstId(); // Target Address
- __SET_UINT16(dstId, data, 8U);
+ __SET_UINT16(dstId, buffer, 8U);
uint16_t sysId = control.getSiteData().sysId(); // System ID
- data[11U] = (sysId >> 8) & 0xFFU;
- data[12U] = (sysId >> 0) & 0xFFU;
+ buffer[11U] = (sysId >> 8) & 0xFFU;
+ buffer[12U] = (sysId >> 0) & 0xFFU;
- data[15U] = control.getMFId(); // MFId
+ buffer[14U] = 0U; // Control Bits
+
+ buffer[15U] = control.getMFId(); // MFId
uint32_t netId = control.getSiteData().netId(); // Network ID
- __SET_UINT16(netId, data, 16U);
+ __SET_UINT16(netId, buffer, 16U);
- data[20U] = lsd.getLSD1(); // LSD 1
- data[21U] = lsd.getLSD2(); // LSD 2
+ buffer[20U] = lsd.getLSD1(); // LSD 1
+ buffer[21U] = lsd.getLSD2(); // LSD 2
- data[22U] = duid; // DUID
+ buffer[22U] = duid; // DUID
if (frameType != p25::P25_FT_TERMINATOR) {
- data[180U] = frameType; // DVM Frame Type
+ buffer[180U] = frameType; // DVM Frame Type
}
// is this the first frame of a call?
if (frameType == p25::P25_FT_HDU_VALID) {
- data[181U] = control.getAlgId(); // Algorithm ID
+ buffer[181U] = control.getAlgId(); // Algorithm ID
uint32_t kid = control.getKId();
- data[182U] = (kid >> 8) & 0xFFU; // Key ID
- data[183U] = (kid >> 0) & 0xFFU;
+ buffer[182U] = (kid >> 8) & 0xFFU; // Key ID
+ buffer[183U] = (kid >> 0) & 0xFFU;
// copy MI data
uint8_t mi[p25::P25_MI_LENGTH_BYTES];
@@ -742,7 +746,7 @@ void BaseNetwork::createP25_MessageHdr(uint8_t* data, uint8_t duid, const p25::l
}
for (uint8_t i = 0; i < p25::P25_MI_LENGTH_BYTES; i++) {
- data[184U + i] = mi[i]; // Message Indicator
+ buffer[184U + i] = mi[i]; // Message Indicator
}
}
}
@@ -1043,6 +1047,8 @@ UInt8Array BaseNetwork::createNXDN_Message(uint32_t& length, const nxdn::lc::RTC
uint32_t dstId = lc.getDstId(); // Target Address
__SET_UINT16(dstId, buffer, 8U);
+ buffer[14U] = 0U; // Control Bits
+
buffer[15U] |= lc.getGroup() ? 0x00U : 0x40U; // Group
// pack raw NXDN message bytes
diff --git a/src/network/BaseNetwork.h b/src/network/BaseNetwork.h
index d1088ce8..618bb56f 100644
--- a/src/network/BaseNetwork.h
+++ b/src/network/BaseNetwork.h
@@ -275,7 +275,7 @@ namespace network
UInt8Array createDMR_Message(uint32_t& length, const uint32_t streamId, const dmr::data::Data& data);
/// Creates an P25 frame message header.
- void createP25_MessageHdr(uint8_t* data, uint8_t duid, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
+ void createP25_MessageHdr(uint8_t* buffer, uint8_t duid, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd,
uint8_t frameType = p25::P25_FT_DATA_UNIT);
/// Creates an P25 LDU1 frame message.
diff --git a/src/p25/Control.cpp b/src/p25/Control.cpp
index 4f7681e9..39c3a15d 100644
--- a/src/p25/Control.cpp
+++ b/src/p25/Control.cpp
@@ -1077,6 +1077,8 @@ void Control::processNetwork()
return;
}
+ bool grantDemand = (buffer[14U] & 0x80U) == 0x80U;
+
// process network message header
uint8_t duid = buffer[22U];
uint8_t MFId = buffer[15U];
@@ -1218,11 +1220,30 @@ void Control::processNetwork()
}
break;
}
- ret = m_voice->processNetwork(data.get(), frameLength, control, lsd, duid, frameType);
+
+ if (m_dedicatedControl && m_voiceOnControl) {
+ ret = m_voice->processNetwork(data.get(), frameLength, control, lsd, duid, frameType);
+ }
break;
case P25_DUID_TDU:
case P25_DUID_TDULC:
+ // is this an TDU with a grant demand?
+ if (duid == P25_DUID_TDU && grantDemand) {
+ uint8_t serviceOptions = (control.getEmergency() ? 0x80U : 0x00U) + // Emergency Flag
+ (control.getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag
+ (control.getPriority() & 0x07U); // Priority
+
+ if (m_verbose) {
+ LogMessage(LOG_NET, P25_TSDU_STR " remote grant demand, srcId = %u, dstId = %u", srcId, dstId);
+ }
+
+ if (!m_trunk->writeRF_TSDU_Grant(srcId, dstId, serviceOptions, true, true)) {
+ LogError(LOG_NET, P25_TSDU_STR " call failure, network call not granted, dstId = %u", dstId);
+ return;
+ }
+ }
+
m_voice->processNetwork(data.get(), frameLength, control, lsd, duid, frameType);
break;