refactor how netLDUx buffers are built, and ensure buffers are filled with appropriate null IMBE frames; add support to remote grant demand to include whether or not the grant should set the encrypted flag; add warning to notify a user that a misconfiguration for a voice channel exists when a dedicated trunked voice channel has control data enabled; fix display of RF power level during startup;

pull/83/head
Bryan Biedenkapp 12 months ago
parent 0d2a53df87
commit 7063ce36a0

@ -637,7 +637,7 @@ bool Host::createModem()
LogInfo(" RX Coarse: %u, Fine: %u", rxCoarse, rxFine);
LogInfo(" TX Coarse: %u, Fine: %u", txCoarse, txFine);
LogInfo(" RSSI Coarse: %u, Fine: %u", rssiCoarse, rssiFine);
LogInfo(" RF Power Level: %u%", rfPower);
LogInfo(" RF Power Level: %u%%", rfPower);
LogInfo(" RX Level: %.1f%%", rxLevel);
LogInfo(" CW Id TX Level: %.1f%%", cwIdTXLevel);
LogInfo(" DMR TX Level: %.1f%%", dmrTXLevel);

@ -441,6 +441,12 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw
}
});
// throw a warning if we are notifying a CC of our presence (this indicates we're a VC) *AND* we have the control
// enable flag set
if (m_enableControl && m_notifyCC) {
LogWarning(LOG_P25, "We are configured as a dedicated trunked voice channel but, have control data enabled. Don't do this, control data for a dedicated trunked voice channel should be disabled. This can cause unintended behavior.");
}
if (printOptions) {
LogInfo(" Silence Threshold: %u (%.1f%%)", m_voice->m_silenceThreshold, float(m_voice->m_silenceThreshold) / 12.33F);
LogInfo(" Frame Loss Threshold: %u", m_frameLossThreshold);
@ -1266,6 +1272,7 @@ void Control::processNetwork()
bool grantDemand = (buffer[14U] & 0x80U) == 0x80U;
bool grantDenial = (buffer[14U] & 0x40U) == 0x40U;
bool grantEncrypt = (buffer[14U] & 0x08U) == 0x08U;
bool unitToUnit = (buffer[14U] & 0x01U) == 0x01U;
// process network message header
@ -1434,12 +1441,15 @@ void Control::processNetwork()
return;
}
if (grantEncrypt)
control.setEncrypted(true); // make sure encrypted flag is set
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, unitToUnit = %u", srcId, dstId, unitToUnit);
LogMessage(LOG_NET, P25_TSDU_STR " remote grant demand, srcId = %u, dstId = %u, unitToUnit = %u, encrypted = %u", srcId, dstId, unitToUnit, grantEncrypt);
}
// are we denying the grant?

@ -75,6 +75,9 @@ void Voice::resetNet()
m_netLastLDU1 = lc;
//m_netLastFrameType = P25_FT_DATA_UNIT;
m_gotNetLDU1 = false;
m_gotNetLDU2 = false;
m_netFrames = 0U;
m_netLost = 0U;
m_vocLDU1Count = 0U;
@ -385,7 +388,12 @@ bool Voice::process(uint8_t* data, uint32_t len)
// send network grant demand TDU
if (m_p25->m_network != nullptr) {
if (!m_p25->m_dedicatedControl && m_p25->m_convNetGrantDemand) {
uint8_t controlByte = 0x80U + ((group) ? 0x00U : 0x01U);
uint8_t controlByte = 0x80U; // Grant Demand Flag
if (encrypted)
controlByte |= 0x08U; // Grant Encrypt Flag
if (!group)
controlByte |= 0x01U; // Unit-to-unit Flag
LogMessage(LOG_RF, P25_HDU_STR " remote grant demand, srcId = %u, dstId = %u", srcId, dstId);
m_p25->m_network->writeP25TDU(lc, m_rfLSD, controlByte);
}
@ -437,7 +445,9 @@ bool Voice::process(uint8_t* data, uint32_t len)
// conventional registration or DVRS support?
if ((m_p25->m_enableControl && !m_p25->m_dedicatedControl) || m_p25->m_voiceOnControl) {
m_p25->m_control->writeRF_TSDU_Grant(srcId, dstId, serviceOptions, group, true, true);
if (!m_p25->m_affiliations.isGranted(dstId)) {
m_p25->m_control->writeRF_TSDU_Grant(srcId, dstId, serviceOptions, group, false, true);
}
// if voice on control; insert grant updates before voice traffic
if (m_p25->m_voiceOnControl) {
@ -1241,6 +1251,8 @@ bool Voice::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::L
m_dfsiLC.decodeLDU1(data + count, m_netLDU1 + 204U);
count += DFSI_LDU1_VOICE9_FRAME_LENGTH_BYTES;
m_gotNetLDU1 = true;
// these aren't set by the DFSI decoder, so we'll manually
// reset them
m_dfsiLC.control()->setNetId(control.getNetId());
@ -1321,6 +1333,8 @@ bool Voice::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::L
m_dfsiLC.decodeLDU2(data + count, m_netLDU2 + 204U);
count += DFSI_LDU2_VOICE18_FRAME_LENGTH_BYTES;
m_gotNetLDU2 = true;
if (m_p25->m_enableControl) {
lc::LC control = lc::LC(*m_dfsiLC.control());
m_p25->m_affiliations.touchGrant(control.getDstId());
@ -1431,7 +1445,9 @@ Voice::Voice(Control* p25, bool debug, bool verbose) :
m_rfLSD(),
m_netLSD(),
m_dfsiLC(),
m_gotNetLDU1(false),
m_netLDU1(nullptr),
m_gotNetLDU2(false),
m_netLDU2(nullptr),
m_lastDUID(DUID::TDU),
m_lastIMBE(nullptr),
@ -1449,7 +1465,9 @@ Voice::Voice(Control* p25, bool debug, bool verbose) :
m_netLDU2 = new uint8_t[9U * 25U];
::memset(m_netLDU1, 0x00U, 9U * 25U);
resetWithNullAudio(m_netLDU1, false);
::memset(m_netLDU2, 0x00U, 9U * 25U);
resetWithNullAudio(m_netLDU2, false);
m_lastIMBE = new uint8_t[RAW_IMBE_LENGTH_BYTES];
::memcpy(m_lastIMBE, NULL_IMBE, RAW_IMBE_LENGTH_BYTES);
@ -1556,8 +1574,8 @@ void Voice::writeNet_TDU()
if (m_p25->m_network != nullptr)
m_p25->m_network->resetP25();
::memset(m_netLDU1, 0x00U, 9U * 25U);
::memset(m_netLDU2, 0x00U, 9U * 25U);
resetWithNullAudio(m_netLDU1, false);
resetWithNullAudio(m_netLDU2, false);
m_p25->m_netTimeout.stop();
m_p25->m_networkWatchdog.stop();
@ -1580,9 +1598,10 @@ void Voice::checkNet_LDU1()
return;
// check for an unflushed LDU1
if (m_netLDU1[10U] != 0x00U || m_netLDU1[26U] != 0x00U || m_netLDU1[55U] != 0x00U ||
if ((m_netLDU1[10U] != 0x00U || m_netLDU1[26U] != 0x00U || m_netLDU1[55U] != 0x00U ||
m_netLDU1[80U] != 0x00U || m_netLDU1[105U] != 0x00U || m_netLDU1[130U] != 0x00U ||
m_netLDU1[155U] != 0x00U || m_netLDU1[180U] != 0x00U || m_netLDU1[204U] != 0x00U)
m_netLDU1[155U] != 0x00U || m_netLDU1[180U] != 0x00U || m_netLDU1[204U] != 0x00U) &&
m_gotNetLDU1)
writeNet_LDU1();
}
@ -1711,33 +1730,35 @@ void Voice::writeNet_LDU1()
(m_netLC.getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag
(m_netLC.getPriority() & 0x07U); // Priority
if (!m_p25->m_control->writeRF_TSDU_Grant(srcId, dstId, serviceOptions, group, true)) {
LogError(LOG_NET, P25_HDU_STR " call rejected, network call not granted, dstId = %u", dstId);
if (!m_p25->m_affiliations.isGranted(dstId)) {
if (!m_p25->m_control->writeRF_TSDU_Grant(srcId, dstId, serviceOptions, group, true)) {
LogError(LOG_NET, P25_HDU_STR " call rejected, network call not granted, dstId = %u", dstId);
if ((!m_p25->m_networkWatchdog.isRunning() || m_p25->m_networkWatchdog.hasExpired()) &&
m_p25->m_netLastDstId != 0U) {
if (m_p25->m_network != nullptr)
m_p25->m_network->resetP25();
if ((!m_p25->m_networkWatchdog.isRunning() || m_p25->m_networkWatchdog.hasExpired()) &&
m_p25->m_netLastDstId != 0U) {
if (m_p25->m_network != nullptr)
m_p25->m_network->resetP25();
::memset(m_netLDU1, 0x00U, 9U * 25U);
::memset(m_netLDU2, 0x00U, 9U * 25U);
resetWithNullAudio(m_netLDU1, false);
resetWithNullAudio(m_netLDU2, false);
m_p25->m_netTimeout.stop();
m_p25->m_networkWatchdog.stop();
m_p25->m_netTimeout.stop();
m_p25->m_networkWatchdog.stop();
m_netLC = lc::LC();
m_netLastLDU1 = lc::LC();
m_netLastFrameType = FrameType::DATA_UNIT;
m_netLC = lc::LC();
m_netLastLDU1 = lc::LC();
m_netLastFrameType = FrameType::DATA_UNIT;
m_p25->m_netState = RS_NET_IDLE;
m_p25->m_netLastDstId = 0U;
m_p25->m_netLastSrcId = 0U;
m_p25->m_netState = RS_NET_IDLE;
m_p25->m_netLastDstId = 0U;
m_p25->m_netLastSrcId = 0U;
if (m_p25->m_rfState == RS_RF_REJECTED) {
m_p25->m_rfState = RS_RF_LISTENING;
}
if (m_p25->m_rfState == RS_RF_REJECTED) {
m_p25->m_rfState = RS_RF_LISTENING;
}
return;
return;
}
}
}
@ -1933,7 +1954,8 @@ void Voice::writeNet_LDU1()
sysId, netId);
}
::memset(m_netLDU1, 0x00U, 9U * 25U);
resetWithNullAudio(m_netLDU1, m_netLC.getAlgId() != P25DEF::ALGO_UNENCRYPT);
m_gotNetLDU1 = false;
m_netFrames += 9U;
}
@ -1946,10 +1968,12 @@ void Voice::checkNet_LDU2()
return;
// check for an unflushed LDU2
if (m_netLDU2[10U] != 0x00U || m_netLDU2[26U] != 0x00U || m_netLDU2[55U] != 0x00U ||
if ((m_netLDU2[10U] != 0x00U || m_netLDU2[26U] != 0x00U || m_netLDU2[55U] != 0x00U ||
m_netLDU2[80U] != 0x00U || m_netLDU2[105U] != 0x00U || m_netLDU2[130U] != 0x00U ||
m_netLDU2[155U] != 0x00U || m_netLDU2[180U] != 0x00U || m_netLDU2[204U] != 0x00U)
m_netLDU2[155U] != 0x00U || m_netLDU2[180U] != 0x00U || m_netLDU2[204U] != 0x00U) &&
m_gotNetLDU2) {
writeNet_LDU2();
}
}
/* Helper to write a network P25 LDU2 packet. */
@ -2022,14 +2046,15 @@ void Voice::writeNet_LDU2()
LogMessage(LOG_NET, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X", m_netLC.getAlgId(), m_netLC.getKId());
}
::memset(m_netLDU2, 0x00U, 9U * 25U);
resetWithNullAudio(m_netLDU2, m_netLC.getAlgId() != P25DEF::ALGO_UNENCRYPT);
m_gotNetLDU2 = false;
m_netFrames += 9U;
}
/* Helper to insert IMBE silence frames for missing audio. */
void Voice::insertMissingAudio(uint8_t *data)
void Voice::insertMissingAudio(uint8_t* data)
{
if (data[10U] == 0x00U) {
::memcpy(data + 10U, m_lastIMBE, 11U);
@ -2186,6 +2211,44 @@ void Voice::insertEncryptedNullAudio(uint8_t *data)
}
}
/* Helper to reset IMBE buffer with null frames. */
void Voice::resetWithNullAudio(uint8_t* data, bool encrypted)
{
if (data == nullptr)
return;
// clear buffer for next sequence
::memset(data, 0x00U, 9U * 25U);
// fill with null
if (!encrypted) {
::memcpy(data + 10U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 26U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 55U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 80U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 105U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 130U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 155U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 180U, P25DEF::NULL_IMBE, 11U);
::memcpy(data + 204U, P25DEF::NULL_IMBE, 11U);
} else {
::memcpy(data + 10U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 26U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 55U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 80U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 105U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 130U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 155U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 180U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
::memcpy(data + 204U, P25DEF::ENCRYPTED_NULL_IMBE, 11U);
}
}
/* Given the last MI, generate the next MI using LFSR. */
void Voice::getNextMI(uint8_t lastMI[9U], uint8_t nextMI[9U])

@ -9,7 +9,7 @@
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL
*
*/
/**
@ -111,7 +111,9 @@ namespace p25
data::LowSpeedData m_netLSD;
dfsi::LC m_dfsiLC;
bool m_gotNetLDU1;
uint8_t* m_netLDU1;
bool m_gotNetLDU2;
uint8_t* m_netLDU2;
defines::DUID::E m_lastDUID;
@ -192,6 +194,14 @@ namespace p25
* @param data Buffer containing frame data.
*/
void insertEncryptedNullAudio(uint8_t* data);
/**
* @brief Helper to reset IMBE buffer with null frames.
* @param data Buffer containing frame data.
* @param encrypted Flag indicating whether or not the data is encrypted.
*/
void resetWithNullAudio(uint8_t* data, bool encrypted);
/**
* @brief Given the last MI, generate the next MI using LFSR.
* @param lastMI Last MI received.

Loading…
Cancel
Save

Powered by TurnKey Linux.