add RF talkgroup steering and preemption coverage for P25 enhanced conventional grants;

pull/134/head
Bryan Biedenkapp 4 weeks ago
parent 7367a5f734
commit 32b8e625e5

@ -281,6 +281,10 @@ protocols:
enableTimeDateAnn: false
# Flag indicating whether or not the source ID validation before granting disabled.
disableGrantSourceIdCheck: false
# Flag indicating whether successful RF group grants may steer the conventional channel talkgroup.
# This applies only when control is enabled and is not dedicated. Active RF grants remain exclusive;
# a different-TG RF grant may preempt a network-originated grant or residual RF talkgroup hang.
allowRFGrantTGSteering: false
# Flag indicating whether or not the adjacent site broadcasts are disabled.
disableAdjSiteBroadcast: false
# Flag indicating a dedicated CC while process call TDUs and explicitly release grants when a call TDU occurs.

@ -1292,6 +1292,15 @@ bool Voice::checkRFTrafficCollision(uint32_t dstId)
}
}
// RF traffic on a different talkgroup preempts the active network call.
if (m_slot->m_netState != RS_NET_IDLE) {
m_slot->m_netState = RS_NET_IDLE;
m_slot->m_netTimeoutTimer.stop();
m_slot->m_netTimeout = false;
m_slot->m_netTGHang.stop();
m_slot->m_networkWatchdog.stop();
}
return false;
}

@ -1220,6 +1220,11 @@ bool Voice::checkRFTrafficCollision(uint32_t srcId, uint32_t dstId)
LogWarning(LOG_RF, "Traffic collision detect, preempting existing network traffic to new RF traffic, rfDstId = %u, netDstId = %u", dstId,
m_nxdn->m_netLastDstId);
resetNet();
m_nxdn->m_netState = RS_NET_IDLE;
m_nxdn->m_netTimeoutTimer.stop();
m_nxdn->m_netTimeout = false;
m_nxdn->m_netTGHang.stop();
m_nxdn->m_networkWatchdog.stop();
if (m_nxdn->m_network != nullptr)
m_nxdn->m_network->resetNXDN();
}

@ -77,6 +77,7 @@ Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t q
m_voiceOnControl(false),
m_controlOnly(false),
m_ackTSBKRequests(true),
m_allowRFGrantTGSteering(false),
m_disableNetworkGrant(false),
m_disableNetworkHDU(false),
m_allowExplicitSourceId(false),
@ -306,6 +307,11 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw
}
m_ackTSBKRequests = control["ackRequests"].as<bool>(true);
m_allowRFGrantTGSteering = control["allowRFGrantTGSteering"].as<bool>(false);
if (m_allowRFGrantTGSteering && (!m_enableControl || m_dedicatedControl)) {
LogWarning(LOG_P25, "RF grant talkgroup steering requires non-dedicated control; disabling it.");
m_allowRFGrantTGSteering = false;
}
m_control->m_ctrlTSDUMBF = !control["disableTSDUMBF"].as<bool>(false);
if (m_isModemDFSI)
m_control->m_ctrlTSDUMBF = false; // force single block TSDUs for DFSI mode
@ -515,6 +521,9 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw
if (!m_control->m_ctrlTSDUMBF) {
LogInfo(" Disable Multi-Block TSDUs: yes");
}
if (m_allowRFGrantTGSteering) {
LogInfo(" RF Grant Talkgroup Steering: yes");
}
LogInfo(" Time/Date Announcement TSBK: %s", m_control->m_ctrlTimeDateAnn ? "yes" : "no");
LogInfo(" Inhibit Unauthorized: %s", m_inhibitUnauth ? "yes" : "no");

@ -319,6 +319,7 @@ namespace p25
bool m_voiceOnControl;
bool m_controlOnly;
bool m_ackTSBKRequests;
bool m_allowRFGrantTGSteering;
bool m_disableNetworkGrant;
bool m_disableNetworkHDU;
bool m_allowExplicitSourceId;

@ -2153,6 +2153,34 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_
bool encryption = ((serviceOptions & 0xFFU) & 0x40U) == 0x40U; // Encryption Flag
uint8_t priority = ((serviceOptions & 0xFFU) & 0x07U); // Priority
const bool rfGrantSteering = m_p25->m_allowRFGrantTGSteering && m_p25->m_enableControl && !m_p25->m_dedicatedControl && !net && grp;
uint32_t activeGrantDstId = 0U;
bool activeGrantFromNetwork = false;
bool preemptNetworkGrant = false;
bool preemptNetworkTraffic = false;
// determine the active grant destination ID and network preemption status
if (rfGrantSteering) {
if (m_p25->m_rfLastDstId != 0U && m_p25->m_affiliations->isGranted(m_p25->m_rfLastDstId)) {
activeGrantDstId = m_p25->m_rfLastDstId;
}
else if (m_p25->m_netLastDstId != 0U && m_p25->m_affiliations->isGranted(m_p25->m_netLastDstId)) {
activeGrantDstId = m_p25->m_netLastDstId;
}
else {
std::unordered_map<uint32_t, uint32_t> grantTable = m_p25->m_affiliations->grantTable();
for (const auto& grant : grantTable) {
if (grant.second == m_p25->m_siteData.channelNo() || grantTable.size() == 1U) {
activeGrantDstId = grant.first;
break;
}
}
}
activeGrantFromNetwork = activeGrantDstId != 0U && m_p25->m_affiliations->isNetGranted(activeGrantDstId);
preemptNetworkTraffic = m_p25->m_netState != RS_NET_IDLE && m_p25->m_netLastDstId != 0U && m_p25->m_netLastDstId != dstId;
}
if (dstId == TGID_ALL) {
return true; // do not generate grant packets for $FFFF (All Call) TGID
}
@ -2193,6 +2221,32 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_
return false;
}
// check if an RF grant is currently active and if it should preempt network traffic
if (rfGrantSteering && activeGrantDstId != 0U) {
if (!activeGrantFromNetwork) {
// An RF-originated grant owns the channel until it is released or expires.
if (activeGrantDstId != dstId) {
LogWarning(LOG_RF, P25_TSDU_STR ", TSBKO, IOSP_GRP_VCH (Group Voice Channel Request) denied, RF grant active, grantedDstId = %u, dstId = %u",
activeGrantDstId, dstId);
writeRF_TSDU_Deny(srcId, dstId, ReasonCode::DENY_PTT_COLLIDE, TSBKO::IOSP_GRP_VCH, true, true);
::ActivityLog("P25", true, "group grant request from %u to TG %u denied", srcId, dstId);
m_p25->m_rfState = RS_RF_REJECTED;
return false;
}
}
else if (activeGrantDstId == dstId) {
// matching network traffic retains ownership to avoid treating a duplicate as a new RF call
LogWarning(LOG_RF, P25_TSDU_STR ", TSBKO, IOSP_GRP_VCH (Group Voice Channel Request) denied, matching network grant active, dstId = %u", dstId);
writeRF_TSDU_Deny(srcId, dstId, ReasonCode::DENY_PTT_COLLIDE, TSBKO::IOSP_GRP_VCH, true, true);
::ActivityLog("P25", true, "group grant request from %u to TG %u denied", srcId, dstId);
m_p25->m_rfState = RS_RF_REJECTED;
return false;
}
else {
preemptNetworkGrant = true;
}
}
// only do the last destination ID checking if we're operating in non-dedicated mode (e.g. DVRS)
if (!m_p25->m_dedicatedControl) {
if (m_p25->m_netState != RS_NET_IDLE && dstId == m_p25->m_netLastDstId) {
@ -2212,7 +2266,7 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_
// don't transmit grants if the destination ID's don't match and the network TG hang timer is running
if (m_p25->m_rfLastDstId != 0U) {
if (m_p25->m_rfLastDstId != dstId && (m_p25->m_rfTGHang.isRunning() && !m_p25->m_rfTGHang.hasExpired())) {
if (m_p25->m_rfLastDstId != dstId && (m_p25->m_rfTGHang.isRunning() && !m_p25->m_rfTGHang.hasExpired()) && !rfGrantSteering) {
if (!net) {
writeRF_TSDU_Deny(srcId, dstId, ReasonCode::DENY_PTT_BONK, (grp) ? TSBKO::IOSP_GRP_VCH : TSBKO::IOSP_UU_VCH, grp, true);
m_p25->m_rfState = RS_RF_REJECTED;
@ -2260,6 +2314,30 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_
}
}
// preempt network traffic or grant if necessary
if (preemptNetworkGrant || preemptNetworkTraffic) {
LogWarning(LOG_RF, P25_TSDU_STR ", TSBKO, IOSP_GRP_VCH (Group Voice Channel Request), preempting network traffic/grant, grantedDstId = %u, dstId = %u",
activeGrantDstId, dstId);
if (preemptNetworkGrant)
m_p25->m_affiliations->releaseGrant(activeGrantDstId, false);
if (m_p25->m_netState != RS_NET_IDLE) {
m_p25->m_voice->resetNet();
m_p25->m_netTimeoutTimer.stop();
m_p25->m_netTimeout = false;
m_p25->m_netTGHang.stop();
m_p25->m_networkWatchdog.stop();
m_p25->m_netState = RS_NET_IDLE;
if (m_p25->m_network != nullptr)
m_p25->m_network->resetP25();
if (m_p25->m_duplex)
m_p25->writeRF_TDU(true);
}
}
if (!m_p25->m_affiliations->rfCh()->isRFChAvailable()) {
if (grp) {
if (!net) {
@ -2550,6 +2628,13 @@ bool ControlSignaling::writeRF_TSDU_Grant(uint32_t srcId, uint32_t dstId, uint8_
}
}
// handle RF grant steering if applicable
if (rfGrantSteering && chNo > 0U) {
m_p25->m_rfLastSrcId = srcId;
m_p25->m_rfLastDstId = dstId;
m_p25->m_rfTGHang.start();
}
return true;
}

@ -759,13 +759,39 @@ TEST_CASE("DMR clearRFReject only clears the targeted slot", "[dmr][host][contro
TEST_CASE("DMR slot rejects mismatched network traffic while RF hang is active", "[dmr][host][control][rf]")
{
DMRHostHarness harness;
HostTestHooks::dmrSetRFCall(*HostTestHooks::dmrSlot1(*harness.m_control), 3001U, 4001U);
HostTestHooks::dmrSetRFHang(*HostTestHooks::dmrSlot1(*harness.m_control), 3001U, 4001U);
HostTestHooks::dmrStartNetVoiceCall(*HostTestHooks::dmrSlot1(*harness.m_control), 1001U, 2001U);
REQUIRE(HostTestHooks::dmrNetState(*HostTestHooks::dmrSlot1(*harness.m_control)) == RS_NET_IDLE);
}
TEST_CASE("DMR slot admits matching network traffic during RF hang", "[dmr][host][control][rf-steering]")
{
DMRHostHarness harness;
dmr::Slot* slot = HostTestHooks::dmrSlot1(*harness.m_control);
HostTestHooks::dmrSetRFHang(*slot, 3001U, 4001U);
HostTestHooks::dmrStartNetVoiceCall(*slot, 1001U, 4001U);
REQUIRE(HostTestHooks::dmrNetState(*slot) == RS_NET_AUDIO);
REQUIRE_FALSE(HostTestHooks::dmrRFTGHang(*slot).isRunning());
}
TEST_CASE("DMR RF traffic preempts only a different active network talkgroup", "[dmr][host][control][rf-steering]")
{
DMRHostHarness harness;
dmr::Slot* slot = HostTestHooks::dmrSlot1(*harness.m_control);
HostTestHooks::dmrStartNetVoiceCall(*slot, 1001U, 2001U);
REQUIRE_FALSE(HostTestHooks::dmrStartRFVoiceCall(*slot, 1002U, 2001U));
REQUIRE(HostTestHooks::dmrNetState(*slot) == RS_NET_AUDIO);
REQUIRE(HostTestHooks::dmrStartRFVoiceCall(*slot, 1002U, 2002U));
REQUIRE(HostTestHooks::dmrRFState(*slot) == RS_RF_AUDIO);
REQUIRE(HostTestHooks::dmrNetState(*slot) == RS_NET_IDLE);
}
TEST_CASE("DMR processFrame accepts a synthetic RF voice call on the targeted slot", "[dmr][host][control][rf]")
{
DMRHostHarness harness;

@ -91,6 +91,16 @@ void HostTestHooks::dmrSetRFCall(dmr::Slot& slot, uint32_t srcId, uint32_t dstId
slot.m_rfTGHang.start();
}
/* Forces DMR slot into residual RF talkgroup hang state. */
void HostTestHooks::dmrSetRFHang(dmr::Slot& slot, uint32_t srcId, uint32_t dstId)
{
slot.m_rfState = RS_RF_LISTENING;
slot.m_rfLastSrcId = srcId;
slot.m_rfLastDstId = dstId;
slot.m_rfTGHang.start();
}
/* Returns DMR slot to RF listening state. */
void HostTestHooks::dmrClearRFCall(dmr::Slot& slot)
@ -351,6 +361,40 @@ Timer& HostTestHooks::p25RFTGHang(p25::Control& control) { return control.m_rfTG
Timer& HostTestHooks::p25RFLossWatchdog(p25::Control& control) { return control.m_rfLossWatchdog; }
/* Configures P25 RF grant talkgroup steering for a test. */
void HostTestHooks::p25ConfigureRFGrantSteering(p25::Control& control, bool enabled, bool dedicated)
{
control.m_enableControl = true;
control.m_dedicatedControl = dedicated;
control.m_allowRFGrantTGSteering = enabled;
}
/* Tests whether the P25 affiliation table contains a grant. */
bool HostTestHooks::p25IsGranted(const p25::Control& control, uint32_t dstId)
{
return control.m_affiliations->isGranted(dstId);
}
/* Seeds a P25 grant for a steering-policy test. */
bool HostTestHooks::p25SeedGrant(p25::Control& control, uint32_t srcId, uint32_t dstId, bool netGranted)
{
return control.m_affiliations->grantCh(dstId, srcId, 15U, true, netGranted);
}
/* Releases a P25 grant for a steering-policy test. */
bool HostTestHooks::p25ReleaseGrant(p25::Control& control, uint32_t dstId)
{
return control.m_affiliations->releaseGrant(dstId, false);
}
/* Gets the last P25 RF destination ID. */
uint32_t HostTestHooks::p25RFLastDstId(const p25::Control& control) { return control.m_rfLastDstId; }
/* Forces P25 control network state and last IDs. */
void HostTestHooks::p25SetNetState(p25::Control& control, RPT_NET_STATE netState, uint32_t srcId, uint32_t dstId)
@ -491,6 +535,16 @@ void HostTestHooks::nxdnSetRFCall(nxdn::Control& control, uint32_t srcId, uint32
control.m_rfTGHang.start();
}
/* Forces NXDN control into residual RF talkgroup hang state. */
void HostTestHooks::nxdnSetRFHang(nxdn::Control& control, uint32_t srcId, uint32_t dstId)
{
control.m_rfState = RS_RF_LISTENING;
control.m_rfLastSrcId = srcId;
control.m_rfLastDstId = dstId;
control.m_rfTGHang.start();
}
/* Returns NXDN control to RF listening state. */
void HostTestHooks::nxdnClearRFCall(nxdn::Control& control)

@ -94,6 +94,13 @@ public:
* @param dstId Destination ID to apply to slot state.
*/
static void dmrSetRFCall(dmr::Slot& slot, uint32_t srcId, uint32_t dstId);
/**
* @brief Forces a DMR slot into residual RF talkgroup hang state for tests.
* @param slot DMR slot instance.
* @param srcId Source ID to retain as the last RF source.
* @param dstId Destination ID to retain as the last RF destination.
*/
static void dmrSetRFHang(dmr::Slot& slot, uint32_t srcId, uint32_t dstId);
/**
* @brief Returns a DMR slot to RF listening state after a synthetic RF call.
* @param slot DMR slot instance.
@ -202,6 +209,42 @@ public:
* @return Timer& Reference to the RF loss watchdog timer.
*/
static Timer& p25RFLossWatchdog(p25::Control& control);
/**
* @brief Configures P25 RF grant talkgroup steering for a test.
* @param control P25 control instance.
* @param enabled Flag indicating whether RF grant steering is enabled.
* @param dedicated Flag indicating whether the control channel is dedicated.
*/
static void p25ConfigureRFGrantSteering(p25::Control& control, bool enabled, bool dedicated);
/**
* @brief Tests whether the P25 affiliation table contains a grant.
* @param control P25 control instance.
* @param dstId Destination ID to test.
* @returns bool True if the destination is granted; otherwise, false.
*/
static bool p25IsGranted(const p25::Control& control, uint32_t dstId);
/**
* @brief Seeds a P25 grant for a steering-policy test.
* @param control P25 control instance.
* @param srcId Source radio ID.
* @param dstId Destination ID.
* @param netGranted Flag indicating whether the grant originated from the network.
* @returns bool True if the grant was created; otherwise, false.
*/
static bool p25SeedGrant(p25::Control& control, uint32_t srcId, uint32_t dstId, bool netGranted);
/**
* @brief Releases a P25 grant for a steering-policy test.
* @param control P25 control instance.
* @param dstId Destination ID to release.
* @returns bool True if the grant was released; otherwise, false.
*/
static bool p25ReleaseGrant(p25::Control& control, uint32_t dstId);
/**
* @brief Gets the last P25 RF destination ID.
* @param control P25 control instance.
* @returns uint32_t Last RF destination ID.
*/
static uint32_t p25RFLastDstId(const p25::Control& control);
/**
* @brief Forces P25 control network state and last IDs for targeted recovery-path tests.
* @param control P25 control instance.
@ -466,6 +509,13 @@ public:
* @param dstId Destination ID to apply to control state.
*/
static void nxdnSetRFCall(nxdn::Control& control, uint32_t srcId, uint32_t dstId);
/**
* @brief Forces NXDN control into residual RF talkgroup hang state for tests.
* @param control NXDN control instance.
* @param srcId Source ID to retain as the last RF source.
* @param dstId Destination ID to retain as the last RF destination.
*/
static void nxdnSetRFHang(nxdn::Control& control, uint32_t srcId, uint32_t dstId);
/**
* @brief Returns NXDN control to RF listening state after a synthetic RF call.
* @param control NXDN control instance.

@ -882,7 +882,7 @@ TEST_CASE("NXDN clearRFReject returns rejected RF state to listening", "[nxdn][h
TEST_CASE("NXDN rejects mismatched network traffic while RF hang is active", "[nxdn][host][control][rf]")
{
NXDNHostHarness harness;
HostTestHooks::nxdnSetRFCall(*harness.m_control, 3001U, 4001U);
HostTestHooks::nxdnSetRFHang(*harness.m_control, 3001U, 4001U);
nxdn::lc::RTCH control;
control.setMessageType(nxdn::defines::MessageType::RTCH_VCALL);
@ -895,6 +895,36 @@ TEST_CASE("NXDN rejects mismatched network traffic while RF hang is active", "[n
REQUIRE(HostTestHooks::nxdnNetState(*harness.m_control) == RS_NET_IDLE);
}
TEST_CASE("NXDN admits matching network traffic during RF hang", "[nxdn][host][control][rf-steering]")
{
NXDNHostHarness harness;
HostTestHooks::nxdnSetRFHang(*harness.m_control, 3001U, 4001U);
nxdn::lc::RTCH control;
control.setMessageType(nxdn::defines::MessageType::RTCH_VCALL);
control.setSrcId(1001U);
control.setDstId(4001U);
control.setGroup(true);
control.setTransmissionMode(nxdn::defines::TransmissionMode::MODE_4800);
REQUIRE(HostTestHooks::nxdnStartNetCall(*harness.m_control, control));
REQUIRE(HostTestHooks::nxdnNetState(*harness.m_control) == RS_NET_AUDIO);
REQUIRE_FALSE(HostTestHooks::nxdnRFTGHang(*harness.m_control).isRunning());
}
TEST_CASE("NXDN RF traffic preempts only a different active network talkgroup", "[nxdn][host][control][rf-steering]")
{
NXDNHostHarness harness;
harness.startNetworkVoiceCall(1001U, 2001U);
REQUIRE_FALSE(HostTestHooks::nxdnStartRFCall(*harness.m_control, 1002U, 2001U));
REQUIRE(HostTestHooks::nxdnNetState(*harness.m_control) == RS_NET_AUDIO);
REQUIRE(HostTestHooks::nxdnStartRFCall(*harness.m_control, 1002U, 2002U));
REQUIRE(HostTestHooks::nxdnRFState(*harness.m_control) == RS_RF_AUDIO);
REQUIRE(HostTestHooks::nxdnNetState(*harness.m_control) == RS_NET_IDLE);
}
TEST_CASE("NXDN processFrame accepts a synthetic RF voice call", "[nxdn][host][control][rf]")
{
NXDNHostHarness harness;

@ -476,6 +476,121 @@ TEST_CASE("P25 active talkgroup updates preserve the working TGID", "[p25][activ
REQUIRE(dstIdB == 0U);
}
TEST_CASE("P25 enhanced conventional RF grants steer without replacing RF ownership", "[p25][host][control][grant-steering]")
{
P25HostHarness harness;
const uint32_t channelNo = 1U;
harness.m_chLookup.setRFChData(channelNo, lookups::VoiceChData(1U, channelNo, "", 0U, ""));
REQUIRE(harness.m_chLookup.initializeRFCh(channelNo));
HostTestHooks::p25ConfigureRFGrantSteering(*harness.m_control, true, false);
harness.m_control->grantTG(1001U, 2001U, true);
REQUIRE(HostTestHooks::p25IsGranted(*harness.m_control, 2001U));
REQUIRE(HostTestHooks::p25RFLastDstId(*harness.m_control) == 2001U);
REQUIRE(HostTestHooks::p25RFTGHang(*harness.m_control).isRunning());
harness.m_control->grantTG(1002U, 2002U, true);
REQUIRE(HostTestHooks::p25IsGranted(*harness.m_control, 2001U));
REQUIRE_FALSE(HostTestHooks::p25IsGranted(*harness.m_control, 2002U));
REQUIRE(HostTestHooks::p25RFLastDstId(*harness.m_control) == 2001U);
}
TEST_CASE("P25 enhanced conventional RF grant preempts a different network grant", "[p25][host][control][grant-steering]")
{
P25HostHarness harness;
const uint32_t channelNo = 1U;
harness.m_chLookup.setRFChData(channelNo, lookups::VoiceChData(1U, channelNo, "", 0U, ""));
REQUIRE(harness.m_chLookup.initializeRFCh(channelNo));
HostTestHooks::p25ConfigureRFGrantSteering(*harness.m_control, true, false);
REQUIRE(HostTestHooks::p25SeedGrant(*harness.m_control, 1001U, 2001U, true));
HostTestHooks::p25SetNetState(*harness.m_control, RS_NET_AUDIO, 1001U, 2001U);
harness.m_control->grantTG(1002U, 2002U, true);
REQUIRE_FALSE(HostTestHooks::p25IsGranted(*harness.m_control, 2001U));
REQUIRE(HostTestHooks::p25IsGranted(*harness.m_control, 2002U));
REQUIRE(HostTestHooks::p25RFLastDstId(*harness.m_control) == 2002U);
REQUIRE(HostTestHooks::p25NetState(*harness.m_control) == RS_NET_IDLE);
}
TEST_CASE("P25 RF grant steering overrides hang only on non-dedicated control", "[p25][host][control][grant-steering]")
{
P25HostHarness harness;
const uint32_t channelNo = 1U;
harness.m_chLookup.setRFChData(channelNo, lookups::VoiceChData(1U, channelNo, "", 0U, ""));
REQUIRE(harness.m_chLookup.initializeRFCh(channelNo));
HostTestHooks::p25ConfigureRFGrantSteering(*harness.m_control, true, false);
harness.m_control->grantTG(1001U, 2001U, true);
REQUIRE(HostTestHooks::p25ReleaseGrant(*harness.m_control, 2001U));
harness.m_control->grantTG(1002U, 2002U, true);
REQUIRE(HostTestHooks::p25IsGranted(*harness.m_control, 2002U));
REQUIRE(HostTestHooks::p25RFLastDstId(*harness.m_control) == 2002U);
REQUIRE(HostTestHooks::p25ReleaseGrant(*harness.m_control, 2002U));
HostTestHooks::p25ConfigureRFGrantSteering(*harness.m_control, true, true);
harness.m_control->grantTG(1003U, 2003U, true);
REQUIRE(HostTestHooks::p25IsGranted(*harness.m_control, 2003U));
REQUIRE(HostTestHooks::p25RFLastDstId(*harness.m_control) == 2002U);
}
TEST_CASE("P25 disabled RF grant steering preserves conventional hang ownership", "[p25][host][control][grant-steering]")
{
P25HostHarness harness;
const uint32_t channelNo = 1U;
harness.m_chLookup.setRFChData(channelNo, lookups::VoiceChData(1U, channelNo, "", 0U, ""));
REQUIRE(harness.m_chLookup.initializeRFCh(channelNo));
HostTestHooks::p25ConfigureRFGrantSteering(*harness.m_control, true, false);
harness.m_control->grantTG(1001U, 2001U, true);
REQUIRE(HostTestHooks::p25ReleaseGrant(*harness.m_control, 2001U));
HostTestHooks::p25ConfigureRFGrantSteering(*harness.m_control, false, false);
harness.m_control->grantTG(1002U, 2002U, true);
REQUIRE_FALSE(HostTestHooks::p25IsGranted(*harness.m_control, 2002U));
REQUIRE(HostTestHooks::p25RFLastDstId(*harness.m_control) == 2001U);
REQUIRE(HostTestHooks::p25RFTGHang(*harness.m_control).isRunning());
}
TEST_CASE("P25 enhanced conventional RF grant cannot replace matching network ownership", "[p25][host][control][grant-steering]")
{
P25HostHarness harness;
const uint32_t channelNo = 1U;
harness.m_chLookup.setRFChData(channelNo, lookups::VoiceChData(1U, channelNo, "", 0U, ""));
REQUIRE(harness.m_chLookup.initializeRFCh(channelNo));
HostTestHooks::p25ConfigureRFGrantSteering(*harness.m_control, true, false);
REQUIRE(HostTestHooks::p25SeedGrant(*harness.m_control, 1001U, 2001U, true));
harness.m_control->grantTG(1002U, 2001U, true);
REQUIRE(HostTestHooks::p25IsGranted(*harness.m_control, 2001U));
REQUIRE(HostTestHooks::p25RFLastDstId(*harness.m_control) == 0U);
}
TEST_CASE("P25 enhanced conventional RF grant permits only the owning source to retry", "[p25][host][control][grant-steering]")
{
P25HostHarness harness;
const uint32_t channelNo = 1U;
harness.m_chLookup.setRFChData(channelNo, lookups::VoiceChData(1U, channelNo, "", 0U, ""));
REQUIRE(harness.m_chLookup.initializeRFCh(channelNo));
HostTestHooks::p25ConfigureRFGrantSteering(*harness.m_control, true, false);
harness.m_control->grantTG(1001U, 2001U, true);
REQUIRE(HostTestHooks::p25IsGranted(*harness.m_control, 2001U));
harness.m_control->grantTG(1001U, 2001U, true);
REQUIRE(HostTestHooks::p25IsGranted(*harness.m_control, 2001U));
REQUIRE(HostTestHooks::p25RFLastDstId(*harness.m_control) == 2001U);
harness.m_control->grantTG(1002U, 2001U, true);
REQUIRE(HostTestHooks::p25IsGranted(*harness.m_control, 2001U));
REQUIRE(HostTestHooks::p25RFLastDstId(*harness.m_control) == 2001U);
}
TEST_CASE("P25 host preserves encrypted network call parameters", "[p25][host][control][crypto]")
{
P25HostHarness harness;

Loading…
Cancel
Save

Powered by TurnKey Linux.