diff --git a/src/host/Defines.h b/src/host/Defines.h index 6190291e..90fafdb0 100644 --- a/src/host/Defines.h +++ b/src/host/Defines.h @@ -31,4 +31,17 @@ #undef __EXE_NAME__ #define __EXE_NAME__ "dvmhost" +/** + * @brief RF Frame Loss Types + */ +enum RPT_RF_LOSS_TYPE { + RF_LOSS_TYPE_TG_HANG_NOT_LISTENING, + RF_LOSS_TYPE_LOSS_WATCHDOG, + RF_LOSS_TYPE_EXCEEDED_FRAME_THRESHOLD, + + RF_LOSS_TYPE_IN_CALL_CONTROL, + + RF_LOSS_TYPE_UNKNOWN +}; + #endif // __DEFINES_H__ diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index c955c6f0..724b6fb0 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -204,7 +204,7 @@ bool Slot::processFrame(uint8_t *data, uint32_t len) if (m_frameLossCnt > m_frameLossThreshold) { m_frameLossCnt = 0U; - processFrameLoss(); + processFrameLoss(RF_LOSS_TYPE_EXCEEDED_FRAME_THRESHOLD); return false; } @@ -545,7 +545,7 @@ void Slot::processInCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId) } } - processFrameLoss(); + processFrameLoss(RF_LOSS_TYPE_IN_CALL_CONTROL); m_rfLastDstId = 0U; m_rfLastSrcId = 0U; @@ -701,7 +701,7 @@ void Slot::clock() if (m_rfLossWatchdog.hasExpired()) { m_rfLossWatchdog.stop(); - processFrameLoss(); + processFrameLoss(RF_LOSS_TYPE_LOSS_WATCHDOG); } } } @@ -790,7 +790,7 @@ void Slot::clock() if (m_frameLossCnt > 0U && m_rfState == RS_RF_LISTENING) m_frameLossCnt = 0U; if (m_frameLossCnt >= m_frameLossThreshold && (m_rfState == RS_RF_AUDIO || m_rfState == RS_RF_DATA)) { - processFrameLoss(); + processFrameLoss(RF_LOSS_TYPE_EXCEEDED_FRAME_THRESHOLD); } } @@ -1226,20 +1226,40 @@ void Slot::addFrame(const uint8_t *data, bool net, bool imm) /* Helper to process loss of frame stream from modem. */ -void Slot::processFrameLoss() +void Slot::processFrameLoss(RPT_RF_LOSS_TYPE type) { + // resolve the type of RF frame loss into a human-readable string + std::string typeStr; + switch (type) { + case RF_LOSS_TYPE_EXCEEDED_FRAME_THRESHOLD: + typeStr = "exceeded frame loss threshold"; + break; + case RF_LOSS_TYPE_LOSS_WATCHDOG: + typeStr = "loss watchdog timeout"; + break; + case RF_LOSS_TYPE_IN_CALL_CONTROL: + typeStr = "in-call control request"; + break; + case RF_LOSS_TYPE_TG_HANG_NOT_LISTENING: + typeStr = "TG hang, RF not listening"; + break; + default: + typeStr = "no loss type set BUGBUG"; + break; + } + if (m_rfState == RS_RF_AUDIO) { if (m_rssi != 0U) { - ::ActivityLog("DMR", true, "Slot %u RF voice transmission lost, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm, loss count: %u", - m_slotNo, float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount, m_frameLossCnt); + ::ActivityLog("DMR", true, "Slot %u RF voice transmission lost, %s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm, loss count: %u", + m_slotNo, typeStr.c_str(), float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount, m_frameLossCnt); } else { - ::ActivityLog("DMR", true, "Slot %u RF voice transmission lost, %.1f seconds, BER: %.1f%%, loss count: %u", - m_slotNo, float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_frameLossCnt); + ::ActivityLog("DMR", true, "Slot %u RF voice transmission lost, %s, %.1f seconds, BER: %.1f%%, loss count: %u", + m_slotNo, typeStr.c_str(), float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_frameLossCnt); } - LogInfoEx(LOG_RF, "DMR Slot %u, total frames: %d, total bits: %d, errors: %d, BER: %.4f%%", - m_slotNo, m_rfFrames, m_rfBits, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits)); + LogInfoEx(LOG_RF, "DMR Slot %u, transmission lost, %s, total frames: %d, total bits: %d, errors: %d, BER: %.4f%%", + m_slotNo, typeStr.c_str(), m_rfFrames, m_rfBits, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits)); // release trunked grant (if necessary) Slot* tscc = s_dmr->getTSCCSlot(); @@ -1272,6 +1292,9 @@ void Slot::processFrameLoss() m_rfLastDstId = 0U; m_rfTGHang.stop(); + m_rfLossWatchdog.stop(); + + m_txQueue.clear(); } /* Helper to send a REST API request to the CC to release a channel grant at the end of a call. */ diff --git a/src/host/dmr/Slot.h b/src/host/dmr/Slot.h index 5803c658..2ec674e9 100644 --- a/src/host/dmr/Slot.h +++ b/src/host/dmr/Slot.h @@ -515,8 +515,9 @@ namespace dmr /** * @brief Helper to process loss of frame stream from modem. + * @param type Type of RF frame loss. */ - void processFrameLoss(); + void processFrameLoss(RPT_RF_LOSS_TYPE type); /** * @brief Helper to send a REST API request to the CC to release a channel grant at the end of a call. diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index 0fc57a7e..26419e86 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -686,6 +686,101 @@ void ModemV24::create_TDU(uint8_t* buffer) ::memcpy(buffer, data, P25_TDU_FRAME_LENGTH_BYTES + 2U); } +/* Advances RX voice frame sequencing and reports when an LDU is ready to emit. */ + +DUID::E ModemV24::updateLDUSequence(DFSIFrameType::E frameType) +{ + // determine the LDU1 N + uint8_t n = 0U; + switch (frameType) { + case DFSIFrameType::LDU1_VOICE1: + n = 1U; + break; + case DFSIFrameType::LDU1_VOICE2: + n = 2U; + break; + case DFSIFrameType::LDU1_VOICE3: + n = 3U; + break; + case DFSIFrameType::LDU1_VOICE4: + n = 4U; + break; + case DFSIFrameType::LDU1_VOICE5: + n = 5U; + break; + case DFSIFrameType::LDU1_VOICE6: + n = 6U; + break; + case DFSIFrameType::LDU1_VOICE7: + n = 7U; + break; + case DFSIFrameType::LDU1_VOICE8: + n = 8U; + break; + case DFSIFrameType::LDU1_VOICE9: + n = 9U; + break; + default: + break; + } + + // advance LDU1 sequence if applicable + if (n != 0U) { + if (n == 1U || n > m_rxCall->ldu1N) { + m_rxCall->ldu1N = n; + return (n == 9U) ? DUID::LDU1 : (DUID::E)0xFFU; + } + + return (DUID::E)0xFFU; + } + + // determine the LDU2 N + n = 0U; + switch (frameType) { + case DFSIFrameType::LDU2_VOICE10: + n = 1U; + break; + case DFSIFrameType::LDU2_VOICE11: + n = 2U; + break; + case DFSIFrameType::LDU2_VOICE12: + n = 3U; + break; + case DFSIFrameType::LDU2_VOICE13: + n = 4U; + break; + case DFSIFrameType::LDU2_VOICE14: + n = 5U; + break; + case DFSIFrameType::LDU2_VOICE15: + n = 6U; + break; + case DFSIFrameType::LDU2_VOICE16: + n = 7U; + break; + case DFSIFrameType::LDU2_VOICE17: + n = 8U; + break; + case DFSIFrameType::LDU2_VOICE18: + n = 9U; + break; + default: + break; + } + + // advance LDU2 sequence if applicable + if (n != 0U) { + if (n == 1U || n > m_rxCall->ldu2N) { + m_rxCall->ldu2N = n; + return (n == 9U) ? DUID::LDU2 : (DUID::E)0xFFU; + } + + return (DUID::E)0xFFU; + } + + return (DUID::E)0xFFU; +} + /* Internal helper to convert from V.24/DFSI to TIA-102 air interface. */ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) @@ -706,6 +801,7 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) } DFSIFrameType::E frameType = (DFSIFrameType::E)dfsiData[0U]; + bool rxVoiceFrameSeen = false; m_rxLastFrameTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); // Switch based on DFSI frame type @@ -721,14 +817,18 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) } } else { if (m_rxCallInProgress) { - m_rxCall->resetCallData(); m_rxCallInProgress = false; if (m_debug) { - ::LogDebugEx(LOG_MODEM, "ModemV24::convertToAirV24()", "V.24 RX, ICW, opcode = $%02X, type = $%02X", start.getOpcode(), start.getArgument1()); + ::LogDebugEx(LOG_MODEM, "ModemV24::convertToAirV24()", "V.24 RX, ICW, opcode = $%02X, type = $%02X, srcId = %u, dstId = %u", start.getOpcode(), start.getArgument1(), m_rxCall->srcId, m_rxCall->dstId); + } + + if (m_rxCall->srcId != 0U || m_rxCall->dstId != 0U) { + // generate a TDU + create_TDU(buffer); + storeConvertedRx(buffer, P25_TDU_FRAME_LENGTH_BYTES + 2U); } - // generate a TDU - create_TDU(buffer); - storeConvertedRx(buffer, P25_TDU_FRAME_LENGTH_BYTES + 2U); + + m_rxCall->resetCallData(); } } } @@ -895,7 +995,7 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) m_rxCall->errors += svf.fullRateVoice->getTotalErrors(); } - m_rxCall->n++; + rxVoiceFrameSeen = true; } break; case DFSIFrameType::LDU2_VOICE10: @@ -947,7 +1047,7 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) m_rxCall->errors += svf.fullRateVoice->getTotalErrors(); } - m_rxCall->n++; + rxVoiceFrameSeen = true; } break; @@ -1561,169 +1661,175 @@ void ModemV24::convertToAirV24(const uint8_t *data, uint32_t length) break; } - // increment our voice frame counter - m_rxCall->n++; + rxVoiceFrameSeen = true; } break; } - // encode LDU1 if ready - if (m_rxCall->n == 9U) { - // decode RS (24,12,13) FEC - // bryanb: for now this won't abort the frame if RS fails, but maybe it should in the future, for now - // we'll just log the error - try { - bool ret = m_rs.decode241213(m_rxCall->LDULC); - if (!ret) { - LogError(LOG_MODEM, "V.24/DFSI LDU1, failed to decode RS (24,12,13) FEC"); + // process received voice frame + if (rxVoiceFrameSeen) { + DUID::E duid = updateLDUSequence(frameType); + + // encode LDU1 if ready + if (duid == DUID::LDU1) { + // decode RS (24,12,13) FEC + // bryanb: for now this won't abort the frame if RS fails, but maybe it should in the future, for now + // we'll just log the error + try { + bool ret = m_rs.decode241213(m_rxCall->LDULC); + if (!ret) { + LogError(LOG_MODEM, "V.24/DFSI LDU1, failed to decode RS (24,12,13) FEC"); + } + } + catch (...) { + Utils::dump(2U, "Modem, V.24 LDU1 RS excepted with input data", m_rxCall->LDULC, P25_LDU_LC_FEC_LENGTH_BYTES); } - } - catch (...) { - Utils::dump(2U, "Modem, V.24 LDU1 RS excepted with input data", m_rxCall->LDULC, P25_LDU_LC_FEC_LENGTH_BYTES); - } - if (m_rxCall->errors > 0U) { - LogWarning(LOG_MODEM, P25_DFSI_LDU1_STR ", V.24, errs = %u/1233 (%.1f%%)", m_rxCall->errors, float(m_rxCall->errors) / 12.33F); - m_rxCall->errors = 0U; - } + if (m_rxCall->errors > 0U) { + LogWarning(LOG_MODEM, P25_DFSI_LDU1_STR ", V.24, errs = %u/1233 (%.1f%%)", m_rxCall->errors, float(m_rxCall->errors) / 12.33F); + m_rxCall->errors = 0U; + } - lc::LC lc = lc::LC(); - lc.setLCO(m_rxCall->lco); - lc.setMFId(m_rxCall->mfId); + m_rxCall->ldu1N = 0U; - if (lc.isStandardMFId()) { - lc.setSrcId(m_rxCall->srcId); - lc.setDstId(m_rxCall->dstId); - } else { - uint8_t rsBuffer[P25_LDU_LC_FEC_LENGTH_BYTES]; - ::memset(rsBuffer, 0x00U, P25_LDU_LC_FEC_LENGTH_BYTES); - - rsBuffer[0U] = m_rxCall->LDULC[0U]; - rsBuffer[1U] = m_rxCall->LDULC[1U]; - rsBuffer[2U] = m_rxCall->LDULC[2U]; - rsBuffer[3U] = m_rxCall->LDULC[3U]; - rsBuffer[4U] = m_rxCall->LDULC[4U]; - rsBuffer[5U] = m_rxCall->LDULC[5U]; - rsBuffer[6U] = m_rxCall->LDULC[6U]; - rsBuffer[7U] = m_rxCall->LDULC[7U]; - rsBuffer[8U] = m_rxCall->LDULC[8U]; - - // combine bytes into ulong64_t (8 byte) value - ulong64_t rsValue = 0U; - rsValue = rsBuffer[1U]; - rsValue = (rsValue << 8) + rsBuffer[2U]; - rsValue = (rsValue << 8) + rsBuffer[3U]; - rsValue = (rsValue << 8) + rsBuffer[4U]; - rsValue = (rsValue << 8) + rsBuffer[5U]; - rsValue = (rsValue << 8) + rsBuffer[6U]; - rsValue = (rsValue << 8) + rsBuffer[7U]; - rsValue = (rsValue << 8) + rsBuffer[8U]; - - lc.setRS(rsValue); - } + lc::LC lc = lc::LC(); + lc.setLCO(m_rxCall->lco); + lc.setMFId(m_rxCall->mfId); - bool emergency = ((m_rxCall->serviceOptions & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag - bool encryption = ((m_rxCall->serviceOptions & 0xFFU) & 0x40U) == 0x40U; // Encryption Flag - uint8_t priority = ((m_rxCall->serviceOptions & 0xFFU) & 0x07U); // Priority - lc.setEmergency(emergency); - lc.setEncrypted(encryption); - lc.setPriority(priority); - - data::LowSpeedData lsd = data::LowSpeedData(); - lsd.setLSD1(m_rxCall->lsd1); - lsd.setLSD2(m_rxCall->lsd2); - - // generate Sync - Sync::addP25Sync(buffer + 2U); - - // generate NID - m_nid->encode(buffer + 2U, DUID::LDU1); - - // generate LDU1 Data - lc.encodeLDU1(buffer + 2U); - - // generate Low Speed Data - lsd.process(buffer + 2U); - - // generate audio - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 10U, 0U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 26U, 1U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 55U, 2U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 80U, 3U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 105U, 4U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 130U, 5U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 155U, 6U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 180U, 7U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 204U, 8U); - - // add status bits - P25Utils::addStatusBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, true, false); - - buffer[0U] = modem::TAG_DATA; - buffer[1U] = 0x01U; - storeConvertedRx(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U); - } - - // encode LDU2 if ready - if (m_rxCall->n == 18U) { - // decode RS (24,16,9) FEC - // bryanb: for now this won't abort the frame if RS fails, but maybe it should in the future, for now - // we'll just log the error - try { - bool ret = m_rs.decode24169(m_rxCall->LDULC); - if (!ret) { - LogError(LOG_MODEM, "V.24/DFSI LDU2, failed to decode RS (24,16,9) FEC"); - } - } - catch (...) { - Utils::dump(2U, "Modem, V.24 LDU2 RS excepted with input data", m_rxCall->LDULC, P25_LDU_LC_FEC_LENGTH_BYTES); - } + if (lc.isStandardMFId()) { + lc.setSrcId(m_rxCall->srcId); + lc.setDstId(m_rxCall->dstId); + } else { + uint8_t rsBuffer[P25_LDU_LC_FEC_LENGTH_BYTES]; + ::memset(rsBuffer, 0x00U, P25_LDU_LC_FEC_LENGTH_BYTES); + + rsBuffer[0U] = m_rxCall->LDULC[0U]; + rsBuffer[1U] = m_rxCall->LDULC[1U]; + rsBuffer[2U] = m_rxCall->LDULC[2U]; + rsBuffer[3U] = m_rxCall->LDULC[3U]; + rsBuffer[4U] = m_rxCall->LDULC[4U]; + rsBuffer[5U] = m_rxCall->LDULC[5U]; + rsBuffer[6U] = m_rxCall->LDULC[6U]; + rsBuffer[7U] = m_rxCall->LDULC[7U]; + rsBuffer[8U] = m_rxCall->LDULC[8U]; + + // combine bytes into ulong64_t (8 byte) value + ulong64_t rsValue = 0U; + rsValue = rsBuffer[1U]; + rsValue = (rsValue << 8) + rsBuffer[2U]; + rsValue = (rsValue << 8) + rsBuffer[3U]; + rsValue = (rsValue << 8) + rsBuffer[4U]; + rsValue = (rsValue << 8) + rsBuffer[5U]; + rsValue = (rsValue << 8) + rsBuffer[6U]; + rsValue = (rsValue << 8) + rsBuffer[7U]; + rsValue = (rsValue << 8) + rsBuffer[8U]; + + lc.setRS(rsValue); + } + + bool emergency = ((m_rxCall->serviceOptions & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag + bool encryption = ((m_rxCall->serviceOptions & 0xFFU) & 0x40U) == 0x40U; // Encryption Flag + uint8_t priority = ((m_rxCall->serviceOptions & 0xFFU) & 0x07U); // Priority + lc.setEmergency(emergency); + lc.setEncrypted(encryption); + lc.setPriority(priority); + + data::LowSpeedData lsd = data::LowSpeedData(); + lsd.setLSD1(m_rxCall->lsd1); + lsd.setLSD2(m_rxCall->lsd2); - if (m_rxCall->errors > 0U) { - LogWarning(LOG_MODEM, P25_DFSI_LDU2_STR ", V.24, errs = %u/1233 (%.1f%%)", m_rxCall->errors, float(m_rxCall->errors) / 12.33F); - m_rxCall->errors = 0U; + // generate Sync + Sync::addP25Sync(buffer + 2U); + + // generate NID + m_nid->encode(buffer + 2U, DUID::LDU1); + + // generate LDU1 Data + lc.encodeLDU1(buffer + 2U); + + // generate Low Speed Data + lsd.process(buffer + 2U); + + // generate audio + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 10U, 0U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 26U, 1U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 55U, 2U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 80U, 3U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 105U, 4U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 130U, 5U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 155U, 6U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 180U, 7U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 204U, 8U); + + // add status bits + P25Utils::addStatusBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, true, false); + + buffer[0U] = modem::TAG_DATA; + buffer[1U] = 0x01U; + storeConvertedRx(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U); } + + // encode LDU2 if ready + if (duid == DUID::LDU2) { + // decode RS (24,16,9) FEC + // bryanb: for now this won't abort the frame if RS fails, but maybe it should in the future, for now + // we'll just log the error + try { + bool ret = m_rs.decode24169(m_rxCall->LDULC); + if (!ret) { + LogError(LOG_MODEM, "V.24/DFSI LDU2, failed to decode RS (24,16,9) FEC"); + } + } + catch (...) { + Utils::dump(2U, "Modem, V.24 LDU2 RS excepted with input data", m_rxCall->LDULC, P25_LDU_LC_FEC_LENGTH_BYTES); + } - lc::LC lc = lc::LC(); - lc.setMI(m_rxCall->MI); - lc.setAlgId(m_rxCall->algoId); - lc.setKId(m_rxCall->kId); + if (m_rxCall->errors > 0U) { + LogWarning(LOG_MODEM, P25_DFSI_LDU2_STR ", V.24, errs = %u/1233 (%.1f%%)", m_rxCall->errors, float(m_rxCall->errors) / 12.33F); + m_rxCall->errors = 0U; + } - data::LowSpeedData lsd = data::LowSpeedData(); - lsd.setLSD1(m_rxCall->lsd1); - lsd.setLSD2(m_rxCall->lsd2); + m_rxCall->ldu2N = 0U; - // generate Sync - Sync::addP25Sync(buffer + 2U); + lc::LC lc = lc::LC(); + lc.setMI(m_rxCall->MI); + lc.setAlgId(m_rxCall->algoId); + lc.setKId(m_rxCall->kId); - // generate NID - m_nid->encode(buffer + 2U, DUID::LDU2); + data::LowSpeedData lsd = data::LowSpeedData(); + lsd.setLSD1(m_rxCall->lsd1); + lsd.setLSD2(m_rxCall->lsd2); - // generate LDU2 data - lc.encodeLDU2(buffer + 2U); + // generate Sync + Sync::addP25Sync(buffer + 2U); - // generate Low Speed Data - lsd.process(buffer + 2U); + // generate NID + m_nid->encode(buffer + 2U, DUID::LDU2); - // generate audio - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 10U, 0U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 26U, 1U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 55U, 2U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 80U, 3U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 105U, 4U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 130U, 5U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 155U, 6U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 180U, 7U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 204U, 8U); + // generate LDU2 data + lc.encodeLDU2(buffer + 2U); - // add status bits - P25Utils::addStatusBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, true, false); + // generate Low Speed Data + lsd.process(buffer + 2U); - buffer[0U] = modem::TAG_DATA; - buffer[1U] = 0x01U; - storeConvertedRx(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U); + // generate audio + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 10U, 0U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 26U, 1U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 55U, 2U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 80U, 3U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 105U, 4U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 130U, 5U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 155U, 6U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 180U, 7U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 204U, 8U); - m_rxCall->n = 0; + // add status bits + P25Utils::addStatusBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, true, false); + + buffer[0U] = modem::TAG_DATA; + buffer[1U] = 0x01U; + storeConvertedRx(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U); + } } } @@ -1785,18 +1891,22 @@ void ModemV24::convertToAirTIA(const uint8_t *data, uint32_t length) { dataOffs += 1U; - // generate Sync - Sync::addP25Sync(buffer + 2U); + if (m_rxCall->srcId != 0U || m_rxCall->dstId != 0U) { + // generate Sync + Sync::addP25Sync(buffer + 2U); - // generate NID - m_nid->encode(buffer + 2U, DUID::TDU); + // generate NID + m_nid->encode(buffer + 2U, DUID::TDU); - // add status bits - P25Utils::setStatusBitsAllIdle(buffer + 2U, P25_TDU_FRAME_LENGTH_BITS); + // add status bits + P25Utils::setStatusBitsAllIdle(buffer + 2U, P25_TDU_FRAME_LENGTH_BITS); - buffer[0U] = modem::TAG_DATA; - buffer[1U] = 0x01U; - storeConvertedRx(buffer, P25_TDU_FRAME_LENGTH_BITS + 2U); + buffer[0U] = modem::TAG_DATA; + buffer[1U] = 0x01U; + storeConvertedRx(buffer, P25_TDU_FRAME_LENGTH_BITS + 2U); + } + + m_rxCall->resetCallData(); } break; @@ -2165,178 +2275,179 @@ void ModemV24::convertToAirTIA(const uint8_t *data, uint32_t length) break; } - // increment our voice frame counter - m_rxCall->n++; - } - break; - - default: - break; - } + DUID::E duid = updateLDUSequence(frameType); - hdrOffs += BlockHeader::LENGTH; - } + // encode LDU1 if ready + if (duid == DUID::LDU1) { + // decode RS (24,12,13) FEC + // bryanb: for now this won't abort the frame if RS fails, but maybe it should in the future, for now + // we'll just log the error + try { + bool ret = m_rs.decode241213(m_rxCall->LDULC); + if (!ret) { + LogError(LOG_MODEM, "TIA/DFSI LDU1, failed to decode RS (24,12,13) FEC"); + } + } + catch (...) { + Utils::dump(2U, "Modem, TIA LDU1, RS excepted with input data", m_rxCall->LDULC, P25_LDU_LC_FEC_LENGTH_BYTES); + } - m_rxLastFrameTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + if (m_rxCall->errors > 0U) { + LogWarning(LOG_MODEM, P25_DFSI_LDU1_STR ", TIA, errs = %u/1233 (%.1f%%)", m_rxCall->errors, float(m_rxCall->errors) / 12.33F); + m_rxCall->errors = 0U; + } - // encode LDU1 if ready - if (m_rxCall->n == 9U) { - // decode RS (24,12,13) FEC - // bryanb: for now this won't abort the frame if RS fails, but maybe it should in the future, for now - // we'll just log the error - try { - bool ret = m_rs.decode241213(m_rxCall->LDULC); - if (!ret) { - LogError(LOG_MODEM, "TIA/DFSI LDU1, failed to decode RS (24,12,13) FEC"); - } - } - catch (...) { - Utils::dump(2U, "Modem, TIA LDU1, RS excepted with input data", m_rxCall->LDULC, P25_LDU_LC_FEC_LENGTH_BYTES); - } + m_rxCall->ldu1N = 0U; - if (m_rxCall->errors > 0U) { - LogWarning(LOG_MODEM, P25_DFSI_LDU1_STR ", TIA, errs = %u/1233 (%.1f%%)", m_rxCall->errors, float(m_rxCall->errors) / 12.33F); - m_rxCall->errors = 0U; - } + lc::LC lc = lc::LC(); + lc.setLCO(m_rxCall->lco); + lc.setMFId(m_rxCall->mfId); - lc::LC lc = lc::LC(); - lc.setLCO(m_rxCall->lco); - lc.setMFId(m_rxCall->mfId); + if (lc.isStandardMFId()) { + lc.setSrcId(m_rxCall->srcId); + lc.setDstId(m_rxCall->dstId); + } else { + uint8_t rsBuffer[P25_LDU_LC_FEC_LENGTH_BYTES]; + ::memset(rsBuffer, 0x00U, P25_LDU_LC_FEC_LENGTH_BYTES); + + rsBuffer[0U] = m_rxCall->LDULC[0U]; + rsBuffer[1U] = m_rxCall->LDULC[1U]; + rsBuffer[2U] = m_rxCall->LDULC[2U]; + rsBuffer[3U] = m_rxCall->LDULC[3U]; + rsBuffer[4U] = m_rxCall->LDULC[4U]; + rsBuffer[5U] = m_rxCall->LDULC[5U]; + rsBuffer[6U] = m_rxCall->LDULC[6U]; + rsBuffer[7U] = m_rxCall->LDULC[7U]; + rsBuffer[8U] = m_rxCall->LDULC[8U]; + + // combine bytes into ulong64_t (8 byte) value + ulong64_t rsValue = 0U; + rsValue = rsBuffer[1U]; + rsValue = (rsValue << 8) + rsBuffer[2U]; + rsValue = (rsValue << 8) + rsBuffer[3U]; + rsValue = (rsValue << 8) + rsBuffer[4U]; + rsValue = (rsValue << 8) + rsBuffer[5U]; + rsValue = (rsValue << 8) + rsBuffer[6U]; + rsValue = (rsValue << 8) + rsBuffer[7U]; + rsValue = (rsValue << 8) + rsBuffer[8U]; + + lc.setRS(rsValue); + } + + bool emergency = ((m_rxCall->serviceOptions & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag + bool encryption = ((m_rxCall->serviceOptions & 0xFFU) & 0x40U) == 0x40U; // Encryption Flag + uint8_t priority = ((m_rxCall->serviceOptions & 0xFFU) & 0x07U); // Priority + lc.setEmergency(emergency); + lc.setEncrypted(encryption); + lc.setPriority(priority); + + data::LowSpeedData lsd = data::LowSpeedData(); + lsd.setLSD1(m_rxCall->lsd1); + lsd.setLSD2(m_rxCall->lsd2); - if (lc.isStandardMFId()) { - lc.setSrcId(m_rxCall->srcId); - lc.setDstId(m_rxCall->dstId); - } else { - uint8_t rsBuffer[P25_LDU_LC_FEC_LENGTH_BYTES]; - ::memset(rsBuffer, 0x00U, P25_LDU_LC_FEC_LENGTH_BYTES); - - rsBuffer[0U] = m_rxCall->LDULC[0U]; - rsBuffer[1U] = m_rxCall->LDULC[1U]; - rsBuffer[2U] = m_rxCall->LDULC[2U]; - rsBuffer[3U] = m_rxCall->LDULC[3U]; - rsBuffer[4U] = m_rxCall->LDULC[4U]; - rsBuffer[5U] = m_rxCall->LDULC[5U]; - rsBuffer[6U] = m_rxCall->LDULC[6U]; - rsBuffer[7U] = m_rxCall->LDULC[7U]; - rsBuffer[8U] = m_rxCall->LDULC[8U]; - - // combine bytes into ulong64_t (8 byte) value - ulong64_t rsValue = 0U; - rsValue = rsBuffer[1U]; - rsValue = (rsValue << 8) + rsBuffer[2U]; - rsValue = (rsValue << 8) + rsBuffer[3U]; - rsValue = (rsValue << 8) + rsBuffer[4U]; - rsValue = (rsValue << 8) + rsBuffer[5U]; - rsValue = (rsValue << 8) + rsBuffer[6U]; - rsValue = (rsValue << 8) + rsBuffer[7U]; - rsValue = (rsValue << 8) + rsBuffer[8U]; - - lc.setRS(rsValue); - } + // generate Sync + Sync::addP25Sync(buffer + 2U); - bool emergency = ((m_rxCall->serviceOptions & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag - bool encryption = ((m_rxCall->serviceOptions & 0xFFU) & 0x40U) == 0x40U; // Encryption Flag - uint8_t priority = ((m_rxCall->serviceOptions & 0xFFU) & 0x07U); // Priority - lc.setEmergency(emergency); - lc.setEncrypted(encryption); - lc.setPriority(priority); - - data::LowSpeedData lsd = data::LowSpeedData(); - lsd.setLSD1(m_rxCall->lsd1); - lsd.setLSD2(m_rxCall->lsd2); - - // generate Sync - Sync::addP25Sync(buffer + 2U); - - // generate NID - m_nid->encode(buffer + 2U, DUID::LDU1); - - // generate LDU1 Data - lc.encodeLDU1(buffer + 2U); - - // generate Low Speed Data - lsd.process(buffer + 2U); - - // generate audio - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 10U, 0U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 26U, 1U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 55U, 2U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 80U, 3U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 105U, 4U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 130U, 5U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 155U, 6U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 180U, 7U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 204U, 8U); - - // add status bits - P25Utils::addStatusBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, true, false); - - buffer[0U] = modem::TAG_DATA; - buffer[1U] = 0x01U; - storeConvertedRx(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U); - } - - // encode LDU2 if ready - if (m_rxCall->n == 18U) { - // decode RS (24,16,9) FEC - // bryanb: for now this won't abort the frame if RS fails, but maybe it should in the future, for now - // we'll just log the error - try { - bool ret = m_rs.decode24169(m_rxCall->LDULC); - if (!ret) { - LogError(LOG_MODEM, "TIA/DFSI LDU2, failed to decode RS (24,16,9) FEC"); + // generate NID + m_nid->encode(buffer + 2U, DUID::LDU1); + + // generate LDU1 Data + lc.encodeLDU1(buffer + 2U); + + // generate Low Speed Data + lsd.process(buffer + 2U); + + // generate audio + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 10U, 0U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 26U, 1U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 55U, 2U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 80U, 3U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 105U, 4U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 130U, 5U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 155U, 6U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 180U, 7U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU1 + 204U, 8U); + + // add status bits + P25Utils::addStatusBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, true, false); + + buffer[0U] = modem::TAG_DATA; + buffer[1U] = 0x01U; + storeConvertedRx(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U); } - } - catch (...) { - Utils::dump(2U, "Modem, TIA LDU2, RS excepted with input data", m_rxCall->LDULC, P25_LDU_LC_FEC_LENGTH_BYTES); - } + + // encode LDU2 if ready + if (duid == DUID::LDU2) { + // decode RS (24,16,9) FEC + // bryanb: for now this won't abort the frame if RS fails, but maybe it should in the future, for now + // we'll just log the error + try { + bool ret = m_rs.decode24169(m_rxCall->LDULC); + if (!ret) { + LogError(LOG_MODEM, "TIA/DFSI LDU2, failed to decode RS (24,16,9) FEC"); + } + } + catch (...) { + Utils::dump(2U, "Modem, TIA LDU2, RS excepted with input data", m_rxCall->LDULC, P25_LDU_LC_FEC_LENGTH_BYTES); + } - if (m_rxCall->errors > 0U) { - LogWarning(LOG_MODEM, P25_DFSI_LDU2_STR ", TIA, errs = %u/1233 (%.1f%%)", m_rxCall->errors, float(m_rxCall->errors) / 12.33F); - m_rxCall->errors = 0U; - } + if (m_rxCall->errors > 0U) { + LogWarning(LOG_MODEM, P25_DFSI_LDU2_STR ", TIA, errs = %u/1233 (%.1f%%)", m_rxCall->errors, float(m_rxCall->errors) / 12.33F); + m_rxCall->errors = 0U; + } - lc::LC lc = lc::LC(); - lc.setMI(m_rxCall->MI); - lc.setAlgId(m_rxCall->algoId); - lc.setKId(m_rxCall->kId); + m_rxCall->ldu2N = 0U; - data::LowSpeedData lsd = data::LowSpeedData(); - lsd.setLSD1(m_rxCall->lsd1); - lsd.setLSD2(m_rxCall->lsd2); + lc::LC lc = lc::LC(); + lc.setMI(m_rxCall->MI); + lc.setAlgId(m_rxCall->algoId); + lc.setKId(m_rxCall->kId); - // generate Sync - Sync::addP25Sync(buffer + 2U); + data::LowSpeedData lsd = data::LowSpeedData(); + lsd.setLSD1(m_rxCall->lsd1); + lsd.setLSD2(m_rxCall->lsd2); - // generate NID - m_nid->encode(buffer + 2U, DUID::LDU2); + // generate Sync + Sync::addP25Sync(buffer + 2U); - // generate LDU2 data - lc.encodeLDU2(buffer + 2U); + // generate NID + m_nid->encode(buffer + 2U, DUID::LDU2); - // generate Low Speed Data - lsd.process(buffer + 2U); + // generate LDU2 data + lc.encodeLDU2(buffer + 2U); - // generate audio - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 10U, 0U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 26U, 1U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 55U, 2U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 80U, 3U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 105U, 4U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 130U, 5U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 155U, 6U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 180U, 7U); - m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 204U, 8U); + // generate Low Speed Data + lsd.process(buffer + 2U); - // add status bits - P25Utils::addStatusBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, true, false); + // generate audio + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 10U, 0U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 26U, 1U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 55U, 2U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 80U, 3U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 105U, 4U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 130U, 5U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 155U, 6U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 180U, 7U); + m_audio.encode(buffer + 2U, m_rxCall->netLDU2 + 204U, 8U); - buffer[0U] = modem::TAG_DATA; - buffer[1U] = 0x01U; - storeConvertedRx(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U); + // add status bits + P25Utils::addStatusBits(buffer + 2U, P25_LDU_FRAME_LENGTH_BITS, true, false); - m_rxCall->n = 0; + buffer[0U] = modem::TAG_DATA; + buffer[1U] = 0x01U; + storeConvertedRx(buffer, P25_LDU_FRAME_LENGTH_BYTES + 2U); + } + } + break; + + default: + break; + } + + hdrOffs += BlockHeader::LENGTH; } + + m_rxLastFrameTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); } /* Helper to add a V.24 data frame to the P25 TX queue with the proper timestamp and formatting */ diff --git a/src/host/modem/ModemV24.h b/src/host/modem/ModemV24.h index 8f33e926..86e41043 100644 --- a/src/host/modem/ModemV24.h +++ b/src/host/modem/ModemV24.h @@ -82,7 +82,8 @@ namespace modem VHDR2(nullptr), LDULC(nullptr), seqNo(0U), - n(0U), + ldu1N(0U), + ldu2N(0U), netLDU1(nullptr), netLDU2(nullptr), pduUserData(nullptr), @@ -180,7 +181,8 @@ namespace modem if (netLDU2 != nullptr) ::memset(netLDU2, 0x00U, 9U * 25U); - n = 0U; + ldu1N = 0U; + ldu2N = 0U; seqNo = 0U; if (pduUserData != nullptr) @@ -260,9 +262,13 @@ namespace modem */ uint32_t seqNo; /** - * @brief + * @brief Number of LDU1 blocks. */ - uint8_t n; + uint8_t ldu1N; + /** + * @brief Number of LDU2 blocks. + */ + uint8_t ldu2N; /** * @brief LDU1 Buffer. @@ -603,6 +609,7 @@ namespace modem uint64_t m_rxLastFrameTime; uint16_t m_callTimeout; + uint32_t m_silenceThreshold; uint16_t m_jitter; uint64_t m_lastP25Tx; @@ -638,6 +645,12 @@ namespace modem */ void create_TDU(uint8_t* buffer); + /** + * @brief Advances LDU voice frame sequencing. + * @param frameType Type of DFSI frame received. + * @returns DUID::E LDU1 if LDU1 is ready, LDU2 if LDU2 is ready, NONE if no LDU is ready. + */ + P25DEF::DUID::E updateLDUSequence(P25DFSIDEF::DFSIFrameType::E frameType); /** * @brief Internal helper to convert from V.24/DFSI to TIA-102 air interface. * @param data Buffer containing data to convert. diff --git a/src/host/nxdn/Control.cpp b/src/host/nxdn/Control.cpp index 92c90a1b..46c7b912 100644 --- a/src/host/nxdn/Control.cpp +++ b/src/host/nxdn/Control.cpp @@ -379,7 +379,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len) if (m_frameLossCnt > m_frameLossThreshold) { m_frameLossCnt = 0U; - processFrameLoss(); + processFrameLoss(RF_LOSS_TYPE_EXCEEDED_FRAME_THRESHOLD); return false; } @@ -692,7 +692,7 @@ void Control::clock() if (m_rfLossWatchdog.hasExpired()) { m_rfLossWatchdog.stop(); - processFrameLoss(); + processFrameLoss(RF_LOSS_TYPE_LOSS_WATCHDOG); } } } @@ -754,7 +754,7 @@ void Control::clock() if (m_frameLossCnt > 0U && m_rfState == RS_RF_LISTENING) m_frameLossCnt = 0U; if (m_frameLossCnt >= m_frameLossThreshold && (m_rfState == RS_RF_AUDIO || m_rfState == RS_RF_DATA)) { - processFrameLoss(); + processFrameLoss(RF_LOSS_TYPE_EXCEEDED_FRAME_THRESHOLD); } } @@ -1029,20 +1029,40 @@ void Control::processNetwork() /* Helper to process loss of frame stream from modem. */ -void Control::processFrameLoss() +void Control::processFrameLoss(RPT_RF_LOSS_TYPE type) { + // resolve the type of RF frame loss into a human-readable string + std::string typeStr; + switch (type) { + case RF_LOSS_TYPE_EXCEEDED_FRAME_THRESHOLD: + typeStr = "exceeded frame loss threshold"; + break; + case RF_LOSS_TYPE_LOSS_WATCHDOG: + typeStr = "loss watchdog timeout"; + break; + case RF_LOSS_TYPE_IN_CALL_CONTROL: + typeStr = "in-call control request"; + break; + case RF_LOSS_TYPE_TG_HANG_NOT_LISTENING: + typeStr = "TG hang RF not listening"; + break; + default: + typeStr = "no loss type set BUGBUG"; + break; + } + if (m_rfState == RS_RF_AUDIO) { if (m_rssi != 0U) { - ::ActivityLog("NXDN", true, "transmission lost, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm, loss count: %u", - float(m_voice->m_rfFrames) / 12.5F, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount, m_frameLossCnt); + ::ActivityLog("NXDN", true, "transmission lost, %s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm, loss count: %u", + typeStr.c_str(), float(m_voice->m_rfFrames) / 12.5F, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount, m_frameLossCnt); } else { - ::ActivityLog("NXDN", true, "transmission lost, %.1f seconds, BER: %.1f%%, loss count: %u", - float(m_voice->m_rfFrames) / 12.5F, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits), m_frameLossCnt); + ::ActivityLog("NXDN", true, "transmission lost, %s, %.1f seconds, BER: %.1f%%, loss count: %u", + typeStr.c_str(), float(m_voice->m_rfFrames) / 12.5F, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits), m_frameLossCnt); } - LogInfoEx(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_TX_REL ", total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", - m_voice->m_rfFrames, m_voice->m_rfBits, m_voice->m_rfUndecodableLC, m_voice->m_rfErrs, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits)); + LogInfoEx(LOG_RF, "NXDN, " NXDN_RTCH_MSG_TYPE_TX_REL ", transmission lost, %s, total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", + typeStr.c_str(), m_voice->m_rfFrames, m_voice->m_rfBits, m_voice->m_rfUndecodableLC, m_voice->m_rfErrs, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits)); m_affiliations->releaseGrant(m_rfLC.getDstId(), false); if (m_notifyCC) { @@ -1057,6 +1077,16 @@ void Control::processFrameLoss() } m_rfState = RS_RF_LISTENING; + m_rfLastDstId = 0U; + m_rfLastSrcId = 0U; + m_rfTGHang.stop(); + m_rfLossWatchdog.stop(); + + m_rfTimeout.stop(); + m_txQueue.clear(); + + if (m_network != nullptr) + m_network->resetNXDN(); m_rfMask = 0x00U; m_rfLC.reset(); @@ -1078,7 +1108,7 @@ void Control::processInCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId) } } - processFrameLoss(); + processFrameLoss(RF_LOSS_TYPE_IN_CALL_CONTROL); m_rfLastDstId = 0U; m_rfLastSrcId = 0U; diff --git a/src/host/nxdn/Control.h b/src/host/nxdn/Control.h index cf358302..7b76adc0 100644 --- a/src/host/nxdn/Control.h +++ b/src/host/nxdn/Control.h @@ -366,8 +366,9 @@ namespace nxdn void processNetwork(); /** * @brief Helper to process loss of frame stream from modem. + * @param type Type of RF frame loss. */ - void processFrameLoss(); + void processFrameLoss(RPT_RF_LOSS_TYPE type); /** * @brief Helper to process an In-Call Control message. * @param command In-Call Control Command. diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index ea2a67aa..f6c27e75 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -588,7 +588,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len) if (m_frameLossCnt > m_frameLossThreshold) { m_frameLossCnt = 0U; - processFrameLoss(); + processFrameLoss(RF_LOSS_TYPE_EXCEEDED_FRAME_THRESHOLD); return false; } @@ -936,7 +936,7 @@ void Control::clock() // has the talkgroup hang timer expired while the modem is in a non-listening state? if (m_rfState != RS_RF_LISTENING) { - processFrameLoss(); + processFrameLoss(RF_LOSS_TYPE_TG_HANG_NOT_LISTENING); } } } @@ -948,7 +948,7 @@ void Control::clock() if (m_rfLossWatchdog.hasExpired()) { m_rfLossWatchdog.stop(); - processFrameLoss(); + processFrameLoss(RF_LOSS_TYPE_LOSS_WATCHDOG); } } } @@ -1038,7 +1038,7 @@ void Control::clock() if (m_frameLossCnt > 0U && m_rfState == RS_RF_LISTENING) m_frameLossCnt = 0U; if (m_frameLossCnt >= m_frameLossThreshold && (m_rfState == RS_RF_AUDIO || m_rfState == RS_RF_DATA)) { - processFrameLoss(); + processFrameLoss(RF_LOSS_TYPE_EXCEEDED_FRAME_THRESHOLD); } // clock data and trunking @@ -1693,20 +1693,40 @@ void Control::processNetwork() /* Helper to process loss of frame stream from modem. */ -void Control::processFrameLoss() +void Control::processFrameLoss(RPT_RF_LOSS_TYPE type) { + // resolve the type of RF frame loss into a human-readable string + std::string typeStr; + switch (type) { + case RF_LOSS_TYPE_EXCEEDED_FRAME_THRESHOLD: + typeStr = "exceeded frame loss threshold"; + break; + case RF_LOSS_TYPE_LOSS_WATCHDOG: + typeStr = "loss watchdog timeout"; + break; + case RF_LOSS_TYPE_IN_CALL_CONTROL: + typeStr = "in-call control request"; + break; + case RF_LOSS_TYPE_TG_HANG_NOT_LISTENING: + typeStr = "TG hang RF not listening"; + break; + default: + typeStr = "no loss type set BUGBUG"; + break; + } + if (m_rfState == RS_RF_AUDIO) { if (m_rssi != 0U) { - ::ActivityLog("P25", true, "transmission lost, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm, loss count: %u", - float(m_voice->m_rfFrames) / 5.56F, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount, m_frameLossCnt); + ::ActivityLog("P25", true, "transmission lost, %s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm, loss count: %u", + typeStr.c_str(), float(m_voice->m_rfFrames) / 5.56F, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount, m_frameLossCnt); } else { - ::ActivityLog("P25", true, "transmission lost, %.1f seconds, BER: %.1f%%, loss count: %u", - float(m_voice->m_rfFrames) / 5.56F, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits), m_frameLossCnt); + ::ActivityLog("P25", true, "transmission lost, %s, %.1f seconds, BER: %.1f%%, loss count: %u", + typeStr.c_str(), float(m_voice->m_rfFrames) / 5.56F, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits), m_frameLossCnt); } - LogInfoEx(LOG_RF, P25_TDU_STR ", total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", - m_voice->m_rfFrames, m_voice->m_rfBits, m_voice->m_rfUndecodableLC, m_voice->m_rfErrs, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits)); + LogInfoEx(LOG_RF, P25_TDU_STR ", transmission lost, %s, total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", + typeStr.c_str(), m_voice->m_rfFrames, m_voice->m_rfBits, m_voice->m_rfUndecodableLC, m_voice->m_rfErrs, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits)); m_affiliations->releaseGrant(m_voice->m_rfLC.getDstId(), false); if (!m_enableControl) { @@ -1722,6 +1742,7 @@ void Control::processFrameLoss() m_rfLastDstId = 0U; m_rfLastSrcId = 0U; m_rfTGHang.stop(); + m_rfLossWatchdog.stop(); m_rfVoiceCallTermTimeout.stop(); m_rfCallTermSrcId = 0U; @@ -1741,6 +1762,7 @@ void Control::processFrameLoss() m_rfLastDstId = 0U; m_rfLastSrcId = 0U; m_rfTGHang.stop(); + m_rfLossWatchdog.stop(); m_tailOnIdle = true; @@ -1782,7 +1804,7 @@ void Control::processInCallCtrl(network::NET_ICC::ENUM command, uint32_t dstId) m_voice->m_rfLC.setDstId(dstId); } - processFrameLoss(); + processFrameLoss(RF_LOSS_TYPE_IN_CALL_CONTROL); m_rfLastDstId = 0U; m_rfLastSrcId = 0U; diff --git a/src/host/p25/Control.h b/src/host/p25/Control.h index 811755d5..d675e244 100644 --- a/src/host/p25/Control.h +++ b/src/host/p25/Control.h @@ -428,8 +428,9 @@ namespace p25 void processNetwork(); /** * @brief Helper to process loss of frame stream from modem. + * @param type Type of RF frame loss. */ - void processFrameLoss(); + void processFrameLoss(RPT_RF_LOSS_TYPE type); /** * @brief Helper to process an In-Call Control message. * @param command In-Call Control Command.