implement CAC long puncturing for NXDN; correct issues with NXDN site information data;

pull/61/head
Bryan Biedenkapp 2 years ago
parent 5fde17f879
commit 51bd9e749d

@ -135,7 +135,7 @@ namespace nxdn
CAC_INBOUND_LONG = 1U, // Common Access Channel - Inbound Long CAC_INBOUND_LONG = 1U, // Common Access Channel - Inbound Long
CAC_INBOUND_SHORT = 3U, // Common Access Channel - Inbound Short CAC_INBOUND_SHORT = 3U, // Common Access Channel - Inbound Short
// Slow Associated Control Channel / User Data Channel // Slow Associated Control Channel / User Specific Channel
USC_SACCH_NS = 0U, // USC_SACCH_NS = 0U, //
USC_UDCH = 1U, // USC_UDCH = 1U, //
USC_SACCH_SS = 2U, // USC_SACCH_SS = 2U, //

@ -30,7 +30,8 @@ namespace nxdn
m_locId(1U), m_locId(1U),
m_channelId(1U), m_channelId(1U),
m_channelNo(1U), m_channelNo(1U),
m_serviceClass(defines::SiteInformation1::VOICE_CALL_SVC | defines::SiteInformation1::DATA_CALL_SVC), m_siteInfo1(defines::SiteInformation1::VOICE_CALL_SVC | defines::SiteInformation1::DATA_CALL_SVC),
m_siteInfo2(0U),
m_isAdjSite(false), m_isAdjSite(false),
m_callsign("CHANGEME"), m_callsign("CHANGEME"),
m_requireReg(false), m_requireReg(false),
@ -42,13 +43,15 @@ namespace nxdn
/// <param name="locId">NXDN Location ID.</param> /// <param name="locId">NXDN Location ID.</param>
/// <param name="channelId">Channel ID.</param> /// <param name="channelId">Channel ID.</param>
/// <param name="channelNo">Channel Number.</param> /// <param name="channelNo">Channel Number.</param>
/// <param name="serviceClass">Service class.</param> /// <param name="siteInfo1">Site Information 1.</param>
/// <param name="siteInfo2">Site Information 2.</param>
/// <param name="requireReg"></param> /// <param name="requireReg"></param>
SiteData(uint32_t locId, uint8_t channelId, uint32_t channelNo, uint8_t serviceClass, bool requireReq) : SiteData(uint32_t locId, uint8_t channelId, uint32_t channelNo, uint8_t siteInfo1, uint8_t siteInfo2, bool requireReq) :
m_locId(locId), m_locId(locId),
m_channelId(channelId), m_channelId(channelId),
m_channelNo(channelNo), m_channelNo(channelNo),
m_serviceClass(defines::SiteInformation1::VOICE_CALL_SVC | defines::SiteInformation1::DATA_CALL_SVC), m_siteInfo1(defines::SiteInformation1::VOICE_CALL_SVC | defines::SiteInformation1::DATA_CALL_SVC),
m_siteInfo2(0U),
m_isAdjSite(false), m_isAdjSite(false),
m_callsign("CHANGEME"), m_callsign("CHANGEME"),
m_requireReg(requireReq), m_requireReg(requireReq),
@ -69,7 +72,8 @@ namespace nxdn
m_channelNo = 1023U; m_channelNo = 1023U;
} }
m_serviceClass = serviceClass; m_siteInfo1 = siteInfo1;
m_siteInfo2 = siteInfo2;
} }
/// <summary>Helper to set the site callsign.</summary> /// <summary>Helper to set the site callsign.</summary>
@ -90,8 +94,9 @@ namespace nxdn
/// <param name="locId">NXDN Location ID.</param> /// <param name="locId">NXDN Location ID.</param>
/// <param name="channelId">Channel ID.</param> /// <param name="channelId">Channel ID.</param>
/// <param name="channelNo">Channel Number.</param> /// <param name="channelNo">Channel Number.</param>
/// <param name="serviceClass">Service class.</param> /// <param name="siteInfo1">Site Information 1.</param>
void setAdjSite(uint32_t locId, uint8_t rfssId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, uint8_t serviceClass) /// <param name="siteInfo2">Site Information 2.</param>
void setAdjSite(uint32_t locId, uint8_t rfssId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, uint8_t siteInfo1, uint8_t siteInfo2)
{ {
if (m_locId > 0xFFFFFFU) if (m_locId > 0xFFFFFFU)
m_locId = 0xFFFFFFU; m_locId = 0xFFFFFFU;
@ -113,7 +118,8 @@ namespace nxdn
m_channelId = channelId; m_channelId = channelId;
m_channelNo = channelNo; m_channelNo = channelNo;
m_serviceClass = serviceClass; m_siteInfo1 = siteInfo1;
m_siteInfo2 = siteInfo2;
m_isAdjSite = true; m_isAdjSite = true;
@ -132,7 +138,8 @@ namespace nxdn
m_channelId = data.m_channelId; m_channelId = data.m_channelId;
m_channelNo = data.m_channelNo; m_channelNo = data.m_channelNo;
m_serviceClass = data.m_serviceClass; m_siteInfo1 = data.m_siteInfo1;
m_siteInfo2 = data.m_siteInfo2;
m_isAdjSite = data.m_isAdjSite; m_isAdjSite = data.m_isAdjSite;
@ -153,8 +160,10 @@ namespace nxdn
__READONLY_PROPERTY_PLAIN(uint8_t, channelId); __READONLY_PROPERTY_PLAIN(uint8_t, channelId);
/// <summary>Channel number.</summary> /// <summary>Channel number.</summary>
__READONLY_PROPERTY_PLAIN(uint32_t, channelNo); __READONLY_PROPERTY_PLAIN(uint32_t, channelNo);
/// <summary>Service class.</summary> /// <summary>Site Information 1.</summary>
__READONLY_PROPERTY_PLAIN(uint8_t, serviceClass); __READONLY_PROPERTY_PLAIN(uint8_t, siteInfo1);
/// <summary>Site Information 2.</summary>
__READONLY_PROPERTY_PLAIN(uint8_t, siteInfo2);
/// <summary>Flag indicating whether this site data is for an adjacent site.</summary> /// <summary>Flag indicating whether this site data is for an adjacent site.</summary>
__READONLY_PROPERTY_PLAIN(bool, isAdjSite); __READONLY_PROPERTY_PLAIN(bool, isAdjSite);
/// <summary>Callsign.</summary> /// <summary>Callsign.</summary>

@ -164,8 +164,9 @@ CAC& CAC::operator=(const CAC& data)
/// Decode a common access channel. /// Decode a common access channel.
/// </summary> /// </summary>
/// <param name="data"></param> /// <param name="data"></param>
/// <param name="longInbound"></param>
/// <returns>True, if CAC was decoded, otherwise false.</returns> /// <returns>True, if CAC was decoded, otherwise false.</returns>
bool CAC::decode(const uint8_t* data) bool CAC::decode(const uint8_t* data, bool longInbound)
{ {
assert(data != nullptr); assert(data != nullptr);
@ -183,50 +184,113 @@ bool CAC::decode(const uint8_t* data)
Utils::dump(2U, "CAC::decode(), CAC Raw", buffer, NXDN_CAC_IN_FEC_LENGTH_BYTES); Utils::dump(2U, "CAC::decode(), CAC Raw", buffer, NXDN_CAC_IN_FEC_LENGTH_BYTES);
#endif #endif
// TODO TODO -- Long CAC Puncturing if (longInbound) {
// depuncture
uint8_t puncture[324U];
::memset(puncture, 0x00U, 324U);
// decode convolution uint32_t n = 0U, index = 0U;
edac::Convolution conv; for (uint32_t i = 0U; i < NXDN_CAC_IN_FEC_LENGTH_BITS; i++) {
conv.start(); if (n == PUNCTURE_LIST_LONG_IN[index]) {
puncture[n++] = 1U;
index++;
}
bool b = READ_BIT(buffer, i);
puncture[n++] = b ? 2U : 0U;
}
for (uint32_t i = 0U; i < 8U; i++) {
puncture[n++] = 0U;
}
// decode convolution
edac::Convolution conv;
conv.start();
n = 0U;
for (uint32_t i = 0U; i < (NXDN_CAC_LONG_CRC_LENGTH_BITS + 4U); i++) {
uint8_t s0 = puncture[n++];
uint8_t s1 = puncture[n++];
if (!conv.decode(s0, s1)) {
LogError(LOG_NXDN, "CAC::decode(), failed to decode convolution");
return false;
}
}
conv.chainback(m_data, NXDN_CAC_LONG_CRC_LENGTH_BITS);
uint32_t n = 0U; #if DEBUG_NXDN_CAC
for (uint32_t i = 0U; i < (NXDN_CAC_SHORT_CRC_LENGTH_BITS + 4U); i++) { Utils::dump(2U, "Decoded CAC", m_data, (NXDN_CAC_LONG_CRC_LENGTH_BITS / 8U) + 1U);
uint8_t s0 = buffer[n++]; #endif
uint8_t s1 = buffer[n++];
if (!conv.decode(s0, s1)) { // check CRC-16
LogError(LOG_NXDN, "CAC::decode(), failed to decode convolution"); bool ret = edac::CRC::checkCRC16(m_data, NXDN_CAC_LONG_LENGTH_BITS);
if (!ret) {
LogError(LOG_NXDN, "CAC::decode(), failed CRC-6 check");
return false; return false;
} }
// store recieved CRC-16
uint8_t crc[2U];
::memset(crc, 0x00U, 2U);
m_ran = m_data[0U] & 0x3FU;
m_structure = (ChStructure::E)((m_data[0U] >> 6) & 0x03U);
uint32_t offset = NXDN_CAC_LONG_CRC_LENGTH_BITS - 20U;
for (uint32_t i = 0U; i < 16U; i++, offset++) {
bool b = READ_BIT(m_data, offset);
WRITE_BIT(crc, i, b);
}
m_rxCRC = (crc[0U] << 8) | (crc[1U] << 0);
} }
else {
// decode convolution
edac::Convolution conv;
conv.start();
uint32_t n = 0U;
for (uint32_t i = 0U; i < (NXDN_CAC_SHORT_CRC_LENGTH_BITS + 4U); i++) {
uint8_t s0 = buffer[n++];
uint8_t s1 = buffer[n++];
if (!conv.decode(s0, s1)) {
LogError(LOG_NXDN, "CAC::decode(), failed to decode convolution");
return false;
}
}
conv.chainback(m_data, NXDN_CAC_SHORT_CRC_LENGTH_BITS); conv.chainback(m_data, NXDN_CAC_SHORT_CRC_LENGTH_BITS);
#if DEBUG_NXDN_CAC #if DEBUG_NXDN_CAC
Utils::dump(2U, "Decoded CAC", m_data, (NXDN_CAC_SHORT_CRC_LENGTH_BITS / 8U) + 1U); Utils::dump(2U, "Decoded CAC", m_data, (NXDN_CAC_SHORT_CRC_LENGTH_BITS / 8U) + 1U);
#endif #endif
// check CRC-16 // check CRC-16
bool ret = edac::CRC::checkCRC16(m_data, NXDN_CAC_SHORT_LENGTH_BITS); bool ret = edac::CRC::checkCRC16(m_data, NXDN_CAC_SHORT_LENGTH_BITS);
if (!ret) { if (!ret) {
LogError(LOG_NXDN, "CAC::decode(), failed CRC-6 check"); LogError(LOG_NXDN, "CAC::decode(), failed CRC-6 check");
return false; return false;
} }
// store recieved CRC-16 // store recieved CRC-16
uint8_t crc[2U]; uint8_t crc[2U];
::memset(crc, 0x00U, 2U); ::memset(crc, 0x00U, 2U);
m_ran = m_data[0U] & 0x3FU; m_ran = m_data[0U] & 0x3FU;
m_structure = (ChStructure::E)((m_data[0U] >> 6) & 0x03U); m_structure = (ChStructure::E)((m_data[0U] >> 6) & 0x03U);
uint32_t offset = NXDN_CAC_SHORT_CRC_LENGTH_BITS - 20U; uint32_t offset = NXDN_CAC_SHORT_CRC_LENGTH_BITS - 20U;
for (uint32_t i = 0U; i < 16U; i++, offset++) { for (uint32_t i = 0U; i < 16U; i++, offset++) {
bool b = READ_BIT(m_data, offset); bool b = READ_BIT(m_data, offset);
WRITE_BIT(crc, i, b); WRITE_BIT(crc, i, b);
} }
m_rxCRC = (crc[0U] << 8) | (crc[1U] << 0); m_rxCRC = (crc[0U] << 8) | (crc[1U] << 0);
}
#if DEBUG_NXDN_CAC #if DEBUG_NXDN_CAC
Utils::dump(2U, "Raw CAC Buffer", m_data, NXDN_CAC_FEC_LENGTH_BYTES); Utils::dump(2U, "Raw CAC Buffer", m_data, NXDN_CAC_FEC_LENGTH_BYTES);

@ -38,7 +38,7 @@ namespace nxdn
CAC& operator=(const CAC& data); CAC& operator=(const CAC& data);
/// <summary>Decode a common access channel.</summary> /// <summary>Decode a common access channel.</summary>
bool decode(const uint8_t* data); bool decode(const uint8_t* data, bool longInbound = false);
/// <summary>Encode a common access channel.</summary> /// <summary>Encode a common access channel.</summary>
void encode(uint8_t* data) const; void encode(uint8_t* data) const;

@ -63,6 +63,10 @@ void MESSAGE_TYPE_SITE_INFO::encode(uint8_t* data, uint32_t length, uint32_t off
{ {
assert(data != nullptr); assert(data != nullptr);
uint8_t siteInfo2 = m_siteData.siteInfo2();
if ((siteInfo2 & SiteInformation2::IP_NETWORK) == SiteInformation2::IP_NETWORK)
siteInfo2 &= ~SiteInformation2::IP_NETWORK; // clear the IP_NETWORK bit -- that will be provided by netActive()
uint8_t rcch[NXDN_RCCH_LC_LENGTH_BYTES + 4U]; uint8_t rcch[NXDN_RCCH_LC_LENGTH_BYTES + 4U];
::memset(rcch, 0x00U, NXDN_RCCH_LC_LENGTH_BYTES + 4U); ::memset(rcch, 0x00U, NXDN_RCCH_LC_LENGTH_BYTES + 4U);
@ -76,16 +80,19 @@ void MESSAGE_TYPE_SITE_INFO::encode(uint8_t* data, uint32_t length, uint32_t off
((m_ccchMultiCnt & 0x07U) << 4) + // ... - Number of Multipurpose Frames ((m_ccchMultiCnt & 0x07U) << 4) + // ... - Number of Multipurpose Frames
((m_rcchIterateCnt & 0x0FU) << 0); // ... - Number of Iteration ((m_rcchIterateCnt & 0x0FU) << 0); // ... - Number of Iteration
rcch[6U] = m_siteData.serviceClass(); // Service Information rcch[6U] = m_siteData.siteInfo1(); // Site Information 1
rcch[7U] = (m_siteData.netActive() ? SiteInformation2::IP_NETWORK : 0x00U); // ... rcch[7U] = (m_siteData.netActive() ? SiteInformation2::IP_NETWORK : 0x00U) + // Site Information 2
siteInfo2;
// bryanb: this is currently fixed -- maybe dynamic in the future // bryanb: this is currently fixed -- maybe dynamic in the future
rcch[8U] = 0U; // Restriction Information - No access restriction / No cycle restriction rcch[8U] = 0U; // Restriction Information - No access restriction / No cycle restriction
rcch[9U] = 0U; // ... - No group restriction / No Location Registration Restriction rcch[9U] = 0U; // ... - No group restriction / No Location Registration Restriction
rcch[10U] = (!m_siteData.netActive() ? 0x01U : 0x00U); // ... - No group ratio restriction / No delay time extension / ISO //rcch[10U] = (!m_siteData.netActive() ? 0x01U : 0x00U); // ... - No group ratio restriction / No delay time extension / ISO
rcch[10U] = 0U;
// bryanb: this is currently fixed -- maybe dynamic in the future // bryanb: this is currently fixed -- maybe dynamic in the future
rcch[11U] = ChAccessBase::FREQ_SYS_DEFINED << 2; // Channel Access Information - Channel Version / Sys Defined Step / Sys Defined Base Freq //rcch[11U] = ChAccessBase::FREQ_SYS_DEFINED << 2; // Channel Access Information - Channel Version / Sys Defined Step / Sys Defined Base Freq
rcch[11U] = 0U;
rcch[14U] = 1U; // Version rcch[14U] = 1U; // Version

@ -58,14 +58,19 @@ void MESSAGE_TYPE_SRV_INFO::encode(uint8_t* data, uint32_t length, uint32_t offs
{ {
assert(data != nullptr); assert(data != nullptr);
uint8_t siteInfo2 = m_siteData.siteInfo2();
if ((siteInfo2 & SiteInformation2::IP_NETWORK) == SiteInformation2::IP_NETWORK)
siteInfo2 &= ~SiteInformation2::IP_NETWORK; // clear the IP_NETWORK bit -- that will be provided by netActive()
uint8_t rcch[NXDN_RCCH_LC_LENGTH_BYTES + 4U]; uint8_t rcch[NXDN_RCCH_LC_LENGTH_BYTES + 4U];
::memset(rcch, 0x00U, NXDN_RCCH_LC_LENGTH_BYTES + 4U); ::memset(rcch, 0x00U, NXDN_RCCH_LC_LENGTH_BYTES + 4U);
rcch[1U] = (m_siteData.locId() >> 16) & 0xFFU; // Location ID rcch[1U] = (m_siteData.locId() >> 16) & 0xFFU; // Location ID
rcch[2U] = (m_siteData.locId() >> 8) & 0xFFU; // ... rcch[2U] = (m_siteData.locId() >> 8) & 0xFFU; // ...
rcch[3U] = (m_siteData.locId() >> 0) & 0xFFU; // ... rcch[3U] = (m_siteData.locId() >> 0) & 0xFFU; // ...
rcch[4U] = m_siteData.serviceClass(); // Service Information rcch[4U] = m_siteData.siteInfo1(); // Site Information 1
rcch[5U] = (m_siteData.netActive() ? SiteInformation2::IP_NETWORK : 0x00U); // ... rcch[5U] = (m_siteData.netActive() ? SiteInformation2::IP_NETWORK : 0x00U) + // Site Information 2
siteInfo2;
// bryanb: this is currently fixed -- maybe dynamic in the future // bryanb: this is currently fixed -- maybe dynamic in the future
rcch[8U] = 0U; // Restriction Information - No access restriction / No cycle restriction rcch[8U] = 0U; // Restriction Information - No access restriction / No cycle restriction

@ -259,11 +259,12 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw
} }
/* /*
** CC Service Class ** CC Site Info
*/ */
uint8_t serviceClass = SiteInformation1::VOICE_CALL_SVC | SiteInformation1::DATA_CALL_SVC; uint8_t siteInfo1 = SiteInformation1::VOICE_CALL_SVC | SiteInformation1::DATA_CALL_SVC;
uint8_t siteInfo2 = SiteInformation2::SHORT_DATA_CALL_SVC;
if (m_enableControl) { if (m_enableControl) {
serviceClass |= SiteInformation1::GRP_REG_SVC; siteInfo1 |= SiteInformation1::LOC_REG_SVC;//| SiteInformation1::GRP_REG_SVC;
} }
/* /*
@ -274,7 +275,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw
locId = (locId << 10) + (sysId & 0x3FFU); locId = (locId << 10) + (sysId & 0x3FFU);
locId = (locId << 12) + (siteId & 0xFFFU); locId = (locId << 12) + (siteId & 0xFFFU);
m_siteData = SiteData(locId, channelId, (channelNo & 0x3FF), serviceClass, false); m_siteData = SiteData(locId, channelId, (channelNo & 0x3FF), siteInfo1, siteInfo2, false);
m_siteData.setCallsign(cwCallsign); m_siteData.setCallsign(cwCallsign);
m_controlChData = controlChData; m_controlChData = controlChData;
@ -492,6 +493,14 @@ bool Control::processFrame(uint8_t* data, uint32_t len)
ret = m_voice->process(fct, option, data, len); ret = m_voice->process(fct, option, data, len);
break; break;
} }
if (m_ccRunning) {
m_ccHalted = false;
}
} else {
if (m_ccRunning) {
m_ccHalted = false;
}
} }
return ret; return ret;

@ -137,7 +137,7 @@ bool ControlSignaling::process(FuncChannelType::E fct, ChOption::E option, uint8
assert(data != nullptr); assert(data != nullptr);
channel::CAC cac; channel::CAC cac;
bool validCAC = cac.decode(data + 2U); bool validCAC = cac.decode(data + 2U, (fct == FuncChannelType::CAC_INBOUND_LONG));
if (m_nxdn->m_rfState == RS_RF_LISTENING && !validCAC) if (m_nxdn->m_rfState == RS_RF_LISTENING && !validCAC)
return false; return false;

Loading…
Cancel
Save

Powered by TurnKey Linux.