diff --git a/Makefile b/Makefile
index eb6fb659..1db893d0 100644
--- a/Makefile
+++ b/Makefile
@@ -68,7 +68,8 @@ HOST_OBJECTS = \
nxdn/channel/LICH.o \
nxdn/channel/SACCH.o \
nxdn/channel/UDCH.o \
- nxdn/data/Layer3.o \
+ nxdn/lc/LC.o \
+ nxdn/lc/PacketInformation.o \
nxdn/packet/Data.o \
nxdn/packet/Voice.o \
nxdn/Audio.o \
diff --git a/network/BaseNetwork.cpp b/network/BaseNetwork.cpp
index 8df9c56e..b560edef 100644
--- a/network/BaseNetwork.cpp
+++ b/network/BaseNetwork.cpp
@@ -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.
///
///
-///
+///
///
///
-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) {
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);
- layer3.setMessageType(messageType);
- layer3.setSrcId((uint16_t)srcId & 0xFFFFU);
- layer3.setDstId((uint16_t)dstId & 0xFFFFU);
+ lc.setMessageType(messageType);
+ lc.setSrcId((uint16_t)srcId & 0xFFFFU);
+ lc.setDstId((uint16_t)dstId & 0xFFFFU);
bool group = (m_buffer[15U] & 0x40U) == 0x40U ? false : true;
- layer3.setGroup(group);
+ lc.setGroup(group);
uint8_t* data = NULL;
len = m_buffer[23U];
@@ -483,11 +483,11 @@ bool BaseNetwork::writeP25PDU(const p25::data::DataHeader& header, const p25::da
///
/// Writes NXDN frame data to the network.
///
-///
+///
///
///
///
-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)
return false;
@@ -498,7 +498,7 @@ bool BaseNetwork::writeNXDN(const nxdn::data::Layer3& layer3, const uint8_t* dat
m_streamId[0] = m_nxdnStreamId;
- return writeNXDN(m_id, m_nxdnStreamId, layer3, data, len);
+ return writeNXDN(m_id, m_nxdnStreamId, lc, data, len);
}
///
@@ -1082,11 +1082,11 @@ bool BaseNetwork::writeP25PDU(const uint32_t id, const uint32_t streamId, const
///
///
///
-///
+///
///
///
///
-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)
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);
- 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);
- uint32_t dstId = layer3.getDstId(); // Target Address
+ uint32_t dstId = lc.getDstId(); // Target Address
__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
diff --git a/network/BaseNetwork.h b/network/BaseNetwork.h
index 1915f3bb..52b2bec6 100644
--- a/network/BaseNetwork.h
+++ b/network/BaseNetwork.h
@@ -43,7 +43,7 @@
#include "p25/lc/TSBK.h"
#include "p25/lc/TDULC.h"
#include "p25/Audio.h"
-#include "nxdn/data/Layer3.h"
+#include "nxdn/lc/LC.h"
#include "network/UDPSocket.h"
#include "RingBuffer.h"
#include "Timer.h"
@@ -141,7 +141,7 @@ namespace network
/// Reads P25 frame data from the P25 ring buffer.
virtual uint8_t* readP25(bool& ret, p25::lc::LC& control, p25::data::LowSpeedData& lsd, uint8_t& duid, uint32_t& len);
/// Reads NXDN frame data from the NXDN ring buffer.
- 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);
/// Reads a channel grant request from the network.
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);
/// Writes NXDN frame data to the network.
- 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);
/// Writes a channel grant request to the network.
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,
const uint8_t* data, const uint32_t len);
/// Writes NXDN frame data to the network.
- 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);
/// Writes data to the network.
virtual bool write(const uint8_t* data, uint32_t length);
diff --git a/nxdn/Control.cpp b/nxdn/Control.cpp
index c81a2882..1bd8e265 100644
--- a/nxdn/Control.cpp
+++ b/nxdn/Control.cpp
@@ -93,8 +93,8 @@ Control::Control(uint32_t ran, uint32_t callHang, uint32_t queueSize, uint32_t t
m_network(network),
m_duplex(duplex),
m_rfLastLICH(),
- m_rfLayer3(),
- m_netLayer3(),
+ m_rfLC(),
+ m_netLC(),
m_rfMask(0U),
m_netMask(0U),
m_idenTable(idenTable),
@@ -162,12 +162,12 @@ void Control::reset()
m_queue.clear();
m_rfMask = 0x00U;
- m_rfLayer3.reset();
+ m_rfLC.reset();
m_netState = RS_NET_IDLE;
m_netMask = 0x00U;
- m_netLayer3.reset();
+ m_netLC.reset();
}
///
@@ -251,7 +251,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len)
if (type == modem::TAG_LOST) {
m_rfState = RS_RF_LISTENING;
m_rfMask = 0x00U;
- m_rfLayer3.reset();
+ m_rfLC.reset();
return false;
}
@@ -463,11 +463,11 @@ void Control::processNetwork()
if (m_rfState != RS_RF_LISTENING && m_netState == RS_NET_IDLE)
return;
- data::Layer3 layer3;
+ lc::LC lc;
uint32_t length = 100U;
bool ret = false;
- uint8_t* data = m_network->readNXDN(ret, layer3, length);
+ uint8_t* data = m_network->readNXDN(ret, lc, length);
if (!ret)
return;
if (length == 0U)
@@ -496,10 +496,10 @@ void Control::processNetwork()
switch (usc) {
case NXDN_LICH_USC_UDCH:
- ret = m_data->processNetwork(option, layer3, data, length);
+ ret = m_data->processNetwork(option, lc, data, length);
break;
default:
- ret = m_voice->processNetwork(usc, option, layer3, data, length);
+ ret = m_voice->processNetwork(usc, option, lc, data, length);
break;
}
@@ -514,7 +514,7 @@ void Control::writeEndRF()
m_rfState = RS_RF_LISTENING;
m_rfMask = 0x00U;
- m_rfLayer3.reset();
+ m_rfLC.reset();
m_rfTimeout.stop();
}
@@ -527,7 +527,7 @@ void Control::writeEndNet()
m_netState = RS_NET_IDLE;
m_netMask = 0x00U;
- m_netLayer3.reset();
+ m_netLC.reset();
m_netTimeout.stop();
m_networkWatchdog.stop();
diff --git a/nxdn/Control.h b/nxdn/Control.h
index 70a2c32f..63eb2688 100644
--- a/nxdn/Control.h
+++ b/nxdn/Control.h
@@ -34,7 +34,7 @@
#include "Defines.h"
#include "nxdn/NXDNDefines.h"
#include "nxdn/channel/LICH.h"
-#include "nxdn/data/Layer3.h"
+#include "nxdn/lc/LC.h"
#include "nxdn/packet/Voice.h"
#include "nxdn/packet/Data.h"
#include "network/BaseNetwork.h"
@@ -111,8 +111,8 @@ namespace nxdn
bool m_duplex;
channel::LICH m_rfLastLICH;
- data::Layer3 m_rfLayer3;
- data::Layer3 m_netLayer3;
+ lc::LC m_rfLC;
+ lc::LC m_netLC;
uint8_t m_rfMask;
uint8_t m_netMask;
diff --git a/nxdn/NXDNDefines.h b/nxdn/NXDNDefines.h
index d24b6ca2..a23d2743 100644
--- a/nxdn/NXDNDefines.h
+++ b/nxdn/NXDNDefines.h
@@ -91,44 +91,74 @@ namespace nxdn
const uint8_t NXDN_SR_2_4 = 2U;
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 MAX_NXDN_VOICE_ERRORS = 144U;
const uint32_t MAX_NXDN_VOICE_ERRORS_STEAL = 94U;
- // Message Types
- const uint8_t MESSAGE_TYPE_VCALL = 0x01U;
- const uint8_t MESSAGE_TYPE_VCALL_IV = 0x03U;
- const uint8_t MESSAGE_TYPE_DCALL_HDR = 0x09U;
- const uint8_t MESSAGE_TYPE_DCALL_DATA = 0x0BU;
- const uint8_t MESSAGE_TYPE_DCALL_ACK = 0x0CU;
- const uint8_t MESSAGE_TYPE_TX_REL = 0x08U;
- const uint8_t MESSAGE_TYPE_HEAD_DLY = 0x0FU;
- const uint8_t MESSAGE_TYPE_SDCALL_REQ_HDR = 0x38U;
- const uint8_t MESSAGE_TYPE_SDCALL_REQ_DATA = 0x39U;
- const uint8_t MESSAGE_TYPE_SDCALL_RESP = 0x3BU;
- const uint8_t MESSAGE_TYPE_SDCALL_IV = 0x3AU;
- const uint8_t MESSAGE_TYPE_STAT_INQ_REQ = 0x30U;
- const uint8_t MESSAGE_TYPE_STAT_INQ_RESP = 0x31U;
- const uint8_t MESSAGE_TYPE_STAT_REQ = 0x32U;
- const uint8_t MESSAGE_TYPE_STAT_RESP = 0x33U;
- const uint8_t MESSAGE_TYPE_REM_CON_REQ = 0x34U;
- const uint8_t MESSAGE_TYPE_REM_CON_RESP = 0x35U;
- const uint8_t MESSAGE_TYPE_IDLE = 0x10U;
- const uint8_t MESSAGE_TYPE_AUTH_INQ_REQ = 0x28U;
- const uint8_t MESSAGE_TYPE_AUTH_INQ_RESP = 0x29U;
- const uint8_t MESSAGE_TYPE_PROP_FORM = 0x3FU;
-
- // Voice Call Options
- const uint8_t VOICE_CALL_OPTION_HALF_DUPLEX = 0x00U;
- const uint8_t VOICE_CALL_OPTION_DUPLEX = 0x10U;
-
- // Data Call Options
- const uint8_t DATA_CALL_OPTION_HALF_DUPLEX = 0x00U;
- const uint8_t DATA_CALL_OPTION_DUPLEX = 0x10U;
- const uint8_t DATA_CALL_OPTION_4800 = 0x00U;
- const uint8_t DATA_CALL_OPTION_9600 = 0x02U;
-
- const uint8_t SACCH_IDLE[] = { MESSAGE_TYPE_IDLE, 0x00U, 0x00U };
+ const uint32_t NXDN_MI_LENGTH_BYTES = 8U;
+ const uint32_t NXDN_PCKT_INFO_LENGTH_BYTES = 3U;
+
+ const uint8_t NXDN_CIPHER_TYPE_NONE = 0x00U;
+
+ const uint8_t DATA_RSP_CLASS_ACK = 0x00U;
+ const uint8_t DATA_RSP_CLASS_ACK_S = 0x01U;
+ const uint8_t DATA_RSP_CLASS_NACK = 0x03U;
+
+ const uint8_t NXDN_CAUSE_RESOURCE_NOT_AVAIL = 0x05U;
+ const uint8_t NXDN_CAUSE_SVC_UNAVAILABLE = 0x06U;
+ const uint8_t NXDN_CAUSE_PROC_ERROR = 0x07U;
+
+ const uint8_t NXDN_CAUSE_MM_NORMAL_1 = 0x01U;
+ const uint8_t NXDN_CAUSE_MM_NORMAL_2 = 0x04U;
+
+ const uint8_t NXDN_CAUSE_VD_NORMAL_1 = 0x01U;
+ const uint8_t NXDN_CAUSE_VD_NORMAL_2 = 0x02U;
+ const uint8_t NXDN_CAUSE_VD_QUEUED = 0x03U;
+
+ const uint8_t NXDN_CAUSE_SS_NORMAL = 0x00U;
+ const uint8_t NXDN_CAUSE_SS_NORMAL_1 = 0x01U;
+ const uint8_t NXDN_CAUSE_SS_NORMAL_2 = 0x02U;
+
+ const uint8_t NXDN_CAUSE_DREQ_NORMAL = 0x01U;
+
+ const uint8_t NXDN_CAUSE_DISC_NORMAL = 0x01U;
+ const uint8_t NXDN_CAUSE_DISC_NORMAL_TC = 0x02U;
+
+ // Common Message Types
+ const uint8_t MESSAGE_TYPE_IDLE = 0x10U; // IDLE - Idle
+
+ // Traffic Channel Message Types
+ 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
#endif // __NXDN_DEFINES_H__
diff --git a/nxdn/channel/FACCH1.cpp b/nxdn/channel/FACCH1.cpp
index 80a5664c..4d79d9dd 100644
--- a/nxdn/channel/FACCH1.cpp
+++ b/nxdn/channel/FACCH1.cpp
@@ -69,25 +69,24 @@ const uint32_t PUNCTURE_LIST[] = {
// ---------------------------------------------------------------------------
///
-/// Initializes a copy instance of the FACCH1 class.
+/// Initializes a new instance of the FACCH1 class.
///
-///
-FACCH1::FACCH1(const FACCH1& data) :
+FACCH1::FACCH1() :
m_verbose(false),
m_data(NULL)
{
m_data = new uint8_t[12U];
- ::memcpy(m_data, data.m_data, 12U);
}
///
-/// Initializes a new instance of the FACCH1 class.
+/// Initializes a copy instance of the FACCH1 class.
///
-FACCH1::FACCH1() :
+///
+FACCH1::FACCH1(const FACCH1& data) :
m_verbose(false),
m_data(NULL)
{
- m_data = new uint8_t[12U];
+ copy(data);
}
///
@@ -254,3 +253,19 @@ void FACCH1::setData(const uint8_t* data)
::memcpy(m_data, data, 10U);
}
+
+// ---------------------------------------------------------------------------
+// Private Class Members
+// ---------------------------------------------------------------------------
+
+///
+/// Internal helper to copy the the class.
+///
+///
+void FACCH1::copy(const FACCH1& data)
+{
+ m_data = new uint8_t[12U];
+ ::memcpy(m_data, data.m_data, 12U);
+
+ m_verbose = data.m_verbose;
+}
diff --git a/nxdn/channel/FACCH1.h b/nxdn/channel/FACCH1.h
index c2f95a0d..64683b56 100644
--- a/nxdn/channel/FACCH1.h
+++ b/nxdn/channel/FACCH1.h
@@ -27,8 +27,8 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#if !defined(__NXDN_FACCH1_H__)
-#define __NXDN_FACCH1_H__
+#if !defined(__NXDN_CHANNEL__FACCH1_H__)
+#define __NXDN_CHANNEL__FACCH1_H__
#include "Defines.h"
@@ -43,10 +43,10 @@ namespace nxdn
class HOST_SW_API FACCH1 {
public:
- /// Initializes a copy instance of the FACCH1 class.
- FACCH1(const FACCH1& data);
/// Initializes a new instance of the FACCH1 class.
FACCH1();
+ /// Initializes a copy instance of the FACCH1 class.
+ FACCH1(const FACCH1& data);
/// Finalizes a instance of the FACCH1 class.
~FACCH1();
@@ -69,8 +69,11 @@ namespace nxdn
private:
uint8_t* m_data;
+
+ /// Internal helper to copy the class.
+ void copy(const FACCH1& data);
};
} // namespace channel
} // namespace nxdn
-#endif // __NXDN_FACCH1_H__
+#endif // __NXDN_CHANNEL__FACCH1_H__
diff --git a/nxdn/channel/LICH.cpp b/nxdn/channel/LICH.cpp
index cb0b017a..7be5b6e0 100644
--- a/nxdn/channel/LICH.cpp
+++ b/nxdn/channel/LICH.cpp
@@ -43,10 +43,9 @@ using namespace nxdn::channel;
// ---------------------------------------------------------------------------
///
-/// Initializes a copy instance of the LICH class.
+/// Initializes a new instance of the LICH class.
///
-///
-LICH::LICH(const LICH& data) :
+LICH::LICH() :
m_rfct(NXDN_LICH_RFCT_RCCH),
m_fct(NXDN_LICH_USC_SACCH_NS),
m_option(0U),
@@ -54,25 +53,20 @@ LICH::LICH(const LICH& data) :
m_data(NULL)
{
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;
}
///
-/// Initializes a new instance of the LICH class.
+/// Initializes a copy instance of the LICH class.
///
-LICH::LICH() :
+///
+LICH::LICH(const LICH& data) :
m_rfct(NXDN_LICH_RFCT_RCCH),
m_fct(NXDN_LICH_USC_SACCH_NS),
m_option(0U),
m_direction(NXDN_LICH_DIRECTION_OUTBOUND),
m_data(NULL)
{
- m_data = new uint8_t[1U];
+ copy(data);
}
///
@@ -168,6 +162,21 @@ void LICH::encode(uint8_t* data)
// Private Class Members
// ---------------------------------------------------------------------------
+///
+/// Internal helper to copy the the class.
+///
+///
+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;
+}
+
///
///
///
diff --git a/nxdn/channel/LICH.h b/nxdn/channel/LICH.h
index 5de73146..72b877c5 100644
--- a/nxdn/channel/LICH.h
+++ b/nxdn/channel/LICH.h
@@ -28,8 +28,8 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#if !defined(__NXDN_LICH_H__)
-#define __NXDN_LICH_H__
+#if !defined(__NXDN_CHANNEL__LICH_H__)
+#define __NXDN_CHANNEL__LICH_H__
#include "Defines.h"
@@ -44,10 +44,10 @@ namespace nxdn
class HOST_SW_API LICH {
public:
- /// Initializes a copy instance of the LICH class.
- LICH(const LICH& lich);
/// Initializes a new instance of the LICH class.
LICH();
+ /// Initializes a copy instance of the LICH class.
+ LICH(const LICH& lich);
/// Finalizes a instance of the LICH class.
~LICH();
@@ -73,10 +73,13 @@ namespace nxdn
private:
uint8_t* m_data;
+ /// Internal helper to copy the class.
+ void copy(const LICH& data);
+
///
bool getParity() const;
};
} // namespace channel
} // namespace nxdn
-#endif // __NXDN_LICH_H__
+#endif // __NXDN_CHANNEL__LICH_H__
diff --git a/nxdn/channel/SACCH.cpp b/nxdn/channel/SACCH.cpp
index c8a75597..d79d2cc3 100644
--- a/nxdn/channel/SACCH.cpp
+++ b/nxdn/channel/SACCH.cpp
@@ -62,32 +62,28 @@ const uint32_t PUNCTURE_LIST[] = { 5U, 11U, 17U, 23U, 29U, 35U, 41U, 47U, 53U, 5
// ---------------------------------------------------------------------------
///
-/// Initializes a copy instance of the SACCH class.
+/// Initializes a new instance of the SACCH class.
///
-///
-SACCH::SACCH(const SACCH& data) :
+SACCH::SACCH() :
m_verbose(false),
m_ran(0U),
m_structure(0U),
m_data(NULL)
{
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;
}
///
-/// Initializes a new instance of the SACCH class.
+/// Initializes a copy instance of the SACCH class.
///
-SACCH::SACCH() :
+///
+SACCH::SACCH(const SACCH& data) :
m_verbose(false),
m_ran(0U),
m_structure(0U),
m_data(NULL)
{
- m_data = new uint8_t[5U];
+ copy(data);
}
///
@@ -280,3 +276,20 @@ void SACCH::setData(const uint8_t* data)
m_ran = m_data[0U] & 0x3FU;
m_structure = (m_data[0U] >> 6) & 0x03U;
}
+
+// ---------------------------------------------------------------------------
+// Private Class Members
+// ---------------------------------------------------------------------------
+
+///
+/// Internal helper to copy the the class.
+///
+///
+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;
+}
diff --git a/nxdn/channel/SACCH.h b/nxdn/channel/SACCH.h
index 9212d8e7..62640ac0 100644
--- a/nxdn/channel/SACCH.h
+++ b/nxdn/channel/SACCH.h
@@ -28,8 +28,8 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#if !defined(__NXDN_SACCH_H__)
-#define __NXDN_SACCH_H__
+#if !defined(__NXDN_CHANNEL__SACCH_H__)
+#define __NXDN_CHANNEL__SACCH_H__
#include "Defines.h"
@@ -44,10 +44,10 @@ namespace nxdn
class HOST_SW_API SACCH {
public:
- /// Initializes a copy instance of the SACCH class.
- SACCH(const SACCH& data);
/// Initializes a new instance of the SACCH class.
SACCH();
+ /// Initializes a copy instance of the SACCH class.
+ SACCH(const SACCH& data);
/// Finalizes a instance of the SACCH class.
~SACCH();
@@ -76,8 +76,11 @@ namespace nxdn
private:
uint8_t* m_data;
+
+ /// Internal helper to copy the class.
+ void copy(const SACCH& data);
};
} // namespace channel
} // namespace nxdn
-#endif // __NXDN_SACCH_H__
+#endif // __NXDN_CHANNEL__SACCH_H__
diff --git a/nxdn/channel/UDCH.cpp b/nxdn/channel/UDCH.cpp
index ae89795f..b498c7e6 100644
--- a/nxdn/channel/UDCH.cpp
+++ b/nxdn/channel/UDCH.cpp
@@ -91,29 +91,26 @@ const uint32_t PUNCTURE_LIST[] = {
// ---------------------------------------------------------------------------
///
-/// Initializes a copy instance of the UDCH class.
+/// Initializes a new instance of the UDCH class.
///
-///
-UDCH::UDCH(const UDCH& data) :
+UDCH::UDCH() :
m_verbose(false),
m_ran(0U),
m_data(NULL)
{
m_data = new uint8_t[26U];
- ::memcpy(m_data, data.m_data, 26U);
-
- m_ran = m_data[0U] & 0x3FU;
}
///
-/// Initializes a new instance of the UDCH class.
+/// Initializes a copy instance of the UDCH class.
///
-UDCH::UDCH() :
+///
+UDCH::UDCH(const UDCH& data) :
m_verbose(false),
m_ran(0U),
m_data(NULL)
{
- m_data = new uint8_t[26U];
+ copy(data);
}
///
@@ -287,3 +284,19 @@ void UDCH::setData(const uint8_t* data)
m_ran = m_data[0U] & 0x3FU;
}
+
+// ---------------------------------------------------------------------------
+// Private Class Members
+// ---------------------------------------------------------------------------
+
+///
+/// Internal helper to copy the the class.
+///
+///
+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;
+}
diff --git a/nxdn/channel/UDCH.h b/nxdn/channel/UDCH.h
index cc65c5e5..26c7e2d3 100644
--- a/nxdn/channel/UDCH.h
+++ b/nxdn/channel/UDCH.h
@@ -28,8 +28,8 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#if !defined(__NXDN_UDCH_H__)
-#define __NXDN_UDCH_H__
+#if !defined(__NXDN_CHANNEL__UDCH_H__)
+#define __NXDN_CHANNEL__UDCH_H__
#include "Defines.h"
@@ -44,10 +44,10 @@ namespace nxdn
class HOST_SW_API UDCH {
public:
- /// Initializes a copy instance of the UDCH class.
- UDCH(const UDCH& data);
/// Initializes a new instance of the UDCH class.
UDCH();
+ /// Initializes a copy instance of the UDCH class.
+ UDCH(const UDCH& data);
/// Finalizes a instance of the UDCH class.
~UDCH();
@@ -74,8 +74,11 @@ namespace nxdn
private:
uint8_t* m_data;
+
+ /// Internal helper to copy the class.
+ void copy(const UDCH& data);
};
} // namespace channel
} // namespace nxdn
-#endif // __NXDN_UDCH_H__
+#endif // __NXDN_CHANNEL__UDCH_H__
diff --git a/nxdn/data/Layer3.cpp b/nxdn/data/Layer3.cpp
deleted file mode 100644
index 9a71cc20..00000000
--- a/nxdn/data/Layer3.cpp
+++ /dev/null
@@ -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
-#include
-#include
-
-// ---------------------------------------------------------------------------
-// Public Class Members
-// ---------------------------------------------------------------------------
-
-///
-/// Initializes a copy instance of the Layer3 class.
-///
-///
-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
-}
-
-///
-/// Initializes a new instance of the Layer3 class.
-///
-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);
-}
-
-///
-/// Finalizes a instance of Layer3 class.
-///
-Layer3::~Layer3()
-{
- delete[] m_data;
-}
-
-///
-/// Equals operator.
-///
-///
-///
-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;
-}
-
-///
-/// Decode layer 3 data.
-///
-///
-/// True, if SACCH was decoded, otherwise false.
-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
-}
-
-///
-/// Encode layer 3 data.
-///
-///
-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);
- }
-}
-
-///
-///
-///
-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;
-}
-
-///
-/// Gets the raw layer 3 data.
-///
-///
-void Layer3::getData(uint8_t* data) const
-{
- ::memcpy(data, m_data, 22U);
-}
-
-///
-/// Sets the raw layer 3 data.
-///
-///
-///
-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;
-}
diff --git a/nxdn/data/Layer3.h b/nxdn/data/Layer3.h
deleted file mode 100644
index 19cf7caf..00000000
--- a/nxdn/data/Layer3.h
+++ /dev/null
@@ -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:
- /// Initializes a copy instance of the Layer3 class.
- Layer3(const Layer3& data);
- /// Initializes a new instance of the Layer3 class.
- Layer3();
- /// Finalizes a instance of the Layer3 class.
- ~Layer3();
-
- /// Equals operator.
- Layer3& operator=(const Layer3& data);
-
- /// Decode layer 3 data.
- void decode(const uint8_t* data, uint32_t length, uint32_t offset = 0U);
- /// Encode layer 3 data.
- void encode(uint8_t* data, uint32_t length, uint32_t offset = 0U);
-
- ///
- void reset();
-
- /// Gets the raw layer 3 data.
- void getData(uint8_t* data) const;
- /// Sets the raw layer 3 data.
- void setData(const uint8_t* data, uint32_t length);
-
- public:
- /// Flag indicating verbose log output.
- __PROPERTY(bool, verbose, Verbose);
-
- /** Common Data */
- /// Message Type
- __PROPERTY(uint8_t, messageType, MessageType);
-
- /// Source ID.
- __PROPERTY(uint16_t, srcId, SrcId);
- /// Destination ID.
- __PROPERTY(uint16_t, dstId, DstId);
-
- /// Flag indicating a group/talkgroup operation.
- __PROPERTY(bool, group, Group);
-
- ///
- __PROPERTY(uint8_t, dataBlocks, DataBlocks);
-
- private:
- uint8_t* m_data;
- };
- } // namespace data
-} // namespace nxdn
-
-#endif // __NXDN_LAYER_3_H__
diff --git a/nxdn/lc/LC.cpp b/nxdn/lc/LC.cpp
new file mode 100644
index 00000000..d1d2bfda
--- /dev/null
+++ b/nxdn/lc/LC.cpp
@@ -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
+#include
+#include
+
+// ---------------------------------------------------------------------------
+// Public Class Members
+// ---------------------------------------------------------------------------
+
+///
+/// Initializes a new instance of the LC class.
+///
+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);
+}
+
+///
+/// Initializes a copy instance of the LC class.
+///
+///
+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);
+}
+
+///
+/// Finalizes a instance of LC class.
+///
+LC::~LC()
+{
+ delete[] m_data;
+ delete[] m_mi;
+}
+
+///
+/// Equals operator.
+///
+///
+///
+LC& LC::operator=(const LC& data)
+{
+ if (&data != this) {
+ ::memcpy(m_data, data.m_data, 22U);
+ decodeLC(m_data);
+ }
+
+ return *this;
+}
+
+///
+/// Decode call link control data.
+///
+///
+/// True, if LC was decoded, otherwise false.
+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);
+}
+
+///
+/// Encode call link control data.
+///
+///
+///
+///
+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);
+ }
+}
+
+///
+///
+///
+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;
+}
+
+///
+/// Gets the raw layer 3 data.
+///
+///
+void LC::getData(uint8_t* data) const
+{
+ ::memcpy(data, m_data, 22U);
+}
+
+///
+/// Sets the raw layer 3 data.
+///
+///
+///
+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
+// ---------------------------------------------------------------------------
+
+
+///
+/// Decode link control.
+///
+///
+///
+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;
+}
+
+///
+/// Encode link control.
+///
+///
+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;
+ }
+}
+
+//
+/// Internal helper to copy the the class.
+///
+///
+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);
+}
diff --git a/nxdn/lc/LC.h b/nxdn/lc/LC.h
new file mode 100644
index 00000000..1dd1a9e7
--- /dev/null
+++ b/nxdn/lc/LC.h
@@ -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:
+ /// Initializes a new instance of the LC class.
+ LC();
+ /// Initializes a copy instance of the LC class.
+ LC(const LC& data);
+ /// Finalizes a instance of the LC class.
+ ~LC();
+
+ /// Equals operator.
+ LC& operator=(const LC& data);
+
+ /// Decode layer 3 data.
+ void decode(const uint8_t* data, uint32_t length, uint32_t offset = 0U);
+ /// Encode layer 3 data.
+ void encode(uint8_t* data, uint32_t length, uint32_t offset = 0U);
+
+ ///
+ void reset();
+
+ /// Gets the raw layer 3 data.
+ void getData(uint8_t* data) const;
+ /// Sets the raw layer 3 data.
+ void setData(const uint8_t* data, uint32_t length);
+
+ public:
+ /// Flag indicating verbose log output.
+ __PROPERTY(bool, verbose, Verbose);
+
+ /** Common Data */
+ /// Message Type
+ __PROPERTY(uint8_t, messageType, MessageType);
+
+ /// Call Type
+ __PROPERTY(uint8_t, callType, CallType);
+
+ /// Source ID.
+ __PROPERTY(uint16_t, srcId, SrcId);
+ /// Destination ID.
+ __PROPERTY(uint16_t, dstId, DstId);
+
+ /** Common Call Options */
+ /// Flag indicating the emergency bits are set.
+ __PROPERTY(bool, emergency, Emergency);
+ /// Flag indicating that encryption is enabled.
+ __PROPERTY(bool, encrypted, Encrypted);
+ /// Flag indicating priority paging.
+ __PROPERTY(bool, priority, Priority);
+ /// Flag indicating a group/talkgroup operation.
+ __PROPERTY(bool, group, Group);
+ /// Flag indicating a half/full duplex operation.
+ __PROPERTY(bool, duplex, Duplex);
+
+ /// Transmission mode.
+ __PROPERTY(uint8_t, transmissionMode, TransmissionMode);
+
+ /** Data Call Data */
+ /// Data packet information.
+ __PROPERTY(PacketInformation, packetInfo, PacketInfo);
+ /// Data packet information.
+ __PROPERTY(PacketInformation, rsp, Response);
+ /// Data packet frame number.
+ __PROPERTY(uint8_t, dataFrameNumber, DataFrameNumber);
+ /// Data packet block number.
+ __PROPERTY(uint8_t, dataBlockNumber, DataBlockNumber);
+
+ /** Header Delay Data */
+ /// Delay count.
+ __PROPERTY(uint16_t, delayCount, DelayCount);
+
+ /** Encryption data */
+ /// Encryption algorithm ID.
+ __PROPERTY(uint8_t, algId, AlgId);
+ /// Encryption key ID.
+ __PROPERTY(uint8_t, kId, KId);
+
+ /// Cause Response.
+ __PROPERTY(uint8_t, causeRsp, CauseResponse);
+
+ private:
+ uint8_t* m_data;
+
+ /** Encryption data */
+ uint8_t* m_mi;
+
+ /// Decode link control.
+ bool decodeLC(const uint8_t* data);
+ /// Encode link control.
+ void encodeLC(uint8_t* data);
+
+ /// Internal helper to copy the class.
+ void copy(const LC& data);
+ };
+ } // namespace lc
+} // namespace nxdn
+
+#endif // __NXDN_LC__LC_H__
diff --git a/nxdn/lc/PacketInformation.cpp b/nxdn/lc/PacketInformation.cpp
new file mode 100644
index 00000000..a4b2320b
--- /dev/null
+++ b/nxdn/lc/PacketInformation.cpp
@@ -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
+#include
+#include
+
+// ---------------------------------------------------------------------------
+// Public Class Members
+// ---------------------------------------------------------------------------
+
+///
+/// Initializes a new instance of the PacketInformation class.
+///
+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 */
+}
+
+///
+/// Finalizes a instance of the PacketInformation class.
+///
+PacketInformation::~PacketInformation()
+{
+ /* stub */
+}
+
+///
+/// Decodes packet information.
+///
+///
+///
+/// True, if packet information was decoded, otherwise false.
+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;
+}
+
+///
+/// Encodes packet information.
+///
+///
+///
+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;
+ }
+}
+
+///
+/// Helper to reset data values to defaults.
+///
+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;
+}
diff --git a/nxdn/lc/PacketInformation.h b/nxdn/lc/PacketInformation.h
new file mode 100644
index 00000000..c916126b
--- /dev/null
+++ b/nxdn/lc/PacketInformation.h
@@ -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:
+ /// Initializes a new instance of the PacketInformation class.
+ PacketInformation();
+ /// Finalizes a instance of the PacketInformation class.
+ ~PacketInformation();
+
+ /// Decodes packet information.
+ bool decode(const uint8_t messageType, const uint8_t* data);
+ /// Encodes packet information.
+ void encode(const uint8_t messageType, uint8_t* data);
+
+ /// Helper to reset data values to defaults.
+ void reset();
+
+ public:
+ /** Common Data **/
+ /// Flag indicating if confirmed delivery is needed.
+ __PROPERTY(bool, delivery, Delivery);
+ /// Flag indicating if the packet is a selective retry packet.
+ __PROPERTY(bool, selectiveRetry, SelectiveRetry);
+ /// Count of data blocks in t he transmission packet.
+ __PROPERTY(uint8_t, blockCount, BlockCount);
+ /// Number of padding octets of the last block.
+ __PROPERTY(uint8_t, padCount, PadCount);
+ /// Flag indicating the first fragment.
+ __PROPERTY(bool, start, Start);
+ /// Flag indicating if the Tx fragment count circulates.
+ __PROPERTY(bool, circular, Circular);
+ /// The number and sequence of fragments.
+ __PROPERTY(uint16_t, fragmentCount, FragmentCount);
+
+ /** Response Data */
+ /// Response class.
+ __PROPERTY(uint8_t, rspClass, ResponseClass);
+ /// Response type.
+ __PROPERTY(uint8_t, rspType, ResponseType);
+ /// Error Block Flag.
+ __PROPERTY(uint16_t, rspErrorBlock, ResponseErrorBlock);
+ };
+ } // namespace lc
+} // namespace nxdn
+
+#endif // __NXDN_LC__PACKET_INFORMATION_H__
diff --git a/nxdn/packet/Data.cpp b/nxdn/packet/Data.cpp
index 1be6fd06..77ff0b01 100644
--- a/nxdn/packet/Data.cpp
+++ b/nxdn/packet/Data.cpp
@@ -62,9 +62,9 @@ using namespace nxdn::packet;
} \
\
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, \
- m_nxdn->m_netLayer3.getSrcId(), m_nxdn->m_netLastDstId); \
+ m_nxdn->m_netLC.getSrcId(), m_nxdn->m_netLastDstId); \
resetRF(); \
return false; \
} \
@@ -196,15 +196,15 @@ bool Data::process(uint8_t option, uint8_t* data, uint32_t len)
uint8_t buffer[23U];
udch.getData(buffer);
- data::Layer3 layer3;
- layer3.decode(buffer, 184U);
- uint16_t dstId = layer3.getDstId();
- uint16_t srcId = layer3.getSrcId();
- bool group = layer3.getGroup();
+ lc::LC lc;
+ lc.decode(buffer, 184U);
+ uint16_t dstId = lc.getDstId();
+ uint16_t srcId = lc.getSrcId();
+ bool group = lc.getGroup();
if (m_nxdn->m_rfState == RS_RF_LISTENING) {
- uint8_t type = layer3.getMessageType();
- if (type != MESSAGE_TYPE_DCALL_HDR)
+ uint8_t type = lc.getMessageType();
+ if (type != RTCH_MESSAGE_TYPE_DCALL_HDR)
return false;
CHECK_TRAFFIC_COLLISION(srcId, dstId);
@@ -215,16 +215,14 @@ bool Data::process(uint8_t option, uint8_t* data, uint32_t len)
// validate destination ID
VALID_DSTID(srcId, dstId, group);
- uint8_t frames = layer3.getDataBlocks();
-
if (m_verbose) {
- LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_DCALL ", srcId = %u, dstId = %u, blocks = %u",
- srcId, dstId, frames);
+ LogMessage(LOG_RF, NXDN_MESSAGE_TYPE_DCALL ", srcId = %u, dstId = %u, ack = %u, blocksToFollow = %u, padCount = %u, firstFragment = %u, fragmentCount = %u",
+ 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);
- m_nxdn->m_rfLayer3 = layer3;
+ m_nxdn->m_rfLC = lc;
m_nxdn->m_voice->m_rfFrames = 0U;
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);
- uint8_t type = MESSAGE_TYPE_DCALL_DATA;
+ uint8_t type = RTCH_MESSAGE_TYPE_DCALL_DATA;
if (validUDCH) {
- type = layer3.getMessageType();
- data[0U] = type == MESSAGE_TYPE_TX_REL ? modem::TAG_EOT : modem::TAG_DATA;
+ type = lc.getMessageType();
+ data[0U] = type == RTCH_MESSAGE_TYPE_TX_REL ? modem::TAG_EOT : modem::TAG_DATA;
udch.setRAN(m_nxdn->m_ran);
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.
///
///
+///
/// Buffer containing data frame.
/// Length of data frame.
///
-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);
@@ -305,18 +304,18 @@ bool Data::processNetwork(uint8_t option, data::Layer3& netLayer3, uint8_t* data
uint8_t buffer[23U];
udch.getData(buffer);
- data::Layer3 layer3;
- layer3.decode(buffer, 184U);
- uint16_t dstId = layer3.getDstId();
- uint16_t srcId = layer3.getSrcId();
- bool group = layer3.getGroup();
+ lc::LC lc;
+ lc.decode(buffer, 184U);
+ uint16_t dstId = lc.getDstId();
+ uint16_t srcId = lc.getSrcId();
+ bool group = lc.getGroup();
if (m_nxdn->m_netState == RS_NET_IDLE) {
- uint8_t type = layer3.getMessageType();
- if (type != MESSAGE_TYPE_DCALL_HDR)
+ uint8_t type = lc.getMessageType();
+ if (type != RTCH_MESSAGE_TYPE_DCALL_HDR)
return false;
- CHECK_NET_TRAFFIC_COLLISION(layer3, srcId, dstId);
+ CHECK_NET_TRAFFIC_COLLISION(lc, srcId, dstId);
// validate source RID
VALID_SRCID(srcId, dstId, group);
@@ -324,16 +323,14 @@ bool Data::processNetwork(uint8_t option, data::Layer3& netLayer3, uint8_t* data
// validate destination ID
VALID_DSTID(srcId, dstId, group);
- uint8_t frames = layer3.getDataBlocks();
-
if (m_verbose) {
- LogMessage(LOG_NET, NXDN_MESSAGE_TYPE_DCALL ", srcId = %u, dstId = %u, blocks = %u",
- srcId, dstId, frames);
+ LogMessage(LOG_NET, NXDN_MESSAGE_TYPE_DCALL ", srcId = %u, dstId = %u, ack = %u, blocksToFollow = %u, padCount = %u, firstFragment = %u, fragmentCount = %u",
+ 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);
- m_nxdn->m_netLayer3 = layer3;
+ m_nxdn->m_netLC = lc;
m_nxdn->m_voice->m_netFrames = 0U;
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.encode(data + 2U);
- uint8_t type = MESSAGE_TYPE_DCALL_DATA;
+ uint8_t type = RTCH_MESSAGE_TYPE_DCALL_DATA;
if (validUDCH) {
- type = layer3.getMessageType();
- data[0U] = type == MESSAGE_TYPE_TX_REL ? modem::TAG_EOT : modem::TAG_DATA;
+ type = lc.getMessageType();
+ data[0U] = type == RTCH_MESSAGE_TYPE_TX_REL ? modem::TAG_EOT : modem::TAG_DATA;
udch.setRAN(m_nxdn->m_ran);
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())
return;
- m_network->writeNXDN(m_nxdn->m_rfLayer3, data, len);
+ m_network->writeNXDN(m_nxdn->m_rfLC, data, len);
}
diff --git a/nxdn/packet/Data.h b/nxdn/packet/Data.h
index 1a09a604..fbbd596d 100644
--- a/nxdn/packet/Data.h
+++ b/nxdn/packet/Data.h
@@ -63,7 +63,7 @@ namespace nxdn
/// Process a data frame from the RF interface.
virtual bool process(uint8_t option, uint8_t* data, uint32_t len);
/// Process a data frame from the network.
- 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:
friend class nxdn::Control;
diff --git a/nxdn/packet/Voice.cpp b/nxdn/packet/Voice.cpp
index 3c5344ad..f9669be0 100644
--- a/nxdn/packet/Voice.cpp
+++ b/nxdn/packet/Voice.cpp
@@ -64,9 +64,9 @@ using namespace nxdn::packet;
} \
\
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, \
- m_nxdn->m_netLayer3.getSrcId(), m_nxdn->m_netLastDstId); \
+ m_nxdn->m_netLC.getSrcId(), m_nxdn->m_netLastDstId); \
resetRF(); \
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];
facch.getData(buffer);
- data::Layer3 layer3;
- layer3.decode(buffer, NXDN_FACCH1_LENGTH_BITS);
- uint16_t dstId = layer3.getDstId();
- uint16_t srcId = layer3.getSrcId();
- bool group = layer3.getGroup();
+ lc::LC lc;
+ lc.decode(buffer, NXDN_FACCH1_LENGTH_BITS);
+ uint16_t dstId = lc.getDstId();
+ uint16_t srcId = lc.getSrcId();
+ bool group = lc.getGroup();
+ bool encrypted = lc.getEncrypted();
- uint8_t type = layer3.getMessageType();
- if (type == MESSAGE_TYPE_TX_REL) {
+ uint8_t type = lc.getMessageType();
+ if (type == RTCH_MESSAGE_TYPE_TX_REL) {
if (m_nxdn->m_rfState != RS_RF_AUDIO) {
m_nxdn->m_rfState = RS_RF_LISTENING;
m_nxdn->m_rfMask = 0x00U;
- m_nxdn->m_rfLayer3.reset();
+ m_nxdn->m_rfLC.reset();
return false;
}
- } else if (type == MESSAGE_TYPE_VCALL) {
+ } else if (type == RTCH_MESSAGE_TYPE_VCALL) {
CHECK_TRAFFIC_COLLISION(srcId, dstId);
// validate source RID
@@ -236,7 +237,7 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
return false;
}
- m_nxdn->m_rfLayer3 = layer3;
+ m_nxdn->m_rfLC = lc;
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
channel::SACCH sacch;
+ sacch.setData(SACCH_IDLE);
sacch.setRAN(m_nxdn->m_ran);
sacch.setStructure(NXDN_SR_SINGLE);
- sacch.setData(SACCH_IDLE);
sacch.encode(data + 2U);
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);
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;
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_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;
@@ -326,14 +332,14 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
uint8_t buffer[10U];
facch.getData(buffer);
- data::Layer3 layer3;
- layer3.decode(buffer, NXDN_FACCH1_LENGTH_BITS);
+ lc::LC lc;
+ lc.decode(buffer, NXDN_FACCH1_LENGTH_BITS);
- hasInfo = layer3.getMessageType() == MESSAGE_TYPE_VCALL;
+ hasInfo = lc.getMessageType() == RTCH_MESSAGE_TYPE_VCALL;
if (!hasInfo)
return false;
- m_nxdn->m_rfLayer3 = layer3;
+ m_nxdn->m_rfLC = lc;
}
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();
switch (structure) {
case NXDN_SR_1_4:
- m_nxdn->m_rfLayer3.decode(message, 18U, 0U);
- if(m_nxdn->m_rfLayer3.getMessageType() == MESSAGE_TYPE_VCALL)
+ m_nxdn->m_rfLC.decode(message, 18U, 0U);
+ if(m_nxdn->m_rfLC.getMessageType() == RTCH_MESSAGE_TYPE_VCALL)
m_nxdn->m_rfMask = 0x01U;
else
m_nxdn->m_rfMask = 0x00U;
break;
case NXDN_SR_2_4:
m_nxdn->m_rfMask |= 0x02U;
- m_nxdn->m_rfLayer3.decode(message, 18U, 18U);
+ m_nxdn->m_rfLC.decode(message, 18U, 18U);
break;
case NXDN_SR_3_4:
m_nxdn->m_rfMask |= 0x04U;
- m_nxdn->m_rfLayer3.decode(message, 18U, 36U);
+ m_nxdn->m_rfLC.decode(message, 18U, 36U);
break;
case NXDN_SR_4_4:
m_nxdn->m_rfMask |= 0x08U;
- m_nxdn->m_rfLayer3.decode(message, 18U, 54U);
+ m_nxdn->m_rfLC.decode(message, 18U, 54U);
break;
default:
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)
return false;
- uint8_t type = m_nxdn->m_rfLayer3.getMessageType();
- if (type != MESSAGE_TYPE_VCALL)
+ uint8_t type = m_nxdn->m_rfLC.getMessageType();
+ if (type != RTCH_MESSAGE_TYPE_VCALL)
return false;
}
- uint16_t dstId = m_nxdn->m_rfLayer3.getDstId();
- uint16_t srcId = m_nxdn->m_rfLayer3.getSrcId();
- bool group = m_nxdn->m_rfLayer3.getGroup();
+ uint16_t dstId = m_nxdn->m_rfLC.getDstId();
+ uint16_t srcId = m_nxdn->m_rfLC.getSrcId();
+ bool group = m_nxdn->m_rfLC.getGroup();
+ bool encrypted = m_nxdn->m_rfLC.getEncrypted();
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_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
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
channel::SACCH sacch;
+ sacch.setData(SACCH_IDLE);
sacch.setRAN(m_nxdn->m_ran);
sacch.setStructure(NXDN_SR_SINGLE);
- sacch.setData(SACCH_IDLE);
sacch.encode(start + 2U);
uint8_t message[22U];
- m_nxdn->m_rfLayer3.getData(message);
+ m_nxdn->m_rfLC.getData(message);
facch.setData(message);
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%%)",
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) {
channel::FACCH1 facch1;
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%%)",
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) {
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%%)",
errors, float(errors) / 0.94F);
}
-/*
- Audio audio;
- audio.decode(data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 0U, netData + 5U + 0U);
-*/
+
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);
if (valid)
@@ -571,10 +571,11 @@ bool Voice::process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len)
///
///
///
+///
/// Buffer containing data frame.
/// Length of data frame.
///
-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);
@@ -600,22 +601,23 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
uint8_t buffer[10U];
facch.getData(buffer);
- data::Layer3 layer3;
- layer3.decode(buffer, NXDN_FACCH1_LENGTH_BITS);
- uint16_t dstId = layer3.getDstId();
- uint16_t srcId = layer3.getSrcId();
- bool group = layer3.getGroup();
+ lc::LC lc;
+ lc.decode(buffer, NXDN_FACCH1_LENGTH_BITS);
+ uint16_t dstId = lc.getDstId();
+ uint16_t srcId = lc.getSrcId();
+ bool group = lc.getGroup();
+ bool encrypted = lc.getEncrypted();
- uint8_t type = layer3.getMessageType();
- if (type == MESSAGE_TYPE_TX_REL) {
+ uint8_t type = lc.getMessageType();
+ if (type == RTCH_MESSAGE_TYPE_TX_REL) {
if (m_nxdn->m_netState != RS_NET_AUDIO) {
m_nxdn->m_netState = RS_NET_IDLE;
m_nxdn->m_netMask = 0x00U;
- m_nxdn->m_netLayer3.reset();
+ m_nxdn->m_netLC.reset();
return false;
}
- } else if (type == MESSAGE_TYPE_VCALL) {
- CHECK_NET_TRAFFIC_COLLISION(layer3, srcId, dstId);
+ } else if (type == RTCH_MESSAGE_TYPE_VCALL) {
+ CHECK_NET_TRAFFIC_COLLISION(lc, srcId, dstId);
// validate source RID
VALID_SRCID(srcId, dstId, group);
@@ -626,7 +628,7 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
return false;
}
- m_nxdn->m_netLayer3 = layer3;
+ m_nxdn->m_netLC = lc;
Sync::addNXDNSync(data + 2U);
@@ -640,9 +642,9 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
// generate the SACCH
channel::SACCH sacch;
+ sacch.setData(SACCH_IDLE);
sacch.setRAN(m_nxdn->m_ran);
sacch.setStructure(NXDN_SR_SINGLE);
- sacch.setData(SACCH_IDLE);
sacch.encode(data + 2U);
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);
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;
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_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;
@@ -700,14 +707,14 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
uint8_t buffer[10U];
facch.getData(buffer);
- data::Layer3 layer3;
- layer3.decode(buffer, NXDN_FACCH1_LENGTH_BITS);
+ lc::LC lc;
+ lc.decode(buffer, NXDN_FACCH1_LENGTH_BITS);
- hasInfo = layer3.getMessageType() == MESSAGE_TYPE_VCALL;
+ hasInfo = lc.getMessageType() == RTCH_MESSAGE_TYPE_VCALL;
if (!hasInfo)
return false;
- m_nxdn->m_netLayer3 = layer3;
+ m_nxdn->m_netLC = lc;
}
if (!hasInfo) {
@@ -717,23 +724,23 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
uint8_t structure = sacch.getStructure();
switch (structure) {
case NXDN_SR_1_4:
- m_nxdn->m_netLayer3.decode(message, 18U, 0U);
- if(m_nxdn->m_netLayer3.getMessageType() == MESSAGE_TYPE_VCALL)
+ m_nxdn->m_netLC.decode(message, 18U, 0U);
+ if(m_nxdn->m_netLC.getMessageType() == RTCH_MESSAGE_TYPE_VCALL)
m_nxdn->m_netMask = 0x01U;
else
m_nxdn->m_netMask = 0x00U;
break;
case NXDN_SR_2_4:
m_nxdn->m_netMask |= 0x02U;
- m_nxdn->m_netLayer3.decode(message, 18U, 18U);
+ m_nxdn->m_netLC.decode(message, 18U, 18U);
break;
case NXDN_SR_3_4:
m_nxdn->m_netMask |= 0x04U;
- m_nxdn->m_netLayer3.decode(message, 18U, 36U);
+ m_nxdn->m_netLC.decode(message, 18U, 36U);
break;
case NXDN_SR_4_4:
m_nxdn->m_netMask |= 0x08U;
- m_nxdn->m_netLayer3.decode(message, 18U, 54U);
+ m_nxdn->m_netLC.decode(message, 18U, 54U);
break;
default:
break;
@@ -742,16 +749,17 @@ bool Voice::processNetwork(uint8_t usc, uint8_t option, data::Layer3& netLayer3,
if (m_nxdn->m_netMask != 0x0FU)
return false;
- uint8_t type = m_nxdn->m_netLayer3.getMessageType();
- if (type != MESSAGE_TYPE_VCALL)
+ uint8_t type = m_nxdn->m_netLC.getMessageType();
+ if (type != RTCH_MESSAGE_TYPE_VCALL)
return false;
}
- uint16_t dstId = m_nxdn->m_netLayer3.getDstId();
- uint16_t srcId = m_nxdn->m_netLayer3.getSrcId();
- bool group = m_nxdn->m_netLayer3.getGroup();
+ uint16_t dstId = m_nxdn->m_netLC.getDstId();
+ uint16_t srcId = m_nxdn->m_netLC.getSrcId();
+ 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
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_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
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
channel::SACCH sacch;
+ sacch.setData(SACCH_IDLE);
sacch.setRAN(m_nxdn->m_ran);
sacch.setStructure(NXDN_SR_SINGLE);
- sacch.setData(SACCH_IDLE);
sacch.encode(start + 2U);
uint8_t message[22U];
- m_nxdn->m_rfLayer3.getData(message);
+ m_nxdn->m_rfLC.getData(message);
facch.setData(message);
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())
return;
- m_network->writeNXDN(m_nxdn->m_rfLayer3, data, len);
+ m_network->writeNXDN(m_nxdn->m_rfLC, data, len);
}
diff --git a/nxdn/packet/Voice.h b/nxdn/packet/Voice.h
index ea0ba4f8..ca6c9cc3 100644
--- a/nxdn/packet/Voice.h
+++ b/nxdn/packet/Voice.h
@@ -64,7 +64,7 @@ namespace nxdn
/// Process a data frame from the RF interface.
virtual bool process(uint8_t usc, uint8_t option, uint8_t* data, uint32_t len);
/// Process a data frame from the network.
- 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:
friend class packet::Data;