diff --git a/src/host/Host.DMR.cpp b/src/host/Host.DMR.cpp index c579f126..e48bca21 100644 --- a/src/host/Host.DMR.cpp +++ b/src/host/Host.DMR.cpp @@ -139,6 +139,16 @@ void Host::writeFramesDMR1(dmr::Control* control, std::function&& afterW // to the modem bool ret = m_modem->hasDMRSpace1(); if (ret) { + uint32_t nextLen = control->peekFrameLength(1U); + if (m_dmrCtrlChannel) { + if (m_dmrDedicatedTxTestTimer.hasExpired()) { + m_dmrDedicatedTxTestTimer.pause(); + if (!m_modem->hasTX() && m_modem->gotModemStatus() && m_state == STATE_DMR && (control->getTSCCSlotNo() == 1U) && control->getCCRunning()) { + LogError(LOG_HOST, "DMR dedicated control not transmitting, running = %u, halted = %u, frameLength1 = %u", control->getCCRunning(), control->getCCHalted(), nextLen); + } + } + } + uint32_t len = control->getFrame(1U, data); if (len > 0U) { // if the state is idle; set to DMR, start mode timer and start DMR idle frames @@ -261,6 +271,16 @@ void Host::writeFramesDMR2(dmr::Control* control, std::function&& afterW // to the modem bool ret = m_modem->hasDMRSpace2(); if (ret) { + uint32_t nextLen = control->peekFrameLength(1U); + if (m_dmrCtrlChannel) { + if (m_dmrDedicatedTxTestTimer.hasExpired()) { + m_dmrDedicatedTxTestTimer.pause(); + if (!m_modem->hasTX() && m_modem->gotModemStatus() && m_state == STATE_DMR && (control->getTSCCSlotNo() == 2U) && control->getCCRunning()) { + LogError(LOG_HOST, "DMR dedicated control not transmitting, running = %u, halted = %u, frameLength2 = %u", control->getCCRunning(), control->getCCHalted(), nextLen); + } + } + } + uint32_t len = control->getFrame(2U, data); if (len > 0U) { // if the state is idle; set to DMR, start mode timer and start DMR idle frames diff --git a/src/host/Host.NXDN.cpp b/src/host/Host.NXDN.cpp index c2ac144f..d3e68ce7 100644 --- a/src/host/Host.NXDN.cpp +++ b/src/host/Host.NXDN.cpp @@ -88,6 +88,16 @@ void Host::writeFramesNXDN(nxdn::Control* control, std::function&& after if (control != nullptr) { bool ret = m_modem->hasNXDNSpace(); if (ret) { + uint32_t nextLen = control->peekFrameLength(); + if (m_nxdnCtrlChannel) { + if (m_nxdnDedicatedTxTestTimer.hasExpired()) { + m_nxdnDedicatedTxTestTimer.pause(); + if (!m_modem->hasTX() && m_modem->gotModemStatus() && m_state == STATE_NXDN && control->getCCRunning()) { + LogError(LOG_HOST, "NXDN dedicated control stopped transmitting, running = %u, halted = %u, frameLength = %u", control->getCCRunning(), control->getCCHalted(), nextLen); + } + } + } + uint32_t len = control->getFrame(data); if (len > 0U) { // if the state is idle; set to NXDN and start mode timer diff --git a/src/host/Host.P25.cpp b/src/host/Host.P25.cpp index 9a1e9c32..e770f431 100644 --- a/src/host/Host.P25.cpp +++ b/src/host/Host.P25.cpp @@ -124,13 +124,22 @@ void Host::readFramesP25(p25::Control* control, std::function&& afterRea /// void Host::writeFramesP25(p25::Control* control, std::function&& afterWriteCallback) { - uint8_t data[p25::P25_LDU_FRAME_LENGTH_BYTES * 2U]; + uint8_t data[p25::P25_PDU_FRAME_LENGTH_BYTES * 2U]; // check if there is space on the modem for P25 frames, // if there is read frames from the P25 controller and write it // to the modem if (control != nullptr) { uint8_t nextLen = control->peekFrameLength(); + if (m_p25CtrlChannel) { + if (m_p25DedicatedTxTestTimer.hasExpired()) { + m_p25DedicatedTxTestTimer.pause(); + if (!m_modem->hasTX() && m_modem->gotModemStatus() && m_state == STATE_P25 && control->getCCRunning()) { + LogError(LOG_HOST, "P25 dedicated control not transmitting, running = %u, halted = %u, frameLength = %u", control->getCCRunning(), control->getCCHalted(), nextLen); + } + } + } + if (nextLen > 0U) { bool ret = m_modem->hasP25Space(nextLen); if (ret) { diff --git a/src/host/Host.cpp b/src/host/Host.cpp index 8c508f41..f4a53908 100644 --- a/src/host/Host.cpp +++ b/src/host/Host.cpp @@ -117,8 +117,11 @@ Host::Host(const std::string& confFile) : m_authoritative(true), m_supervisor(false), m_dmrBeaconDurationTimer(1000U), + m_dmrDedicatedTxTestTimer(1000U, 0U, 125U), m_p25BcastDurationTimer(1000U), + m_p25DedicatedTxTestTimer(1000U, 0U, 125U), m_nxdnBcastDurationTimer(1000U), + m_nxdnDedicatedTxTestTimer(1000U, 0U, 125U), m_activeTickDelay(5U), m_idleTickDelay(5U), m_restAddress("0.0.0.0"), @@ -1127,13 +1130,42 @@ int Host::run() if (m_network != nullptr) m_network->clock(ms); - if (dmr != nullptr) + if (dmr != nullptr) { dmr->clock(ms); - if (p25 != nullptr) + + if (m_dmrCtrlChannel) { + if (!m_dmrDedicatedTxTestTimer.isRunning()) { + m_dmrDedicatedTxTestTimer.start(); + } else { + m_dmrDedicatedTxTestTimer.clock(ms); + } + } + } + + if (p25 != nullptr) { p25->clock(ms); - if (nxdn != nullptr) + + if (m_p25CtrlChannel) { + if (!m_p25DedicatedTxTestTimer.isRunning()) { + m_p25DedicatedTxTestTimer.start(); + } else { + m_p25DedicatedTxTestTimer.clock(ms); + } + } + } + + if (nxdn != nullptr) { nxdn->clock(ms); + if (m_nxdnCtrlChannel) { + if (!m_nxdnDedicatedTxTestTimer.isRunning()) { + m_nxdnDedicatedTxTestTimer.start(); + } else { + m_nxdnDedicatedTxTestTimer.clock(ms); + } + } + } + // ------------------------------------------------------ // -- Timer Clocking -- // ------------------------------------------------------ diff --git a/src/host/Host.h b/src/host/Host.h index 577a9f82..5fe8a6c6 100644 --- a/src/host/Host.h +++ b/src/host/Host.h @@ -143,8 +143,11 @@ private: bool m_supervisor; Timer m_dmrBeaconDurationTimer; + Timer m_dmrDedicatedTxTestTimer; Timer m_p25BcastDurationTimer; + Timer m_p25DedicatedTxTestTimer; Timer m_nxdnBcastDurationTimer; + Timer m_nxdnDedicatedTxTestTimer; uint8_t m_activeTickDelay; uint8_t m_idleTickDelay; diff --git a/src/host/dmr/Control.cpp b/src/host/dmr/Control.cpp index 748c397a..c221b589 100644 --- a/src/host/dmr/Control.cpp +++ b/src/host/dmr/Control.cpp @@ -331,9 +331,28 @@ bool Control::processFrame(uint32_t slotNo, uint8_t *data, uint32_t len) } } +/// +/// Get the frame data length for the next frame in the data ring buffer. +/// +/// +/// Length of frame data retrieved. +uint32_t Control::peekFrameLength(uint32_t slotNo) +{ + switch (slotNo) { + case 1U: + return m_slot1->peekFrameLength(); + case 2U: + return m_slot2->peekFrameLength(); + default: + LogError(LOG_DMR, "DMR, invalid slot, slotNo = %u", slotNo); + return 0U; + } +} + /// /// Get a data frame for slot, from data ring buffer. /// +/// /// Buffer to put retrieved DMR data frame data. /// Length of data retrieved from DMR ring buffer. uint32_t Control::getFrame(uint32_t slotNo, uint8_t* data) diff --git a/src/host/dmr/Control.h b/src/host/dmr/Control.h index 909f2761..d1377a56 100644 --- a/src/host/dmr/Control.h +++ b/src/host/dmr/Control.h @@ -69,6 +69,8 @@ namespace dmr /// Process a data frame for slot, from the RF interface. bool processFrame(uint32_t slotNo, uint8_t* data, uint32_t len); + /// Get the frame data length for the next frame in the data ring buffer. + uint32_t peekFrameLength(uint32_t slotNo); /// Get a data frame for slot, from data ring buffer. uint32_t getFrame(uint32_t slotNo, uint8_t* data); @@ -91,6 +93,8 @@ namespace dmr /// Helper to return the slot carrying the TSCC. Slot* getTSCCSlot() const; + /// Helper to return the slot number carrying the TSCC. + uint8_t getTSCCSlotNo() const { return m_tsccSlotNo; } /// Helper to payload activate the slot carrying granted payload traffic. void tsccActivateSlot(uint32_t slotNo, uint32_t dstId, uint32_t srcId, bool group, bool voice); /// Helper to clear an activated payload slot. diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index e46dd8ac..e34929af 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -327,6 +327,28 @@ bool Slot::processFrame(uint8_t *data, uint32_t len) return m_voice->process(data, len); } +/// +/// Get the frame data length for the next frame in the data ring buffer. +/// +/// Length of frame data retrieved. +uint32_t Slot::peekFrameLength() +{ + if (m_txQueue.isEmpty() && m_txImmQueue.isEmpty()) + return 0U; + + uint8_t len = 0U; + + // tx immediate queue takes priority + if (!m_txImmQueue.isEmpty()) { + m_txImmQueue.peek(&len, 1U); + } + else { + m_txQueue.peek(&len, 1U); + } + + return len; +} + /// /// Get frame data from data ring buffer. /// diff --git a/src/host/dmr/Slot.h b/src/host/dmr/Slot.h index 2544422f..89b59908 100644 --- a/src/host/dmr/Slot.h +++ b/src/host/dmr/Slot.h @@ -85,6 +85,8 @@ namespace dmr /// Process a data frame from the RF interface. bool processFrame(uint8_t* data, uint32_t len); + /// Get the frame data length for the next frame in the data ring buffer. + uint32_t peekFrameLength(); /// Get data frame from data ring buffer. uint32_t getFrame(uint8_t* data); diff --git a/src/host/modem/Modem.cpp b/src/host/modem/Modem.cpp index a24830c9..a1e16781 100644 --- a/src/host/modem/Modem.cpp +++ b/src/host/modem/Modem.cpp @@ -174,6 +174,7 @@ Modem::Modem(port::IModemPort* port, bool duplex, bool rxInvert, bool txInvert, m_error(false), m_ignoreModemConfigArea(ignoreModemConfigArea), m_flashDisabled(false), + m_gotModemStatus(false), m_dumpModemStatus(dumpModemStatus), m_trace(trace), m_debug(debug) @@ -524,6 +525,7 @@ void Modem::setCloseHandler(std::function handler) bool Modem::open() { LogMessage(LOG_MODEM, "Initializing modem"); + m_gotModemStatus = false; bool ret = m_port->open(); if (!ret) @@ -905,6 +907,7 @@ void Modem::clock(uint32_t ms) m_rxP25Queue.length(), m_rxP25Queue.dataSize(), m_rxP25Queue.freeSpace(), m_rxNXDNQueue.length(), m_rxNXDNQueue.dataSize(), m_rxNXDNQueue.freeSpace()); } + m_gotModemStatus = true; m_inactivityTimer.start(); } break; @@ -972,6 +975,8 @@ void Modem::close() LogDebug(LOG_MODEM, "Closing the modem"); m_port->close(); + m_gotModemStatus = false; + // do we have a close port handler? if (m_closePortHandler != nullptr) { m_closePortHandler(this); @@ -1167,6 +1172,15 @@ bool Modem::hasError() const return m_error; } +/// +/// Flag indicating whether or not the air interface modem has sent the initial modem status. +/// +/// True, if the air interface modem has sent the initial status, otherwise false. +bool Modem::gotModemStatus() const +{ + return m_gotModemStatus; +} + /// /// Clears any buffered DMR Slot 1 frame data to be sent to the air interface modem. /// @@ -1467,7 +1481,10 @@ bool Modem::writeP25Frame(const uint8_t* data, uint32_t length) assert(length > 0U); if (m_p25Enabled) { - const uint16_t MAX_LENGTH = 520U; + uint16_t MAX_LENGTH = 520U; + if (m_protoVer <= 3U) { + MAX_LENGTH = 251U; // for older firmware always ensure frames are shorter then 252 bytes + } if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) return false; diff --git a/src/host/modem/Modem.h b/src/host/modem/Modem.h index 8521d4e2..4f0d55c9 100644 --- a/src/host/modem/Modem.h +++ b/src/host/modem/Modem.h @@ -311,6 +311,9 @@ namespace modem /// Flag indicating whether or not the air interface modem is currently in an error condition. bool hasError() const; + /// Flag indicating whether or not the air interface modem has sent the initial modem status. + bool gotModemStatus() const; + /// Clears any buffered DMR Slot 1 frame data to be sent to the air interface modem. void clearDMRFrame1(); /// Clears any buffered DMR Slot 2 frame data to be sent to the air interface modem. @@ -489,6 +492,8 @@ namespace modem bool m_ignoreModemConfigArea; bool m_flashDisabled; + bool m_gotModemStatus; + bool m_dumpModemStatus; /// Internal helper to warm reset the connection to the modem. diff --git a/src/host/nxdn/Control.cpp b/src/host/nxdn/Control.cpp index 4d23e8ef..40bae491 100644 --- a/src/host/nxdn/Control.cpp +++ b/src/host/nxdn/Control.cpp @@ -480,6 +480,28 @@ bool Control::processFrame(uint8_t* data, uint32_t len) return ret; } +/// +/// Get the frame data length for the next frame in the data ring buffer. +/// +/// Length of frame data retrieved. +uint32_t Control::peekFrameLength() +{ + if (m_txQueue.isEmpty() && m_txImmQueue.isEmpty()) + return 0U; + + uint8_t len = 0U; + + // tx immediate queue takes priority + if (!m_txImmQueue.isEmpty()) { + m_txImmQueue.peek(&len, 1U); + } + else { + m_txQueue.peek(&len, 1U); + } + + return len; +} + /// /// Get frame data from data ring buffer. /// diff --git a/src/host/nxdn/Control.h b/src/host/nxdn/Control.h index 08b3fe22..cbb18fbe 100644 --- a/src/host/nxdn/Control.h +++ b/src/host/nxdn/Control.h @@ -80,6 +80,8 @@ namespace nxdn /// Process a data frame from the RF interface. bool processFrame(uint8_t* data, uint32_t len); + /// Get the frame data length for the next frame in the data ring buffer. + uint32_t peekFrameLength(); /// Get frame data from data ring buffer. uint32_t getFrame(uint8_t* data);