rework the NXDN "Layer 3" class;

pull/12/head
Bryan Biedenkapp 4 years ago
parent 820753a815
commit 4cef6e5aea

@ -68,7 +68,8 @@ HOST_OBJECTS = \
nxdn/channel/LICH.o \ nxdn/channel/LICH.o \
nxdn/channel/SACCH.o \ nxdn/channel/SACCH.o \
nxdn/channel/UDCH.o \ nxdn/channel/UDCH.o \
nxdn/data/Layer3.o \ nxdn/lc/LC.o \
nxdn/lc/PacketInformation.o \
nxdn/packet/Data.o \ nxdn/packet/Data.o \
nxdn/packet/Voice.o \ nxdn/packet/Voice.o \
nxdn/Audio.o \ nxdn/Audio.o \

@ -258,10 +258,10 @@ uint8_t* BaseNetwork::readP25(bool& ret, p25::lc::LC& control, p25::data::LowSpe
/// Reads NXDN frame data from the NXDN ring buffer. /// Reads NXDN frame data from the NXDN ring buffer.
/// </summary> /// </summary>
/// <param name="ret"></param> /// <param name="ret"></param>
/// <param name="layer3"></param> /// <param name="lc"></param>
/// <param name="len"></param> /// <param name="len"></param>
/// <returns></returns> /// <returns></returns>
uint8_t* BaseNetwork::readNXDN(bool& ret, nxdn::data::Layer3& layer3, uint32_t& len) uint8_t* BaseNetwork::readNXDN(bool& ret, nxdn::lc::LC& lc, uint32_t& len)
{ {
if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING) { if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING) {
ret = false; ret = false;
@ -283,12 +283,12 @@ uint8_t* BaseNetwork::readNXDN(bool& ret, nxdn::data::Layer3& layer3, uint32_t&
uint32_t dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0); uint32_t dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0);
layer3.setMessageType(messageType); lc.setMessageType(messageType);
layer3.setSrcId((uint16_t)srcId & 0xFFFFU); lc.setSrcId((uint16_t)srcId & 0xFFFFU);
layer3.setDstId((uint16_t)dstId & 0xFFFFU); lc.setDstId((uint16_t)dstId & 0xFFFFU);
bool group = (m_buffer[15U] & 0x40U) == 0x40U ? false : true; bool group = (m_buffer[15U] & 0x40U) == 0x40U ? false : true;
layer3.setGroup(group); lc.setGroup(group);
uint8_t* data = NULL; uint8_t* data = NULL;
len = m_buffer[23U]; len = m_buffer[23U];
@ -483,11 +483,11 @@ bool BaseNetwork::writeP25PDU(const p25::data::DataHeader& header, const p25::da
/// <summary> /// <summary>
/// Writes NXDN frame data to the network. /// Writes NXDN frame data to the network.
/// </summary> /// </summary>
/// <param name="layer3"></param> /// <param name="lc"></param>
/// <param name="data"></param> /// <param name="data"></param>
/// <param name="len"></param> /// <param name="len"></param>
/// <returns></returns> /// <returns></returns>
bool BaseNetwork::writeNXDN(const nxdn::data::Layer3& layer3, const uint8_t* data, const uint32_t len) bool BaseNetwork::writeNXDN(const nxdn::lc::LC& lc, const uint8_t* data, const uint32_t len)
{ {
if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING) if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING)
return false; return false;
@ -498,7 +498,7 @@ bool BaseNetwork::writeNXDN(const nxdn::data::Layer3& layer3, const uint8_t* dat
m_streamId[0] = m_nxdnStreamId; m_streamId[0] = m_nxdnStreamId;
return writeNXDN(m_id, m_nxdnStreamId, layer3, data, len); return writeNXDN(m_id, m_nxdnStreamId, lc, data, len);
} }
/// <summary> /// <summary>
@ -1082,11 +1082,11 @@ bool BaseNetwork::writeP25PDU(const uint32_t id, const uint32_t streamId, const
/// </summary> /// </summary>
/// <param name="id"></param> /// <param name="id"></param>
/// <param name="streamId"></param> /// <param name="streamId"></param>
/// <param name="layer3"></param> /// <param name="lc"></param>
/// <param name="data"></param> /// <param name="data"></param>
/// <param name="len"></param> /// <param name="len"></param>
/// <returns></returns> /// <returns></returns>
bool BaseNetwork::writeNXDN(const uint32_t id, const uint32_t streamId, const nxdn::data::Layer3& layer3, const uint8_t* data, const uint32_t len) bool BaseNetwork::writeNXDN(const uint32_t id, const uint32_t streamId, const nxdn::lc::LC& lc, const uint8_t* data, const uint32_t len)
{ {
if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING) if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING)
return false; return false;
@ -1098,15 +1098,15 @@ bool BaseNetwork::writeNXDN(const uint32_t id, const uint32_t streamId, const nx
::memcpy(buffer + 0U, TAG_NXDN_DATA, 4U); ::memcpy(buffer + 0U, TAG_NXDN_DATA, 4U);
buffer[4U] = layer3.getMessageType(); // Message Type buffer[4U] = lc.getMessageType(); // Message Type
uint32_t srcId = layer3.getSrcId(); // Source Address uint32_t srcId = lc.getSrcId(); // Source Address
__SET_UINT16(srcId, buffer, 5U); __SET_UINT16(srcId, buffer, 5U);
uint32_t dstId = layer3.getDstId(); // Target Address uint32_t dstId = lc.getDstId(); // Target Address
__SET_UINT16(dstId, buffer, 8U); __SET_UINT16(dstId, buffer, 8U);
buffer[15U] |= layer3.getGroup() ? 0x00U : 0x40U; // Group buffer[15U] |= lc.getGroup() ? 0x00U : 0x40U; // Group
__SET_UINT32(streamId, buffer, 16U); // Stream ID __SET_UINT32(streamId, buffer, 16U); // Stream ID

@ -43,7 +43,7 @@
#include "p25/lc/TSBK.h" #include "p25/lc/TSBK.h"
#include "p25/lc/TDULC.h" #include "p25/lc/TDULC.h"
#include "p25/Audio.h" #include "p25/Audio.h"
#include "nxdn/data/Layer3.h" #include "nxdn/lc/LC.h"
#include "network/UDPSocket.h" #include "network/UDPSocket.h"
#include "RingBuffer.h" #include "RingBuffer.h"
#include "Timer.h" #include "Timer.h"
@ -141,7 +141,7 @@ namespace network
/// <summary>Reads P25 frame data from the P25 ring buffer.</summary> /// <summary>Reads P25 frame data from the P25 ring buffer.</summary>
virtual uint8_t* readP25(bool& ret, p25::lc::LC& control, p25::data::LowSpeedData& lsd, uint8_t& duid, uint32_t& len); virtual uint8_t* readP25(bool& ret, p25::lc::LC& control, p25::data::LowSpeedData& lsd, uint8_t& duid, uint32_t& len);
/// <summary>Reads NXDN frame data from the NXDN ring buffer.</summary> /// <summary>Reads NXDN frame data from the NXDN ring buffer.</summary>
virtual uint8_t* readNXDN(bool& ret, nxdn::data::Layer3& layer3, uint32_t& len); virtual uint8_t* readNXDN(bool& ret, nxdn::lc::LC& lc, uint32_t& len);
/// <summary>Reads a channel grant request from the network.</summary> /// <summary>Reads a channel grant request from the network.</summary>
virtual bool readGrantRsp(bool& grp, uint32_t& srcId, uint32_t& dstId, uint32_t& grpVchNo); virtual bool readGrantRsp(bool& grp, uint32_t& srcId, uint32_t& dstId, uint32_t& grpVchNo);
@ -161,7 +161,7 @@ namespace network
const uint8_t* data, const uint32_t len); const uint8_t* data, const uint32_t len);
/// <summary>Writes NXDN frame data to the network.</summary> /// <summary>Writes NXDN frame data to the network.</summary>
virtual bool writeNXDN(const nxdn::data::Layer3& layer3, const uint8_t* data, const uint32_t len); virtual bool writeNXDN(const nxdn::lc::LC& lc, const uint8_t* data, const uint32_t len);
/// <summary>Writes a channel grant request to the network.</summary> /// <summary>Writes a channel grant request to the network.</summary>
virtual bool writeGrantReq(const bool grp, const uint32_t srcId, const uint32_t dstId); virtual bool writeGrantReq(const bool grp, const uint32_t srcId, const uint32_t dstId);
@ -240,7 +240,7 @@ namespace network
bool writeP25PDU(const uint32_t id, const uint32_t streamId, const p25::data::DataHeader& header, const p25::data::DataHeader& secHeader, const uint8_t currentBlock, bool writeP25PDU(const uint32_t id, const uint32_t streamId, const p25::data::DataHeader& header, const p25::data::DataHeader& secHeader, const uint8_t currentBlock,
const uint8_t* data, const uint32_t len); const uint8_t* data, const uint32_t len);
/// <summary>Writes NXDN frame data to the network.</summary> /// <summary>Writes NXDN frame data to the network.</summary>
bool writeNXDN(const uint32_t id, const uint32_t streamId, const nxdn::data::Layer3& layer3, const uint8_t* data, const uint32_t len); bool writeNXDN(const uint32_t id, const uint32_t streamId, const nxdn::lc::LC& layer3, const uint8_t* data, const uint32_t len);
/// <summary>Writes data to the network.</summary> /// <summary>Writes data to the network.</summary>
virtual bool write(const uint8_t* data, uint32_t length); virtual bool write(const uint8_t* data, uint32_t length);

@ -93,8 +93,8 @@ Control::Control(uint32_t ran, uint32_t callHang, uint32_t queueSize, uint32_t t
m_network(network), m_network(network),
m_duplex(duplex), m_duplex(duplex),
m_rfLastLICH(), m_rfLastLICH(),
m_rfLayer3(), m_rfLC(),
m_netLayer3(), m_netLC(),
m_rfMask(0U), m_rfMask(0U),
m_netMask(0U), m_netMask(0U),
m_idenTable(idenTable), m_idenTable(idenTable),
@ -162,12 +162,12 @@ void Control::reset()
m_queue.clear(); m_queue.clear();
m_rfMask = 0x00U; m_rfMask = 0x00U;
m_rfLayer3.reset(); m_rfLC.reset();
m_netState = RS_NET_IDLE; m_netState = RS_NET_IDLE;
m_netMask = 0x00U; m_netMask = 0x00U;
m_netLayer3.reset(); m_netLC.reset();
} }
/// <summary> /// <summary>
@ -251,7 +251,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len)
if (type == modem::TAG_LOST) { if (type == modem::TAG_LOST) {
m_rfState = RS_RF_LISTENING; m_rfState = RS_RF_LISTENING;
m_rfMask = 0x00U; m_rfMask = 0x00U;
m_rfLayer3.reset(); m_rfLC.reset();
return false; return false;
} }
@ -463,11 +463,11 @@ void Control::processNetwork()
if (m_rfState != RS_RF_LISTENING && m_netState == RS_NET_IDLE) if (m_rfState != RS_RF_LISTENING && m_netState == RS_NET_IDLE)
return; return;
data::Layer3 layer3; lc::LC lc;
uint32_t length = 100U; uint32_t length = 100U;
bool ret = false; bool ret = false;
uint8_t* data = m_network->readNXDN(ret, layer3, length); uint8_t* data = m_network->readNXDN(ret, lc, length);
if (!ret) if (!ret)
return; return;
if (length == 0U) if (length == 0U)
@ -496,10 +496,10 @@ void Control::processNetwork()
switch (usc) { switch (usc) {
case NXDN_LICH_USC_UDCH: case NXDN_LICH_USC_UDCH:
ret = m_data->processNetwork(option, layer3, data, length); ret = m_data->processNetwork(option, lc, data, length);
break; break;
default: default:
ret = m_voice->processNetwork(usc, option, layer3, data, length); ret = m_voice->processNetwork(usc, option, lc, data, length);
break; break;
} }
@ -514,7 +514,7 @@ void Control::writeEndRF()
m_rfState = RS_RF_LISTENING; m_rfState = RS_RF_LISTENING;
m_rfMask = 0x00U; m_rfMask = 0x00U;
m_rfLayer3.reset(); m_rfLC.reset();
m_rfTimeout.stop(); m_rfTimeout.stop();
} }
@ -527,7 +527,7 @@ void Control::writeEndNet()
m_netState = RS_NET_IDLE; m_netState = RS_NET_IDLE;
m_netMask = 0x00U; m_netMask = 0x00U;
m_netLayer3.reset(); m_netLC.reset();
m_netTimeout.stop(); m_netTimeout.stop();
m_networkWatchdog.stop(); m_networkWatchdog.stop();

@ -34,7 +34,7 @@
#include "Defines.h" #include "Defines.h"
#include "nxdn/NXDNDefines.h" #include "nxdn/NXDNDefines.h"
#include "nxdn/channel/LICH.h" #include "nxdn/channel/LICH.h"
#include "nxdn/data/Layer3.h" #include "nxdn/lc/LC.h"
#include "nxdn/packet/Voice.h" #include "nxdn/packet/Voice.h"
#include "nxdn/packet/Data.h" #include "nxdn/packet/Data.h"
#include "network/BaseNetwork.h" #include "network/BaseNetwork.h"
@ -111,8 +111,8 @@ namespace nxdn
bool m_duplex; bool m_duplex;
channel::LICH m_rfLastLICH; channel::LICH m_rfLastLICH;
data::Layer3 m_rfLayer3; lc::LC m_rfLC;
data::Layer3 m_netLayer3; lc::LC m_netLC;
uint8_t m_rfMask; uint8_t m_rfMask;
uint8_t m_netMask; uint8_t m_netMask;

@ -91,44 +91,74 @@ namespace nxdn
const uint8_t NXDN_SR_2_4 = 2U; const uint8_t NXDN_SR_2_4 = 2U;
const uint8_t NXDN_SR_1_4 = 3U; const uint8_t NXDN_SR_1_4 = 3U;
const uint8_t SACCH_IDLE[] = { 0x10U, 0x00U, 0x00U };
const uint32_t DEFAULT_SILENCE_THRESHOLD = 14U; const uint32_t DEFAULT_SILENCE_THRESHOLD = 14U;
const uint32_t MAX_NXDN_VOICE_ERRORS = 144U; const uint32_t MAX_NXDN_VOICE_ERRORS = 144U;
const uint32_t MAX_NXDN_VOICE_ERRORS_STEAL = 94U; const uint32_t MAX_NXDN_VOICE_ERRORS_STEAL = 94U;
// Message Types const uint32_t NXDN_MI_LENGTH_BYTES = 8U;
const uint8_t MESSAGE_TYPE_VCALL = 0x01U; const uint32_t NXDN_PCKT_INFO_LENGTH_BYTES = 3U;
const uint8_t MESSAGE_TYPE_VCALL_IV = 0x03U;
const uint8_t MESSAGE_TYPE_DCALL_HDR = 0x09U; const uint8_t NXDN_CIPHER_TYPE_NONE = 0x00U;
const uint8_t MESSAGE_TYPE_DCALL_DATA = 0x0BU;
const uint8_t MESSAGE_TYPE_DCALL_ACK = 0x0CU; const uint8_t DATA_RSP_CLASS_ACK = 0x00U;
const uint8_t MESSAGE_TYPE_TX_REL = 0x08U; const uint8_t DATA_RSP_CLASS_ACK_S = 0x01U;
const uint8_t MESSAGE_TYPE_HEAD_DLY = 0x0FU; const uint8_t DATA_RSP_CLASS_NACK = 0x03U;
const uint8_t MESSAGE_TYPE_SDCALL_REQ_HDR = 0x38U;
const uint8_t MESSAGE_TYPE_SDCALL_REQ_DATA = 0x39U; const uint8_t NXDN_CAUSE_RESOURCE_NOT_AVAIL = 0x05U;
const uint8_t MESSAGE_TYPE_SDCALL_RESP = 0x3BU; const uint8_t NXDN_CAUSE_SVC_UNAVAILABLE = 0x06U;
const uint8_t MESSAGE_TYPE_SDCALL_IV = 0x3AU; const uint8_t NXDN_CAUSE_PROC_ERROR = 0x07U;
const uint8_t MESSAGE_TYPE_STAT_INQ_REQ = 0x30U;
const uint8_t MESSAGE_TYPE_STAT_INQ_RESP = 0x31U; const uint8_t NXDN_CAUSE_MM_NORMAL_1 = 0x01U;
const uint8_t MESSAGE_TYPE_STAT_REQ = 0x32U; const uint8_t NXDN_CAUSE_MM_NORMAL_2 = 0x04U;
const uint8_t MESSAGE_TYPE_STAT_RESP = 0x33U;
const uint8_t MESSAGE_TYPE_REM_CON_REQ = 0x34U; const uint8_t NXDN_CAUSE_VD_NORMAL_1 = 0x01U;
const uint8_t MESSAGE_TYPE_REM_CON_RESP = 0x35U; const uint8_t NXDN_CAUSE_VD_NORMAL_2 = 0x02U;
const uint8_t MESSAGE_TYPE_IDLE = 0x10U; const uint8_t NXDN_CAUSE_VD_QUEUED = 0x03U;
const uint8_t MESSAGE_TYPE_AUTH_INQ_REQ = 0x28U;
const uint8_t MESSAGE_TYPE_AUTH_INQ_RESP = 0x29U; const uint8_t NXDN_CAUSE_SS_NORMAL = 0x00U;
const uint8_t MESSAGE_TYPE_PROP_FORM = 0x3FU; const uint8_t NXDN_CAUSE_SS_NORMAL_1 = 0x01U;
const uint8_t NXDN_CAUSE_SS_NORMAL_2 = 0x02U;
// Voice Call Options
const uint8_t VOICE_CALL_OPTION_HALF_DUPLEX = 0x00U; const uint8_t NXDN_CAUSE_DREQ_NORMAL = 0x01U;
const uint8_t VOICE_CALL_OPTION_DUPLEX = 0x10U;
const uint8_t NXDN_CAUSE_DISC_NORMAL = 0x01U;
// Data Call Options const uint8_t NXDN_CAUSE_DISC_NORMAL_TC = 0x02U;
const uint8_t DATA_CALL_OPTION_HALF_DUPLEX = 0x00U;
const uint8_t DATA_CALL_OPTION_DUPLEX = 0x10U; // Common Message Types
const uint8_t DATA_CALL_OPTION_4800 = 0x00U; const uint8_t MESSAGE_TYPE_IDLE = 0x10U; // IDLE - Idle
const uint8_t DATA_CALL_OPTION_9600 = 0x02U;
// Traffic Channel Message Types
const uint8_t SACCH_IDLE[] = { MESSAGE_TYPE_IDLE, 0x00U, 0x00U }; const uint8_t RTCH_MESSAGE_TYPE_VCALL = 0x01U; // VCALL - Voice Call
const uint8_t RTCH_MESSAGE_TYPE_VCALL_IV = 0x03U; // VCALL_IV - Voice Call Initialization Vector
const uint8_t RTCH_MESSAGE_TYPE_TX_REL_EX = 0x07U; // TX_REL_EX - Transmission Release Extension
const uint8_t RTCH_MESSAGE_TYPE_TX_REL = 0x08U; // TX_REL - Transmission Release
const uint8_t RTCH_MESSAGE_TYPE_DCALL_HDR = 0x09U; // DCALL - Data Call (Header)
const uint8_t RTCH_MESSAGE_TYPE_DCALL_DATA = 0x0BU; // DCALL - Data Call (User Data Format)
const uint8_t RTCH_MESSAGE_TYPE_DCALL_ACK = 0x0CU; // DCALL_ACK - Data Call Acknowledge
const uint8_t RTCH_MESSAGE_TYPE_HEAD_DLY = 0x0FU; // HEAD_DLY - Header Delay
const uint8_t RTCH_MESSAGE_TYPE_SDCALL_REQ_HDR = 0x38U; // SDCALL_REQ - Short Data Call Request (Header)
const uint8_t RTCH_MESSAGE_TYPE_SDCALL_REQ_DATA = 0x39U; // SDCALL_REQ - Short Data Call Request (User Data Format)
const uint8_t RTCH_MESSAGE_TYPE_SDCALL_IV = 0x3AU; // SDCALL_IV - Short Data Call Initialization Vector
const uint8_t RTCH_MESSAGE_TYPE_SDCALL_RESP = 0x3BU; // SDCALL_RESP - Short Data Call Response
// Control Channel Message Types
const uint8_t RCCH_MESSAGE_TYPE_PROP_FORM = 0x3FU; // PROP_FORM - Proprietary Form
// Call Types
const uint8_t CALL_TYPE_BROADCAST = 0x00U;
const uint8_t CALL_TYPE_CONFERENCE = 0x01U;
const uint8_t CALL_TYPE_UNSPECIFIED = 0x02U;
const uint8_t CALL_TYPE_INDIVIDUAL = 0x04U;
const uint8_t CALL_TYPE_INTERCONNECT = 0x06U;
const uint8_t CALL_TYPE_SPEED_DIAL = 0x07U;
// Transmission Mode
const uint8_t TRANSMISSION_MODE_4800 = 0x00U;
const uint8_t TRANSMISSION_MODE_9600 = 0x02U;
const uint8_t TRANSMISSION_MODE_9600_EFR = 0x03U; // should never be used on data calls
} // namespace nxdn } // namespace nxdn
#endif // __NXDN_DEFINES_H__ #endif // __NXDN_DEFINES_H__

@ -69,25 +69,24 @@ const uint32_t PUNCTURE_LIST[] = {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Initializes a copy instance of the FACCH1 class. /// Initializes a new instance of the FACCH1 class.
/// </summary> /// </summary>
/// <param name="data"></param> FACCH1::FACCH1() :
FACCH1::FACCH1(const FACCH1& data) :
m_verbose(false), m_verbose(false),
m_data(NULL) m_data(NULL)
{ {
m_data = new uint8_t[12U]; m_data = new uint8_t[12U];
::memcpy(m_data, data.m_data, 12U);
} }
/// <summary> /// <summary>
/// Initializes a new instance of the FACCH1 class. /// Initializes a copy instance of the FACCH1 class.
/// </summary> /// </summary>
FACCH1::FACCH1() : /// <param name="data"></param>
FACCH1::FACCH1(const FACCH1& data) :
m_verbose(false), m_verbose(false),
m_data(NULL) m_data(NULL)
{ {
m_data = new uint8_t[12U]; copy(data);
} }
/// <summary> /// <summary>
@ -254,3 +253,19 @@ void FACCH1::setData(const uint8_t* data)
::memcpy(m_data, data, 10U); ::memcpy(m_data, data, 10U);
} }
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Internal helper to copy the the class.
/// </summary>
/// <param name="data"></param>
void FACCH1::copy(const FACCH1& data)
{
m_data = new uint8_t[12U];
::memcpy(m_data, data.m_data, 12U);
m_verbose = data.m_verbose;
}

@ -27,8 +27,8 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#if !defined(__NXDN_FACCH1_H__) #if !defined(__NXDN_CHANNEL__FACCH1_H__)
#define __NXDN_FACCH1_H__ #define __NXDN_CHANNEL__FACCH1_H__
#include "Defines.h" #include "Defines.h"
@ -43,10 +43,10 @@ namespace nxdn
class HOST_SW_API FACCH1 { class HOST_SW_API FACCH1 {
public: public:
/// <summary>Initializes a copy instance of the FACCH1 class.</summary>
FACCH1(const FACCH1& data);
/// <summary>Initializes a new instance of the FACCH1 class.</summary> /// <summary>Initializes a new instance of the FACCH1 class.</summary>
FACCH1(); FACCH1();
/// <summary>Initializes a copy instance of the FACCH1 class.</summary>
FACCH1(const FACCH1& data);
/// <summary>Finalizes a instance of the FACCH1 class.</summary> /// <summary>Finalizes a instance of the FACCH1 class.</summary>
~FACCH1(); ~FACCH1();
@ -69,8 +69,11 @@ namespace nxdn
private: private:
uint8_t* m_data; uint8_t* m_data;
/// <summary>Internal helper to copy the class.</summary>
void copy(const FACCH1& data);
}; };
} // namespace channel } // namespace channel
} // namespace nxdn } // namespace nxdn
#endif // __NXDN_FACCH1_H__ #endif // __NXDN_CHANNEL__FACCH1_H__

@ -43,10 +43,9 @@ using namespace nxdn::channel;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Initializes a copy instance of the LICH class. /// Initializes a new instance of the LICH class.
/// </summary> /// </summary>
/// <param name="data"></param> LICH::LICH() :
LICH::LICH(const LICH& data) :
m_rfct(NXDN_LICH_RFCT_RCCH), m_rfct(NXDN_LICH_RFCT_RCCH),
m_fct(NXDN_LICH_USC_SACCH_NS), m_fct(NXDN_LICH_USC_SACCH_NS),
m_option(0U), m_option(0U),
@ -54,25 +53,20 @@ LICH::LICH(const LICH& data) :
m_data(NULL) m_data(NULL)
{ {
m_data = new uint8_t[1U]; m_data = new uint8_t[1U];
m_data[0U] = data.m_data[0U];
m_rfct = (m_data[0U] >> 6) & 0x03U;
m_fct = (m_data[0U] >> 4) & 0x03U;
m_option = (m_data[0U] >> 2) & 0x03U;
m_direction = (m_data[0U] >> 1) & 0x01U;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the LICH class. /// Initializes a copy instance of the LICH class.
/// </summary> /// </summary>
LICH::LICH() : /// <param name="data"></param>
LICH::LICH(const LICH& data) :
m_rfct(NXDN_LICH_RFCT_RCCH), m_rfct(NXDN_LICH_RFCT_RCCH),
m_fct(NXDN_LICH_USC_SACCH_NS), m_fct(NXDN_LICH_USC_SACCH_NS),
m_option(0U), m_option(0U),
m_direction(NXDN_LICH_DIRECTION_OUTBOUND), m_direction(NXDN_LICH_DIRECTION_OUTBOUND),
m_data(NULL) m_data(NULL)
{ {
m_data = new uint8_t[1U]; copy(data);
} }
/// <summary> /// <summary>
@ -168,6 +162,21 @@ void LICH::encode(uint8_t* data)
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary>
/// Internal helper to copy the the class.
/// </summary>
/// <param name="data"></param>
void LICH::copy(const LICH& data)
{
m_data = new uint8_t[1U];
m_data[0U] = data.m_data[0U];
m_rfct = (m_data[0U] >> 6) & 0x03U;
m_fct = (m_data[0U] >> 4) & 0x03U;
m_option = (m_data[0U] >> 2) & 0x03U;
m_direction = (m_data[0U] >> 1) & 0x01U;
}
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>

@ -28,8 +28,8 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#if !defined(__NXDN_LICH_H__) #if !defined(__NXDN_CHANNEL__LICH_H__)
#define __NXDN_LICH_H__ #define __NXDN_CHANNEL__LICH_H__
#include "Defines.h" #include "Defines.h"
@ -44,10 +44,10 @@ namespace nxdn
class HOST_SW_API LICH { class HOST_SW_API LICH {
public: public:
/// <summary>Initializes a copy instance of the LICH class.</summary>
LICH(const LICH& lich);
/// <summary>Initializes a new instance of the LICH class.</summary> /// <summary>Initializes a new instance of the LICH class.</summary>
LICH(); LICH();
/// <summary>Initializes a copy instance of the LICH class.</summary>
LICH(const LICH& lich);
/// <summary>Finalizes a instance of the LICH class.</summary> /// <summary>Finalizes a instance of the LICH class.</summary>
~LICH(); ~LICH();
@ -73,10 +73,13 @@ namespace nxdn
private: private:
uint8_t* m_data; uint8_t* m_data;
/// <summary>Internal helper to copy the class.</summary>
void copy(const LICH& data);
/// <summary></summary> /// <summary></summary>
bool getParity() const; bool getParity() const;
}; };
} // namespace channel } // namespace channel
} // namespace nxdn } // namespace nxdn
#endif // __NXDN_LICH_H__ #endif // __NXDN_CHANNEL__LICH_H__

@ -62,32 +62,28 @@ const uint32_t PUNCTURE_LIST[] = { 5U, 11U, 17U, 23U, 29U, 35U, 41U, 47U, 53U, 5
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Initializes a copy instance of the SACCH class. /// Initializes a new instance of the SACCH class.
/// </summary> /// </summary>
/// <param name="data"></param> SACCH::SACCH() :
SACCH::SACCH(const SACCH& data) :
m_verbose(false), m_verbose(false),
m_ran(0U), m_ran(0U),
m_structure(0U), m_structure(0U),
m_data(NULL) m_data(NULL)
{ {
m_data = new uint8_t[5U]; m_data = new uint8_t[5U];
::memcpy(m_data, data.m_data, 5U);
m_ran = m_data[0U] & 0x3FU;
m_structure = (m_data[0U] >> 6) & 0x03U;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the SACCH class. /// Initializes a copy instance of the SACCH class.
/// </summary> /// </summary>
SACCH::SACCH() : /// <param name="data"></param>
SACCH::SACCH(const SACCH& data) :
m_verbose(false), m_verbose(false),
m_ran(0U), m_ran(0U),
m_structure(0U), m_structure(0U),
m_data(NULL) m_data(NULL)
{ {
m_data = new uint8_t[5U]; copy(data);
} }
/// <summary> /// <summary>
@ -280,3 +276,20 @@ void SACCH::setData(const uint8_t* data)
m_ran = m_data[0U] & 0x3FU; m_ran = m_data[0U] & 0x3FU;
m_structure = (m_data[0U] >> 6) & 0x03U; m_structure = (m_data[0U] >> 6) & 0x03U;
} }
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Internal helper to copy the the class.
/// </summary>
/// <param name="data"></param>
void SACCH::copy(const SACCH& data)
{
m_data = new uint8_t[5U];
::memcpy(m_data, data.m_data, 5U);
m_ran = m_data[0U] & 0x3FU;
m_structure = (m_data[0U] >> 6) & 0x03U;
}

@ -28,8 +28,8 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#if !defined(__NXDN_SACCH_H__) #if !defined(__NXDN_CHANNEL__SACCH_H__)
#define __NXDN_SACCH_H__ #define __NXDN_CHANNEL__SACCH_H__
#include "Defines.h" #include "Defines.h"
@ -44,10 +44,10 @@ namespace nxdn
class HOST_SW_API SACCH { class HOST_SW_API SACCH {
public: public:
/// <summary>Initializes a copy instance of the SACCH class.</summary>
SACCH(const SACCH& data);
/// <summary>Initializes a new instance of the SACCH class.</summary> /// <summary>Initializes a new instance of the SACCH class.</summary>
SACCH(); SACCH();
/// <summary>Initializes a copy instance of the SACCH class.</summary>
SACCH(const SACCH& data);
/// <summary>Finalizes a instance of the SACCH class.</summary> /// <summary>Finalizes a instance of the SACCH class.</summary>
~SACCH(); ~SACCH();
@ -76,8 +76,11 @@ namespace nxdn
private: private:
uint8_t* m_data; uint8_t* m_data;
/// <summary>Internal helper to copy the class.</summary>
void copy(const SACCH& data);
}; };
} // namespace channel } // namespace channel
} // namespace nxdn } // namespace nxdn
#endif // __NXDN_SACCH_H__ #endif // __NXDN_CHANNEL__SACCH_H__

@ -91,29 +91,26 @@ const uint32_t PUNCTURE_LIST[] = {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// <summary> /// <summary>
/// Initializes a copy instance of the UDCH class. /// Initializes a new instance of the UDCH class.
/// </summary> /// </summary>
/// <param name="data"></param> UDCH::UDCH() :
UDCH::UDCH(const UDCH& data) :
m_verbose(false), m_verbose(false),
m_ran(0U), m_ran(0U),
m_data(NULL) m_data(NULL)
{ {
m_data = new uint8_t[26U]; m_data = new uint8_t[26U];
::memcpy(m_data, data.m_data, 26U);
m_ran = m_data[0U] & 0x3FU;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the UDCH class. /// Initializes a copy instance of the UDCH class.
/// </summary> /// </summary>
UDCH::UDCH() : /// <param name="data"></param>
UDCH::UDCH(const UDCH& data) :
m_verbose(false), m_verbose(false),
m_ran(0U), m_ran(0U),
m_data(NULL) m_data(NULL)
{ {
m_data = new uint8_t[26U]; copy(data);
} }
/// <summary> /// <summary>
@ -287,3 +284,19 @@ void UDCH::setData(const uint8_t* data)
m_ran = m_data[0U] & 0x3FU; m_ran = m_data[0U] & 0x3FU;
} }
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Internal helper to copy the the class.
/// </summary>
/// <param name="data"></param>
void UDCH::copy(const UDCH& data)
{
m_data = new uint8_t[26U];
::memcpy(m_data, data.m_data, 26U);
m_ran = m_data[0U] & 0x3FU;
}

@ -28,8 +28,8 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#if !defined(__NXDN_UDCH_H__) #if !defined(__NXDN_CHANNEL__UDCH_H__)
#define __NXDN_UDCH_H__ #define __NXDN_CHANNEL__UDCH_H__
#include "Defines.h" #include "Defines.h"
@ -44,10 +44,10 @@ namespace nxdn
class HOST_SW_API UDCH { class HOST_SW_API UDCH {
public: public:
/// <summary>Initializes a copy instance of the UDCH class.</summary>
UDCH(const UDCH& data);
/// <summary>Initializes a new instance of the UDCH class.</summary> /// <summary>Initializes a new instance of the UDCH class.</summary>
UDCH(); UDCH();
/// <summary>Initializes a copy instance of the UDCH class.</summary>
UDCH(const UDCH& data);
/// <summary>Finalizes a instance of the UDCH class.</summary> /// <summary>Finalizes a instance of the UDCH class.</summary>
~UDCH(); ~UDCH();
@ -74,8 +74,11 @@ namespace nxdn
private: private:
uint8_t* m_data; uint8_t* m_data;
/// <summary>Internal helper to copy the class.</summary>
void copy(const UDCH& data);
}; };
} // namespace channel } // namespace channel
} // namespace nxdn } // namespace nxdn
#endif // __NXDN_UDCH_H__ #endif // __NXDN_CHANNEL__UDCH_H__

@ -1,222 +0,0 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
//
// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2018 by Jonathan Naylor G4KLX
* Copyright (C) 2022 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "nxdn/NXDNDefines.h"
#include "nxdn/data/Layer3.h"
#include "Utils.h"
using namespace nxdn;
using namespace nxdn::data;
#include <cstdio>
#include <cassert>
#include <cstring>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a copy instance of the Layer3 class.
/// </summary>
/// <param name="data"></param>
Layer3::Layer3(const Layer3& data) :
m_verbose(false),
m_messageType(MESSAGE_TYPE_IDLE),
m_srcId(0U),
m_dstId(0U),
m_group(true),
m_dataBlocks(0U),
m_data(NULL)
{
m_data = new uint8_t[22U];
::memcpy(m_data, data.m_data, 22U);
m_messageType = m_data[0U] & 0x3FU; // Message Type
m_group = (m_data[2U] & 0x80U) != 0x80U;
m_srcId = (m_data[3U] << 8) | m_data[4U]; // Source Radio Address
m_dstId = (m_data[5U] << 8) | m_data[6U]; // Target Radio Address
m_dataBlocks = (m_data[8U] & 0x0FU) + 1U; // Data Blocks
}
/// <summary>
/// Initializes a new instance of the Layer3 class.
/// </summary>
Layer3::Layer3() :
m_verbose(false),
m_messageType(MESSAGE_TYPE_IDLE),
m_srcId(0U),
m_dstId(0U),
m_group(true),
m_dataBlocks(0U),
m_data(NULL)
{
m_data = new uint8_t[22U];
::memset(m_data, 0x00U, 22U);
}
/// <summary>
/// Finalizes a instance of Layer3 class.
/// </summary>
Layer3::~Layer3()
{
delete[] m_data;
}
/// <summary>
/// Equals operator.
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
Layer3& Layer3::operator=(const Layer3& data)
{
if (&data != this) {
::memcpy(m_data, data.m_data, 22U);
m_verbose = data.m_verbose;
m_messageType = data.m_messageType;
m_group = data.m_group;
m_srcId = data.m_srcId;
m_dstId = data.m_dstId;
m_dataBlocks = m_dataBlocks;
}
return *this;
}
/// <summary>
/// Decode layer 3 data.
/// </summary>
/// <param name="data"></param>
/// <returns>True, if SACCH was decoded, otherwise false.</returns>
void Layer3::decode(const uint8_t* data, uint32_t length, uint32_t offset)
{
assert(data != NULL);
for (uint32_t i = 0U; i < length; i++, offset++) {
bool b = READ_BIT(data, i);
WRITE_BIT(m_data, offset, b);
}
if (m_verbose) {
Utils::dump(2U, "Decoded Layer 3 Data", m_data, 22U);
}
m_messageType = m_data[0U] & 0x3FU; // Message Type
m_group = (m_data[2U] & 0x80U) != 0x80U;
m_srcId = (m_data[3U] << 8) | m_data[4U]; // Source Radio Address
m_dstId = (m_data[5U] << 8) | m_data[6U]; // Target Radio Address
m_dataBlocks = (m_data[8U] & 0x0FU) + 1U; // Data Blocks
}
/// <summary>
/// Encode layer 3 data.
/// </summary>
/// <param name="data"></param>
void Layer3::encode(uint8_t* data, uint32_t length, uint32_t offset)
{
assert(data != NULL);
m_data[0U] = m_messageType & 0x3FU; // Message Type
m_data[2U] = (m_group ? 0x80U : 0x00U);
m_data[3U] = (m_srcId >> 8U) & 0xFFU; // Source Radio Address
m_data[4U] = (m_srcId >> 0U) & 0xFFU; // ...
m_data[5U] = (m_dstId >> 8U) & 0xFFU; // Target Radio Address
m_data[6U] = (m_dstId >> 0U) & 0xFFU; // ...
m_data[8U] = m_dataBlocks & 0x0FU; // Data Blocks
for (uint32_t i = 0U; i < length; i++, offset++) {
bool b = READ_BIT(m_data, offset);
WRITE_BIT(data, i, b);
}
if (m_verbose) {
Utils::dump(2U, "Encoded Layer 3 Data", data, length);
}
}
/// <summary>
///
/// </summary>
void Layer3::reset()
{
::memset(m_data, 0x00U, 22U);
m_messageType = MESSAGE_TYPE_IDLE;
m_srcId = 0U;
m_dstId = 0U;
m_group = true;
m_dataBlocks = 0U;
}
/// <summary>
/// Gets the raw layer 3 data.
/// </summary>
/// <param name="data"></param>
void Layer3::getData(uint8_t* data) const
{
::memcpy(data, m_data, 22U);
}
/// <summary>
/// Sets the raw layer 3 data.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
void Layer3::setData(const uint8_t* data, uint32_t length)
{
::memset(m_data, 0x00U, 22U);
::memcpy(m_data, data, length);
m_messageType = m_data[0U] & 0x3FU;
m_group = (m_data[2U] & 0x80U) != 0x80U;
m_srcId = (m_data[3U] << 8) | m_data[4U];
m_dstId = (m_data[5U] << 8) | m_data[6U];
m_dataBlocks = (m_data[8U] & 0x0FU) + 1U;
}

@ -1,95 +0,0 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
//
// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2018 by Jonathan Naylor G4KLX
* Copyright (C) 2022 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(__NXDN_LAYER_3_H__)
#define __NXDN_LAYER_3_H__
#include "Defines.h"
namespace nxdn
{
namespace data
{
// ---------------------------------------------------------------------------
// Class Declaration
// Implements NXDN Layer 3 Connection Control.
// ---------------------------------------------------------------------------
class HOST_SW_API Layer3 {
public:
/// <summary>Initializes a copy instance of the Layer3 class.</summary>
Layer3(const Layer3& data);
/// <summary>Initializes a new instance of the Layer3 class.</summary>
Layer3();
/// <summary>Finalizes a instance of the Layer3 class.</summary>
~Layer3();
/// <summary>Equals operator.</summary>
Layer3& operator=(const Layer3& data);
/// <summary>Decode layer 3 data.</summary>
void decode(const uint8_t* data, uint32_t length, uint32_t offset = 0U);
/// <summary>Encode layer 3 data.</summary>
void encode(uint8_t* data, uint32_t length, uint32_t offset = 0U);
/// <summary></summary>
void reset();
/// <summary>Gets the raw layer 3 data.</summary>
void getData(uint8_t* data) const;
/// <summary>Sets the raw layer 3 data.</summary>
void setData(const uint8_t* data, uint32_t length);
public:
/// <summary>Flag indicating verbose log output.</summary>
__PROPERTY(bool, verbose, Verbose);
/** Common Data */
/// <summary>Message Type</summary>
__PROPERTY(uint8_t, messageType, MessageType);
/// <summary>Source ID.</summary>
__PROPERTY(uint16_t, srcId, SrcId);
/// <summary>Destination ID.</summary>
__PROPERTY(uint16_t, dstId, DstId);
/// <summary>Flag indicating a group/talkgroup operation.</summary>
__PROPERTY(bool, group, Group);
/// <summary></summary>
__PROPERTY(uint8_t, dataBlocks, DataBlocks);
private:
uint8_t* m_data;
};
} // namespace data
} // namespace nxdn
#endif // __NXDN_LAYER_3_H__

@ -0,0 +1,480 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
//
// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2018 by Jonathan Naylor G4KLX
* Copyright (C) 2022 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "nxdn/NXDNDefines.h"
#include "nxdn/lc/LC.h"
#include "Log.h"
#include "Utils.h"
using namespace nxdn;
using namespace nxdn::lc;
#include <cstdio>
#include <cassert>
#include <cstring>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the LC class.
/// </summary>
LC::LC() :
m_verbose(false),
m_messageType(MESSAGE_TYPE_IDLE),
m_callType(CALL_TYPE_UNSPECIFIED),
m_srcId(0U),
m_dstId(0U),
m_emergency(false),
m_encrypted(false),
m_priority(false),
m_group(true),
m_duplex(false),
m_transmissionMode(TRANSMISSION_MODE_4800),
m_packetInfo(),
m_rsp(),
m_dataFrameNumber(0U),
m_dataBlockNumber(0U),
m_delayCount(0U),
m_algId(NXDN_CIPHER_TYPE_NONE),
m_kId(0U),
m_causeRsp(NXDN_CAUSE_VD_NORMAL_1),
m_data(NULL)
{
m_data = new uint8_t[22U];
::memset(m_data, 0x00U, 22U);
m_mi = new uint8_t[NXDN_MI_LENGTH_BYTES];
::memset(m_mi, 0x00U, NXDN_MI_LENGTH_BYTES);
}
/// <summary>
/// Initializes a copy instance of the LC class.
/// </summary>
/// <param name="data"></param>
LC::LC(const LC& data) :
m_verbose(false),
m_messageType(MESSAGE_TYPE_IDLE),
m_callType(CALL_TYPE_UNSPECIFIED),
m_srcId(0U),
m_dstId(0U),
m_emergency(false),
m_encrypted(false),
m_priority(false),
m_group(true),
m_duplex(false),
m_transmissionMode(TRANSMISSION_MODE_4800),
m_packetInfo(),
m_rsp(),
m_dataFrameNumber(0U),
m_dataBlockNumber(0U),
m_delayCount(0U),
m_algId(NXDN_CIPHER_TYPE_NONE),
m_kId(0U),
m_causeRsp(NXDN_CAUSE_VD_NORMAL_1),
m_data(NULL)
{
copy(data);
}
/// <summary>
/// Finalizes a instance of LC class.
/// </summary>
LC::~LC()
{
delete[] m_data;
delete[] m_mi;
}
/// <summary>
/// Equals operator.
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
LC& LC::operator=(const LC& data)
{
if (&data != this) {
::memcpy(m_data, data.m_data, 22U);
decodeLC(m_data);
}
return *this;
}
/// <summary>
/// Decode call link control data.
/// </summary>
/// <param name="data"></param>
/// <returns>True, if LC was decoded, otherwise false.</returns>
void LC::decode(const uint8_t* data, uint32_t length, uint32_t offset)
{
assert(data != NULL);
for (uint32_t i = 0U; i < length; i++, offset++) {
bool b = READ_BIT(data, i);
WRITE_BIT(m_data, offset, b);
}
if (m_verbose) {
Utils::dump(2U, "Decoded LC Data", m_data, 22U);
}
decodeLC(m_data);
}
/// <summary>
/// Encode call link control data.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <param name="offset"></param>
void LC::encode(uint8_t* data, uint32_t length, uint32_t offset)
{
assert(data != NULL);
encodeLC(m_data);
for (uint32_t i = 0U; i < length; i++, offset++) {
bool b = READ_BIT(m_data, offset);
WRITE_BIT(data, i, b);
}
if (m_verbose) {
Utils::dump(2U, "Encoded LC Data", data, length);
}
}
/// <summary>
///
/// </summary>
void LC::reset()
{
::memset(m_data, 0x00U, 22U);
m_messageType = MESSAGE_TYPE_IDLE;
m_callType = CALL_TYPE_UNSPECIFIED;
m_srcId = 0U;
m_dstId = 0U;
m_emergency = false;
m_encrypted = false;
m_priority = false;
m_group = true;
m_duplex = false;
m_transmissionMode = TRANSMISSION_MODE_4800;
m_packetInfo = PacketInformation();
m_rsp = PacketInformation();
m_dataFrameNumber = 0U;
m_dataBlockNumber = 0U;
m_delayCount = 0U;
m_algId = NXDN_CIPHER_TYPE_NONE;
m_kId = 0U;
m_causeRsp = NXDN_CAUSE_VD_NORMAL_1;
}
/// <summary>
/// Gets the raw layer 3 data.
/// </summary>
/// <param name="data"></param>
void LC::getData(uint8_t* data) const
{
::memcpy(data, m_data, 22U);
}
/// <summary>
/// Sets the raw layer 3 data.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
void LC::setData(const uint8_t* data, uint32_t length)
{
::memset(m_data, 0x00U, 22U);
::memcpy(m_data, data, length);
decodeLC(m_data);
}
/// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Decode link control.
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
bool LC::decodeLC(const uint8_t* data)
{
m_messageType = data[0U] & 0x3FU; // Message Type
// message type opcodes
switch (m_messageType) {
case RTCH_MESSAGE_TYPE_VCALL:
m_callType = (data[2U] >> 5) & 0x07U; // Call Type
m_emergency = (data[1U] & 0x80U) == 0x80U; // Emergency Flag
m_priority = (data[1U] & 0x20U) == 0x20U; // Priority Flag
m_duplex = (data[2U] & 0x10U) == 0x10U; // Half/Full Duplex Flag
m_transmissionMode = (data[2U] & 0x07U); // Transmission Mode
m_srcId = (uint16_t)((data[3U] << 8) | data[4U]) & 0xFFFFU; // Source Radio Address
m_dstId = (uint16_t)((data[5U] << 8) | data[6U]) & 0xFFFFU; // Target Radio Address
m_algId = (data[7U] >> 6) & 0x03U; // Cipher Type
m_kId = (data[7U] & 0x3FU); // Key ID
break;
case RTCH_MESSAGE_TYPE_VCALL_IV:
case RTCH_MESSAGE_TYPE_SDCALL_IV:
if (m_algId != NXDN_CIPHER_TYPE_NONE && m_kId > 0U) {
m_mi = new uint8_t[NXDN_MI_LENGTH_BYTES];
::memset(m_mi, 0x00U, NXDN_MI_LENGTH_BYTES);
::memcpy(m_mi, data + 1U, NXDN_MI_LENGTH_BYTES); // Message Indicator
}
break;
case RTCH_MESSAGE_TYPE_TX_REL:
m_callType = (data[2U] >> 5) & 0x07U; // Call Type
m_emergency = (data[1U] & 0x80U) == 0x80U; // Emergency Flag
m_priority = (data[1U] & 0x20U) == 0x20U; // Priority Flag
m_srcId = (uint16_t)((data[3U] << 8) | data[4U]) & 0xFFFFU; // Source Radio Address
m_dstId = (uint16_t)((data[5U] << 8) | data[6U]) & 0xFFFFU; // Target Radio Address
break;
case RTCH_MESSAGE_TYPE_DCALL_HDR:
m_callType = (data[2U] >> 5) & 0x07U; // Call Type
m_emergency = (data[1U] & 0x80U) == 0x80U; // Emergency Flag
m_priority = (data[1U] & 0x20U) == 0x20U; // Priority Flag
m_duplex = (data[2U] & 0x10U) == 0x10U; // Half/Full Duplex Flag
m_transmissionMode = (data[2U] & 0x07U); // Transmission Mode
m_srcId = (uint16_t)((data[3U] << 8) | data[4U]) & 0xFFFFU; // Source Radio Address
m_dstId = (uint16_t)((data[5U] << 8) | data[6U]) & 0xFFFFU; // Target Radio Address
m_algId = (data[7U] >> 6) & 0x03U; // Cipher Type
m_kId = (data[7U] & 0x3FU); // Key ID
m_packetInfo = PacketInformation();
m_packetInfo.decode(m_messageType, data + 8U); // Packet Information
if (m_algId != NXDN_CIPHER_TYPE_NONE && m_kId > 0U) {
::memset(m_mi, 0x00U, NXDN_MI_LENGTH_BYTES);
::memcpy(m_mi, data + 11U, NXDN_MI_LENGTH_BYTES); // Message Indicator
}
break;
case RTCH_MESSAGE_TYPE_DCALL_DATA:
case RTCH_MESSAGE_TYPE_SDCALL_REQ_DATA:
m_dataFrameNumber = (data[1U] >> 4) & 0x0FU; // Frame Number
m_dataBlockNumber = (data[1U] & 0x0FU); // Block Number
break;
case RTCH_MESSAGE_TYPE_DCALL_ACK:
m_callType = (data[2U] >> 5) & 0x07U; // Call Type
m_emergency = (data[1U] & 0x80U) == 0x80U; // Emergency Flag
m_priority = (data[1U] & 0x20U) == 0x20U; // Priority Flag
m_duplex = (data[2U] & 0x10U) == 0x10U; // Half/Full Duplex Flag
m_transmissionMode = (data[2U] & 0x07U); // Transmission Mode
m_srcId = (uint16_t)((data[3U] << 8) | data[4U]) & 0xFFFFU; // Source Radio Address
m_dstId = (uint16_t)((data[5U] << 8) | data[6U]) & 0xFFFFU; // Target Radio Address
m_rsp = PacketInformation();
m_rsp.decode(m_messageType, data + 7U); // Response
break;
case RTCH_MESSAGE_TYPE_HEAD_DLY:
m_callType = (data[2U] >> 5) & 0x07U; // Call Type
m_emergency = (data[1U] & 0x80U) == 0x80U; // Emergency Flag
m_priority = (data[1U] & 0x20U) == 0x20U; // Priority Flag
m_srcId = (uint16_t)((data[3U] << 8) | data[4U]) & 0xFFFFU; // Source Radio Address
m_dstId = (uint16_t)((data[5U] << 8) | data[6U]) & 0xFFFFU; // Target Radio Address
m_delayCount = (uint16_t)((data[7U] << 8) | data[8U]) & 0xFFFFU; // Delay Count
break;
case MESSAGE_TYPE_IDLE:
break;
case RTCH_MESSAGE_TYPE_SDCALL_REQ_HDR:
m_callType = (data[2U] >> 5) & 0x07U; // Call Type
m_emergency = (data[1U] & 0x80U) == 0x80U; // Emergency Flag
m_priority = (data[1U] & 0x20U) == 0x20U; // Priority Flag
m_duplex = (data[2U] & 0x10U) == 0x10U; // Half/Full Duplex Flag
m_transmissionMode = (data[2U] & 0x07U); // Transmission Mode
m_srcId = (uint16_t)((data[3U] << 8) | data[4U]) & 0xFFFFU; // Source Radio Address
m_dstId = (uint16_t)((data[5U] << 8) | data[6U]) & 0xFFFFU; // Target Radio Address
m_algId = (data[7U] >> 6) & 0x03U; // Cipher Type
m_kId = (data[7U] & 0x3FU); // Key ID
m_packetInfo = PacketInformation();
m_packetInfo.decode(m_messageType, data + 8U); // Packet Information
break;
case RTCH_MESSAGE_TYPE_SDCALL_RESP:
m_callType = (data[2U] >> 5) & 0x07U; // Call Type
m_emergency = (data[1U] & 0x80U) == 0x80U; // Emergency Flag
m_priority = (data[1U] & 0x20U) == 0x20U; // Priority Flag
m_duplex = (data[2U] & 0x10U) == 0x10U; // Half/Full Duplex Flag
m_transmissionMode = (data[2U] & 0x07U); // Transmission Mode
m_srcId = (uint16_t)((data[3U] << 8) | data[4U]) & 0xFFFFU; // Source Radio Address
m_dstId = (uint16_t)((data[5U] << 8) | data[6U]) & 0xFFFFU; // Target Radio Address
m_causeRsp = data[7U]; // Cause (SS)
break;
default:
LogError(LOG_NXDN, "LC::decodeLC(), unknown LC value, messageType = $%02X", m_messageType);
return false;
}
return true;
}
/// <summary>
/// Encode link control.
/// </summary>
/// <param name="rs"></param>
void LC::encodeLC(uint8_t* data)
{
m_messageType = m_data[0U] & 0x3FU; // Message Type
// message type opcodes
switch (m_messageType) {
case RTCH_MESSAGE_TYPE_VCALL:
m_data[1U] = (m_emergency ? 0x80U : 0x00U) + // Emergency Flag
(m_priority ? 0x20U : 0x00U); // Priority Flag
m_data[2U] = ((m_callType & 0x07U) << 5) + // Call Type
(m_duplex ? 0x10U : 0x00U) + // Half/Full Duplex Flag
(m_transmissionMode & 0x07U); // Transmission Mode
m_data[3U] = (m_srcId >> 8U) & 0xFFU; // Source Radio Address
m_data[4U] = (m_srcId >> 0U) & 0xFFU; // ...
m_data[5U] = (m_dstId >> 8U) & 0xFFU; // Target Radio Address
m_data[6U] = (m_dstId >> 0U) & 0xFFU; // ...
m_data[7U] = ((m_algId & 0x03U) << 6) + // Cipher Type
(m_kId & 0x3FU); // Key ID
break;
case RTCH_MESSAGE_TYPE_VCALL_IV:
if (m_algId != NXDN_CIPHER_TYPE_NONE && m_kId > 0U) {
::memcpy(m_data + 1U, m_mi, NXDN_MI_LENGTH_BYTES); // Message Indicator
}
break;
case RTCH_MESSAGE_TYPE_TX_REL:
m_data[1U] = (m_emergency ? 0x80U : 0x00U) + // Emergency Flag
(m_priority ? 0x20U : 0x00U); // Priority Flag
m_data[2U] = (m_callType & 0x07U) << 5; // Call Type
m_data[3U] = (m_srcId >> 8U) & 0xFFU; // Source Radio Address
m_data[4U] = (m_srcId >> 0U) & 0xFFU; // ...
m_data[5U] = (m_dstId >> 8U) & 0xFFU; // Target Radio Address
m_data[6U] = (m_dstId >> 0U) & 0xFFU; // ...
break;
case RTCH_MESSAGE_TYPE_DCALL_HDR:
m_data[1U] = (m_emergency ? 0x80U : 0x00U) + // Emergency Flag
(m_priority ? 0x20U : 0x00U); // Priority Flag
m_data[2U] = ((m_callType & 0x07U) << 5) + // Call Type
(m_duplex ? 0x10U : 0x00U) + // Half/Full Duplex Flag
(m_transmissionMode & 0x07U); // Transmission Mode
m_data[3U] = (m_srcId >> 8U) & 0xFFU; // Source Radio Address
m_data[4U] = (m_srcId >> 0U) & 0xFFU; // ...
m_data[5U] = (m_dstId >> 8U) & 0xFFU; // Target Radio Address
m_data[6U] = (m_dstId >> 0U) & 0xFFU; // ...
m_data[7U] = ((m_algId & 0x03U) << 6) + // Cipher Type
(m_kId & 0x3FU); // Key ID
m_packetInfo.encode(m_messageType, data + 8U); // Packet Information
if (m_algId != NXDN_CIPHER_TYPE_NONE && m_kId > 0U) {
::memcpy(m_data + 11U, m_mi, NXDN_MI_LENGTH_BYTES); // Message Indicator
}
break;
case RTCH_MESSAGE_TYPE_DCALL_DATA:
case RTCH_MESSAGE_TYPE_SDCALL_REQ_DATA:
data[1U] = (m_dataFrameNumber & 0x0FU << 4) + // Frame Number
(m_dataBlockNumber & 0x0FU); // Block Number
break;
case RTCH_MESSAGE_TYPE_DCALL_ACK:
m_data[1U] = (m_emergency ? 0x80U : 0x00U) + // Emergency Flag
(m_priority ? 0x20U : 0x00U); // Priority Flag
m_data[2U] = ((m_callType & 0x07U) << 5) + // Call Type
(m_duplex ? 0x10U : 0x00U) + // Half/Full Duplex Flag
(m_transmissionMode & 0x07U); // Transmission Mode
m_data[3U] = (m_srcId >> 8U) & 0xFFU; // Source Radio Address
m_data[4U] = (m_srcId >> 0U) & 0xFFU; // ...
m_data[5U] = (m_dstId >> 8U) & 0xFFU; // Target Radio Address
m_data[6U] = (m_dstId >> 0U) & 0xFFU; // ...
m_rsp.encode(m_messageType, data + 7U); // Response
break;
case RTCH_MESSAGE_TYPE_HEAD_DLY:
m_data[1U] = (m_emergency ? 0x80U : 0x00U) + // Emergency Flag
(m_priority ? 0x20U : 0x00U); // Priority Flag
m_data[2U] = (m_callType & 0x07U) << 5; // Call Type
m_data[3U] = (m_srcId >> 8U) & 0xFFU; // Source Radio Address
m_data[4U] = (m_srcId >> 0U) & 0xFFU; // ...
m_data[5U] = (m_dstId >> 8U) & 0xFFU; // Target Radio Address
m_data[6U] = (m_dstId >> 0U) & 0xFFU; // ...
m_data[7U] = (m_delayCount >> 8U) & 0xFFU; // Delay Count
m_data[8U] = (m_delayCount >> 0U) & 0xFFU; // ...
break;
case MESSAGE_TYPE_IDLE:
break;
case RTCH_MESSAGE_TYPE_SDCALL_REQ_HDR:
m_data[1U] = (m_emergency ? 0x80U : 0x00U) + // Emergency Flag
(m_priority ? 0x20U : 0x00U); // Priority Flag
m_data[2U] = ((m_callType & 0x07U) << 5) + // Call Type
(m_duplex ? 0x10U : 0x00U) + // Half/Full Duplex Flag
(m_transmissionMode & 0x07U); // Transmission Mode
m_data[3U] = (m_srcId >> 8U) & 0xFFU; // Source Radio Address
m_data[4U] = (m_srcId >> 0U) & 0xFFU; // ...
m_data[5U] = (m_dstId >> 8U) & 0xFFU; // Target Radio Address
m_data[6U] = (m_dstId >> 0U) & 0xFFU; // ...
m_data[7U] = ((m_algId & 0x03U) << 6) + // Cipher Type
(m_kId & 0x3FU); // Key ID
m_packetInfo.encode(m_messageType, data + 8U); // Packet Information
break;
default:
LogError(LOG_NXDN, "LC::encodeLC(), unknown LC value, messageType = $%02X", m_messageType);
return;
}
}
// <summary>
/// Internal helper to copy the the class.
/// </summary>
/// <param name="data"></param>
void LC::copy(const LC& data)
{
m_data = new uint8_t[22U];
::memcpy(m_data, data.m_data, 22U);
m_verbose = data.m_verbose;
decodeLC(m_data);
}

@ -0,0 +1,142 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
//
// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2018 by Jonathan Naylor G4KLX
* Copyright (C) 2022 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(__NXDN_LC__LC_H__)
#define __NXDN_LC__LC_H__
#include "Defines.h"
#include "nxdn/lc/PacketInformation.h"
namespace nxdn
{
namespace lc
{
// ---------------------------------------------------------------------------
// Class Declaration
// Represents link control data for NXDN calls.
// ---------------------------------------------------------------------------
class HOST_SW_API LC {
public:
/// <summary>Initializes a new instance of the LC class.</summary>
LC();
/// <summary>Initializes a copy instance of the LC class.</summary>
LC(const LC& data);
/// <summary>Finalizes a instance of the LC class.</summary>
~LC();
/// <summary>Equals operator.</summary>
LC& operator=(const LC& data);
/// <summary>Decode layer 3 data.</summary>
void decode(const uint8_t* data, uint32_t length, uint32_t offset = 0U);
/// <summary>Encode layer 3 data.</summary>
void encode(uint8_t* data, uint32_t length, uint32_t offset = 0U);
/// <summary></summary>
void reset();
/// <summary>Gets the raw layer 3 data.</summary>
void getData(uint8_t* data) const;
/// <summary>Sets the raw layer 3 data.</summary>
void setData(const uint8_t* data, uint32_t length);
public:
/// <summary>Flag indicating verbose log output.</summary>
__PROPERTY(bool, verbose, Verbose);
/** Common Data */
/// <summary>Message Type</summary>
__PROPERTY(uint8_t, messageType, MessageType);
/// <summary>Call Type</summary>
__PROPERTY(uint8_t, callType, CallType);
/// <summary>Source ID.</summary>
__PROPERTY(uint16_t, srcId, SrcId);
/// <summary>Destination ID.</summary>
__PROPERTY(uint16_t, dstId, DstId);
/** Common Call Options */
/// <summary>Flag indicating the emergency bits are set.</summary>
__PROPERTY(bool, emergency, Emergency);
/// <summary>Flag indicating that encryption is enabled.</summary>
__PROPERTY(bool, encrypted, Encrypted);
/// <summary>Flag indicating priority paging.</summary>
__PROPERTY(bool, priority, Priority);
/// <summary>Flag indicating a group/talkgroup operation.</summary>
__PROPERTY(bool, group, Group);
/// <summary>Flag indicating a half/full duplex operation.</summary>
__PROPERTY(bool, duplex, Duplex);
/// <summary>Transmission mode.</summary>
__PROPERTY(uint8_t, transmissionMode, TransmissionMode);
/** Data Call Data */
/// <summary>Data packet information.</summary>
__PROPERTY(PacketInformation, packetInfo, PacketInfo);
/// <summary>Data packet information.</summary>
__PROPERTY(PacketInformation, rsp, Response);
/// <summary>Data packet frame number.</summary>
__PROPERTY(uint8_t, dataFrameNumber, DataFrameNumber);
/// <summary>Data packet block number.</summary>
__PROPERTY(uint8_t, dataBlockNumber, DataBlockNumber);
/** Header Delay Data */
/// <summary>Delay count.</summary>
__PROPERTY(uint16_t, delayCount, DelayCount);
/** Encryption data */
/// <summary>Encryption algorithm ID.</summary>
__PROPERTY(uint8_t, algId, AlgId);
/// <summary>Encryption key ID.</summary>
__PROPERTY(uint8_t, kId, KId);
/// <summary>Cause Response.</summary>
__PROPERTY(uint8_t, causeRsp, CauseResponse);
private:
uint8_t* m_data;
/** Encryption data */
uint8_t* m_mi;
/// <summary>Decode link control.</summary>
bool decodeLC(const uint8_t* data);
/// <summary>Encode link control.</summary>
void encodeLC(uint8_t* data);
/// <summary>Internal helper to copy the class.</summary>
void copy(const LC& data);
};
} // namespace lc
} // namespace nxdn
#endif // __NXDN_LC__LC_H__

@ -0,0 +1,187 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
/*
* Copyright (C) 2022 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Defines.h"
#include "nxdn/NXDNDefines.h"
#include "nxdn/lc/PacketInformation.h"
#include "Log.h"
#include "Utils.h"
using namespace nxdn::lc;
using namespace nxdn;
#include <cstdio>
#include <cassert>
#include <cstring>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the PacketInformation class.
/// </summary>
PacketInformation::PacketInformation() :
m_delivery(false),
m_selectiveRetry(false),
m_blockCount(0U),
m_padCount(0U),
m_start(true),
m_circular(false),
m_fragmentCount(0U),
m_rspClass(DATA_RSP_CLASS_ACK),
m_rspType(1U),
m_rspErrorBlock(0U)
{
/* stub */
}
/// <summary>
/// Finalizes a instance of the PacketInformation class.
/// </summary>
PacketInformation::~PacketInformation()
{
/* stub */
}
/// <summary>
/// Decodes packet information.
/// </summary>
/// <param name="messageType"></param>
/// <param name="data"></param>
/// <returns>True, if packet information was decoded, otherwise false.</returns>
bool PacketInformation::decode(const uint8_t messageType, const uint8_t* data)
{
assert(data != NULL);
switch (messageType)
{
case RTCH_MESSAGE_TYPE_DCALL_HDR:
m_delivery = (data[0U] & 0x80U) == 0x80U; // Delivery
m_selectiveRetry = (data[0U] & 0x20U) == 0x20U; // Selective Retry
m_blockCount = (data[0U] & 0x0FU); // Block Count
m_padCount = (data[1U] >> 3) & 0x1FU; // Pad Count
m_start = (data[1U] & 0x08U) == 0x08U; // Start/First Fragment
m_circular = (data[1U] & 0x04U) == 0x04U; // Circular Fragment Count
m_fragmentCount = ((data[1U] & 0x01U) << 8) + data[2U]; // Fragment Count
break;
case RTCH_MESSAGE_TYPE_DCALL_ACK:
m_rspClass = (data[0U] >> 4) & 0x03U; // Response Class
m_rspType = (data[0U] >> 1) & 0x07U; // Response Type
m_fragmentCount = ((data[0U] & 0x01U) << 8) + data[1U]; // Fragment Count
break;
case RTCH_MESSAGE_TYPE_SDCALL_REQ_HDR:
m_delivery = (data[0U] & 0x80U) == 0x80U; // Delivery
m_selectiveRetry = (data[0U] & 0x20U) == 0x20U; // Selective Retry
m_blockCount = (data[0U] & 0x0FU); // Block Count
m_padCount = (data[1U] >> 3) & 0x1FU; // Pad Count
m_start = (data[1U] & 0x08U) == 0x08U; // Start/First Fragment
m_circular = (data[1U] & 0x04U) == 0x04U; // Circular Fragment Count
break;
default:
LogError(LOG_NXDN, "PacketInformation::decode(), unknown LC value, messageType = $%02X", messageType);
return false;
}
return true;
}
/// <summary>
/// Encodes packet information.
/// </summary>
/// <param name="messageType"></param>
/// <param name="data"></param>
void PacketInformation::encode(const uint8_t messageType, uint8_t* data)
{
assert(data != NULL);
switch (messageType)
{
case RTCH_MESSAGE_TYPE_DCALL_HDR:
{
::memset(data, 0x00U, NXDN_PCKT_INFO_LENGTH_BYTES);
data[0U] = (m_delivery ? 0x80U : 0x00U) + // Delivery
(m_selectiveRetry ? 0x20U : 0x00U) + // Selective Retry
m_blockCount; // Block Count
data[1U] = (m_padCount << 3) + // Pad Count
(m_start ? 0x08U : 0x00U) + // Start/First Fragment
(m_circular ? 0x04U : 0x00U); // Circular Fragment Count
bool highFragCount = (m_fragmentCount & 0x100U) == 0x100U;
data[1U] += (highFragCount ? 0x01U : 0x00U); // Fragment Count - bit 8
data[2U] = m_fragmentCount & 0xFFU; // Fragment Count - bit 0 - 7
}
break;
case RTCH_MESSAGE_TYPE_DCALL_ACK:
{
data[0U] = (m_rspClass & 0x03U << 4) + // Response Class
(m_rspType & 0x07U << 1); // Response Type
bool highFragCount = (m_fragmentCount & 0x100U) == 0x100U;
data[0U] += (highFragCount ? 0x01U : 0x00U); // Fragment Count - bit 8
data[1U] = m_fragmentCount & 0xFFU; // Fragment Count - bit 0 - 7
}
break;
case RTCH_MESSAGE_TYPE_SDCALL_REQ_HDR:
::memset(data, 0x00U, NXDN_PCKT_INFO_LENGTH_BYTES);
data[0U] = (m_delivery ? 0x80U : 0x00U) + // Delivery
(m_selectiveRetry ? 0x20U : 0x00U) + // Selective Retry
m_blockCount; // Block Count
data[1U] = (m_padCount << 3) + // Pad Count
(m_start ? 0x08U : 0x00U) + // Start/First Fragment
(m_circular ? 0x04U : 0x00U); // Circular Fragment Count
break;
default:
LogError(LOG_NXDN, "PacketInformation::encode(), unknown LC value, messageType = $%02X", messageType);
break;
}
}
/// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void PacketInformation::reset()
{
m_delivery = false;
m_selectiveRetry = false;
m_blockCount = 0U;
m_padCount = 0U;
m_start = true;
m_circular = false;
m_fragmentCount = 0U;
m_rspClass = DATA_RSP_CLASS_ACK;
m_rspType = 0U;
m_rspErrorBlock = 0U;
}

@ -0,0 +1,83 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
/*
* Copyright (C) 2022 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(__NXDN_LC__PACKET_INFORMATION_H__)
#define __NXDN_LC__PACKET_INFORMATION_H__
#include "Defines.h"
namespace nxdn
{
namespace lc
{
// ---------------------------------------------------------------------------
// Class Declaration
// Represents the packet information data for link control data.
// ---------------------------------------------------------------------------
class HOST_SW_API PacketInformation {
public:
/// <summary>Initializes a new instance of the PacketInformation class.</summary>
PacketInformation();
/// <summary>Finalizes a instance of the PacketInformation class.</summary>
~PacketInformation();
/// <summary>Decodes packet information.</summary>
bool decode(const uint8_t messageType, const uint8_t* data);
/// <summary>Encodes packet information.</summary>
void encode(const uint8_t messageType, uint8_t* data);
/// <summary>Helper to reset data values to defaults.</summary>
void reset();
public:
/** Common Data **/
/// <summary>Flag indicating if confirmed delivery is needed.</summary>
__PROPERTY(bool, delivery, Delivery);
/// <summary>Flag indicating if the packet is a selective retry packet.</summary>
__PROPERTY(bool, selectiveRetry, SelectiveRetry);
/// <summary>Count of data blocks in t he transmission packet.</summary>
__PROPERTY(uint8_t, blockCount, BlockCount);
/// <summary>Number of padding octets of the last block.</summary>
__PROPERTY(uint8_t, padCount, PadCount);
/// <summary>Flag indicating the first fragment.</summary>
__PROPERTY(bool, start, Start);
/// <summary>Flag indicating if the Tx fragment count circulates.</summary>
__PROPERTY(bool, circular, Circular);
/// <summary>The number and sequence of fragments.</summary>
__PROPERTY(uint16_t, fragmentCount, FragmentCount);
/** Response Data */
/// <summary>Response class.</summary>
__PROPERTY(uint8_t, rspClass, ResponseClass);
/// <summary>Response type.</summary>
__PROPERTY(uint8_t, rspType, ResponseType);
/// <summary>Error Block Flag.</summary>
__PROPERTY(uint16_t, rspErrorBlock, ResponseErrorBlock);
};
} // namespace lc
} // namespace nxdn
#endif // __NXDN_LC__PACKET_INFORMATION_H__

@ -62,9 +62,9 @@ using namespace nxdn::packet;
} \ } \
\ \
if (m_nxdn->m_netState != RS_NET_IDLE) { \ if (m_nxdn->m_netState != RS_NET_IDLE) { \
if (m_nxdn->m_netLayer3.getSrcId() == _SRC_ID && m_nxdn->m_netLastDstId == _DST_ID) { \ if (m_nxdn->m_netLC.getSrcId() == _SRC_ID && m_nxdn->m_netLastDstId == _DST_ID) { \
LogWarning(LOG_RF, "Traffic collision detect, preempting new RF traffic to existing RF traffic (Are we in a voting condition?), rfSrcId = %u, rfDstId = %u, netSrcId = %u, netDstId = %u", srcId, dstId, \ LogWarning(LOG_RF, "Traffic collision detect, preempting new RF traffic to existing RF traffic (Are we in a voting condition?), rfSrcId = %u, rfDstId = %u, netSrcId = %u, netDstId = %u", srcId, dstId, \
m_nxdn->m_netLayer3.getSrcId(), m_nxdn->m_netLastDstId); \ m_nxdn->m_netLC.getSrcId(), m_nxdn->m_netLastDstId); \
resetRF(); \ resetRF(); \
return false; \ return false; \
} \ } \
@ -196,15 +196,15 @@ bool Data::process(uint8_t option, uint8_t* data, uint32_t len)
uint8_t buffer[23U]; uint8_t buffer[23U];
udch.getData(buffer); udch.getData(buffer);
data::Layer3 layer3; lc::LC lc;
layer3.decode(buffer, 184U); lc.decode(buffer, 184U);
uint16_t dstId = layer3.getDstId(); uint16_t dstId = lc.getDstId();
uint16_t srcId = layer3.getSrcId(); uint16_t srcId = lc.getSrcId();
bool group = layer3.getGroup(); bool group = lc.getGroup();
if (m_nxdn->m_rfState == RS_RF_LISTENING) { if (m_nxdn->m_rfState == RS_RF_LISTENING) {
uint8_t type = layer3.getMessageType(); uint8_t type = lc.getMessageType();
if (type != MESSAGE_TYPE_DCALL_HDR) if (type != RTCH_MESSAGE_TYPE_DCALL_HDR)
return false; return false;
CHECK_TRAFFIC_COLLISION(srcId, dstId); CHECK_TRAFFIC_COLLISION(srcId, dstId);
@ -215,16 +215,14 @@ bool Data::process(uint8_t option, uint8_t* data, uint32_t len)
// validate destination ID // validate destination ID
VALID_DSTID(srcId, dstId, group); VALID_DSTID(srcId, dstId, group);
uint8_t frames = layer3.getDataBlocks();
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_DCALL ", srcId = %u, dstId = %u, blocks = %u", LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_DCALL ", srcId = %u, dstId = %u, ack = %u, blocksToFollow = %u, padCount = %u, firstFragment = %u, fragmentCount = %u",
srcId, dstId, frames); srcId, dstId, lc.getPacketInfo().getDelivery(), lc.getPacketInfo().getBlockCount(), lc.getPacketInfo().getPadCount(), lc.getPacketInfo().getStart(), lc.getPacketInfo().getFragmentCount());
} }
::ActivityLog("NXDN", true, "RF data transmission from %u to %s%u", srcId, group ? "TG " : "", dstId); ::ActivityLog("NXDN", true, "RF data transmission from %u to %s%u", srcId, group ? "TG " : "", dstId);
m_nxdn->m_rfLayer3 = layer3; m_nxdn->m_rfLC = lc;
m_nxdn->m_voice->m_rfFrames = 0U; m_nxdn->m_voice->m_rfFrames = 0U;
m_nxdn->m_rfState = RS_RF_DATA; m_nxdn->m_rfState = RS_RF_DATA;
@ -244,10 +242,10 @@ bool Data::process(uint8_t option, uint8_t* data, uint32_t len)
lich.setDirection(NXDN_LICH_DIRECTION_INBOUND); lich.setDirection(NXDN_LICH_DIRECTION_INBOUND);
uint8_t type = MESSAGE_TYPE_DCALL_DATA; uint8_t type = RTCH_MESSAGE_TYPE_DCALL_DATA;
if (validUDCH) { if (validUDCH) {
type = layer3.getMessageType(); type = lc.getMessageType();
data[0U] = type == MESSAGE_TYPE_TX_REL ? modem::TAG_EOT : modem::TAG_DATA; data[0U] = type == RTCH_MESSAGE_TYPE_TX_REL ? modem::TAG_EOT : modem::TAG_DATA;
udch.setRAN(m_nxdn->m_ran); udch.setRAN(m_nxdn->m_ran);
udch.encode(data + 2U); udch.encode(data + 2U);
@ -282,10 +280,11 @@ bool Data::process(uint8_t option, uint8_t* data, uint32_t len)
/// Process a data frame from the RF interface. /// Process a data frame from the RF interface.
/// </summary> /// </summary>
/// <param name="option"></param> /// <param name="option"></param>
/// <param name="netLC"></param>
/// <param name="data">Buffer containing data frame.</param> /// <param name="data">Buffer containing data frame.</param>
/// <param name="len">Length of data frame.</param> /// <param name="len">Length of data frame.</param>
/// <returns></returns> /// <returns></returns>
bool Data::processNetwork(uint8_t option, data::Layer3& netLayer3, uint8_t* data, uint32_t len) bool Data::processNetwork(uint8_t option, lc::LC& netLC, uint8_t* data, uint32_t len)
{ {
assert(data != NULL); assert(data != NULL);
@ -305,18 +304,18 @@ bool Data::processNetwork(uint8_t option, data::Layer3& netLayer3, uint8_t* data
uint8_t buffer[23U]; uint8_t buffer[23U];
udch.getData(buffer); udch.getData(buffer);
data::Layer3 layer3; lc::LC lc;
layer3.decode(buffer, 184U); lc.decode(buffer, 184U);
uint16_t dstId = layer3.getDstId(); uint16_t dstId = lc.getDstId();
uint16_t srcId = layer3.getSrcId(); uint16_t srcId = lc.getSrcId();
bool group = layer3.getGroup(); bool group = lc.getGroup();
if (m_nxdn->m_netState == RS_NET_IDLE) { if (m_nxdn->m_netState == RS_NET_IDLE) {
uint8_t type = layer3.getMessageType(); uint8_t type = lc.getMessageType();
if (type != MESSAGE_TYPE_DCALL_HDR) if (type != RTCH_MESSAGE_TYPE_DCALL_HDR)
return false; return false;
CHECK_NET_TRAFFIC_COLLISION(layer3, srcId, dstId); CHECK_NET_TRAFFIC_COLLISION(lc, srcId, dstId);
// validate source RID // validate source RID
VALID_SRCID(srcId, dstId, group); VALID_SRCID(srcId, dstId, group);
@ -324,16 +323,14 @@ bool Data::processNetwork(uint8_t option, data::Layer3& netLayer3, uint8_t* data
// validate destination ID // validate destination ID
VALID_DSTID(srcId, dstId, group); VALID_DSTID(srcId, dstId, group);
uint8_t frames = layer3.getDataBlocks();
if (m_verbose) { if (m_verbose) {
LogMessage(LOG_NET, NXDN_MESSAGE_TYPE_DCALL ", srcId = %u, dstId = %u, blocks = %u", LogMessage(LOG_NET, NXDN_MESSAGE_TYPE_DCALL ", srcId = %u, dstId = %u, ack = %u, blocksToFollow = %u, padCount = %u, firstFragment = %u, fragmentCount = %u",
srcId, dstId, frames); srcId, dstId, lc.getPacketInfo().getDelivery(), lc.getPacketInfo().getBlockCount(), lc.getPacketInfo().getPadCount(), lc.getPacketInfo().getStart(), lc.getPacketInfo().getFragmentCount());
} }
::ActivityLog("NXDN", false, "network data transmission from %u to %s%u", srcId, group ? "TG " : "", dstId); ::ActivityLog("NXDN", false, "network data transmission from %u to %s%u", srcId, group ? "TG " : "", dstId);
m_nxdn->m_netLayer3 = layer3; m_nxdn->m_netLC = lc;
m_nxdn->m_voice->m_netFrames = 0U; m_nxdn->m_voice->m_netFrames = 0U;
m_nxdn->m_netState = RS_NET_DATA; m_nxdn->m_netState = RS_NET_DATA;
@ -351,10 +348,10 @@ bool Data::processNetwork(uint8_t option, data::Layer3& netLayer3, uint8_t* data
lich.setDirection(NXDN_LICH_DIRECTION_OUTBOUND); lich.setDirection(NXDN_LICH_DIRECTION_OUTBOUND);
lich.encode(data + 2U); lich.encode(data + 2U);
uint8_t type = MESSAGE_TYPE_DCALL_DATA; uint8_t type = RTCH_MESSAGE_TYPE_DCALL_DATA;
if (validUDCH) { if (validUDCH) {
type = layer3.getMessageType(); type = lc.getMessageType();
data[0U] = type == MESSAGE_TYPE_TX_REL ? modem::TAG_EOT : modem::TAG_DATA; data[0U] = type == RTCH_MESSAGE_TYPE_TX_REL ? modem::TAG_EOT : modem::TAG_DATA;
udch.setRAN(m_nxdn->m_ran); udch.setRAN(m_nxdn->m_ran);
udch.encode(data + 2U); udch.encode(data + 2U);
@ -427,5 +424,5 @@ void Data::writeNetwork(const uint8_t *data, uint32_t len)
if (m_nxdn->m_rfTimeout.isRunning() && m_nxdn->m_rfTimeout.hasExpired()) if (m_nxdn->m_rfTimeout.isRunning() && m_nxdn->m_rfTimeout.hasExpired())
return; return;
m_network->writeNXDN(m_nxdn->m_rfLayer3, data, len); m_network->writeNXDN(m_nxdn->m_rfLC, data, len);
} }

@ -63,7 +63,7 @@ namespace nxdn
/// <summary>Process a data frame from the RF interface.</summary> /// <summary>Process a data frame from the RF interface.</summary>
virtual bool process(uint8_t option, uint8_t* data, uint32_t len); virtual bool process(uint8_t option, uint8_t* data, uint32_t len);
/// <summary>Process a data frame from the network.</summary> /// <summary>Process a data frame from the network.</summary>
virtual bool processNetwork(uint8_t option, data::Layer3& netLayer3, uint8_t* data, uint32_t len); virtual bool processNetwork(uint8_t option, lc::LC& netLC, uint8_t* data, uint32_t len);
protected: protected:
friend class nxdn::Control; friend class nxdn::Control;

@ -64,9 +64,9 @@ using namespace nxdn::packet;
} \ } \
\ \
if (m_nxdn->m_netState != RS_NET_IDLE) { \ if (m_nxdn->m_netState != RS_NET_IDLE) { \
if (m_nxdn->m_netLayer3.getSrcId() == _SRC_ID && m_nxdn->m_netLastDstId == _DST_ID) { \ if (m_nxdn->m_netLC.getSrcId() == _SRC_ID && m_nxdn->m_netLastDstId == _DST_ID) { \
LogWarning(LOG_RF, "Traffic collision detect, preempting new RF traffic to existing RF traffic (Are we in a voting condition?), rfSrcId = %u, rfDstId = %u, netSrcId = %u, netDstId = %u", srcId, dstId, \ LogWarning(LOG_RF, "Traffic collision detect, preempting new RF traffic to existing RF traffic (Are we in a voting condition?), rfSrcId = %u, rfDstId = %u, netSrcId = %u, netDstId = %u", srcId, dstId, \
m_nxdn->m_netLayer3.getSrcId(), m_nxdn->m_netLastDstId); \ m_nxdn->m_netLC.getSrcId(), m_nxdn->m_netLastDstId); \
resetRF(); \ resetRF(); \
return false; \ return false; \
} \ } \
@ -210,21 +210,22 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
uint8_t buffer[10U]; uint8_t buffer[10U];
facch.getData(buffer); facch.getData(buffer);
data::Layer3 layer3; lc::LC lc;
layer3.decode(buffer, NXDN_FACCH1_LENGTH_BITS); lc.decode(buffer, NXDN_FACCH1_LENGTH_BITS);
uint16_t dstId = layer3.getDstId(); uint16_t dstId = lc.getDstId();
uint16_t srcId = layer3.getSrcId(); uint16_t srcId = lc.getSrcId();
bool group = layer3.getGroup(); bool group = lc.getGroup();
bool encrypted = lc.getEncrypted();
uint8_t type = layer3.getMessageType(); uint8_t type = lc.getMessageType();
if (type == MESSAGE_TYPE_TX_REL) { if (type == RTCH_MESSAGE_TYPE_TX_REL) {
if (m_nxdn->m_rfState != RS_RF_AUDIO) { if (m_nxdn->m_rfState != RS_RF_AUDIO) {
m_nxdn->m_rfState = RS_RF_LISTENING; m_nxdn->m_rfState = RS_RF_LISTENING;
m_nxdn->m_rfMask = 0x00U; m_nxdn->m_rfMask = 0x00U;
m_nxdn->m_rfLayer3.reset(); m_nxdn->m_rfLC.reset();
return false; return false;
} }
} else if (type == MESSAGE_TYPE_VCALL) { } else if (type == RTCH_MESSAGE_TYPE_VCALL) {
CHECK_TRAFFIC_COLLISION(srcId, dstId); CHECK_TRAFFIC_COLLISION(srcId, dstId);
// validate source RID // validate source RID
@ -236,7 +237,7 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
return false; return false;
} }
m_nxdn->m_rfLayer3 = layer3; m_nxdn->m_rfLC = lc;
Sync::addNXDNSync(data + 2U); Sync::addNXDNSync(data + 2U);
@ -250,9 +251,9 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
// generate the SACCH // generate the SACCH
channel::SACCH sacch; channel::SACCH sacch;
sacch.setData(SACCH_IDLE);
sacch.setRAN(m_nxdn->m_ran); sacch.setRAN(m_nxdn->m_ran);
sacch.setStructure(NXDN_SR_SINGLE); sacch.setStructure(NXDN_SR_SINGLE);
sacch.setData(SACCH_IDLE);
sacch.encode(data + 2U); sacch.encode(data + 2U);
facch.encode(data + 2U, NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS); facch.encode(data + 2U, NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS);
@ -263,7 +264,7 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
writeNetwork(data, NXDN_FRAME_LENGTH_BYTES + 2U); writeNetwork(data, NXDN_FRAME_LENGTH_BYTES + 2U);
if (m_nxdn->m_duplex) { if (m_nxdn->m_duplex) {
data[0U] = type == MESSAGE_TYPE_TX_REL ? modem::TAG_EOT : modem::TAG_DATA; data[0U] = type == RTCH_MESSAGE_TYPE_TX_REL ? modem::TAG_EOT : modem::TAG_DATA;
data[1U] = 0x00U; data[1U] = 0x00U;
m_nxdn->addFrame(data, NXDN_FRAME_LENGTH_BYTES + 2U); m_nxdn->addFrame(data, NXDN_FRAME_LENGTH_BYTES + 2U);
@ -297,7 +298,12 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
m_nxdn->m_aveRSSI = m_nxdn->m_rssi; m_nxdn->m_aveRSSI = m_nxdn->m_rssi;
m_nxdn->m_rssiCount = 1U; m_nxdn->m_rssiCount = 1U;
::ActivityLog("NXDN", true, "RF voice transmission from %u to %s%u", srcId, group ? "TG " : "", dstId); if (m_verbose) {
LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%02X",
srcId, dstId, group, lc.getEmergency(), encrypted, lc.getPriority(), lc.getAlgId(), lc.getKId());
}
::ActivityLog("NXDN", true, "RF %svoice transmission from %u to %s%u", encrypted ? "encrypted " : "", srcId, group ? "TG " : "", dstId);
} }
return true; return true;
@ -326,14 +332,14 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
uint8_t buffer[10U]; uint8_t buffer[10U];
facch.getData(buffer); facch.getData(buffer);
data::Layer3 layer3; lc::LC lc;
layer3.decode(buffer, NXDN_FACCH1_LENGTH_BITS); lc.decode(buffer, NXDN_FACCH1_LENGTH_BITS);
hasInfo = layer3.getMessageType() == MESSAGE_TYPE_VCALL; hasInfo = lc.getMessageType() == RTCH_MESSAGE_TYPE_VCALL;
if (!hasInfo) if (!hasInfo)
return false; return false;
m_nxdn->m_rfLayer3 = layer3; m_nxdn->m_rfLC = lc;
} }
if (!hasInfo) { if (!hasInfo) {
@ -343,23 +349,23 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
uint8_t structure = sacch.getStructure(); uint8_t structure = sacch.getStructure();
switch (structure) { switch (structure) {
case NXDN_SR_1_4: case NXDN_SR_1_4:
m_nxdn->m_rfLayer3.decode(message, 18U, 0U); m_nxdn->m_rfLC.decode(message, 18U, 0U);
if(m_nxdn->m_rfLayer3.getMessageType() == MESSAGE_TYPE_VCALL) if(m_nxdn->m_rfLC.getMessageType() == RTCH_MESSAGE_TYPE_VCALL)
m_nxdn->m_rfMask = 0x01U; m_nxdn->m_rfMask = 0x01U;
else else
m_nxdn->m_rfMask = 0x00U; m_nxdn->m_rfMask = 0x00U;
break; break;
case NXDN_SR_2_4: case NXDN_SR_2_4:
m_nxdn->m_rfMask |= 0x02U; m_nxdn->m_rfMask |= 0x02U;
m_nxdn->m_rfLayer3.decode(message, 18U, 18U); m_nxdn->m_rfLC.decode(message, 18U, 18U);
break; break;
case NXDN_SR_3_4: case NXDN_SR_3_4:
m_nxdn->m_rfMask |= 0x04U; m_nxdn->m_rfMask |= 0x04U;
m_nxdn->m_rfLayer3.decode(message, 18U, 36U); m_nxdn->m_rfLC.decode(message, 18U, 36U);
break; break;
case NXDN_SR_4_4: case NXDN_SR_4_4:
m_nxdn->m_rfMask |= 0x08U; m_nxdn->m_rfMask |= 0x08U;
m_nxdn->m_rfLayer3.decode(message, 18U, 54U); m_nxdn->m_rfLC.decode(message, 18U, 54U);
break; break;
default: default:
break; break;
@ -368,14 +374,15 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
if (m_nxdn->m_rfMask != 0x0FU) if (m_nxdn->m_rfMask != 0x0FU)
return false; return false;
uint8_t type = m_nxdn->m_rfLayer3.getMessageType(); uint8_t type = m_nxdn->m_rfLC.getMessageType();
if (type != MESSAGE_TYPE_VCALL) if (type != RTCH_MESSAGE_TYPE_VCALL)
return false; return false;
} }
uint16_t dstId = m_nxdn->m_rfLayer3.getDstId(); uint16_t dstId = m_nxdn->m_rfLC.getDstId();
uint16_t srcId = m_nxdn->m_rfLayer3.getSrcId(); uint16_t srcId = m_nxdn->m_rfLC.getSrcId();
bool group = m_nxdn->m_rfLayer3.getGroup(); bool group = m_nxdn->m_rfLC.getGroup();
bool encrypted = m_nxdn->m_rfLC.getEncrypted();
CHECK_TRAFFIC_COLLISION(srcId, dstId); CHECK_TRAFFIC_COLLISION(srcId, dstId);
@ -396,7 +403,12 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
m_nxdn->m_aveRSSI = m_nxdn->m_rssi; m_nxdn->m_aveRSSI = m_nxdn->m_rssi;
m_nxdn->m_rssiCount = 1U; m_nxdn->m_rssiCount = 1U;
::ActivityLog("NXDN", true, "RF late entry from %u to %s%u", srcId, group ? "TG " : "", dstId); if (m_verbose) {
LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%04X",
srcId, dstId, group, m_nxdn->m_rfLC.getEmergency(), encrypted, m_nxdn->m_rfLC.getPriority(), m_nxdn->m_rfLC.getAlgId(), m_nxdn->m_rfLC.getKId());
}
::ActivityLog("NXDN", true, "RF %slate entry from %u to %s%u", encrypted ? "encrypted ": "", srcId, group ? "TG " : "", dstId);
// create a dummy start message // create a dummy start message
uint8_t start[NXDN_FRAME_LENGTH_BYTES + 2U]; uint8_t start[NXDN_FRAME_LENGTH_BYTES + 2U];
@ -417,13 +429,13 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
// generate the SACCH // generate the SACCH
channel::SACCH sacch; channel::SACCH sacch;
sacch.setData(SACCH_IDLE);
sacch.setRAN(m_nxdn->m_ran); sacch.setRAN(m_nxdn->m_ran);
sacch.setStructure(NXDN_SR_SINGLE); sacch.setStructure(NXDN_SR_SINGLE);
sacch.setData(SACCH_IDLE);
sacch.encode(start + 2U); sacch.encode(start + 2U);
uint8_t message[22U]; uint8_t message[22U];
m_nxdn->m_rfLayer3.getData(message); m_nxdn->m_rfLC.getData(message);
facch.setData(message); facch.setData(message);
facch.encode(start + 2U, NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS); facch.encode(start + 2U, NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS);
@ -482,11 +494,6 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", audio, errs = %u/141 (%.1f%%)", LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", audio, errs = %u/141 (%.1f%%)",
errors, float(errors) / 1.88F); errors, float(errors) / 1.88F);
} }
/*
Audio audio;
audio.decode(data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 0U, netData + 5U + 0U);
audio.decode(data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 18U, netData + 5U + 14U);
*/
} else if (option == NXDN_LICH_STEAL_FACCH1_1) { } else if (option == NXDN_LICH_STEAL_FACCH1_1) {
channel::FACCH1 facch1; channel::FACCH1 facch1;
bool valid = facch1.decode(data + 2U, NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS); bool valid = facch1.decode(data + 2U, NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS);
@ -507,10 +514,6 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", audio, errs = %u/94 (%.1f%%)", LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", audio, errs = %u/94 (%.1f%%)",
errors, float(errors) / 0.94F); errors, float(errors) / 0.94F);
} }
/*
Audio audio;
audio.decode(data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 18U, netData + 5U + 14U);
*/
} else if (option == NXDN_LICH_STEAL_FACCH1_2) { } else if (option == NXDN_LICH_STEAL_FACCH1_2) {
edac::AMBEFEC ambe; edac::AMBEFEC ambe;
@ -526,10 +529,7 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", audio, errs = %u/94 (%.1f%%)", LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_VCALL ", audio, errs = %u/94 (%.1f%%)",
errors, float(errors) / 0.94F); errors, float(errors) / 0.94F);
} }
/*
Audio audio;
audio.decode(data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 0U, netData + 5U + 0U);
*/
channel::FACCH1 facch1; channel::FACCH1 facch1;
bool valid = facch1.decode(data + 2U, NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS); bool valid = facch1.decode(data + 2U, NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS);
if (valid) if (valid)
@ -571,10 +571,11 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
/// </summary> /// </summary>
/// <param name="usc"></param> /// <param name="usc"></param>
/// <param name="option"></param> /// <param name="option"></param>
/// <param name="netLC"></param>
/// <param name="data">Buffer containing data frame.</param> /// <param name="data">Buffer containing data frame.</param>
/// <param name="len">Length of data frame.</param> /// <param name="len">Length of data frame.</param>
/// <returns></returns> /// <returns></returns>
bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3, uint8_t* data, uint32_t len) bool Voice::processNetwork(uint8_t usc, uint8_t option, lc::LC& netLC, uint8_t *data, uint32_t len)
{ {
assert(data != NULL); assert(data != NULL);
@ -600,22 +601,23 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
uint8_t buffer[10U]; uint8_t buffer[10U];
facch.getData(buffer); facch.getData(buffer);
data::Layer3 layer3; lc::LC lc;
layer3.decode(buffer, NXDN_FACCH1_LENGTH_BITS); lc.decode(buffer, NXDN_FACCH1_LENGTH_BITS);
uint16_t dstId = layer3.getDstId(); uint16_t dstId = lc.getDstId();
uint16_t srcId = layer3.getSrcId(); uint16_t srcId = lc.getSrcId();
bool group = layer3.getGroup(); bool group = lc.getGroup();
bool encrypted = lc.getEncrypted();
uint8_t type = layer3.getMessageType(); uint8_t type = lc.getMessageType();
if (type == MESSAGE_TYPE_TX_REL) { if (type == RTCH_MESSAGE_TYPE_TX_REL) {
if (m_nxdn->m_netState != RS_NET_AUDIO) { if (m_nxdn->m_netState != RS_NET_AUDIO) {
m_nxdn->m_netState = RS_NET_IDLE; m_nxdn->m_netState = RS_NET_IDLE;
m_nxdn->m_netMask = 0x00U; m_nxdn->m_netMask = 0x00U;
m_nxdn->m_netLayer3.reset(); m_nxdn->m_netLC.reset();
return false; return false;
} }
} else if (type == MESSAGE_TYPE_VCALL) { } else if (type == RTCH_MESSAGE_TYPE_VCALL) {
CHECK_NET_TRAFFIC_COLLISION(layer3, srcId, dstId); CHECK_NET_TRAFFIC_COLLISION(lc, srcId, dstId);
// validate source RID // validate source RID
VALID_SRCID(srcId, dstId, group); VALID_SRCID(srcId, dstId, group);
@ -626,7 +628,7 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
return false; return false;
} }
m_nxdn->m_netLayer3 = layer3; m_nxdn->m_netLC = lc;
Sync::addNXDNSync(data + 2U); Sync::addNXDNSync(data + 2U);
@ -640,9 +642,9 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
// generate the SACCH // generate the SACCH
channel::SACCH sacch; channel::SACCH sacch;
sacch.setData(SACCH_IDLE);
sacch.setRAN(m_nxdn->m_ran); sacch.setRAN(m_nxdn->m_ran);
sacch.setStructure(NXDN_SR_SINGLE); sacch.setStructure(NXDN_SR_SINGLE);
sacch.setData(SACCH_IDLE);
sacch.encode(data + 2U); sacch.encode(data + 2U);
facch.encode(data + 2U, NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS); facch.encode(data + 2U, NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS);
@ -651,7 +653,7 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
m_nxdn->scrambler(data + 2U); m_nxdn->scrambler(data + 2U);
if (m_nxdn->m_duplex) { if (m_nxdn->m_duplex) {
data[0U] = type == MESSAGE_TYPE_TX_REL ? modem::TAG_EOT : modem::TAG_DATA; data[0U] = type == RTCH_MESSAGE_TYPE_TX_REL ? modem::TAG_EOT : modem::TAG_DATA;
data[1U] = 0x00U; data[1U] = 0x00U;
m_nxdn->addFrame(data, NXDN_FRAME_LENGTH_BYTES + 2U, true); m_nxdn->addFrame(data, NXDN_FRAME_LENGTH_BYTES + 2U, true);
@ -671,7 +673,12 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
m_nxdn->m_netTimeout.start(); m_nxdn->m_netTimeout.start();
m_nxdn->m_netState = RS_NET_AUDIO; m_nxdn->m_netState = RS_NET_AUDIO;
::ActivityLog("NXDN", false, "network voice transmission from %u to %s%u", srcId, group ? "TG " : "", dstId); if (m_verbose) {
LogMessage(LOG_NET, NXDN_MESSAGE_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%02X",
srcId, dstId, group, lc.getEmergency(), encrypted, lc.getPriority(), lc.getAlgId(), lc.getKId());
}
::ActivityLog("NXDN", false, "network %svoice transmission from %u to %s%u", encrypted ? "encrypted " : "", srcId, group ? "TG " : "", dstId);
} }
return true; return true;
@ -700,14 +707,14 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
uint8_t buffer[10U]; uint8_t buffer[10U];
facch.getData(buffer); facch.getData(buffer);
data::Layer3 layer3; lc::LC lc;
layer3.decode(buffer, NXDN_FACCH1_LENGTH_BITS); lc.decode(buffer, NXDN_FACCH1_LENGTH_BITS);
hasInfo = layer3.getMessageType() == MESSAGE_TYPE_VCALL; hasInfo = lc.getMessageType() == RTCH_MESSAGE_TYPE_VCALL;
if (!hasInfo) if (!hasInfo)
return false; return false;
m_nxdn->m_netLayer3 = layer3; m_nxdn->m_netLC = lc;
} }
if (!hasInfo) { if (!hasInfo) {
@ -717,23 +724,23 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
uint8_t structure = sacch.getStructure(); uint8_t structure = sacch.getStructure();
switch (structure) { switch (structure) {
case NXDN_SR_1_4: case NXDN_SR_1_4:
m_nxdn->m_netLayer3.decode(message, 18U, 0U); m_nxdn->m_netLC.decode(message, 18U, 0U);
if(m_nxdn->m_netLayer3.getMessageType() == MESSAGE_TYPE_VCALL) if(m_nxdn->m_netLC.getMessageType() == RTCH_MESSAGE_TYPE_VCALL)
m_nxdn->m_netMask = 0x01U; m_nxdn->m_netMask = 0x01U;
else else
m_nxdn->m_netMask = 0x00U; m_nxdn->m_netMask = 0x00U;
break; break;
case NXDN_SR_2_4: case NXDN_SR_2_4:
m_nxdn->m_netMask |= 0x02U; m_nxdn->m_netMask |= 0x02U;
m_nxdn->m_netLayer3.decode(message, 18U, 18U); m_nxdn->m_netLC.decode(message, 18U, 18U);
break; break;
case NXDN_SR_3_4: case NXDN_SR_3_4:
m_nxdn->m_netMask |= 0x04U; m_nxdn->m_netMask |= 0x04U;
m_nxdn->m_netLayer3.decode(message, 18U, 36U); m_nxdn->m_netLC.decode(message, 18U, 36U);
break; break;
case NXDN_SR_4_4: case NXDN_SR_4_4:
m_nxdn->m_netMask |= 0x08U; m_nxdn->m_netMask |= 0x08U;
m_nxdn->m_netLayer3.decode(message, 18U, 54U); m_nxdn->m_netLC.decode(message, 18U, 54U);
break; break;
default: default:
break; break;
@ -742,16 +749,17 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
if (m_nxdn->m_netMask != 0x0FU) if (m_nxdn->m_netMask != 0x0FU)
return false; return false;
uint8_t type = m_nxdn->m_netLayer3.getMessageType(); uint8_t type = m_nxdn->m_netLC.getMessageType();
if (type != MESSAGE_TYPE_VCALL) if (type != RTCH_MESSAGE_TYPE_VCALL)
return false; return false;
} }
uint16_t dstId = m_nxdn->m_netLayer3.getDstId(); uint16_t dstId = m_nxdn->m_netLC.getDstId();
uint16_t srcId = m_nxdn->m_netLayer3.getSrcId(); uint16_t srcId = m_nxdn->m_netLC.getSrcId();
bool group = m_nxdn->m_netLayer3.getGroup(); bool group = m_nxdn->m_netLC.getGroup();
bool encrypted = m_nxdn->m_netLC.getEncrypted();
CHECK_NET_TRAFFIC_COLLISION(m_nxdn->m_netLayer3, srcId, dstId); CHECK_NET_TRAFFIC_COLLISION(m_nxdn->m_netLC, srcId, dstId);
// validate source RID // validate source RID
VALID_SRCID(srcId, dstId, group); VALID_SRCID(srcId, dstId, group);
@ -765,7 +773,12 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
m_nxdn->m_netTimeout.start(); m_nxdn->m_netTimeout.start();
m_nxdn->m_netState = RS_NET_AUDIO; m_nxdn->m_netState = RS_NET_AUDIO;
::ActivityLog("NXDN", false, "network late entry from %u to %s%u", srcId, group ? "TG " : "", dstId); if (m_verbose) {
LogMessage(LOG_NET, NXDN_MESSAGE_TYPE_VCALL ", srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, algo = $%02X, kid = $%04X",
srcId, dstId, group, m_nxdn->m_netLC.getEmergency(), encrypted, m_nxdn->m_netLC.getPriority(), m_nxdn->m_netLC.getAlgId(), m_nxdn->m_netLC.getKId());
}
::ActivityLog("NXDN", false, "network %slate entry from %u to %s%u", encrypted ? "encrypted ": "", srcId, group ? "TG " : "", dstId);
// create a dummy start message // create a dummy start message
uint8_t start[NXDN_FRAME_LENGTH_BYTES + 2U]; uint8_t start[NXDN_FRAME_LENGTH_BYTES + 2U];
@ -784,13 +797,13 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
// generate the SACCH // generate the SACCH
channel::SACCH sacch; channel::SACCH sacch;
sacch.setData(SACCH_IDLE);
sacch.setRAN(m_nxdn->m_ran); sacch.setRAN(m_nxdn->m_ran);
sacch.setStructure(NXDN_SR_SINGLE); sacch.setStructure(NXDN_SR_SINGLE);
sacch.setData(SACCH_IDLE);
sacch.encode(start + 2U); sacch.encode(start + 2U);
uint8_t message[22U]; uint8_t message[22U];
m_nxdn->m_rfLayer3.getData(message); m_nxdn->m_rfLC.getData(message);
facch.setData(message); facch.setData(message);
facch.encode(start + 2U, NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS); facch.encode(start + 2U, NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS);
@ -965,5 +978,5 @@ void Voice::writeNetwork(const uint8_t *data, uint32_t len)
if (m_nxdn->m_rfTimeout.isRunning() && m_nxdn->m_rfTimeout.hasExpired()) if (m_nxdn->m_rfTimeout.isRunning() && m_nxdn->m_rfTimeout.hasExpired())
return; return;
m_network->writeNXDN(m_nxdn->m_rfLayer3, data, len); m_network->writeNXDN(m_nxdn->m_rfLC, data, len);
} }

@ -64,7 +64,7 @@ namespace nxdn
/// <summary>Process a data frame from the RF interface.</summary> /// <summary>Process a data frame from the RF interface.</summary>
virtual bool process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len); virtual bool process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len);
/// <summary>Process a data frame from the network.</summary> /// <summary>Process a data frame from the network.</summary>
virtual bool processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3, uint8_t* data, uint32_t len); virtual bool processNetwork(uint8_t usc, uint8_t option, lc::LC& netLC, uint8_t* data, uint32_t len);
protected: protected:
friend class packet::Data; friend class packet::Data;

Loading…
Cancel
Save

Powered by TurnKey Linux.