diff --git a/p25/TrunkPacket.cpp b/p25/TrunkPacket.cpp
index b0024e00..101ae6cf 100644
--- a/p25/TrunkPacket.cpp
+++ b/p25/TrunkPacket.cpp
@@ -309,9 +309,7 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len, bool preDecoded)
m_rfTSBK.getDataServiceOptions(), m_rfTSBK.getDataAccessControl(), srcId);
}
- // SNDCP data channel requests are currently unsupported -- maybe in the future?
-
- writeRF_TSDU_Deny(P25_DENY_RSN_SYS_UNSUPPORTED_SVC, TSBK_ISP_SNDCP_CH_REQ);
+ writeRF_TSDU_Grant(false, false, false, true);
break;
case TSBK_IOSP_STS_UPDT:
// validate the source RID
@@ -2017,8 +2015,9 @@ void TrunkPacket::queueRF_TSBK_Ctrl(uint8_t lco)
///
///
///
+///
///
-bool TrunkPacket::writeRF_TSDU_Grant(bool grp, bool skip, bool net)
+bool TrunkPacket::writeRF_TSDU_Grant(bool grp, bool skip, bool net, bool sndcpGrant)
{
uint8_t lco = m_rfTSBK.getLCO();
@@ -2101,6 +2100,7 @@ bool TrunkPacket::writeRF_TSDU_Grant(bool grp, bool skip, bool net)
m_grantChTable[m_rfTSBK.getDstId()] = chNo;
m_rfTSBK.setGrpVchNo(chNo);
+ m_rfTSBK.setDataChnNo(chNo);
m_grantTimers[m_rfTSBK.getDstId()] = Timer(1000U, GRANT_TIMER_TIMEOUT);
m_grantTimers[m_rfTSBK.getDstId()].start();
@@ -2112,11 +2112,30 @@ bool TrunkPacket::writeRF_TSDU_Grant(bool grp, bool skip, bool net)
else {
uint32_t chNo = m_grantChTable[m_rfTSBK.getDstId()];
m_rfTSBK.setGrpVchNo(chNo);
+ m_rfTSBK.setDataChnNo(chNo);
m_grantTimers[m_rfTSBK.getDstId()].start();
}
}
+ if (sndcpGrant) {
+ if (!net) {
+ ::ActivityLog("P25", true, "SNDCP grant request from to %u", m_rfTSBK.getDstId());
+ }
+
+ if (m_verbose) {
+ LogMessage((net) ? LOG_NET : LOG_RF, P25_TSDU_STR ", TSBK_OSP_SNDCP_CH_GNT (SNDCP Data Channel Grant), chNo = %u, dstId = %u",
+ m_rfTSBK.getDataChnNo(), m_rfTSBK.getDstId());
+ }
+
+ // transmit SNDCP grant
+ m_rfTSBK.setLCO(TSBK_OSP_SNDCP_CH_GNT);
+ writeRF_TSDU_SBF(false, true, net);
+
+ m_rfTSBK.setLCO(lco);
+ return true;
+ }
+
if (grp) {
if (!net) {
::ActivityLog("P25", true, "group grant request from %u to TG %u", m_rfTSBK.getSrcId(), m_rfTSBK.getDstId());
diff --git a/p25/TrunkPacket.h b/p25/TrunkPacket.h
index b74c4ea4..70707ba1 100644
--- a/p25/TrunkPacket.h
+++ b/p25/TrunkPacket.h
@@ -196,7 +196,7 @@ namespace p25
void queueRF_TSBK_Ctrl(uint8_t lco);
/// Helper to write a grant packet.
- bool writeRF_TSDU_Grant(bool grp, bool skip = false, bool net = false);
+ bool writeRF_TSDU_Grant(bool grp, bool skip = false, bool net = false, bool sndcpGrant = false);
/// Helper to write a unit to unit answer request packet.
void writeRF_TSDU_UU_Ans_Req(uint32_t srcId, uint32_t dstId);
/// Helper to write a acknowledge packet.
diff --git a/p25/lc/TSBK.cpp b/p25/lc/TSBK.cpp
index 599687d4..4162aa7c 100644
--- a/p25/lc/TSBK.cpp
+++ b/p25/lc/TSBK.cpp
@@ -590,6 +590,26 @@ void TSBK::encode(uint8_t* data, bool rawTSBK, bool noTrellis)
tsbkValue = (tsbkValue << 32) + m_dstId; // Target Radio Address
tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address
break;
+ case TSBK_OSP_SNDCP_CH_GNT:
+ {
+ uint32_t calcSpace = (uint32_t)(m_siteIdenEntry.chSpaceKhz() / 0.125);
+ float calcTxOffset = m_siteIdenEntry.txOffsetMhz() * 1000000;
+
+ uint32_t txFrequency = (uint32_t)((m_siteIdenEntry.baseFrequency() + ((calcSpace * 125) * m_dataChannelNo)));
+ uint32_t rxFrequency = (uint32_t)(txFrequency + calcTxOffset);
+
+ uint32_t rootFreq = rxFrequency - m_siteIdenEntry.baseFrequency();
+ uint32_t rxChNo = rootFreq / (m_siteIdenEntry.chSpaceKhz() * 1000);
+
+ tsbkValue = 0U;
+ tsbkValue = (tsbkValue << 8) + m_dataServiceOptions; // Data Service Options
+ tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (T) ID
+ tsbkValue = (tsbkValue << 12) + m_dataChannelNo; // Channel (T) Number
+ tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel (R) ID
+ tsbkValue = (tsbkValue << 12) + (rxChNo & 0xFFFU); // Channel (R) Number
+ tsbkValue = (tsbkValue << 24) + m_dstId; // Target Radio Address
+ }
+ break;
case TSBK_IOSP_STS_UPDT:
tsbkValue = 0U;
tsbkValue = (tsbkValue << 16) + m_statusValue; // Status Value
@@ -1111,6 +1131,9 @@ TSBK::TSBK(SiteData siteData) :
m_messageValue(0U),
m_statusValue(0U),
m_extendedFunction(P25_EXT_FNCT_CHECK),
+ m_dataServiceOptions(0U),
+ m_dataAccessControl(0U),
+ m_dataChannelNo(0U),
m_adjCFVA(P25_CFVA_FAILURE),
m_adjRfssId(0U),
m_adjSiteId(0U),
@@ -1176,6 +1199,8 @@ void TSBK::copy(const TSBK& data)
m_extendedFunction = data.m_extendedFunction;
+ m_dataChannelNo = data.m_dataChannelNo;
+
m_adjCFVA = data.m_adjCFVA;
m_adjRfssId = data.m_adjRfssId;
m_adjSiteId = data.m_adjSiteId;
diff --git a/p25/lc/TSBK.h b/p25/lc/TSBK.h
index 610ba158..8d4e0c6c 100644
--- a/p25/lc/TSBK.h
+++ b/p25/lc/TSBK.h
@@ -148,6 +148,9 @@ namespace p25
/// SNDCP Data Access Control
__PROPERTY(uint32_t, dataAccessControl, DataAccessControl);
+ /// SNDCP grant channel number.
+ __PROPERTY(uint32_t, dataChannelNo, DataChnNo);
+
/** Adjacent Site Data */
/// Adjacent site CFVA flags.
__PROPERTY(uint8_t, adjCFVA, AdjSiteCFVA);