diff --git a/config.yml b/config.yml index 85838372..69be2ef5 100644 --- a/config.yml +++ b/config.yml @@ -13,7 +13,6 @@ network: rconAddress: 127.0.0.1 rconPort: 9990 jitter: 360 - talkgroupHang: 10 password: "PASSWORD" slot1: true slot2: true @@ -38,7 +37,7 @@ protocols: debug: false p25: enable: true - preambleCount: 4 + tduPreambleCount: 6 control: enable: false continuous: false @@ -74,6 +73,7 @@ system: # rfModeHang: 10 # netModeHang: 10 # fixedMode: false + rfTalkgroupHang: 10 info: latitude: 0.0 longitude: 0.0 @@ -99,8 +99,8 @@ system: pttInvert: false dcBlocker: true cosLockout: false - txDelay: 1 - dmrDelay: 7 + fdmaPreamble: 80 + dmrRxDelay: 7 rxDCOffset: 0 txDCOffset: 0 rxLevel: 50 diff --git a/dmr/DataPacket.cpp b/dmr/DataPacket.cpp index c46ceaf5..43f08ac4 100644 --- a/dmr/DataPacket.cpp +++ b/dmr/DataPacket.cpp @@ -63,16 +63,16 @@ using namespace dmr; return false; \ } -#define CHECK_NET_TG_HANG(_DST_ID) \ +#define CHECK_TG_HANG(_DST_ID) \ if (m_slot->m_rfLastDstId != 0U) { \ - if (m_slot->m_rfLastDstId != _DST_ID && (m_slot->m_networkTGHang.isRunning() && !m_slot->m_networkTGHang.hasExpired())) { \ + if (m_slot->m_rfLastDstId != _DST_ID && (m_slot->m_rfTGHang.isRunning() && !m_slot->m_rfTGHang.hasExpired())) { \ return; \ } \ } -#define CHECK_NET_TG_HANG_DELLC(_DST_ID) \ +#define CHECK_TG_HANG_DELLC(_DST_ID) \ if (m_slot->m_rfLastDstId != 0U) { \ - if (m_slot->m_rfLastDstId != _DST_ID && (m_slot->m_networkTGHang.isRunning() && !m_slot->m_networkTGHang.hasExpired())) { \ + if (m_slot->m_rfLastDstId != _DST_ID && (m_slot->m_rfTGHang.isRunning() && !m_slot->m_rfTGHang.hasExpired())) { \ delete lc; \ return; \ } \ @@ -577,7 +577,7 @@ void DataPacket::processNetwork(const data::Data& dmrData) uint32_t dstId = lc->getDstId(); uint8_t flco = lc->getFLCO(); - CHECK_NET_TG_HANG_DELLC(dstId); + CHECK_TG_HANG_DELLC(dstId); if (dstId != dmrData.getDstId() || srcId != dmrData.getSrcId() || flco != dmrData.getFLCO()) LogWarning(LOG_NET, "DMR Slot %u, DT_VOICE_LC_HEADER, header doesn't match the DMR RF header: %u->%s%u %u->%s%u", m_slot->m_slotNo, @@ -655,7 +655,7 @@ void DataPacket::processNetwork(const data::Data& dmrData) uint32_t srcId = lc->getSrcId(); uint32_t dstId = lc->getDstId(); - CHECK_NET_TG_HANG_DELLC(dstId); + CHECK_TG_HANG_DELLC(dstId); m_netLC = lc; @@ -793,7 +793,7 @@ void DataPacket::processNetwork(const data::Data& dmrData) uint32_t srcId = csbk.getSrcId(); uint32_t dstId = csbk.getDstId(); - CHECK_NET_TG_HANG(dstId); + CHECK_TG_HANG(dstId); // Regenerate the CSBK data csbk.encode(data + 2U); @@ -923,7 +923,7 @@ void DataPacket::processNetwork(const data::Data& dmrData) uint32_t srcId = dataHeader.getSrcId(); uint32_t dstId = dataHeader.getDstId(); - CHECK_NET_TG_HANG(dstId); + CHECK_TG_HANG(dstId); m_slot->m_netFrames = dataHeader.getBlocks(); diff --git a/dmr/Slot.cpp b/dmr/Slot.cpp index a34cdd14..0506a793 100644 --- a/dmr/Slot.cpp +++ b/dmr/Slot.cpp @@ -98,9 +98,9 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz m_rfSeqNo(0U), m_networkWatchdog(1000U, 0U, 1500U), m_rfTimeoutTimer(1000U, timeout), + m_rfTGHang(1000U, tgHang), m_netTimeoutTimer(1000U, timeout), m_packetTimer(1000U, 0U, 50U), - m_networkTGHang(1000U, tgHang), m_interval(), m_elapsed(), m_rfFrames(0U), @@ -180,6 +180,7 @@ bool Slot::processFrame(uint8_t *data, uint32_t len) if (data[0U] == TAG_LOST) { m_rfState = RS_RF_LISTENING; m_rfLastDstId = 0U; + m_rfTGHang.stop(); return false; } @@ -234,7 +235,7 @@ bool Slot::processFrame(uint8_t *data, uint32_t len) } if ((dataSync || voiceSync) && m_rfState != RS_RF_LISTENING) - m_networkTGHang.start(); + m_rfTGHang.start(); if (dataSync) { return m_data->process(data, len); @@ -275,6 +276,17 @@ void Slot::processNetwork(const data::Data& dmrData) return; } + // don't process network frames if the destination ID's don't match and the network TG hang timer is running + if (m_rfLastDstId != 0U) { + if (m_rfLastDstId != dmrData.getDstId() && (m_rfTGHang.isRunning() && !m_rfTGHang.hasExpired())) { + return; + } + + if (m_rfLastDstId == dmrData.getDstId() && (m_rfTGHang.isRunning() && !m_rfTGHang.hasExpired())) { + m_rfTGHang.start(); + } + } + m_networkWatchdog.start(); uint8_t dataType = dmrData.getDataType(); @@ -322,11 +334,14 @@ void Slot::clock() } } - if (m_networkTGHang.isRunning()) { - m_networkTGHang.clock(ms); + if (m_rfTGHang.isRunning()) { + m_rfTGHang.clock(ms); - if (m_networkTGHang.hasExpired()) { - m_networkTGHang.stop(); + if (m_rfTGHang.hasExpired()) { + m_rfTGHang.stop(); + if (m_verbose) { + LogMessage(LOG_RF, "Slot %u, talkgroup hang has expired, lastDstId = %u", m_slotNo, m_rfLastDstId); + } m_rfLastDstId = 0U; } } diff --git a/dmr/Slot.h b/dmr/Slot.h index 2114c2ca..349f38b3 100644 --- a/dmr/Slot.h +++ b/dmr/Slot.h @@ -104,9 +104,9 @@ namespace dmr Timer m_networkWatchdog; Timer m_rfTimeoutTimer; + Timer m_rfTGHang; Timer m_netTimeoutTimer; Timer m_packetTimer; - Timer m_networkTGHang; StopWatch m_interval; StopWatch m_elapsed; diff --git a/dmr/VoicePacket.cpp b/dmr/VoicePacket.cpp index 6d0cc8f3..09bd751c 100644 --- a/dmr/VoicePacket.cpp +++ b/dmr/VoicePacket.cpp @@ -93,6 +93,9 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) m_slot->m_rfBits += 141U; m_slot->m_rfFrames++; + m_slot->m_rfTGHang.start(); + m_slot->m_rfLastDstId = m_slot->m_data->m_rfLC->getDstId(); + m_rfEmbeddedReadN = (m_rfEmbeddedReadN + 1U) % 2U; m_rfEmbeddedWriteN = (m_rfEmbeddedWriteN + 1U) % 2U; m_rfEmbeddedData[m_rfEmbeddedWriteN].reset(); @@ -143,6 +146,9 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) m_slot->m_rfBits += 141U; m_slot->m_rfFrames++; + m_slot->m_rfTGHang.start(); + m_slot->m_rfLastDstId = m_slot->m_data->m_rfLC->getDstId(); + // Get the LCSS from the EMB data::EMB emb; emb.decode(data + 2U); @@ -381,6 +387,8 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) m_slot->writeNetworkRF(data, DT_VOICE, errors); m_slot->m_rfState = RS_RF_AUDIO; + + m_slot->m_rfTGHang.start(); m_slot->m_rfLastDstId = dstId; if (m_slot->m_netState == RS_NET_IDLE) { diff --git a/host/Host.cpp b/host/Host.cpp index 53023764..ebc1007a 100644 --- a/host/Host.cpp +++ b/host/Host.cpp @@ -79,8 +79,8 @@ Host::Host(const std::string& confFile) : m_fixedMode(false), m_timeout(180U), m_rfModeHang(10U), + m_rfTalkgroupHang(10U), m_netModeHang(3U), - m_netTalkgroupHang(10U), m_identity(), m_cwCallsign(), m_cwIdTime(0U), @@ -309,7 +309,7 @@ int Host::run() g_fireDMRBeacon = true; } - dmr = new dmr::Control(m_dmrColorCode, callHang, dmrQueueSize, embeddedLCOnly, dumpTAData, m_timeout, m_netTalkgroupHang, + 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_dmrTXTimer.setTimeout(txHang); @@ -330,7 +330,7 @@ int Host::run() LogInfo(" Enabled: %s", m_p25Enabled ? "yes" : "no"); if (m_p25Enabled) { yaml::Node p25Protocol = protocolConf["p25"]; - uint32_t preambleCount = p25Protocol["preambleCount"].as(4U); + uint32_t tduPreambleCount = p25Protocol["tduPreambleCount"].as(8U); m_controlData = p25Protocol["control"]["enable"].as(false); bool controlBcstContinuous = p25Protocol["control"]["continuous"].as(false); bool p25DumpDataPacket = p25Protocol["dumpDataPacket"].as(false); @@ -340,7 +340,7 @@ int Host::run() bool p25Verbose = p25Protocol["verbose"].as(true); bool p25Debug = p25Protocol["debug"].as(false); - LogInfo(" Preamble Count: %u", preambleCount); + LogInfo(" TDU Preamble before Voice: %u", tduPreambleCount); LogInfo(" Dump Packet Data: %s", p25DumpDataPacket ? "yes" : "no"); LogInfo(" Repeat Packet Data: %s", p25RepeatDataPacket ? "yes" : "no"); LogInfo(" Call Hang: %us", callHang); @@ -371,7 +371,7 @@ int Host::run() g_interruptP25Control = false; } - p25 = new p25::Control(m_p25NAC, callHang, p25QueueSize, m_modem, m_network, m_timeout, m_netTalkgroupHang, + 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); p25->setOptions(m_conf, m_cwCallsign, m_voiceChNo, m_p25PatchSuperGroup, m_p25NetId, m_p25SysId, m_p25RfssId, m_p25SiteId, m_channelId, m_channelNo, true); @@ -1029,6 +1029,7 @@ bool Host::readParams() m_timeout = systemConf["timeout"].as(120U); m_rfModeHang = systemConf["rfModeHang"].as(10U); + m_rfTalkgroupHang = systemConf["rfTalkgroupHang"].as(10U); m_netModeHang = systemConf["netModeHang"].as(3U); if (!systemConf["modeHang"].isNone()) { m_rfModeHang = m_netModeHang = systemConf["modeHang"].as(); @@ -1045,6 +1046,7 @@ bool Host::readParams() LogInfo(" Duplex: %s", m_duplex ? "yes" : "no"); LogInfo(" Timeout: %us", m_timeout); LogInfo(" RF Mode Hang: %us", m_rfModeHang); + LogInfo(" RF Talkgroup Hang: %us", m_rfTalkgroupHang); LogInfo(" Net Mode Hang: %us", m_netModeHang); LogInfo(" Identity: %s", m_identity.c_str()); LogInfo(" Fixed Mode: %s", m_fixedMode ? "yes" : "no"); @@ -1196,8 +1198,8 @@ bool Host::createModem() bool pttInvert = modemConf["pttInvert"].as(false); bool dcBlocker = modemConf["dcBlocker"].as(true); bool cosLockout = modemConf["cosLockout"].as(false); - uint32_t txDelay = modemConf["txDelay"].as(1U); - uint32_t dmrDelay = modemConf["dmrDelay"].as(7U); + uint8_t fdmaPreamble = (uint8_t)modemConf["fdmaPreamble"].as(80U); + uint8_t dmrRxDelay = (uint8_t)modemConf["dmrRxDelay"].as(7U); int rxDCOffset = modemConf["rxDCOffset"].as(0); int txDCOffset = modemConf["txDCOffset"].as(0); float rxLevel = modemConf["rxLevel"].as(50.0F); @@ -1218,10 +1220,10 @@ bool Host::createModem() LogInfo(" PTT Invert: %s", pttInvert ? "yes" : "no"); LogInfo(" DC Blocker: %s", dcBlocker ? "yes" : "no"); LogInfo(" COS Lockout: %s", cosLockout ? "yes" : "no"); - LogInfo(" TX Delay: %u (%ums)", txDelay, (txDelay * 10)); + LogInfo(" FDMA Preambles: %u (%.1fms)", fdmaPreamble, float(fdmaPreamble) * 0.2083F); + LogInfo(" DMR RX Delay: %u (%.1fms)", dmrRxDelay, float(dmrRxDelay) * 0.0416666F); LogInfo(" RX DC Offset: %d", rxDCOffset); LogInfo(" TX DC Offset: %d", txDCOffset); - LogInfo(" DMR Delay: %u (%.1fms)", dmrDelay, float(dmrDelay) * 0.0416666F); LogInfo(" RX Level: %.1f%%", rxLevel); LogInfo(" CW Id TX Level: %.1f%%", cwIdTXLevel); LogInfo(" DMR TX Level: %.1f%%", dmrTXLevel); @@ -1232,7 +1234,7 @@ bool Host::createModem() LogInfo(" Debug: yes"); } - m_modem = Modem::createModem(port, m_duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, txDelay, dmrDelay, disableOFlowReset, trace, debug); + m_modem = Modem::createModem(port, m_duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, fdmaPreamble, dmrRxDelay, disableOFlowReset, trace, debug); m_modem->setModeParams(m_dmrEnabled, m_p25Enabled); m_modem->setLevels(rxLevel, cwIdTXLevel, dmrTXLevel, p25TXLevel); m_modem->setDCOffsetParams(txDCOffset, rxDCOffset); @@ -1261,7 +1263,6 @@ bool Host::createNetwork() uint32_t rconPort = networkConf["rconPort"].as(RCON_DEFAULT_PORT); uint32_t id = networkConf["id"].as(0U); uint32_t jitter = networkConf["talkgroupHang"].as(360U); - m_netTalkgroupHang = networkConf["talkgroupHang"].as(10U); std::string password = networkConf["password"].as(); bool slot1 = networkConf["slot1"].as(true); bool slot2 = networkConf["slot2"].as(true); @@ -1282,7 +1283,6 @@ bool Host::createNetwork() LogInfo(" RCON Address: %s", rconAddress.c_str()); LogInfo(" RCON Port: %u", rconPort); LogInfo(" DMR Jitter: %ums", jitter); - LogInfo(" Talkgroup Hang: %us", m_netTalkgroupHang); LogInfo(" Slot 1: %s", slot1 ? "enabled" : "disabled"); LogInfo(" Slot 2: %s", slot2 ? "enabled" : "disabled"); LogInfo(" Transfer Activity Log: %s", transferActivityLog ? "enabled" : "disabled"); diff --git a/host/Host.h b/host/Host.h index c8528bb3..fc3858fc 100644 --- a/host/Host.h +++ b/host/Host.h @@ -86,8 +86,8 @@ private: uint32_t m_timeout; uint32_t m_rfModeHang; + uint32_t m_rfTalkgroupHang; uint32_t m_netModeHang; - uint32_t m_netTalkgroupHang; std::string m_identity; std::string m_cwCallsign; diff --git a/host/calibrate/HostCal.cpp b/host/calibrate/HostCal.cpp index 6985899f..8cb74129 100644 --- a/host/calibrate/HostCal.cpp +++ b/host/calibrate/HostCal.cpp @@ -148,8 +148,8 @@ HostCal::HostCal(const std::string& confFile) : m_p25Rx1K(false), m_txDCOffset(0), m_rxDCOffset(0), - m_txDelay(1U), - m_dmrDelay(7U), + m_fdmaPreamble(80U), + m_dmrRxDelay(7U), m_debug(false), m_mode(STATE_DMR_CAL), m_modeStr(DMR_CAL_STR), @@ -235,8 +235,8 @@ int HostCal::run() m_rxLevel = modemConf["rxLevel"].as(50.0F); m_txLevel = modemConf["txLevel"].as(50.0F); - m_txDelay = modemConf["txDelay"].as(1U); - m_dmrDelay = modemConf["dmrDelay"].as(7U); + m_fdmaPreamble = (uint8_t)modemConf["fdmaPreamble"].as(80U); + m_dmrRxDelay = (uint8_t)modemConf["dmrRxDelay"].as(7U); writeConfig(); @@ -1456,7 +1456,12 @@ bool HostCal::writeConfig(uint8_t modeOverride) if (m_p25Enabled) buffer[4U] |= 0x08U; - buffer[5U] = m_txDelay; + if (m_fdmaPreamble > MAX_FDMA_PREAMBLE) { + LogWarning(LOG_P25, "oversized FDMA preamble count, reducing to maximum %u", MAX_FDMA_PREAMBLE); + m_fdmaPreamble = MAX_FDMA_PREAMBLE; + } + + buffer[5U] = m_fdmaPreamble; buffer[6U] = modeOverride; @@ -1468,7 +1473,7 @@ bool HostCal::writeConfig(uint8_t modeOverride) buffer[9U] = 1U; - buffer[10U] = m_dmrDelay; + buffer[10U] = m_dmrRxDelay; buffer[11U] = 128U; buffer[13U] = (uint8_t)(m_txLevel * 2.55F + 0.5F); @@ -1562,7 +1567,7 @@ 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", m_rxLevel, m_txLevel, m_txDCOffset, m_rxDCOffset); - LogMessage(LOG_CAL, " - TX Delay: %u (%ums), DMR Delay: %u (%.1fms)", m_txDelay, (m_txDelay * 10), m_dmrDelay, float(m_dmrDelay) * 0.0416666F); + 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()); uint8_t buffer[50U]; diff --git a/host/calibrate/HostCal.h b/host/calibrate/HostCal.h index 243d6fc0..0a4990e3 100644 --- a/host/calibrate/HostCal.h +++ b/host/calibrate/HostCal.h @@ -86,8 +86,8 @@ private: int m_txDCOffset; int m_rxDCOffset; - uint32_t m_txDelay; - uint32_t m_dmrDelay; + uint8_t m_fdmaPreamble; + uint8_t m_dmrRxDelay; bool m_debug; diff --git a/modem/Modem.cpp b/modem/Modem.cpp index 7b99d71d..e634e9bb 100644 --- a/modem/Modem.cpp +++ b/modem/Modem.cpp @@ -64,13 +64,13 @@ using namespace modem; /// Flag indicating the PTT polarity should be inverted. /// Flag indicating whether the DSP DC-level blocking should be enabled. /// Flag indicating whether the COS signal should be used to lockout the modem. -/// Compensation for transmitter to settle in ms. -/// Compensate for delay in transmitter audio chain in ms. Usually DSP based. +/// Count of FDMA preambles to transmit before data. (P25/DMR DMO) +/// Compensate for delay in receiver audio chain in ms. Usually DSP based. /// Flag indicating whether the ADC/DAC overflow reset logic is disabled. /// Flag indicating whether modem DSP trace is enabled. /// Flag indicating whether modem DSP debug is enabled. Modem::Modem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, bool dcBlocker, - bool cosLockout, uint32_t txDelay, uint32_t dmrDelay, bool disableOFlowReset, bool trace, bool debug) : + bool cosLockout, uint8_t fdmaPreamble, uint8_t dmrRxDelay, bool disableOFlowReset, bool trace, bool debug) : m_port(port), m_dmrColorCode(0U), m_duplex(duplex), @@ -79,8 +79,8 @@ Modem::Modem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, m_pttInvert(pttInvert), m_dcBlocker(dcBlocker), m_cosLockout(cosLockout), - m_txDelay(txDelay), - m_dmrDelay(dmrDelay), + m_fdmaPreamble(fdmaPreamble), + m_dmrRxDelay(dmrRxDelay), m_rxLevel(0U), m_cwIdTXLevel(0U), m_dmrTXLevel(0U), @@ -1028,19 +1028,19 @@ bool Modem::sendCWId(const std::string& callsign) /// Flag indicating the PTT polarity should be inverted. /// Flag indicating whether the DSP DC-level blocking should be enabled. /// Flag indicating whether the COS signal should be used to lockout the modem. -/// Amount of time to delay before transmitting frames. -/// +/// Count of FDMA preambles to transmit before data. (P25/DMR DMO) +/// Compensate for delay in receiver audio chain in ms. Usually DSP based. /// Flag indicating whether the ADC/DAC overflow reset logic is disabled. /// Flag indicating whether modem DSP trace is enabled. /// Flag indicating whether modem DSP debug is enabled. Modem* Modem::createModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, bool dcBlocker, - bool cosLockout, uint32_t txDelay, uint32_t dmrDelay, bool disableOFlowReset, bool trace, bool debug) + bool cosLockout, uint8_t fdmaPreamble, uint8_t dmrRxDelay, bool disableOFlowReset, bool trace, bool debug) { if (port == NULL_MODEM) { - return new NullModem(port, duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, txDelay, dmrDelay, disableOFlowReset, trace, debug); + return new NullModem(port, duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, fdmaPreamble, dmrRxDelay, disableOFlowReset, trace, debug); } else { - return new Modem(port, duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, txDelay, dmrDelay, disableOFlowReset, trace, debug); + return new Modem(port, duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, fdmaPreamble, dmrRxDelay, disableOFlowReset, trace, debug); } } @@ -1137,7 +1137,12 @@ bool Modem::writeConfig() if (m_p25Enabled) buffer[4U] |= 0x08U; - buffer[5U] = m_txDelay / 10U; // In 10ms units + if (m_fdmaPreamble > MAX_FDMA_PREAMBLE) { + LogWarning(LOG_P25, "oversized FDMA preamble count, reducing to maximum %u", MAX_FDMA_PREAMBLE); + m_fdmaPreamble = MAX_FDMA_PREAMBLE; + } + + buffer[5U] = m_fdmaPreamble; buffer[6U] = STATE_IDLE; @@ -1147,7 +1152,7 @@ bool Modem::writeConfig() buffer[9U] = m_dmrColorCode; - buffer[10U] = m_dmrDelay; + buffer[10U] = m_dmrRxDelay; buffer[11U] = 128U; // Was OscOffset diff --git a/modem/Modem.h b/modem/Modem.h index bfeecfc6..a15f9230 100644 --- a/modem/Modem.h +++ b/modem/Modem.h @@ -132,12 +132,13 @@ namespace modem RSN_INVALID_REQUEST = 4U, RSN_RINGBUFF_FULL = 8U, - RSN_INVALID_TXDELAY = 10U, + RSN_INVALID_FDMA_PREAMBLE = 10U, RSN_INVALID_MODE = 11U, RSN_INVALID_DMR_CC = 12U, RSN_INVALID_DMR_SLOT = 13U, RSN_INVALID_DMR_START = 14U, + RSN_INVALID_DMR_RX_DELAY = 15U, RSN_DMR_DISABLED = 63U, RSN_P25_DISABLED = 64U, @@ -145,6 +146,8 @@ namespace modem const uint8_t DVM_FRAME_START = 0xFEU; + const uint8_t MAX_FDMA_PREAMBLE = 255U; + const uint32_t MAX_RESPONSES = 30U; const uint32_t BUFFER_LENGTH = 2000U; @@ -160,7 +163,7 @@ namespace modem public: /// Initializes a new instance of the Modem class. Modem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, bool dcBlocker, - bool cosLockout, uint32_t txDelay, uint32_t dmrDelay, bool disableOFlowReset, bool trace, bool debug); + bool cosLockout, uint8_t fdmaPreamble, uint8_t dmrRxDelay, bool disableOFlowReset, bool trace, bool debug); /// Finalizes a instance of the Modem class. virtual ~Modem(); @@ -244,7 +247,7 @@ namespace modem /// Helper to create an instance of the Modem class. static Modem* createModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, bool dcBlocker, - bool cosLockout, uint32_t txDelay, uint32_t dmrDelay, bool disableOFlowReset, bool trace, bool debug); + bool cosLockout, uint8_t fdmaPreamble, uint8_t dmrRxDelay, bool disableOFlowReset, bool trace, bool debug); private: std::string m_port; @@ -260,8 +263,8 @@ namespace modem bool m_dcBlocker; bool m_cosLockout; - uint32_t m_txDelay; - uint32_t m_dmrDelay; + uint8_t m_fdmaPreamble; + uint8_t m_dmrRxDelay; float m_rxLevel; float m_cwIdTXLevel; diff --git a/modem/NullModem.cpp b/modem/NullModem.cpp index 5104d438..000a2723 100644 --- a/modem/NullModem.cpp +++ b/modem/NullModem.cpp @@ -47,14 +47,14 @@ using namespace modem; /// Flag indicating the PTT polarity should be inverted. /// Flag indicating whether the DSP DC-level blocking should be enabled. /// Flag indicating whether the COS signal should be used to lockout the modem. -/// Compensation for transmitter to settle in ms. -/// Compensate for delay in transmitter audio chain in ms. Usually DSP based. +/// Count of FDMA preambles to transmit before data. (P25/DMR DMO) +/// Compensate for delay in receiver audio chain in ms. Usually DSP based. /// Flag indicating whether the ADC/DAC overflow reset logic is disabled. /// Flag indicating whether modem DSP trace is enabled. /// Flag indicating whether modem DSP debug is enabled. NullModem::NullModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, bool dcBlocker, - bool cosLockout, uint32_t txDelay, uint32_t dmrDelay, bool disableOFlowReset, bool trace, bool debug) : - Modem(port, duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, txDelay, dmrDelay, disableOFlowReset, trace, debug) + bool cosLockout, uint8_t fdmaPreamble, uint8_t dmrRxDelay, bool disableOFlowReset, bool trace, bool debug) : + Modem(port, duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, fdmaPreamble, dmrRxDelay, disableOFlowReset, trace, debug) { /* stub */ } diff --git a/modem/NullModem.h b/modem/NullModem.h index 421560a5..405c8026 100644 --- a/modem/NullModem.h +++ b/modem/NullModem.h @@ -45,7 +45,7 @@ namespace modem public: /// Initializes a new instance of the NullModem class. NullModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, bool dcBlocker, - bool cosLockout, uint32_t txDelay, uint32_t dmrDelay, bool disableOFlowReset, bool trace, bool debug); + bool cosLockout, uint8_t fdmaPreamble, uint8_t dmrRxDelay, bool disableOFlowReset, bool trace, bool debug); /// Finalizes a instance of the NullModem class. ~NullModem(); diff --git a/p25/Control.cpp b/p25/Control.cpp index a9b95b28..74dae48f 100644 --- a/p25/Control.cpp +++ b/p25/Control.cpp @@ -51,7 +51,7 @@ using namespace p25; // --------------------------------------------------------------------------- const uint32_t TSBK_PCH_CCH_CNT = 6U; -const uint32_t MAX_PREAMBLE_CNT = 85U; +const uint32_t MAX_PREAMBLE_TDU_CNT = 64U; // --------------------------------------------------------------------------- // Public Class Members @@ -107,11 +107,11 @@ Control::Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Mod m_ccRunning(false), m_ccBcstInterval(ccBcstInterval), m_rfTimeout(1000U, timeout), + m_rfTGHang(1000U, tgHang), m_netTimeout(1000U, timeout), m_networkWatchdog(1000U, 0U, 1500U), - m_networkTGHang(1000U, tgHang), m_hangCount(3U * 8U), - m_preambleCount(0U), + m_tduPreambleCount(8U), m_ccFrameCnt(0U), m_ccSeq(0U), m_nid(nac), @@ -154,7 +154,6 @@ Control::~Control() void Control::reset() { m_rfState = RS_RF_LISTENING; - m_rfLastDstId = 0U; m_voice->resetRF(); m_trunk->resetRF(); @@ -176,7 +175,7 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s m_trunk->setCallsign(cwCallsign); - m_preambleCount = p25Protocol["preambleCount"].as(4U); + m_tduPreambleCount = p25Protocol["tduPreambleCount"].as(8U); m_trunk->m_patchSuperGroup = pSuperGroup; m_trunk->setSiteData(netId, sysId, rfssId, siteId, 0U, channelId, channelNo); @@ -292,6 +291,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len) m_rfState = RS_RF_LISTENING; m_rfLastDstId = 0U; + m_rfTGHang.stop(); m_tailOnIdle = true; @@ -307,6 +307,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len) if (data[0U] == TAG_LOST && m_rfState == RS_RF_DATA) { m_rfState = RS_RF_LISTENING; m_rfLastDstId = 0U; + m_rfTGHang.stop(); m_tailOnIdle = true; @@ -320,7 +321,6 @@ bool Control::processFrame(uint8_t* data, uint32_t len) if (data[0U] == TAG_LOST) { m_rfState = RS_RF_LISTENING; - m_rfLastDstId = 0U; m_voice->resetRF(); m_trunk->resetRF(); @@ -544,11 +544,14 @@ void Control::clock(uint32_t ms) m_rfTimeout.clock(ms); m_netTimeout.clock(ms); - if (m_networkTGHang.isRunning()) { - m_networkTGHang.clock(ms); + if (m_rfTGHang.isRunning()) { + m_rfTGHang.clock(ms); - if (m_networkTGHang.hasExpired()) { - m_networkTGHang.stop(); + if (m_rfTGHang.hasExpired()) { + m_rfTGHang.stop(); + if (m_verbose) { + LogMessage(LOG_RF, "talkgroup hang has expired, lastDstId = %u", m_rfLastDstId); + } m_rfLastDstId = 0U; } } @@ -716,26 +719,23 @@ void Control::processNetwork() void Control::writeRF_Nulls() { const uint8_t NULLS_LENGTH_BYTES = 25U; + + // write null bits (0x00) uint8_t data[NULLS_LENGTH_BYTES + 2U]; ::memset(data + 2U, 0x00U, NULLS_LENGTH_BYTES); data[0U] = TAG_EOT; data[1U] = 0x00U; - // fill nulls - for (uint8_t i = 0; i < NULLS_LENGTH_BYTES; i++) { - data[i + 2U] = 0x00U; - } - writeQueueRF(data, NULLS_LENGTH_BYTES + 2U); } /// -/// Helper to write preamble packet burst. +/// Helper to write TDU preamble packet burst. /// void Control::writeRF_Preamble() { - if (m_modem->hasTX() || m_preambleCount == 0U) { + if (m_modem->hasTX() || m_tduPreambleCount == 0U) { return; } @@ -743,12 +743,13 @@ void Control::writeRF_Preamble() return; } - if (m_preambleCount > MAX_PREAMBLE_CNT) { - m_preambleCount = MAX_PREAMBLE_CNT; + if (m_tduPreambleCount > MAX_PREAMBLE_TDU_CNT) { + LogWarning(LOG_P25, "oversized TDU preamble count, reducing to maximum %u", MAX_PREAMBLE_TDU_CNT); + m_tduPreambleCount = MAX_PREAMBLE_TDU_CNT; } - writeRF_Nulls(); - for (uint8_t i = 0U; i < m_preambleCount; i++) { + // write TDUs if requested + for (uint8_t i = 0U; i < m_tduPreambleCount; i++) { writeRF_TDU(true); } } diff --git a/p25/Control.h b/p25/Control.h index 137b5190..55c369ae 100644 --- a/p25/Control.h +++ b/p25/Control.h @@ -146,12 +146,12 @@ namespace p25 uint32_t m_ccBcstInterval; Timer m_rfTimeout; + Timer m_rfTGHang; Timer m_netTimeout; Timer m_networkWatchdog; - Timer m_networkTGHang; uint32_t m_hangCount; - uint32_t m_preambleCount; + uint32_t m_tduPreambleCount; uint8_t m_ccFrameCnt; uint8_t m_ccSeq; @@ -178,7 +178,7 @@ namespace p25 /// Helper to write data nulls. void writeRF_Nulls(); - /// Helper to write preamble packet burst. + /// Helper to write TDU preamble packet burst. void writeRF_Preamble(); /// Helper to write a P25 TDU packet. void writeRF_TDU(bool noNetwork); diff --git a/p25/DataPacket.cpp b/p25/DataPacket.cpp index cf27190d..ab79af19 100644 --- a/p25/DataPacket.cpp +++ b/p25/DataPacket.cpp @@ -386,8 +386,6 @@ bool DataPacket::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, da // write data to RF interface? if (m_repeatPDU) { if (m_netDataBlockCnt >= m_netBlocksToFollow) { - m_p25->writeRF_Preamble(); - uint32_t bitLength = ((m_netDataHeader.getBlocksToFollow() + 1U) * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS; uint32_t offset = P25_PREAMBLE_LENGTH_BITS; @@ -597,8 +595,6 @@ void DataPacket::writeNetworkRF(const uint8_t dataType, const uint8_t *data, uin /// void DataPacket::writeRF_PDU() { - m_p25->writeRF_Preamble(); - uint32_t bitLength = ((m_rfDataHeader.getBlocksToFollow() + 1U) * P25_PDU_FEC_LENGTH_BITS) + P25_PREAMBLE_LENGTH_BITS; uint32_t offset = P25_PREAMBLE_LENGTH_BITS; diff --git a/p25/P25Defines.h b/p25/P25Defines.h index d917a9d1..82e960bb 100644 --- a/p25/P25Defines.h +++ b/p25/P25Defines.h @@ -75,6 +75,7 @@ namespace p25 const uint8_t P25_SYNC_BYTES[] = { 0x55U, 0x75U, 0xF5U, 0xFFU, 0x77U, 0xFFU }; const uint32_t P25_SYNC_LENGTH_BYTES = 6U; const uint32_t P25_SYNC_LENGTH_BITS = P25_SYNC_LENGTH_BYTES * 8U; + const uint8_t P25_START_SYNC = 0x5FU; const uint32_t P25_PREAMBLE_LENGTH_BYTES = P25_SYNC_LENGTH_BYTES + P25_NID_LENGTH_BYTES; const uint32_t P25_PREAMBLE_LENGTH_BITS = P25_SYNC_LENGTH_BITS + P25_NID_LENGTH_BITS; diff --git a/p25/TrunkPacket.cpp b/p25/TrunkPacket.cpp index e0758112..8c5ede4b 100644 --- a/p25/TrunkPacket.cpp +++ b/p25/TrunkPacket.cpp @@ -319,8 +319,6 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len) resetStatusCommand(m_rfTSBK); - m_p25->writeRF_Preamble(); - switch (m_rfTSBK.getLCO()) { case TSBK_IOSP_GRP_VCH: // make sure control data is supported @@ -1251,7 +1249,6 @@ TrunkPacket::TrunkPacket(Control* p25, network::BaseNetwork* network, bool debug m_siteData(), m_adjSiteUpdateTimer(1000U), m_adjSiteUpdateInterval(ADJ_SITE_TIMER_TIMEOUT), - m_skipSBFPreamble(false), m_verbose(verbose), m_debug(debug) { @@ -1560,12 +1557,6 @@ void TrunkPacket::writeRF_TSDU_SBF(bool noNetwork, bool clearBeforeWrite) m_p25->m_queue.clear(); } - if (!m_skipSBFPreamble) { - m_p25->writeRF_Preamble(); - } - - m_skipSBFPreamble = false; - if (m_p25->m_duplex) { data[0U] = TAG_DATA; data[1U] = 0x00U; @@ -1866,7 +1857,7 @@ bool TrunkPacket::writeRF_TSDU_Grant(bool grp, bool skip) // don't transmit grants if the destination ID's don't match and the network TG hang timer is running if (m_p25->m_rfLastDstId != 0U) { - if (m_p25->m_rfLastDstId != m_rfTSBK.getDstId() && (m_p25->m_networkTGHang.isRunning() && !m_p25->m_networkTGHang.hasExpired())) { + if (m_p25->m_rfLastDstId != m_rfTSBK.getDstId() && (m_p25->m_rfTGHang.isRunning() && !m_p25->m_rfTGHang.hasExpired())) { m_rfTSBK.setLCO(lco); return false; } diff --git a/p25/TrunkPacket.h b/p25/TrunkPacket.h index 267c8910..d50c785c 100644 --- a/p25/TrunkPacket.h +++ b/p25/TrunkPacket.h @@ -179,8 +179,6 @@ namespace p25 Timer m_adjSiteUpdateTimer; uint32_t m_adjSiteUpdateInterval; - bool m_skipSBFPreamble; - bool m_verbose; bool m_debug; diff --git a/p25/VoicePacket.cpp b/p25/VoicePacket.cpp index 58b71430..17a52301 100644 --- a/p25/VoicePacket.cpp +++ b/p25/VoicePacket.cpp @@ -111,8 +111,9 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) g_interruptP25Control = true; } - if (m_p25->m_rfState != RS_RF_LISTENING) - m_p25->m_networkTGHang.start(); + if (m_p25->m_rfState != RS_RF_LISTENING) { + m_p25->m_rfTGHang.start(); + } // handle individual DUIDs if (duid == P25_DUID_HDU) { @@ -125,12 +126,6 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) m_p25->m_queue.clear(); resetRF(); resetNet(); - - m_p25->writeRF_Preamble(); - - if (!m_p25->m_ccRunning) { - m_p25->m_trunk->writeRF_ControlData(255U, 0U, false); - } } if (m_p25->m_rfState == RS_RF_LISTENING || m_p25->m_rfState == RS_RF_AUDIO) { @@ -148,6 +143,24 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) LogMessage(LOG_RF, P25_HDU_STR ", HDU_BSDWNACT, dstId = %u, algo = $%02X, kid = $%04X", m_rfLC.getDstId(), m_rfLC.getAlgId(), m_rfLC.getKId()); } + // don't process RF frames if the network isn't in a idle state and the RF destination is the network destination + if (m_p25->m_netState != RS_NET_IDLE && m_rfLC.getDstId() == m_p25->m_netLastDstId) { + LogWarning(LOG_RF, "Traffic collision detect, preempting new RF traffic to existing network traffic!"); + resetRF(); + return false; + } + + // stop network frames from processing -- RF wants to transmit on a different talkgroup + if (m_p25->m_netState != RS_NET_IDLE) { + LogWarning(LOG_RF, "Traffic collision detect, preempting existing network traffic to new RF traffic, rfDstId = %u, netDstId = %u", m_rfLC.getDstId(), + m_p25->m_netLastDstId); + resetNet(); + m_p25->writeRF_TDU(true); + } + + m_p25->m_rfTGHang.start(); + m_p25->m_rfLastDstId = m_rfLC.getDstId(); + m_rfLastHDU.reset(); m_rfLastHDU = m_rfLC; } @@ -161,6 +174,10 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) m_lastDUID = P25_DUID_LDU1; if (m_p25->m_rfState == RS_RF_LISTENING) { + if (!m_p25->m_ccRunning && m_p25->m_voiceOnControl) { + m_p25->m_trunk->writeRF_ControlData(255U, 0U, false); + } + bool ret = m_rfLC.decodeLDU1(data + 2U); if (!ret) { return false; @@ -172,13 +189,21 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) uint32_t srcId = m_rfLC.getSrcId(); uint32_t dstId = m_rfLC.getDstId(); - // don't process RF frames if the network isn't in a idle state + // don't process RF frames if the network isn't in a idle state and the RF destination is the network destination if (m_p25->m_netState != RS_NET_IDLE && dstId == m_p25->m_netLastDstId) { LogWarning(LOG_RF, "Traffic collision detect, preempting new RF traffic to existing network traffic!"); resetRF(); return false; } + // stop network frames from processing -- RF wants to transmit on a different talkgroup + if (m_p25->m_netState != RS_NET_IDLE) { + LogWarning(LOG_RF, "Traffic collision detect, preempting existing network traffic to new RF traffic, rfDstId = %u, netDstId = %u", m_rfLC.getDstId(), + m_p25->m_netLastDstId); + resetNet(); + m_p25->writeRF_TDU(true); + } + m_p25->m_trunk->setRFLC(m_rfLC); // validate the source RID @@ -238,14 +263,12 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) // are we auto-registering legacy radios to groups? if (m_p25->m_legacyGroupReg && m_rfLC.getGroup()) { if (!m_p25->m_trunk->hasSrcIdGrpAff(srcId, dstId)) { - m_p25->m_trunk->m_skipSBFPreamble = true; // HACK: force an SBF to skip generating preambles if (!m_p25->m_trunk->writeRF_TSDU_Grp_Aff_Rsp(srcId, dstId)) { return false; } } } - m_p25->m_trunk->m_skipSBFPreamble = true; // HACK: force an SBF to skip generating preambles if (!m_p25->m_trunk->writeRF_TSDU_Grant(m_rfLC.getGroup(), false)) { return false; } @@ -282,7 +305,11 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) } } + m_p25->writeRF_Preamble(); + m_p25->m_rfState = RS_RF_AUDIO; + + m_p25->m_rfTGHang.start(); m_p25->m_rfLastDstId = dstId; uint8_t buffer[P25_HDU_FRAME_LENGTH_BYTES + 2U]; @@ -895,10 +922,14 @@ void VoicePacket::writeNet_HDU(const lc::LC& control, const data::LowSpeedData& // don't process network frames if the destination ID's don't match and the network TG hang timer is running if (m_p25->m_rfLastDstId != 0U) { - if (m_p25->m_rfLastDstId != dstId && (m_p25->m_networkTGHang.isRunning() && !m_p25->m_networkTGHang.hasExpired())) { + if (m_p25->m_rfLastDstId != dstId && (m_p25->m_rfTGHang.isRunning() && !m_p25->m_rfTGHang.hasExpired())) { resetNet(); return; } + + if (m_p25->m_rfLastDstId == dstId && (m_p25->m_rfTGHang.isRunning() && !m_p25->m_rfTGHang.hasExpired())) { + m_p25->m_rfTGHang.start(); + } } // ensure our srcId and dstId are sane from the last LDU1 @@ -988,6 +1019,8 @@ void VoicePacket::writeNet_HDU(const lc::LC& control, const data::LowSpeedData& m_p25->m_trunk->setRFLC(m_rfLC); + m_p25->writeRF_Preamble(); + if (m_p25->m_control) { if (group && (m_lastPatchGroup != dstId) && (dstId != m_p25->m_trunk->m_patchSuperGroup)) { @@ -1146,10 +1179,14 @@ void VoicePacket::writeNet_LDU1(const lc::LC& control, const data::LowSpeedData& // don't process network frames if the destination ID's don't match and the network TG hang timer is running if (m_p25->m_rfLastDstId != 0U) { - if (m_p25->m_rfLastDstId != dstId && (m_p25->m_networkTGHang.isRunning() && !m_p25->m_networkTGHang.hasExpired())) { + if (m_p25->m_rfLastDstId != dstId && (m_p25->m_rfTGHang.isRunning() && !m_p25->m_rfTGHang.hasExpired())) { resetNet(); return; } + + if (m_p25->m_rfLastDstId == dstId && (m_p25->m_rfTGHang.isRunning() && !m_p25->m_rfTGHang.hasExpired())) { + m_p25->m_rfTGHang.start(); + } } uint8_t serviceOptions = (uint8_t)(m_netLDU1[201U]); @@ -1256,6 +1293,14 @@ void VoicePacket::writeNet_LDU2(const lc::LC& control, const data::LowSpeedData& uint8_t algId = m_netLDU2[126U]; uint32_t kId = (m_netLDU2[127U] << 8) + m_netLDU2[128U]; + // don't process network frames if the destination ID's don't match and the network TG hang timer is running + if (m_p25->m_rfLastDstId != 0U) { + if (m_p25->m_rfLastDstId != m_netLastLDU1.getDstId() && (m_p25->m_rfTGHang.isRunning() && !m_p25->m_rfTGHang.hasExpired())) { + resetNet(); + return; + } + } + uint8_t mi[P25_MI_LENGTH_BYTES]; ::memcpy(mi + 0U, m_netLDU2 + 51U, 3U); ::memcpy(mi + 3U, m_netLDU2 + 76U, 3U);