diff --git a/DVMHost.vcxproj b/DVMHost.vcxproj index 6764ea0a..aa6f0524 100644 --- a/DVMHost.vcxproj +++ b/DVMHost.vcxproj @@ -202,6 +202,7 @@ + @@ -272,6 +273,7 @@ + diff --git a/DVMHost.vcxproj.filters b/DVMHost.vcxproj.filters index f39f91b1..c34140d2 100644 --- a/DVMHost.vcxproj.filters +++ b/DVMHost.vcxproj.filters @@ -326,6 +326,9 @@ Header Files\network + + Header Files\p25\data + @@ -523,6 +526,9 @@ Source Files\network + + Source Files\p25\data + diff --git a/Makefile b/Makefile index 659505b1..edd12a62 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,7 @@ OBJECTS = \ p25/acl/AccessControl.o \ p25/data/DataBlock.o \ p25/data/DataHeader.o \ + p25/data/DataRspHeader.o \ p25/data/LowSpeedData.o \ p25/edac/Trellis.o \ p25/lc/LC.o \ diff --git a/Makefile.arm b/Makefile.arm index eb6789e8..31a7e406 100644 --- a/Makefile.arm +++ b/Makefile.arm @@ -39,6 +39,7 @@ OBJECTS = \ p25/acl/AccessControl.o \ p25/data/DataBlock.o \ p25/data/DataHeader.o \ + p25/data/DataRspHeader.o \ p25/data/LowSpeedData.o \ p25/edac/Trellis.o \ p25/lc/LC.o \ diff --git a/Makefile.rpi-arm b/Makefile.rpi-arm index ecfec395..4a6a50eb 100644 --- a/Makefile.rpi-arm +++ b/Makefile.rpi-arm @@ -39,6 +39,7 @@ OBJECTS = \ p25/acl/AccessControl.o \ p25/data/DataBlock.o \ p25/data/DataHeader.o \ + p25/data/DataRspHeader.o \ p25/data/LowSpeedData.o \ p25/edac/Trellis.o \ p25/lc/LC.o \ diff --git a/config.yml b/config.yml index 66bf89b1..a773a86f 100644 --- a/config.yml +++ b/config.yml @@ -30,6 +30,7 @@ protocols: dumpTAData: true dumpDataPacket: false repeatDataPacket: true + dumpCsbkData: false callHang: 5 txHang: 8 queueSize: 10000 @@ -40,6 +41,7 @@ protocols: tduPreambleCount: 6 control: enable: false + ackRequests: true continuous: false interval: 60 duration: 1 @@ -51,6 +53,7 @@ protocols: verifyReg: false dumpDataPacket: false repeatDataPacket: true + dumpTsbkData: false callHang: 5 noStatusAck: false noMessageAck: true diff --git a/dmr/Control.cpp b/dmr/Control.cpp index 7a7ec585..da54ddd7 100644 --- a/dmr/Control.cpp +++ b/dmr/Control.cpp @@ -57,12 +57,13 @@ using namespace dmr; /// /// /// +/// /// Flag indicating whether DMR debug is enabled. /// Flag indicating whether DMR verbose logging is enabled. Control::Control(uint32_t colorCode, uint32_t callHang, uint32_t queueSize, bool embeddedLCOnly, bool dumpTAData, uint32_t timeout, uint32_t tgHang, modem::Modem* modem, network::BaseNetwork* network, bool duplex, lookups::RadioIdLookup* ridLookup, lookups::TalkgroupIdLookup* tidLookup, lookups::RSSIInterpolator* rssi, - uint32_t jitter, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose) : + uint32_t jitter, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose) : m_colorCode(colorCode), m_modem(modem), m_network(network), @@ -70,6 +71,7 @@ Control::Control(uint32_t colorCode, uint32_t callHang, uint32_t queueSize, bool m_slot2(NULL), m_ridLookup(ridLookup), m_tidLookup(tidLookup), + m_dumpCSBKData(dumpCSBKData), m_verbose(verbose), m_debug(debug) { @@ -81,8 +83,8 @@ Control::Control(uint32_t colorCode, uint32_t callHang, uint32_t queueSize, bool acl::AccessControl::init(m_ridLookup, m_tidLookup); Slot::init(colorCode, embeddedLCOnly, dumpTAData, callHang, modem, network, duplex, m_ridLookup, m_tidLookup, rssi, jitter); - m_slot1 = new Slot(1U, timeout, tgHang, queueSize, dumpDataPacket, repeatDataPacket, debug, verbose); - m_slot2 = new Slot(2U, timeout, tgHang, queueSize, dumpDataPacket, repeatDataPacket, debug, verbose); + m_slot1 = new Slot(1U, timeout, tgHang, queueSize, dumpDataPacket, repeatDataPacket, dumpCSBKData, debug, verbose); + m_slot2 = new Slot(2U, timeout, tgHang, queueSize, dumpDataPacket, repeatDataPacket, dumpCSBKData, debug, verbose); } /// @@ -108,7 +110,9 @@ bool Control::processWakeup(const uint8_t* data) return false; // generate a new CSBK and check validity - lc::CSBK csbk = lc::CSBK(m_debug); + lc::CSBK csbk = lc::CSBK(); + csbk.setVerbose(m_dumpCSBKData); + bool valid = csbk.decode(data + 2U); if (!valid) return false; diff --git a/dmr/Control.h b/dmr/Control.h index a3ce4863..3f433796 100644 --- a/dmr/Control.h +++ b/dmr/Control.h @@ -59,7 +59,7 @@ namespace dmr Control(uint32_t colorCode, uint32_t callHang, uint32_t queueSize, bool embeddedLCOnly, bool dumpTAData, uint32_t timeout, uint32_t tgHang, modem::Modem* modem, network::BaseNetwork* network, bool duplex, lookups::RadioIdLookup* ridLookup, lookups::TalkgroupIdLookup* tidLookup, lookups::RSSIInterpolator* rssi, - uint32_t jitter, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose); + uint32_t jitter, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose); /// Finalizes a instance of the Control class. ~Control(); @@ -95,6 +95,7 @@ namespace dmr lookups::RadioIdLookup* m_ridLookup; lookups::TalkgroupIdLookup* m_tidLookup; + bool m_dumpCSBKData; bool m_verbose; bool m_debug; }; diff --git a/dmr/DataPacket.cpp b/dmr/DataPacket.cpp index 43f08ac4..bf73eaf8 100644 --- a/dmr/DataPacket.cpp +++ b/dmr/DataPacket.cpp @@ -273,7 +273,9 @@ bool DataPacket::process(uint8_t* data, uint32_t len) } else if (dataType == DT_CSBK) { // generate a new CSBK and check validity - lc::CSBK csbk = lc::CSBK(m_debug); + lc::CSBK csbk = lc::CSBK(); + csbk.setVerbose(m_dumpCSBKData); + bool valid = csbk.decode(data + 2U); if (!valid) return false; @@ -778,7 +780,9 @@ void DataPacket::processNetwork(const data::Data& dmrData) writeEndNet(); } else if (dataType == DT_CSBK) { - lc::CSBK csbk = lc::CSBK(m_debug); + lc::CSBK csbk = lc::CSBK(); + csbk.setVerbose(m_dumpCSBKData); + bool valid = csbk.decode(data + 2U); if (!valid) { LogError(LOG_NET, "DMR Slot %u, DT_CSBK, unable to decode the network CSBK", m_slot->m_slotNo); @@ -1055,9 +1059,10 @@ void DataPacket::processNetwork(const data::Data& dmrData) /// Instance of the BaseNetwork class. /// /// +/// /// Flag indicating whether DMR debug is enabled. /// Flag indicating whether DMR verbose logging is enabled. -DataPacket::DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose) : +DataPacket::DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose) : m_slot(slot), m_rfLC(NULL), m_netLC(NULL), @@ -1065,6 +1070,7 @@ DataPacket::DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataP m_pduDataOffset(0U), m_dumpDataPacket(dumpDataPacket), m_repeatDataPacket(repeatDataPacket), + m_dumpCSBKData(dumpCSBKData), m_verbose(verbose), m_debug(debug) { diff --git a/dmr/DataPacket.h b/dmr/DataPacket.h index 178fc06a..ed28bd9d 100644 --- a/dmr/DataPacket.h +++ b/dmr/DataPacket.h @@ -81,11 +81,12 @@ namespace dmr bool m_dumpDataPacket; bool m_repeatDataPacket; + bool m_dumpCSBKData; bool m_verbose; bool m_debug; /// Initializes a new instance of the DataPacket class. - DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose); + DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose); /// Finalizes a instance of the DataPacket class. ~DataPacket(); diff --git a/dmr/Slot.cpp b/dmr/Slot.cpp index d7a1c1d8..e2e12417 100644 --- a/dmr/Slot.cpp +++ b/dmr/Slot.cpp @@ -85,10 +85,11 @@ bool Slot::m_voice2 = true; /// Amount of time to hang on the last talkgroup mode from RF. /// /// +/// /// Flag indicating whether DMR debug is enabled. /// Flag indicating whether DMR verbose logging is enabled. Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSize, bool dumpDataPacket, bool repeatDataPacket, - bool debug, bool verbose) : + bool dumpCSBKData, bool debug, bool verbose) : m_slotNo(slotNo), m_queue(queueSize, "DMR Slot"), m_rfState(RS_RF_LISTENING), @@ -118,13 +119,14 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz m_minRSSI(0U), m_aveRSSI(0U), m_rssiCount(0U), + m_dumpCSBKData(dumpCSBKData), m_verbose(verbose), m_debug(debug) { m_interval.start(); m_voice = new VoicePacket(this, m_network, m_embeddedLCOnly, m_dumpTAData, debug, verbose); - m_data = new DataPacket(this, m_network, dumpDataPacket, repeatDataPacket, debug, verbose); + m_data = new DataPacket(this, m_network, dumpDataPacket, repeatDataPacket, dumpCSBKData, debug, verbose); } /// @@ -561,7 +563,8 @@ void Slot::writeRF_Ext_Func(uint32_t func, uint32_t arg, uint32_t dstId) slotType.setColorCode(m_colorCode); slotType.setDataType(DT_CSBK); - lc::CSBK csbk = lc::CSBK(false); + lc::CSBK csbk = lc::CSBK(); + csbk.setVerbose(m_dumpCSBKData); csbk.setCSBKO(CSBKO_EXT_FNCT); csbk.setFID(FID_DMRA); @@ -609,7 +612,8 @@ void Slot::writeRF_Call_Alrt(uint32_t srcId, uint32_t dstId) slotType.setColorCode(m_colorCode); slotType.setDataType(DT_CSBK); - lc::CSBK csbk = lc::CSBK(false); + lc::CSBK csbk = lc::CSBK(); + csbk.setVerbose(m_dumpCSBKData); csbk.setCSBKO(CSBKO_CALL_ALRT); csbk.setFID(FID_DMRA); diff --git a/dmr/Slot.h b/dmr/Slot.h index 349f38b3..002310a4 100644 --- a/dmr/Slot.h +++ b/dmr/Slot.h @@ -64,7 +64,7 @@ namespace dmr public: /// Initializes a new instance of the Slot class. Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSize, bool dumpDataPacket, bool repeatDataPacket, - bool debug, bool verbose); + bool dumpCSBKData, bool debug, bool verbose); /// Finalizes a instance of the Slot class. ~Slot(); @@ -130,6 +130,7 @@ namespace dmr uint32_t m_aveRSSI; uint32_t m_rssiCount; + bool m_dumpCSBKData; bool m_verbose; bool m_debug; diff --git a/dmr/lc/CSBK.cpp b/dmr/lc/CSBK.cpp index ec436a17..7eb29851 100644 --- a/dmr/lc/CSBK.cpp +++ b/dmr/lc/CSBK.cpp @@ -32,6 +32,7 @@ #include "dmr/lc/CSBK.h" #include "edac/BPTC19696.h" #include "edac/CRC.h" +#include "Log.h" #include "Utils.h" using namespace dmr::lc; @@ -46,8 +47,8 @@ using namespace dmr; /// /// Initializes a new instance of the CSBK class. /// -/// -CSBK::CSBK(bool debug) : +CSBK::CSBK() : + m_verbose(false), m_CSBKO(CSBKO_NONE), m_FID(0x00U), m_bsId(0U), @@ -56,8 +57,7 @@ CSBK::CSBK(bool debug) : m_dstId(0U), m_dataContent(false), m_CBF(0U), - m_data(NULL), - m_debug(debug) + m_data(NULL) { m_data = new uint8_t[12U]; } @@ -95,6 +95,10 @@ bool CSBK::decode(const uint8_t* bytes) m_data[10U] ^= CSBK_CRC_MASK[0U]; m_data[11U] ^= CSBK_CRC_MASK[1U]; + if (m_verbose) { + Utils::dump(2U, "Decoded CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES); + } + m_CSBKO = m_data[0U] & 0x3FU; m_FID = m_data[1U]; @@ -105,8 +109,6 @@ bool CSBK::decode(const uint8_t* bytes) m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; m_dataContent = false; m_CBF = 0U; - if (m_debug) - Utils::dump(1U, "Downlink Activate CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES); break; case CSBKO_UU_V_REQ: @@ -115,8 +117,6 @@ bool CSBK::decode(const uint8_t* bytes) m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; m_dataContent = false; m_CBF = 0U; - if (m_debug) - Utils::dump(1U, "Unit to Unit Service Request CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES); break; case CSBKO_UU_ANS_RSP: @@ -125,8 +125,6 @@ bool CSBK::decode(const uint8_t* bytes) m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; m_dataContent = false; m_CBF = 0U; - if (m_debug) - Utils::dump(1U, "Unit to Unit Service Answer Response CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES); break; case CSBKO_PRECCSBK: @@ -135,8 +133,6 @@ bool CSBK::decode(const uint8_t* bytes) m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; m_dataContent = (m_data[2U] & 0x80U) == 0x80U; m_CBF = m_data[3U]; - if (m_debug) - Utils::dump(1U, "Preamble CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES); break; case CSBKO_CALL_ALRT: @@ -145,8 +141,6 @@ bool CSBK::decode(const uint8_t* bytes) m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; m_dataContent = (m_data[2U] & 0x80U) == 0x80U; m_CBF = m_data[3U]; - if (m_debug) - Utils::dump(1U, "Call Alert CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES); break; case CSBKO_ACK_RSP: @@ -155,8 +149,6 @@ bool CSBK::decode(const uint8_t* bytes) m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; m_dataContent = (m_data[2U] & 0x80U) == 0x80U; m_CBF = m_data[3U]; - if (m_debug) - Utils::dump(1U, "ACK Response CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES); break; case CSBKO_EXT_FNCT: @@ -165,8 +157,6 @@ bool CSBK::decode(const uint8_t* bytes) m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; m_dataContent = (m_data[2U] & 0x80U) == 0x80U; m_CBF = m_data[3U]; - if (m_debug) - Utils::dump(1U, "Extended Function CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES); break; case CSBKO_NACK_RSP: @@ -175,8 +165,6 @@ bool CSBK::decode(const uint8_t* bytes) m_dstId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; m_dataContent = false; m_CBF = 0U; - if (m_debug) - Utils::dump(1U, "Negative Acknowledge Response CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES); break; default: @@ -185,8 +173,7 @@ bool CSBK::decode(const uint8_t* bytes) m_dstId = 0U; m_dataContent = false; m_CBF = 0U; - if (m_debug) - Utils::dump("Unhandled CSBK type", m_data, DMR_LC_HEADER_LENGTH_BYTES); + LogError(LOG_DMR, "unknown CSBK type, csbko = $%02X", m_CSBKO); return true; } @@ -241,6 +228,10 @@ void CSBK::encode(uint8_t* bytes) const m_data[10U] ^= CSBK_CRC_MASK[0U]; m_data[11U] ^= CSBK_CRC_MASK[1U]; + if (m_verbose) { + Utils::dump(2U, "Encoded CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES); + } + // encode BPTC (196,96) FEC edac::BPTC19696 bptc; bptc.encode(m_data, bytes); diff --git a/dmr/lc/CSBK.h b/dmr/lc/CSBK.h index b9fcfb26..1e0f9c78 100644 --- a/dmr/lc/CSBK.h +++ b/dmr/lc/CSBK.h @@ -46,7 +46,7 @@ namespace dmr class HOST_SW_API CSBK { public: /// Initializes a new instance of the CSBK class. - CSBK(bool debug); + CSBK(); /// Finalizes a instance of the CSBK class. ~CSBK(); @@ -56,6 +56,9 @@ namespace dmr void encode(uint8_t* bytes) const; public: + /// + __PROPERTY(bool, verbose, Verbose); + // Generic fields /// CSBK opcode. __PROPERTY(uint8_t, CSBKO, CSBKO); @@ -82,7 +85,6 @@ namespace dmr private: uint8_t* m_data; - bool m_debug; }; } // namespace lc } // namespace dmr diff --git a/edac/CRC.cpp b/edac/CRC.cpp index 44564f63..17546b5f 100644 --- a/edac/CRC.cpp +++ b/edac/CRC.cpp @@ -68,38 +68,38 @@ const uint8_t CRC8_TABLE[] = { 0xFA, 0xFD, 0xF4, 0xF3, 0x01 }; const uint16_t CRC9_TABLE[] = { - 0x000, 0x259, 0x2B2, 0x0EB, 0x23D, 0x064, 0x08F, 0x2D6, - 0x27A, 0x023, 0x0C8, 0x291, 0x047, 0x21E, 0x2F5, 0x0AC, - 0x0F4, 0x2AD, 0x246, 0x01F, 0x2C9, 0x090, 0x07B, 0x222, - 0x28E, 0x0D7, 0x03C, 0x265, 0x0B3, 0x2EA, 0x201, 0x058, - 0x2B1, 0x0E8, 0x003, 0x25A, 0x08C, 0x2D5, 0x23E, 0x067, - 0x0CB, 0x292, 0x279, 0x020, 0x2F6, 0x0AF, 0x044, 0x21D, - 0x245, 0x01C, 0x0F7, 0x2AE, 0x078, 0x221, 0x2CA, 0x093, - 0x03F, 0x266, 0x28D, 0x0D4, 0x202, 0x05B, 0x0B0, 0x2E9, - 0x03B, 0x262, 0x289, 0x0D0, 0x206, 0x05F, 0x0B4, 0x2ED, - 0x241, 0x018, 0x0F3, 0x2AA, 0x07C, 0x225, 0x2CE, 0x097, - 0x0CF, 0x296, 0x27D, 0x024, 0x2F2, 0x0AB, 0x040, 0x219, - 0x2B5, 0x0EC, 0x007, 0x25E, 0x088, 0x2D1, 0x23A, 0x063, - 0x28A, 0x0D3, 0x038, 0x261, 0x0B7, 0x2EE, 0x205, 0x05C, - 0x0F0, 0x2A9, 0x242, 0x01B, 0x2CD, 0x094, 0x07F, 0x226, - 0x27E, 0x027, 0x0CC, 0x295, 0x043, 0x21A, 0x2F1, 0x0A8, - 0x004, 0x25D, 0x2B6, 0x0EF, 0x239, 0x060, 0x08B, 0x2D2, - 0x276, 0x02F, 0x0C4, 0x29D, 0x04B, 0x212, 0x2F9, 0x0A0, - 0x00C, 0x255, 0x2BE, 0x0E7, 0x231, 0x068, 0x083, 0x2DA, - 0x282, 0x0DB, 0x030, 0x269, 0x0BF, 0x2E6, 0x20D, 0x054, - 0x0F8, 0x2A1, 0x24A, 0x013, 0x2C5, 0x09C, 0x077, 0x22E, - 0x0C7, 0x29E, 0x275, 0x02C, 0x2FA, 0x0A3, 0x048, 0x211, - 0x2BD, 0x0E4, 0x00F, 0x256, 0x080, 0x2D9, 0x232, 0x06B, - 0x033, 0x26A, 0x281, 0x0D8, 0x20E, 0x057, 0x0BC, 0x2E5, - 0x249, 0x010, 0x0FB, 0x2A2, 0x074, 0x22D, 0x2C6, 0x09F, - 0x24D, 0x014, 0x0FF, 0x2A6, 0x070, 0x229, 0x2C2, 0x09B, - 0x037, 0x26E, 0x285, 0x0DC, 0x20A, 0x053, 0x0B8, 0x2E1, - 0x2B9, 0x0E0, 0x00B, 0x252, 0x084, 0x2DD, 0x236, 0x06F, - 0x0C3, 0x29A, 0x271, 0x028, 0x2FE, 0x0A7, 0x04C, 0x215, - 0x0FC, 0x2A5, 0x24E, 0x017, 0x2C1, 0x098, 0x073, 0x22A, - 0x286, 0x0DF, 0x034, 0x26D, 0x0BB, 0x2E2, 0x209, 0x050, - 0x008, 0x251, 0x2BA, 0x0E3, 0x235, 0x06C, 0x087, 0x2DE, - 0x272, 0x02B, 0x0C0, 0x299, 0x04F, 0x216, 0x2FD, 0x0A4 }; + 0x000, 0x059, 0x0B2, 0x0EB, 0x164, 0x13D, 0x1D6, 0x18F, + 0x091, 0x0C8, 0x023, 0x07A, 0x1F5, 0x1AC, 0x147, 0x11E, + 0x122, 0x17B, 0x190, 0x1C9, 0x046, 0x01F, 0x0F4, 0x0AD, + 0x1B3, 0x1EA, 0x101, 0x158, 0x0d7, 0x08E, 0x065, 0x03C, + 0x01D, 0x044, 0x0AF, 0x0F6, 0x179, 0x120, 0x1CB, 0x192, + 0x08C, 0x0D5, 0x03E, 0x067, 0x1E8, 0x1B1, 0x15A, 0x103, + 0x13F, 0x166, 0x18D, 0x1D4, 0x05B, 0x002, 0x0E9, 0x0B0, + 0x1AE, 0x1F7, 0x11C, 0x145, 0x0CA, 0x093, 0x078, 0x021, + 0x03A, 0x063, 0x088, 0x0D1, 0x15E, 0x107, 0x1EC, 0x1B5, + 0x0AB, 0x0F2, 0x019, 0x040, 0x1CF, 0x196, 0x17D, 0x124, + 0x118, 0x141, 0x1AA, 0x1F3, 0x07C, 0x025, 0x0CE, 0x097, + 0x189, 0x1D0, 0x13B, 0x162, 0x0ED, 0x0B4, 0x05F, 0x006, + 0x027, 0x07E, 0x095, 0x0CC, 0x143, 0x11A, 0x1F1, 0x1A8, + 0x0B6, 0x0EF, 0x004, 0x05D, 0x1D2, 0x18B, 0x160, 0x139, + 0x105, 0x15C, 0x1B7, 0x1EE, 0x061, 0x038, 0x0D3, 0x08A, + 0x194, 0x1CD, 0x126, 0x17F, 0x0F0, 0x0A9, 0x042, 0x01B, + 0x074, 0x02D, 0x0C6, 0x09F, 0x110, 0x149, 0x1A2, 0x1FB, + 0x0E5, 0x0BC, 0x057, 0x00E, 0x181, 0x1D8, 0x133, 0x16A, + 0x156, 0x10F, 0x1E4, 0x1BD, 0x032, 0x06B, 0x080, 0x0D9, + 0x1C7, 0x19E, 0x175, 0x12C, 0x0A3, 0x0FA, 0x011, 0x048, + 0x069, 0x030, 0x0DB, 0x082, 0x10D, 0x154, 0x1BF, 0x1E6, + 0x0F8, 0x0A1, 0x04A, 0x013, 0x19C, 0x1C5, 0x12E, 0x177, + 0x14B, 0x112, 0x1F9, 0x1A0, 0x02F, 0x076, 0x09D, 0x0C4, + 0x1DA, 0x183, 0x168, 0x131, 0x0BE, 0x0E7, 0x00C, 0x055, + 0x04E, 0x017, 0x0FC, 0x0A5, 0x12A, 0x173, 0x198, 0x1C1, + 0x0DF, 0x086, 0x06D, 0x034, 0x1BB, 0x1E2, 0x109, 0x150, + 0x16C, 0x135, 0x1DE, 0x187, 0x008, 0x051, 0x0BA, 0x0E3, + 0x1FD, 0x1A4, 0x14F, 0x116, 0x099, 0x0C0, 0x02B, 0x072, + 0x053, 0x00A, 0x0E1, 0x0B8, 0x137, 0x16E, 0x185, 0x1DC, + 0x0C2, 0x09B, 0x070, 0x029, 0x1A6, 0x1FF, 0x114, 0x14D, + 0x171, 0x128, 0x1C3, 0x19A, 0x015, 0x04C, 0x0A7, 0x0FE, + 0x1E0, 0x1B9, 0x152, 0x10B, 0x084, 0x0DD, 0x036, 0x06F }; const uint16_t CCITT16_TABLE1[] = { 0x0000U, 0x1189U, 0x2312U, 0x329bU, 0x4624U, 0x57adU, 0x6536U, 0x74bfU, @@ -434,9 +434,20 @@ uint16_t CRC::crc9(const uint8_t* in, uint32_t length) assert(in != NULL); uint16_t crc = 0U; + uint16_t tmp = 0U; - for (uint32_t i = 0U; i < length; i++) - crc = CRC9_TABLE[(crc ^ in[i]) & 0xFF]; + while (length--) + { + tmp = (crc >> 1) & 0xFF; + crc = crc << 8; + crc = crc ^ CRC9_TABLE[tmp ^ (*in >> 8)]; - return crc; + tmp = (crc >> 1) & 0xFF; + crc = crc << 8; + crc = crc ^ CRC9_TABLE[tmp ^ (*in & 0xFF)]; + + in++; + } + + return ((~crc) & 0x1FF); } diff --git a/host/Host.cpp b/host/Host.cpp index f36588bd..888f585f 100644 --- a/host/Host.cpp +++ b/host/Host.cpp @@ -268,6 +268,7 @@ int Host::run() bool embeddedLCOnly = dmrProtocol["embeddedLCOnly"].as(false); bool dmrDumpDataPacket = dmrProtocol["dumpDataPacket"].as(false); bool dmrRepeatDataPacket = dmrProtocol["repeatDataPacket"].as(true); + bool dmrDumpCsbkData = dmrProtocol["dumpCsbkData"].as(false); bool dumpTAData = dmrProtocol["dumpTAData"].as(true); uint32_t callHang = dmrProtocol["callHang"].as(3U); uint32_t txHang = dmrProtocol["txHang"].as(4U); @@ -289,6 +290,7 @@ int Host::run() LogInfo(" Dump Talker Alias Data: %s", dumpTAData ? "yes" : "no"); LogInfo(" Dump Packet Data: %s", dmrDumpDataPacket ? "yes" : "no"); LogInfo(" Repeat Packet Data: %s", dmrRepeatDataPacket ? "yes" : "no"); + LogInfo(" Dump CSBK Data: %s", dmrDumpCsbkData ? "yes" : "no"); LogInfo(" Call Hang: %us", callHang); LogInfo(" TX Hang: %us", txHang); LogInfo(" Queue Size: %u", dmrQueueSize); @@ -310,7 +312,8 @@ int Host::run() } dmr = new dmr::Control(m_dmrColorCode, callHang, dmrQueueSize, embeddedLCOnly, dumpTAData, m_timeout, m_rfTalkgroupHang, - m_modem, m_network, m_duplex, m_ridLookup, m_tidLookup, rssi, jitter, dmrDumpDataPacket, dmrRepeatDataPacket, dmrDebug, dmrVerbose); + m_modem, m_network, m_duplex, m_ridLookup, m_tidLookup, rssi, jitter, dmrDumpDataPacket, dmrRepeatDataPacket, + dmrDumpCsbkData, dmrDebug, dmrVerbose); m_dmrTXTimer.setTimeout(txHang); if (dmrVerbose) { @@ -335,6 +338,7 @@ int Host::run() bool controlBcstContinuous = p25Protocol["control"]["continuous"].as(false); bool p25DumpDataPacket = p25Protocol["dumpDataPacket"].as(false); bool p25RepeatDataPacket = p25Protocol["repeatDataPacket"].as(true); + bool p25DumpTsbkData = p25Protocol["dumpTsbkData"].as(false); uint32_t callHang = p25Protocol["callHang"].as(3U); uint32_t p25QueueSize = p25Protocol["queueSize"].as(8192U); bool p25Verbose = p25Protocol["verbose"].as(true); @@ -343,6 +347,7 @@ int Host::run() LogInfo(" TDU Preamble before Voice: %u", tduPreambleCount); LogInfo(" Dump Packet Data: %s", p25DumpDataPacket ? "yes" : "no"); LogInfo(" Repeat Packet Data: %s", p25RepeatDataPacket ? "yes" : "no"); + LogInfo(" Dump TSBK Data: %s", p25DumpTsbkData ? "yes" : "no"); LogInfo(" Call Hang: %us", callHang); LogInfo(" Queue Size: %u", p25QueueSize); @@ -372,7 +377,8 @@ int Host::run() } p25 = new p25::Control(m_p25NAC, callHang, p25QueueSize, m_modem, m_network, m_timeout, m_rfTalkgroupHang, - p25ControlBcstInterval, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, p25DumpDataPacket, p25RepeatDataPacket, p25Debug, p25Verbose); + p25ControlBcstInterval, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, p25DumpDataPacket, p25RepeatDataPacket, + p25DumpTsbkData, p25Debug, p25Verbose); p25->setOptions(m_conf, m_cwCallsign, m_voiceChNo, m_p25PatchSuperGroup, m_p25NetId, m_p25SysId, m_p25RfssId, m_p25SiteId, m_channelId, m_channelNo, true); @@ -1202,6 +1208,10 @@ bool Host::createModem() uint8_t dmrRxDelay = (uint8_t)modemConf["dmrRxDelay"].as(7U); int rxDCOffset = modemConf["rxDCOffset"].as(0); int txDCOffset = modemConf["txDCOffset"].as(0); + int dmrSymLevel3Adj = modemConf["dmrSymLvl3Adj"].as(0); + int dmrSymLevel1Adj = modemConf["dmrSymLvl1Adj"].as(0); + int p25SymLevel3Adj = modemConf["p25SymLvl3Adj"].as(0); + int p25SymLevel1Adj = modemConf["p25SymLvl1Adj"].as(0); float rxLevel = modemConf["rxLevel"].as(50.0F); float cwIdTXLevel = modemConf["cwIdTxLevel"].as(50.0F); float dmrTXLevel = modemConf["dmrTxLevel"].as(50.0F); @@ -1243,6 +1253,7 @@ bool Host::createModem() m_modem = Modem::createModem(port, m_duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, fdmaPreamble, dmrRxDelay, packetPlayoutTime, disableOFlowReset, trace, debug); m_modem->setModeParams(m_dmrEnabled, m_p25Enabled); m_modem->setLevels(rxLevel, cwIdTXLevel, dmrTXLevel, p25TXLevel); + m_modem->setSymbolAdjust(dmrSymLevel3Adj, dmrSymLevel1Adj, p25SymLevel3Adj, p25SymLevel1Adj); m_modem->setDCOffsetParams(txDCOffset, rxDCOffset); m_modem->setDMRColorCode(m_dmrColorCode); m_modem->setP25NAC(m_p25NAC); diff --git a/host/calibrate/HostCal.cpp b/host/calibrate/HostCal.cpp index 8875ce51..b8aadaf6 100644 --- a/host/calibrate/HostCal.cpp +++ b/host/calibrate/HostCal.cpp @@ -148,6 +148,10 @@ HostCal::HostCal(const std::string& confFile) : m_p25Rx1K(false), m_txDCOffset(0), m_rxDCOffset(0), + m_dmrSymLevel3Adj(0), + m_dmrSymLevel1Adj(0), + m_p25SymLevel3Adj(0), + m_p25SymLevel1Adj(0), m_fdmaPreamble(80U), m_dmrRxDelay(7U), m_debug(false), @@ -235,6 +239,11 @@ int HostCal::run() m_rxLevel = modemConf["rxLevel"].as(50.0F); m_txLevel = modemConf["txLevel"].as(50.0F); + m_dmrSymLevel3Adj = modemConf["dmrSymLvl3Adj"].as(0); + m_dmrSymLevel1Adj = modemConf["dmrSymLvl1Adj"].as(0); + m_p25SymLevel3Adj = modemConf["p25SymLvl3Adj"].as(0); + m_p25SymLevel1Adj = modemConf["p25SymLvl1Adj"].as(0); + m_fdmaPreamble = (uint8_t)modemConf["fdmaPreamble"].as(80U); m_dmrRxDelay = (uint8_t)modemConf["dmrRxDelay"].as(7U); @@ -307,6 +316,32 @@ int HostCal::run() setTXDCOffset(1); break; + case '-': + setDMRSymLevel3Adj(-1); + break; + case '=': + setDMRSymLevel3Adj(1); + break; + case '_': + setDMRSymLevel1Adj(-1); + break; + case '+': + setDMRSymLevel1Adj(1); + break; + + case '[': + setP25SymLevel3Adj(-1); + break; + case ']': + setP25SymLevel3Adj(1); + break; + case '{': + setP25SymLevel1Adj(-1); + break; + case '}': + setP25SymLevel1Adj(1); + break; + case '1': { m_duplex = true; @@ -626,6 +661,15 @@ void HostCal::displayHelp() LogMessage(LOG_CAL, " b %s", P25_FEC_STR); LogMessage(LOG_CAL, " j %s", P25_FEC_1K_STR); LogMessage(LOG_CAL, " x %s", RSSI_CAL_STR); + LogMessage(LOG_CAL, "Engineering Commands:"); + LogMessage(LOG_CAL, " -/= Increase/Decrease DMR +/- 3 Symbol Level"); + LogMessage(LOG_CAL, " _/+ Increase/Decrease DMR +/- 1 Symbol Level"); + LogMessage(LOG_CAL, " [/] Increase/Decrease P25 +/- 3 Symbol Level"); + LogMessage(LOG_CAL, " {/} Increase/Decrease P25 +/- 1 Symbol Level"); + LogMessage(LOG_CAL, " 1 Transmit DMR/P25 +3 Symbols Only"); + LogMessage(LOG_CAL, " 2 Transmit DMR/P25 +1 Symbols Only"); + LogMessage(LOG_CAL, " 3 Transmit DMR/P25 -1 Symbols Only"); + LogMessage(LOG_CAL, " 4 Transmit DMR/P25 +1 Symbols Only"); } /// @@ -736,6 +780,94 @@ bool HostCal::setRXDCOffset(int incr) return true; } +/// +/// Helper to change the DMR Symbol Level 3 adjust. +/// +/// Amount to change. +/// True, if setting was applied, otherwise false. +bool HostCal::setDMRSymLevel3Adj(int incr) +{ + if (incr > 0 && m_dmrSymLevel3Adj < 127) { + m_dmrSymLevel3Adj++; + LogMessage(LOG_CAL, " - DMR Symbol Level +/- 3 Adjust: %d", m_dmrSymLevel3Adj); + return writeSymbolAdjust(); + } + + if (incr < 0 && m_dmrSymLevel3Adj > -127) { + m_dmrSymLevel3Adj--; + LogMessage(LOG_CAL, " - DMR Symbol Level +/- 3 Adjust: %d", m_dmrSymLevel3Adj); + return writeSymbolAdjust(); + } + + return true; +} + +/// +/// Helper to change the DMR Symbol Level 1 adjust. +/// +/// Amount to change. +/// True, if setting was applied, otherwise false. +bool HostCal::setDMRSymLevel1Adj(int incr) +{ + if (incr > 0 && m_dmrSymLevel1Adj < 127) { + m_dmrSymLevel1Adj++; + LogMessage(LOG_CAL, " - DMR Symbol Level +/- 1 Adjust: %d", m_dmrSymLevel1Adj); + return writeSymbolAdjust(); + } + + if (incr < 0 && m_dmrSymLevel1Adj > -127) { + m_dmrSymLevel1Adj--; + LogMessage(LOG_CAL, " - DMR Symbol Level +/- 1 Adjust: %d", m_dmrSymLevel1Adj); + return writeSymbolAdjust(); + } + + return true; +} + +/// +/// Helper to change the P25 Symbol Level 3 adjust. +/// +/// Amount to change. +/// True, if setting was applied, otherwise false. +bool HostCal::setP25SymLevel3Adj(int incr) +{ + if (incr > 0 && m_p25SymLevel3Adj < 127) { + m_p25SymLevel3Adj++; + LogMessage(LOG_CAL, " - P25 Symbol Level +/- 3 Adjust: %d", m_p25SymLevel3Adj); + return writeSymbolAdjust(); + } + + if (incr < 0 && m_p25SymLevel3Adj > -127) { + m_p25SymLevel3Adj--; + LogMessage(LOG_CAL, " - P25 Symbol Level +/- 3 Adjust: %d", m_p25SymLevel3Adj); + return writeSymbolAdjust(); + } + + return true; +} + +/// +/// Helper to change the P25 Symbol Level 1 adjust. +/// +/// Amount to change. +/// True, if setting was applied, otherwise false. +bool HostCal::setP25SymLevel1Adj(int incr) +{ + if (incr > 0 && m_p25SymLevel1Adj < 127) { + m_p25SymLevel1Adj++; + LogMessage(LOG_CAL, " - P25 Symbol Level +/- 1 Adjust: %d", m_p25SymLevel1Adj); + return writeSymbolAdjust(); + } + + if (incr < 0 && m_p25SymLevel1Adj > -127) { + m_p25SymLevel1Adj--; + LogMessage(LOG_CAL, " - P25 Symbol Level +/- 1 Adjust: %d", m_p25SymLevel1Adj); + return writeSymbolAdjust(); + } + + return true; +} + /// /// Helper to toggle modem transmit mode. /// @@ -1432,7 +1564,7 @@ bool HostCal::writeConfig(uint8_t modeOverride) uint8_t buffer[50U]; buffer[0U] = DVM_FRAME_START; - buffer[1U] = 16U; + buffer[1U] = 17U; buffer[2U] = CMD_SET_CONFIG; buffer[3U] = 0x00U; @@ -1491,7 +1623,52 @@ bool HostCal::writeConfig(uint8_t modeOverride) m_conf["system"]["modem"]["rxDCOffset"] = __INT_STR(m_rxDCOffset); buffer[17U] = (uint8_t)(m_rxDCOffset + 128); - int ret = m_serial.write(buffer, 16U); + int ret = m_serial.write(buffer, 17U); + if (ret <= 0) + return false; + + sleep(10U); + + ret = readModem(buffer, 50U); + if (ret <= 0) + return false; + + if (buffer[2U] == CMD_NAK) { + LogError(LOG_CAL, "Got a NAK from the modem"); + return false; + } + + if (buffer[2U] != CMD_ACK) { + Utils::dump("Invalid response", buffer, ret); + return false; + } + + return true; +} + +/// +/// Write symbol level adjustments to the modem DSP. +/// +/// True, if level adjustments are written, otherwise false. +bool HostCal::writeSymbolAdjust() +{ + uint8_t buffer[10U]; + + buffer[0U] = DVM_FRAME_START; + buffer[1U] = 7U; + buffer[2U] = CMD_SET_SYMLVLADJ; + + m_conf["system"]["modem"]["dmrSymLvl3Adj"] = __INT_STR(m_dmrSymLevel3Adj); + buffer[3U] = (uint8_t)(m_dmrSymLevel3Adj + 128); + m_conf["system"]["modem"]["dmrSymLvl1Adj"] = __INT_STR(m_dmrSymLevel1Adj); + buffer[4U] = (uint8_t)(m_dmrSymLevel1Adj + 128); + + m_conf["system"]["modem"]["p25SymLvl3Adj"] = __INT_STR(m_p25SymLevel3Adj); + buffer[5U] = (uint8_t)(m_p25SymLevel3Adj + 128); + m_conf["system"]["modem"]["p25SymLvl1Adj"] = __INT_STR(m_p25SymLevel1Adj); + buffer[6U] = (uint8_t)(m_p25SymLevel1Adj + 128); + + int ret = m_serial.write(buffer, 7U); if (ret <= 0) return false; @@ -1571,9 +1748,12 @@ void HostCal::timerStop() /// void HostCal::printStatus() { - LogMessage(LOG_CAL, " - PTT Invert: %s, RX Invert: %s, TX Invert: %s, DC Blocker: %s, RX Level: %.1f%%, TX Level: %.1f%%, TX DC Offset: %d, RX DC Offset: %d", - m_pttInvert ? "yes" : "no", m_rxInvert ? "yes" : "no", m_txInvert ? "yes" : "no", m_dcBlocker ? "yes" : "no", + LogMessage(LOG_CAL, " - PTT Invert: %s, RX Invert: %s, TX Invert: %s, DC Blocker: %s", + m_pttInvert ? "yes" : "no", m_rxInvert ? "yes" : "no", m_txInvert ? "yes" : "no", m_dcBlocker ? "yes" : "no"); + LogMessage(LOG_CAL, " - RX Level: %.1f%%, TX Level: %.1f%%, TX DC Offset: %d, RX DC Offset: %d", m_rxLevel, m_txLevel, m_txDCOffset, m_rxDCOffset); + LogMessage(LOG_CAL, " - DMR Symbol +/- 3 Level Adj.: %d, DMR Symbol +/- 1 Level Adj.: %d, P25 Symbol +/- 3 Level Adj.: %d, P25 Symbol +/- 1 Level Adj.: %d", + m_dmrSymLevel3Adj, m_dmrSymLevel1Adj, m_p25SymLevel3Adj, m_p25SymLevel1Adj); LogMessage(LOG_CAL, " - FDMA Preambles: %u (%.1fms), DMR Rx Delay: %u (%.1fms)", m_fdmaPreamble, float(m_fdmaPreamble) * 0.2083F, m_dmrRxDelay, float(m_dmrRxDelay) * 0.0416666F); LogMessage(LOG_CAL, " - Operating Mode: %s", m_modeStr.c_str()); diff --git a/host/calibrate/HostCal.h b/host/calibrate/HostCal.h index 0a4990e3..2a9a9c45 100644 --- a/host/calibrate/HostCal.h +++ b/host/calibrate/HostCal.h @@ -86,6 +86,11 @@ private: int m_txDCOffset; int m_rxDCOffset; + int m_dmrSymLevel3Adj; + int m_dmrSymLevel1Adj; + int m_p25SymLevel3Adj; + int m_p25SymLevel1Adj; + uint8_t m_fdmaPreamble; uint8_t m_dmrRxDelay; @@ -117,6 +122,15 @@ private: /// Helper to toggle modem transmit mode. bool setTransmit(); + /// Helper to change the DMR Symbol Level 3 adjust. + bool setDMRSymLevel3Adj(int incr); + /// Helper to change the DMR Symbol Level 1 adjust. + bool setDMRSymLevel1Adj(int incr); + /// Helper to change the P25 Symbol Level 3 adjust. + bool setP25SymLevel3Adj(int incr); + /// Helper to change the P25 Symbol Level 1 adjust. + bool setP25SymLevel1Adj(int incr); + /// Initializes the modem DSP. bool initModem(); /// Read data frames from the modem DSP. @@ -135,6 +149,8 @@ private: bool writeConfig(); /// Write configuration to the modem DSP. bool writeConfig(uint8_t modeOverride); + /// Write symbol level adjustments to the modem DSP. + bool writeSymbolAdjust(); /// void sleep(uint32_t ms); diff --git a/modem/Modem.cpp b/modem/Modem.cpp index 377c3eea..f10035ec 100644 --- a/modem/Modem.cpp +++ b/modem/Modem.cpp @@ -94,6 +94,10 @@ Modem::Modem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, m_p25Enabled(false), m_rxDCOffset(0), m_txDCOffset(0), + m_dmrSymLevel3Adj(0), + m_dmrSymLevel1Adj(0), + m_p25SymLevel3Adj(0), + m_p25SymLevel1Adj(0), m_adcOverFlowCount(0U), m_dacOverFlowCount(0U), m_serial(port, SERIAL_115200, true), @@ -167,6 +171,40 @@ void Modem::setLevels(float rxLevel, float cwIdTXLevel, float dmrTXLevel, float m_p25TXLevel = p25TXLevel; } +/// +/// Sets the modem DSP Symbol adjustment levels +/// +/// +/// +/// +/// +void Modem::setSymbolAdjust(int dmrSymLevel3Adj, int dmrSymLevel1Adj, int p25SymLevel3Adj, int p25SymLevel1Adj) +{ + m_dmrSymLevel3Adj = dmrSymLevel3Adj; + if (dmrSymLevel3Adj > 128) + m_dmrSymLevel3Adj = 0; + if (dmrSymLevel3Adj < -128) + m_dmrSymLevel3Adj = 0; + + m_dmrSymLevel1Adj = dmrSymLevel1Adj; + if (dmrSymLevel1Adj > 128) + m_dmrSymLevel1Adj = 0; + if (dmrSymLevel1Adj < -128) + m_dmrSymLevel1Adj = 0; + + m_p25SymLevel3Adj = p25SymLevel3Adj; + if (p25SymLevel3Adj > 128) + m_p25SymLevel3Adj = 0; + if (p25SymLevel3Adj < -128) + m_p25SymLevel3Adj = 0; + + m_p25SymLevel1Adj = p25SymLevel1Adj; + if (p25SymLevel1Adj > 128) + m_p25SymLevel1Adj = 0; + if (p25SymLevel1Adj < -128) + m_p25SymLevel1Adj = 0; +} + /// /// Sets the modem DSP DMR color code. /// @@ -263,6 +301,8 @@ bool Modem::open() return false; } + writeSymbolAdjust(); + m_statusTimer.start(); m_error = false; @@ -1125,7 +1165,7 @@ bool Modem::writeConfig() uint8_t buffer[20U]; buffer[0U] = DVM_FRAME_START; - buffer[1U] = 16U; + buffer[1U] = 17U; buffer[2U] = CMD_SET_CONFIG; buffer[3U] = 0x00U; @@ -1177,10 +1217,10 @@ bool Modem::writeConfig() buffer[16U] = (uint8_t)(m_txDCOffset + 128); buffer[17U] = (uint8_t)(m_rxDCOffset + 128); - // Utils::dump(1U, "Written", buffer, 16U); + // Utils::dump(1U, "Written", buffer, 17U); - int ret = m_serial.write(buffer, 16U); - if (ret != 16) + int ret = m_serial.write(buffer, 17U); + if (ret != 17) return false; uint32_t count = 0U; @@ -1210,6 +1250,55 @@ bool Modem::writeConfig() return true; } +/// +/// Write symbol level adjustments to the modem DSP. +/// +/// True, if level adjustments are written, otherwise false. +bool Modem::writeSymbolAdjust() +{ + uint8_t buffer[10U]; + + buffer[0U] = DVM_FRAME_START; + buffer[1U] = 7U; + buffer[2U] = CMD_SET_SYMLVLADJ; + + buffer[3U] = (uint8_t)(m_dmrSymLevel3Adj + 128); + buffer[4U] = (uint8_t)(m_dmrSymLevel1Adj + 128); + + buffer[5U] = (uint8_t)(m_p25SymLevel3Adj + 128); + buffer[6U] = (uint8_t)(m_p25SymLevel1Adj + 128); + + int ret = m_serial.write(buffer, 7U); + if (ret <= 0) + return false; + + uint32_t count = 0U; + RESP_TYPE_DVM resp; + do { + Thread::sleep(10U); + + resp = getResponse(); + if (resp == RTM_OK && m_buffer[2U] != CMD_ACK && m_buffer[2U] != CMD_NAK) { + count++; + if (count >= MAX_RESPONSES) { + LogError(LOG_MODEM, "No response, SET_SYMLVLADJ command"); + return false; + } + } + } while (resp == RTM_OK && m_buffer[2U] != CMD_ACK && m_buffer[2U] != CMD_NAK); + + // Utils::dump(1U, "Response", m_buffer, m_length); + + if (resp == RTM_OK && m_buffer[2U] == CMD_NAK) { + LogError(LOG_MODEM, "NAK to the SET_SYMLVLADJ command from the modem"); + return false; + } + + m_playoutTimer.start(); + + return true; +} + /// /// /// diff --git a/modem/Modem.h b/modem/Modem.h index 2f366713..0c52a8fe 100644 --- a/modem/Modem.h +++ b/modem/Modem.h @@ -93,6 +93,7 @@ namespace modem CMD_SET_CONFIG = 0x02U, CMD_SET_MODE = 0x03U, + CMD_SET_SYMLVLADJ = 0x04U, CMD_SET_RXLEVEL = 0x05U, CMD_CAL_DATA = 0x08U, @@ -175,6 +176,8 @@ namespace modem virtual void setModeParams(bool dmrEnabled, bool p25Enabled); /// Sets the modem DSP RF deviation levels. virtual void setLevels(float rxLevel, float cwIdTXLevel, float dmrTXLevel, float p25TXLevel); + /// Sets the modem DSP Symbol adjustment levels. + virtual void setSymbolAdjust(int dmrSymLevel3Adj, int dmrSymLevel1Adj, int p25SymLevel3Adj, int p25SymLevel1Adj); /// Sets the modem DSP DMR color code. virtual void setDMRColorCode(uint32_t colorCode); /// Sets the modem DSP P25 NAC. @@ -285,6 +288,11 @@ namespace modem int m_rxDCOffset; int m_txDCOffset; + int m_dmrSymLevel3Adj; + int m_dmrSymLevel1Adj; + int m_p25SymLevel3Adj; + int m_p25SymLevel1Adj; + uint32_t m_adcOverFlowCount; uint32_t m_dacOverFlowCount; @@ -319,6 +327,8 @@ namespace modem bool getStatus(); /// Write configuration to the modem DSP. bool writeConfig(); + /// Write symbol level adjustments to the modem DSP. + bool writeSymbolAdjust(); /// void printDebug(); diff --git a/modem/NullModem.h b/modem/NullModem.h index 2185f7e0..cb22662b 100644 --- a/modem/NullModem.h +++ b/modem/NullModem.h @@ -55,6 +55,8 @@ namespace modem virtual void setModeParams(bool dmrEnabled, bool p25Enabled) { return; } /// Sets the modem DSP RF deviation levels. virtual void setLevels(float rxLevel, float cwIdTXLevel, float dmrTXLevel, float p25TXLevel) { return; } + /// Sets the modem DSP Symbol adjustment levels + virtual void setSymbolAdjust(int dmrSymLevel3Adj, int dmrSymLevel1Adj, int p25SymLevel3Adj, int p25SymLevel1Adj) { return; } /// Sets the modem DSP DMR color code. virtual void setDMRColorCode(uint32_t colorCode) { return; } /// Sets the modem DSP RF receive deviation levels. diff --git a/p25/Control.cpp b/p25/Control.cpp index 0d5b7079..e1f2ff6f 100644 --- a/p25/Control.cpp +++ b/p25/Control.cpp @@ -74,12 +74,13 @@ const uint32_t MAX_PREAMBLE_TDU_CNT = 64U; /// Instance of the CRSSIInterpolator class. /// /// +/// /// Flag indicating whether P25 debug is enabled. /// Flag indicating whether P25 verbose logging is enabled. Control::Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Modem* modem, network::BaseNetwork* network, uint32_t timeout, uint32_t tgHang, uint32_t ccBcstInterval, bool duplex, lookups::RadioIdLookup* ridLookup, lookups::TalkgroupIdLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper, - bool dumpPDUData, bool repeatPDU, bool debug, bool verbose) : + bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose) : m_voice(NULL), m_data(NULL), m_trunk(NULL), @@ -94,6 +95,7 @@ Control::Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Mod m_control(false), m_continuousControl(false), m_voiceOnControl(false), + m_ackTSBKRequests(true), m_idenTable(idenTable), m_ridLookup(ridLookup), m_tidLookup(tidLookup), @@ -135,7 +137,7 @@ Control::Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Mod m_voice = new VoicePacket(this, network, debug, verbose); m_data = new DataPacket(this, network, dumpPDUData, repeatPDU, debug, verbose); - m_trunk = new TrunkPacket(this, network, debug, verbose); + m_trunk = new TrunkPacket(this, network, dumpTSBKData, debug, verbose); } /// @@ -208,6 +210,7 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s } m_voiceOnControl = p25Protocol["voiceOnControl"].as(false); + m_ackTSBKRequests = control["ackRequests"].as(true); m_voice->m_silenceThreshold = p25Protocol["silenceThreshold"].as(p25::DEFAULT_SILENCE_THRESHOLD); @@ -224,6 +227,7 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s if (m_control) { LogInfo(" Voice on Control: %s", m_voiceOnControl ? "yes" : "no"); + LogInfo(" Ack Requests: %s", m_ackTSBKRequests ? "yes" : "no"); } LogInfo(" Inhibit Illegal: %s", m_inhibitIllegal ? "yes" : "no"); @@ -823,7 +827,15 @@ void Control::addBusyBits(uint8_t* data, uint32_t length, bool b1, bool b2) { assert(data != NULL); + // insert the "10" (Unknown, use for inbound or outbound) status bits for (uint32_t ss0Pos = P25_SS0_START; ss0Pos < length; ss0Pos += P25_SS_INCREMENT) { + uint32_t ss1Pos = ss0Pos + 1U; + WRITE_BIT(data, ss0Pos, true); // 1 + WRITE_BIT(data, ss1Pos, false); // 0 + } + + // interleave the requested status bits (every other) + for (uint32_t ss0Pos = P25_SS0_START; ss0Pos < length; ss0Pos += (P25_SS_INCREMENT * 2)) { uint32_t ss1Pos = ss0Pos + 1U; WRITE_BIT(data, ss0Pos, b1); WRITE_BIT(data, ss1Pos, b2); diff --git a/p25/Control.h b/p25/Control.h index 55c369ae..9d1c51a1 100644 --- a/p25/Control.h +++ b/p25/Control.h @@ -69,7 +69,7 @@ namespace p25 Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Modem* modem, network::BaseNetwork* network, uint32_t timeout, uint32_t tgHang, uint32_t ccBcstInterval, bool duplex, lookups::RadioIdLookup* ridLookup, lookups::TalkgroupIdLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper, - bool dumpPDUData, bool repeatPDU, bool debug, bool verbose); + bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose); /// Finalizes a instance of the Control class. ~Control(); @@ -128,6 +128,7 @@ namespace p25 bool m_control; bool m_continuousControl; bool m_voiceOnControl; + bool m_ackTSBKRequests; lookups::IdenTableLookup* m_idenTable; lookups::RadioIdLookup* m_ridLookup; diff --git a/p25/DataPacket.cpp b/p25/DataPacket.cpp index 3c5fa37a..d356adc6 100644 --- a/p25/DataPacket.cpp +++ b/p25/DataPacket.cpp @@ -40,6 +40,7 @@ #include "Utils.h" using namespace p25; +using namespace p25::data; #include #include @@ -163,8 +164,6 @@ bool DataPacket::process(uint8_t* data, uint32_t len) } writeNetworkRF(P25_DT_DATA_HEADER, buffer, P25_PDU_FEC_LENGTH_BYTES); - - ::ActivityLog("P25", true, "received RF data transmission from %u to %u, %u blocks", m_rfDataHeader.getLLId(), m_rfDataHeader.getLLId(), m_rfDataHeader.getBlocksToFollow()); } if (m_p25->m_rfState == RS_RF_DATA) { @@ -274,19 +273,21 @@ bool DataPacket::process(uint8_t* data, uint32_t len) LogMessage(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_REQ_CNCT (Registration Request Connect), llId = %u, ipAddr = %u", llId, ipAddr); } - if (!hasLLIdFNEReg(llId)) { - // update dynamic FNE registration table entry - m_fneRegTable[llId] = ipAddr; - - if (m_verbose) { - LogMessage(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_RSP_ACCPT (Registration Response Accept), llId = %u, ipAddr = %u", llId, ipAddr); - } - writeRF_PDU_Reg_Response(PDU_REG_TYPE_RSP_ACCPT, llId, ipAddr); - } - else { + if (!acl::AccessControl::validateSrcId(llId)) { LogWarning(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_RSP_DENY (Registration Response Deny), llId = %u, ipAddr = %u", llId, ipAddr); writeRF_PDU_Reg_Response(PDU_REG_TYPE_RSP_DENY, llId, ipAddr); } + else { + if (!hasLLIdFNEReg(llId)) { + // update dynamic FNE registration table entry + m_fneRegTable[llId] = ipAddr; + + if (m_verbose) { + LogMessage(LOG_RF, P25_PDU_STR ", PDU_REG_TYPE_RSP_ACCPT (Registration Response Accept), llId = %u, ipAddr = %u", llId, ipAddr); + } + writeRF_PDU_Reg_Response(PDU_REG_TYPE_RSP_ACCPT, llId, ipAddr); + } + } } break; case PDU_REG_TYPE_REQ_DISCNCT: @@ -316,6 +317,8 @@ bool DataPacket::process(uint8_t* data, uint32_t len) } break; default: + ::ActivityLog("P25", true, "received RF data transmission from %u to %u, %u blocks", m_rfDataHeader.getLLId(), m_rfDataHeader.getLLId(), m_rfDataHeader.getBlocksToFollow()); + if (m_repeatPDU) { if (m_verbose) { LogMessage(LOG_RF, P25_PDU_STR ", repeating PDU, llId = %u", (m_rfDataHeader.getSAP() == PDU_SAP_EXT_ADDR) ? m_rfSecondHeader.getLLId() : m_rfDataHeader.getLLId()); @@ -323,11 +326,11 @@ bool DataPacket::process(uint8_t* data, uint32_t len) writeRF_PDU(); // re-generate PDU and send it on } + + ::ActivityLog("P25", true, "end RF data transmission"); break; } - ::ActivityLog("P25", true, "end RF data transmission"); - m_rfDataHeader.reset(); m_rfDataBlockCnt = 0U; m_rfPDUCount = 0U; @@ -696,23 +699,16 @@ void DataPacket::writeRF_PDU_Reg_Response(uint8_t regType, uint32_t llId, ulong6 uint8_t buffer[P25_PDU_FEC_LENGTH_BYTES]; ::memset(buffer, 0x00U, P25_PDU_FEC_LENGTH_BYTES); - m_rfDataHeader.reset(); - - // set header fields - m_rfDataHeader.setAckNeeded(true); - m_rfDataHeader.setOutbound(true); - m_rfDataHeader.setSAP(PDU_SAP_REG); - m_rfDataHeader.setLLId(llId); - m_rfDataHeader.setFullMessage(true); - m_rfDataHeader.setBlocksToFollow(1U); - m_rfDataHeader.setPadCount(0U); - m_rfDataHeader.setSync(false); - m_rfDataHeader.setN(0U); - m_rfDataHeader.setSeqNo(8U); - m_rfDataHeader.setHeaderOffset(0U); + DataRspHeader rspHeader = DataRspHeader(); + rspHeader.setOutbound(true); + rspHeader.setClass(PDU_ACK_CLASS_ACK); + rspHeader.setType(PDU_ACK_TYPE_ACK); + rspHeader.setLLId(llId); + rspHeader.setSrcLLId(P25_WUID_FNE); + rspHeader.setBlocksToFollow(1U); // Generate the PDU header and 1/2 rate Trellis - m_rfDataHeader.encode(buffer); + rspHeader.encode(buffer); Utils::setBitRange(buffer, m_rfPDU, offset, P25_PDU_FEC_LENGTH_BITS); offset += P25_PDU_FEC_LENGTH_BITS; @@ -733,7 +729,7 @@ void DataPacket::writeRF_PDU_Reg_Response(uint8_t regType, uint32_t llId, ulong6 edac::CRC::addCRC32(buffer, P25_PDU_CONFIRMED_LENGTH_BYTES); // Generate the PDU data - m_rfData[0].setFormat(m_rfDataHeader); + m_rfData[0].setFormat(PDU_FMT_RSP); m_rfData[0].setSerialNo(0U); m_rfData[0].setHalfRateTrellis(true); m_rfData[0].setData(buffer); diff --git a/p25/DataPacket.h b/p25/DataPacket.h index 174cfd82..41682ce3 100644 --- a/p25/DataPacket.h +++ b/p25/DataPacket.h @@ -34,6 +34,7 @@ #include "Defines.h" #include "p25/data/DataBlock.h" #include "p25/data/DataHeader.h" +#include "p25/data/DataRspHeader.h" #include "p25/data/LowSpeedData.h" #include "p25/lc/LC.h" #include "p25/Control.h" diff --git a/p25/P25Defines.h b/p25/P25Defines.h index 82e960bb..8c74c642 100644 --- a/p25/P25Defines.h +++ b/p25/P25Defines.h @@ -201,6 +201,22 @@ namespace p25 const uint8_t PDU_SAP_UNENC_KMM = 0x28U; const uint8_t PDU_SAP_ENC_KMM = 0x29U; + // PDU ACK Class + const uint8_t PDU_ACK_CLASS_ACK = 0x00U; + const uint8_t PDU_ACK_CLASS_NACK = 0x01U; + const uint8_t PDU_ACK_CLASS_ACK_RETRY = 0x02U; + + // PDU ACK Type(s) + const uint8_t PDU_ACK_TYPE_ACK = 0x01U; + + const uint8_t PDU_ACK_TYPE_NACK_ILLEGAL = 0x00U; // Illegal Format + const uint8_t PDU_ACK_TYPE_NACK_PACKET_CRC = 0x01U; // Packet CRC + const uint8_t PDU_ACK_TYPE_NACK_MEMORY_FULL = 0x02U; // Memory Full + const uint8_t PDU_ACK_TYPE_NACK_SEQ = 0x03U; // Out of logical sequence FSN + const uint8_t PDU_ACK_TYPE_NACK_UNDELIVERABLE = 0x04U;// Undeliverable + const uint8_t PDU_ACK_TYPE_NACK_OUT_OF_SEQ = 0x05U; // Out of sequence, N(S) != V(R) or V(R) + 1 + const uint8_t PDU_ACK_TYPE_NACK_INVL_USER = 0x06U; // Invalid User disallowed by the system + // PDU Registration Type(s) const uint8_t PDU_REG_TYPE_REQ_CNCT = 0x00U; const uint8_t PDU_REG_TYPE_REQ_DISCNCT = 0x01U; diff --git a/p25/TrunkPacket.cpp b/p25/TrunkPacket.cpp index 0b8d005f..ca2ec31e 100644 --- a/p25/TrunkPacket.cpp +++ b/p25/TrunkPacket.cpp @@ -377,7 +377,9 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len) m_rfTSBK.getResponse(), srcId, dstId); } - writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_UU_ANS, true); + if (m_p25->m_ackTSBKRequests) { + writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_UU_ANS, false, true); + } if (m_rfTSBK.getResponse() == P25_ANS_RSP_PROCEED) { writeRF_TSDU_Grant(false, false); @@ -401,14 +403,16 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len) m_rfTSBK.getResponse(), srcId); } - writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_TELE_INT_ANS, true); + if (m_p25->m_ackTSBKRequests) { + writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_TELE_INT_ANS, false, true); + } if (m_rfTSBK.getResponse() == P25_ANS_RSP_PROCEED) { //writeRF_TSDU_Grant(false); writeRF_TSDU_Deny(P25_DENY_RSN_SYS_UNSUPPORTED_SVC, TSBK_IOSP_TELE_INT_ANS); } else if (m_rfTSBK.getResponse() == P25_ANS_RSP_DENY) { - writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_TELE_INT_ANS, true); + writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_TELE_INT_ANS, false, true); } else if (m_rfTSBK.getResponse() == P25_ANS_RSP_WAIT) { writeRF_TSDU_Queue(P25_QUE_RSN_TGT_UNIT_QUEUED, TSBK_IOSP_TELE_INT_ANS); @@ -429,7 +433,7 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len) ::ActivityLog("P25", true, "received status update from %u", srcId); if (!m_noStatusAck) { - writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_STS_UPDT, false); + writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_STS_UPDT, false, false); } if (m_statusCmdEnable) { @@ -448,7 +452,7 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len) } if (!m_noMessageAck) { - writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_MSG_UPDT, false); + writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_MSG_UPDT, false, false); } ::ActivityLog("P25", true, "received message update from %u", srcId); @@ -510,7 +514,7 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len) ::ActivityLog("P25", true, "received cancel service request from %u", srcId); - writeRF_TSDU_ACK_FNE(srcId, TSBK_ISP_CAN_SRV_REQ, true); + writeRF_TSDU_ACK_FNE(srcId, TSBK_ISP_CAN_SRV_REQ, false, true); break; case TSBK_IOSP_EXT_FNCT: if (m_verbose) { @@ -552,7 +556,9 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len) LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_IOSP_GRP_AFF (Group Affiliation Request), srcId = %u, dstId = %u", srcId, dstId); } - writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_GRP_AFF, true); + if (m_p25->m_ackTSBKRequests) { + writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_GRP_AFF, true, true); + } writeRF_TSDU_Grp_Aff_Rsp(srcId, dstId); break; case TSBK_ISP_GRP_AFF_Q_RSP: @@ -578,7 +584,9 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len) dstId = P25_WUID_SYS; } - writeRF_TSDU_ACK_FNE(srcId, TSBK_ISP_U_DEREG_REQ, true); + if (m_p25->m_ackTSBKRequests) { + writeRF_TSDU_ACK_FNE(srcId, TSBK_ISP_U_DEREG_REQ, true, true); + } writeRF_TSDU_U_Dereg_Ack(srcId); break; case TSBK_IOSP_U_REG: @@ -589,7 +597,9 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len) LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_ISP_U_REG_REQ (Unit Registration Request), srcId = %u", srcId); } - writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_U_REG, true); + if (m_p25->m_ackTSBKRequests) { + writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_U_REG, true, true); + } writeRF_TSDU_U_Reg_Rsp(srcId); break; case TSBK_ISP_LOC_REG_REQ: @@ -1211,9 +1221,10 @@ void TrunkPacket::writeRF_TSDU_Mot_Patch(uint32_t group1, uint32_t group2, uint3 /// /// Instance of the Control class. /// Instance of the BaseNetwork class. +/// /// Flag indicating whether P25 debug is enabled. /// Flag indicating whether P25 verbose logging is enabled. -TrunkPacket::TrunkPacket(Control* p25, network::BaseNetwork* network, bool debug, bool verbose) : +TrunkPacket::TrunkPacket(Control* p25, network::BaseNetwork* network, bool dumpTSBKData, bool debug, bool verbose) : m_p25(p25), m_network(network), m_patchSuperGroup(0xFFFFU), @@ -1260,9 +1271,13 @@ TrunkPacket::TrunkPacket(Control* p25, network::BaseNetwork* network, bool debug m_netTSBK.setSiteData(m_siteData); m_rfTSBK.setCallsign("CHANGEME"); m_netTSBK.setCallsign("CHANGEME"); + m_rfTSBK.setVerbose(dumpTSBKData); + m_netTSBK.setVerbose(dumpTSBKData); m_rfTDULC.setSiteData(m_siteData); m_netTDULC.setSiteData(m_siteData); + m_rfTDULC.setVerbose(dumpTSBKData); + m_netTDULC.setVerbose(dumpTSBKData); m_voiceChTable.clear(); @@ -1534,7 +1549,7 @@ void TrunkPacket::writeRF_TSDU_SBF(bool noNetwork, bool clearBeforeWrite) } // Add busy bits - m_p25->addBusyBits(data + 2U, P25_TSDU_FRAME_LENGTH_BITS, true, false); + m_p25->addBusyBits(data + 2U, P25_TSDU_FRAME_LENGTH_BITS, false, true); // Set first busy bits to 1,1 m_p25->setBusyBits(data + 2U, P25_SS0_START, true, true); @@ -1637,7 +1652,7 @@ void TrunkPacket::writeRF_TSDU_MBF(bool clearBeforeWrite) P25Utils::encode(tsdu, data + 2U, 114U, 720U); // Add busy bits - m_p25->addBusyBits(data + 2U, P25_TSDU_TRIPLE_FRAME_LENGTH_BITS, true, false); + m_p25->addBusyBits(data + 2U, P25_TSDU_TRIPLE_FRAME_LENGTH_BITS, false, true); // Add idle bits addIdleBits(data + 2U, P25_TSDU_TRIPLE_FRAME_LENGTH_BITS, true, true); @@ -1959,7 +1974,7 @@ void TrunkPacket::writeRF_TSDU_UU_Ans_Req(uint32_t srcId, uint32_t dstId) /// /// /// -void TrunkPacket::writeRF_TSDU_ACK_FNE(uint32_t srcId, uint32_t service, bool noNetwork) +void TrunkPacket::writeRF_TSDU_ACK_FNE(uint32_t srcId, uint32_t service, bool extended, bool noNetwork) { uint8_t lco = m_rfTSBK.getLCO(); uint8_t mfId = m_rfTSBK.getMFId(); @@ -1968,9 +1983,14 @@ void TrunkPacket::writeRF_TSDU_ACK_FNE(uint32_t srcId, uint32_t service, bool no m_rfTSBK.setMFId(P25_MFG_STANDARD); m_rfTSBK.setService(service); + if (extended) { + m_rfTSBK.setAIV(true); + m_rfTSBK.setEX(true); + } + if (m_verbose) { - LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_IOSP_ACK_RSP (Acknowledge Response), AIV = %u, serviceType = $%02X, srcId = %u", - m_rfTSBK.getAIV(), m_rfTSBK.getService(), srcId); + LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_IOSP_ACK_RSP (Acknowledge Response), AIV = %u, EX = %u, serviceType = $%02X, srcId = %u", + m_rfTSBK.getAIV(), m_rfTSBK.getEX(), m_rfTSBK.getService(), srcId); } writeRF_TSDU_SBF(noNetwork); @@ -2086,9 +2106,7 @@ void TrunkPacket::writeRF_TSDU_U_Reg_Rsp(uint32_t srcId) } } - // because Motorola -- we'll set both Source and Destination to the Source ID - // for the U_REG_RSP apparently should have the SUID set this way... - m_rfTSBK.setSrcId(srcId/*P25_WUID_REG*/); + m_rfTSBK.setSrcId(srcId); m_rfTSBK.setDstId(srcId); writeRF_TSDU_SBF(true); @@ -2176,7 +2194,7 @@ void TrunkPacket::writeNet_TSDU_From_RF(uint8_t* data) m_rfTSBK.encode(data, true); // Add busy bits - m_p25->addBusyBits(data, P25_TSDU_FRAME_LENGTH_BYTES, true, false); + m_p25->addBusyBits(data, P25_TSDU_FRAME_LENGTH_BYTES, false, true); // Set first busy bits to 1,1 m_p25->setBusyBits(data, P25_SS0_START, true, true); @@ -2255,7 +2273,7 @@ void TrunkPacket::writeNet_TSDU() m_netTSBK.encode(buffer + 2U, true); // Add busy bits - m_p25->addBusyBits(buffer + 2U, P25_TSDU_FRAME_LENGTH_BYTES, true, false); + m_p25->addBusyBits(buffer + 2U, P25_TSDU_FRAME_LENGTH_BYTES, false, true); // Set first busy bits to 1,1 m_p25->setBusyBits(buffer + 2U, P25_SS0_START, true, true); @@ -2360,7 +2378,7 @@ bool TrunkPacket::processStatusCommand(uint32_t srcId, uint32_t dstId) resetStatusCommand(); } - writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_CALL_ALRT, false); + writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_CALL_ALRT, false, false); return true; } else { diff --git a/p25/TrunkPacket.h b/p25/TrunkPacket.h index d50c785c..ad374248 100644 --- a/p25/TrunkPacket.h +++ b/p25/TrunkPacket.h @@ -183,7 +183,7 @@ namespace p25 bool m_debug; /// Initializes a new instance of the TrunkPacket class. - TrunkPacket(Control* p25, network::BaseNetwork* network, bool debug, bool verbose); + TrunkPacket(Control* p25, network::BaseNetwork* network, bool dumpTSBKData, bool debug, bool verbose); /// Finalizes a instance of the TrunkPacket class. ~TrunkPacket(); @@ -213,7 +213,7 @@ namespace p25 /// Helper to write a unit to unit answer request packet. void writeRF_TSDU_UU_Ans_Req(uint32_t srcId, uint32_t dstId); /// Helper to write a acknowledge packet. - void writeRF_TSDU_ACK_FNE(uint32_t srcId, uint32_t service, bool noNetwork); + void writeRF_TSDU_ACK_FNE(uint32_t srcId, uint32_t service, bool extended, bool noNetwork); /// Helper to write a deny packet. void writeRF_TSDU_Deny(uint8_t reason, uint8_t service); /// Helper to write a group affiliation response packet. diff --git a/p25/data/DataBlock.cpp b/p25/data/DataBlock.cpp index 83af467b..bb64e6bb 100644 --- a/p25/data/DataBlock.cpp +++ b/p25/data/DataBlock.cpp @@ -177,6 +177,13 @@ void DataBlock::encode(uint8_t* data) } /// Sets the data format. +/// +void DataBlock::setFormat(const uint8_t fmt) +{ + m_fmt = fmt; +} + +/// Sets the data format from the data header. /// void DataBlock::setFormat(const DataHeader header) { diff --git a/p25/data/DataBlock.h b/p25/data/DataBlock.h index 07f3900c..98799612 100644 --- a/p25/data/DataBlock.h +++ b/p25/data/DataBlock.h @@ -58,6 +58,8 @@ namespace p25 void encode(uint8_t* data); /// Sets the data format. + void setFormat(const uint8_t fmt); + /// Sets the data format from the data header. void setFormat(const DataHeader header); /// Gets the data format. uint8_t getFormat() const; diff --git a/p25/data/DataRspHeader.cpp b/p25/data/DataRspHeader.cpp new file mode 100644 index 00000000..c755c0fa --- /dev/null +++ b/p25/data/DataRspHeader.cpp @@ -0,0 +1,196 @@ +/** +* 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) 2020 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 "p25/P25Defines.h" +#include "p25/data/DataRspHeader.h" +#include "edac/CRC.h" +#include "Utils.h" + +using namespace p25::data; +using namespace p25; + +#include +#include +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- +/// +/// Initializes a new instance of the DataRspHeader class. +/// +DataRspHeader::DataRspHeader() : + m_outbound(true), + m_rspClass(PDU_ACK_CLASS_NACK), + m_rspType(PDU_ACK_TYPE_NACK_ILLEGAL), + m_rspStatus(0U), + m_mfId(P25_MFG_STANDARD), + m_llId(0U), + m_srcLlId(0U), + m_extended(true), + m_trellis(), + m_blocksToFollow(0U), + m_dataOctets(0U) +{ + /* stub */ +} + +/// +/// Finalizes a instance of the DataRspHeader class. +/// +DataRspHeader::~DataRspHeader() +{ + /* stub */ +} + +/// +/// Decodes P25 PDU data response header. +/// +/// +/// True, if PDU data response header was decoded, otherwise false. +bool DataRspHeader::decode(const uint8_t* data) +{ + assert(data != NULL); + + uint8_t header[P25_PDU_HEADER_LENGTH_BYTES]; + ::memset(header, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); + + // decode 1/2 rate Trellis & check CRC-CCITT 16 + bool valid = m_trellis.decode12(data, header); + if (valid) + valid = edac::CRC::checkCCITT162(header, P25_PDU_HEADER_LENGTH_BYTES); + if (!valid) { + return false; + } + + // Utils::dump(1U, "PDU Response Header Data", data, P25_PDU_HEADER_LENGTH_BYTES); + + m_outbound = (header[0U] & 0x20U) == 0x20U; // Inbound/Outbound + + m_rspClass = (header[1U] >> 6) & 0x03U; // Response Class + m_rspType = (header[1U] >> 3) & 0x07U; // Response Type + m_rspStatus = header[1U] & 0x07U; // Response Status + + m_mfId = header[2U]; // Mfg Id. + + m_llId = (header[3U] << 16) + (header[4U] << 8) + header[5U]; // Logical Link ID + + m_extended = (header[6U] & 0x80U) == 0x80U; // Extended Addressing + m_blocksToFollow = header[6U] & 0x7FU; // Block Frames to Follow + + m_srcLlId = (header[7U] << 16) + (header[8U] << 8) + header[9U]; // Source Logical Link ID + + return true; +} + +/// +/// Encodes P25 PDU data response header. +/// +/// +void DataRspHeader::encode(uint8_t * data) +{ + assert(data != NULL); + + uint8_t header[P25_PDU_HEADER_LENGTH_BYTES]; + ::memset(header, 0x00U, P25_PDU_HEADER_LENGTH_BYTES); + + header[0U] = PDU_FMT_RSP; + header[0U] = (m_outbound ? 0x20U : 0x00U) + // Inbound/Outbound + (PDU_FMT_RSP & 0x1FU); // Packet Format + + header[1U] = ((m_rspClass & 0x03U) << 6) + // Response Class + ((m_rspType & 0x07U) << 3) + // Response Type + ((m_rspStatus & 0x07U)); // Response Status + + header[2U] = m_mfId; // Mfg Id. + + header[3U] = (m_llId >> 16) & 0xFFU; // Logical Link ID + header[4U] = (m_llId >> 8) & 0xFFU; + header[5U] = (m_llId >> 0) & 0xFFU; + + header[6U] = (m_extended ? 0x80U : 0x00U) + // Extended Addressing + (m_blocksToFollow & 0x7FU); // Blocks Frames to Follow + + header[7U] = (m_srcLlId >> 16) & 0xFFU; // Source Logical Link ID + header[8U] = (m_srcLlId >> 8) & 0xFFU; + header[9U] = (m_srcLlId >> 0) & 0xFFU; + + // compute CRC-CCITT 16 + edac::CRC::addCCITT162(header, P25_PDU_HEADER_LENGTH_BYTES); + + // encode 1/2 rate Trellis + m_trellis.encode12(header, data); +} + +/// +/// Helper to reset data values to defaults. +/// +void DataRspHeader::reset() +{ + m_outbound = false; + + m_rspClass = PDU_ACK_CLASS_NACK; + m_rspType = PDU_ACK_TYPE_NACK_ILLEGAL; + m_rspStatus = 0U; + + m_mfId = P25_MFG_STANDARD; + m_llId = 0U; + m_srcLlId = 0U; + + m_extended = true; + + m_blocksToFollow = 0U; + m_dataOctets = 0U; +} + +/// Gets the total number of data octets. +/// +uint32_t DataRspHeader::getDataOctets() const +{ + return m_dataOctets; +} + +/** Common Data */ +/// Sets the total number of blocks to follow this header. +/// +void DataRspHeader::setBlocksToFollow(uint8_t blocksToFollow) +{ + m_blocksToFollow = blocksToFollow; + + // recalculate count of data octets + m_dataOctets = 16 * m_blocksToFollow - 4; +} + +/// Gets the total number of blocks to follow this header. +/// +uint8_t DataRspHeader::getBlocksToFollow() const +{ + return m_blocksToFollow; +} diff --git a/p25/data/DataRspHeader.h b/p25/data/DataRspHeader.h new file mode 100644 index 00000000..ea565f2b --- /dev/null +++ b/p25/data/DataRspHeader.h @@ -0,0 +1,98 @@ +/** +* 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) 2020 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(__P25_DATA__DATA_RSP_HEADER_H__) +#define __P25_DATA__DATA_RSP_HEADER_H__ + +#include "Defines.h" +#include "p25/edac/Trellis.h" + +#include + +namespace p25 +{ + namespace data + { + // --------------------------------------------------------------------------- + // Class Declaration + // Represents the data response header for PDU P25 packets. + // --------------------------------------------------------------------------- + + class HOST_SW_API DataRspHeader { + public: + /// Initializes a new instance of the DataRspHeader class. + DataRspHeader(); + /// Finalizes a instance of the DataRspHeader class. + ~DataRspHeader(); + + /// Decodes P25 PDU data response header. + bool decode(const uint8_t* data); + /// Encodes P25 PDU data response header. + void encode(uint8_t* data); + + /// Helper to reset data values to defaults. + void reset(); + + /// Gets the total number of data octets. + uint32_t getDataOctets() const; + + /** Common Data */ + /// Sets the total number of blocks to follow this header. + void setBlocksToFollow(uint8_t blocksToFollow); + /// Gets the total number of blocks to follow this header. + uint8_t getBlocksToFollow() const; + + public: + /// Flag indicating if this is an outbound data packet. + __PROPERTY(bool, outbound, Outbound); + /// Response class. + __PROPERTY(uint8_t, rspClass, Class); + /// Response type. + __PROPERTY(uint8_t, rspType, Type); + /// Response status. + __PROPERTY(uint8_t, rspStatus, Status); + /// Manufacturer ID. + __PROPERTY(uint8_t, mfId, MFId); + /// Logical link ID. + __PROPERTY(uint32_t, llId, LLId); + /// Source Logical link ID. + __PROPERTY(uint32_t, srcLlId, SrcLLId); + /// Flag indicating whether or not this response packet is to extended addressing. + __PROPERTY(bool, extended, Extended); + + private: + edac::Trellis m_trellis; + + uint8_t m_blocksToFollow; + uint32_t m_dataOctets; + }; + } // namespace data +} // namespace p25 + +#endif // __P25_DATA__DATA_RSP_HEADER_H__ diff --git a/p25/lc/TDULC.cpp b/p25/lc/TDULC.cpp index a89215d0..95e13bbd 100644 --- a/p25/lc/TDULC.cpp +++ b/p25/lc/TDULC.cpp @@ -50,6 +50,7 @@ using namespace p25; /// Initializes a new instance of the TDULC class. /// TDULC::TDULC() : + m_verbose(false), m_protect(false), m_lco(LC_GROUP), m_mfId(P25_MFG_STANDARD), @@ -107,7 +108,9 @@ bool TDULC::decode(const uint8_t* data) return false; } - // Utils::dump(2U, "TDULC", rs, P25_TDULC_LENGTH_BYTES); + if (m_verbose) { + Utils::dump(2U, "Decoded TDULC", rs, P25_TDULC_LENGTH_BYTES); + } return decodeLC(rs); } @@ -126,7 +129,9 @@ void TDULC::encode(uint8_t * data) encodeLC(rs); - // Utils::dump(2U, "TDULC", rs, P25_TDULC_LENGTH_BYTES); + if (m_verbose) { + Utils::dump(2U, "Encoded TDULC", rs, P25_TDULC_LENGTH_BYTES); + } // encode RS (24,12,13) FEC m_rs.encode241213(rs); diff --git a/p25/lc/TDULC.h b/p25/lc/TDULC.h index de0335ea..0ec3cd60 100644 --- a/p25/lc/TDULC.h +++ b/p25/lc/TDULC.h @@ -80,6 +80,9 @@ namespace p25 void setNetActive(bool netActive); public: + /// + __PROPERTY(bool, verbose, Verbose); + /** Common Data */ /// Flag indicating the link control data is protected. __PROPERTY(bool, protect, Protect); diff --git a/p25/lc/TSBK.cpp b/p25/lc/TSBK.cpp index 08badb8f..7225a2d7 100644 --- a/p25/lc/TSBK.cpp +++ b/p25/lc/TSBK.cpp @@ -49,6 +49,7 @@ using namespace p25; /// Initializes a new instance of the TSBK class. /// TSBK::TSBK() : + m_verbose(false), m_protect(false), m_lco(LC_GROUP), m_mfId(P25_MFG_STANDARD), @@ -56,6 +57,7 @@ TSBK::TSBK() : m_dstId(0U), m_lastBlock(false), m_aivFlag(true), + m_extendedAddrFlag(false), m_emergency(false), m_encrypted(false), m_priority(4U), @@ -122,7 +124,9 @@ bool TSBK::decode(const uint8_t* data) return false; } - // Utils::dump(2U, "TSDU TSBK", tsbk, P25_TSBK_LENGTH_BYTES); + if (m_verbose) { + Utils::dump(2U, "Decoded TSBK", tsbk, P25_TSBK_LENGTH_BYTES); + } m_lco = tsbk[0U] & 0x3F; // LCO m_lastBlock = (tsbk[0U] & 0x80U) == 0x80U; // Protect Flag @@ -357,7 +361,14 @@ void TSBK::encode(uint8_t * data, bool singleBlock) case TSBK_IOSP_ACK_RSP: tsbkValue = (m_service & 0x3F); // Service Type tsbkValue |= (m_aivFlag) ? 0x80U : 0x00U; // Additional Info. Valid Flag - tsbkValue = (tsbkValue << 32) + m_dstId; // Target Radio Address + tsbkValue |= (m_extendedAddrFlag) ? 0x40U : 0x00U; // Extended Addressing Flag + if (m_aivFlag && m_extendedAddrFlag) { + tsbkValue = (tsbkValue << 20) + m_siteData.netId(); // Network ID + tsbkValue = (tsbkValue << 12) + m_siteData.sysId(); // System ID + } + else { + tsbkValue = (tsbkValue << 32) + m_dstId; // Target Radio Address + } tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; case TSBK_IOSP_EXT_FNCT: @@ -688,7 +699,9 @@ void TSBK::encode(uint8_t * data, bool singleBlock) // compute CRC-CCITT 16 edac::CRC::addCCITT162(tsbk, P25_TSBK_LENGTH_BYTES); - // Utils::dump(2U, "TSBK", tsbk, P25_TSBK_LENGTH_BYTES); + if (m_verbose) { + Utils::dump(2U, "Encoded TSBK", tsbk, P25_TSBK_LENGTH_BYTES); + } uint8_t raw[P25_TSBK_FEC_LENGTH_BYTES]; ::memset(raw, 0x00U, P25_TSBK_FEC_LENGTH_BYTES); @@ -724,6 +737,7 @@ void TSBK::reset() m_lastBlock = true; m_aivFlag = true; + m_extendedAddrFlag = false; m_service = 0U; m_response = P25_RSP_ACCEPT; diff --git a/p25/lc/TSBK.h b/p25/lc/TSBK.h index a70589ae..03ddb775 100644 --- a/p25/lc/TSBK.h +++ b/p25/lc/TSBK.h @@ -12,7 +12,7 @@ // /* * Copyright (C) 2016 by Jonathan Naylor G4KLX -* Copyright (C) 2017-2019 by Bryan Biedenkapp N2PLL +* Copyright (C) 2017-2020 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 @@ -87,6 +87,9 @@ namespace p25 void setSiteChCnt(uint8_t chCnt); public: + /// + __PROPERTY(bool, verbose, Verbose); + /** Common Data */ /// Flag indicating the link control data is protected. __PROPERTY(bool, protect, Protect); @@ -104,6 +107,8 @@ namespace p25 __PROPERTY(bool, lastBlock, LastBlock); /// Flag indicating this TSBK contains additional information. __PROPERTY(bool, aivFlag, AIV); + /// Flag indicating this TSBK contains extended addressing. + __PROPERTY(bool, extendedAddrFlag, EX); /// Service type. __PROPERTY(uint8_t, service, Service);