diff --git a/NXDNTX.cpp b/NXDNTX.cpp index 45d8ffa..8e80d15 100644 --- a/NXDNTX.cpp +++ b/NXDNTX.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2018 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2018,2020 by Jonathan Naylor G4KLX * Copyright (C) 2017 by Andy Uribe CA6JAU * * This program is free software; you can redistribute it and/or modify @@ -53,7 +53,9 @@ m_sincState(), m_poBuffer(), m_poLen(0U), m_poPtr(0U), -m_txDelay(240U) // 200ms +m_txDelay(240U), // 200ms +m_txHang(6000U), // 5s +m_txCount(0U) { ::memset(m_modState, 0x00U, 16U * sizeof(q15_t)); ::memset(m_sincState, 0x00U, 70U * sizeof(q15_t)); @@ -70,7 +72,7 @@ m_txDelay(240U) // 200ms void CNXDNTX::process() { - if (m_buffer.getData() == 0U && m_poLen == 0U) + if (m_buffer.getData() == 0U && m_poLen == 0U && m_txCount == 0U) return; if (m_poLen == 0U) { @@ -98,6 +100,8 @@ void CNXDNTX::process() writeByte(c); space -= 4U * NXDN_RADIO_SYMBOL_LENGTH; + if (m_duplex) + m_txCount = m_txHang; if (m_poPtr >= m_poLen) { m_poPtr = 0U; @@ -105,6 +109,19 @@ void CNXDNTX::process() return; } } + } else if (m_txCount > 0U) { + // Transmit silence until the hang timer has expired. + uint16_t space = io.getSpace(); + + while (space > (4U * NXDN_RADIO_SYMBOL_LENGTH)) { + writeSilence(); + + space -= 4U * NXDN_RADIO_SYMBOL_LENGTH; + m_txCount--; + + if (m_txCount == 0U) + return; + } } } @@ -155,6 +172,19 @@ void CNXDNTX::writeByte(uint8_t c) io.write(STATE_NXDN, outBuffer, NXDN_RADIO_SYMBOL_LENGTH * 4U); } +void CNXDNTX::writeSilence() +{ + q15_t inBuffer[4U] = {0x00U, 0x00U, 0x00U, 0x00U}; + q15_t intBuffer[NXDN_RADIO_SYMBOL_LENGTH * 4U]; + q15_t outBuffer[NXDN_RADIO_SYMBOL_LENGTH * 4U]; + + ::arm_fir_interpolate_q15(&m_modFilter, inBuffer, intBuffer, 4U); + + ::arm_fir_fast_q15(&m_sincFilter, intBuffer, outBuffer, NXDN_RADIO_SYMBOL_LENGTH * 4U); + + io.write(STATE_NXDN, outBuffer, NXDN_RADIO_SYMBOL_LENGTH * 4U); +} + void CNXDNTX::setTXDelay(uint8_t delay) { m_txDelay = 300U + uint16_t(delay) * 6U; // 500ms + tx delay @@ -168,3 +198,7 @@ uint8_t CNXDNTX::getSpace() const return m_buffer.getSpace() / NXDN_FRAME_LENGTH_BYTES; } +void CNXDNTX::setParams(uint8_t txHang) +{ + m_txHang = txHang * 1200U; +} diff --git a/NXDNTX.h b/NXDNTX.h index eec74d4..bd65818 100644 --- a/NXDNTX.h +++ b/NXDNTX.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX * * 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 @@ -35,6 +35,8 @@ public: uint8_t getSpace() const; + void setParams(uint8_t txHang); + private: CSerialRB m_buffer; arm_fir_interpolate_instance_q15 m_modFilter; @@ -45,9 +47,11 @@ private: uint16_t m_poLen; uint16_t m_poPtr; uint16_t m_txDelay; + uint32_t m_txHang; + uint32_t m_txCount; void writeByte(uint8_t c); + void writeSilence(); }; #endif - diff --git a/P25TX.cpp b/P25TX.cpp index 9db1747..b3ee59c 100644 --- a/P25TX.cpp +++ b/P25TX.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2016,2017,2020 by Jonathan Naylor G4KLX * Copyright (C) 2017 by Andy Uribe CA6JAU * * This program is free software; you can redistribute it and/or modify @@ -51,7 +51,9 @@ m_lpState(), m_poBuffer(), m_poLen(0U), m_poPtr(0U), -m_txDelay(240U) // 200ms +m_txDelay(240U), // 200ms +m_txHang(6000U), // 5s +m_txCount(0U) { ::memset(m_modState, 0x00U, 16U * sizeof(q15_t)); ::memset(m_lpState, 0x00U, 60U * sizeof(q15_t)); @@ -68,7 +70,7 @@ m_txDelay(240U) // 200ms void CP25TX::process() { - if (m_buffer.getData() == 0U && m_poLen == 0U) + if (m_buffer.getData() == 0U && m_poLen == 0U && m_txCount == 0U) return; if (m_poLen == 0U) { @@ -94,13 +96,28 @@ void CP25TX::process() writeByte(c); space -= 4U * P25_RADIO_SYMBOL_LENGTH; - + if (m_duplex) + m_txCount = m_txHang; + if (m_poPtr >= m_poLen) { m_poPtr = 0U; m_poLen = 0U; return; } } + } else if (m_txCount > 0U) { + // Transmit silence until the hang timer has expired. + uint16_t space = io.getSpace(); + + while (space > (4U * P25_RADIO_SYMBOL_LENGTH)) { + writeSilence(); + + space -= 4U * P25_RADIO_SYMBOL_LENGTH; + m_txCount--; + + if (m_txCount == 0U) + return; + } } } @@ -152,6 +169,19 @@ void CP25TX::writeByte(uint8_t c) io.write(STATE_P25, outBuffer, P25_RADIO_SYMBOL_LENGTH * 4U); } +void CP25TX::writeSilence() +{ + q15_t inBuffer[4U] = {0x00U, 0x00U, 0x00U, 0x00U}; + q15_t intBuffer[P25_RADIO_SYMBOL_LENGTH * 4U]; + q15_t outBuffer[P25_RADIO_SYMBOL_LENGTH * 4U]; + + ::arm_fir_interpolate_q15(&m_modFilter, inBuffer, intBuffer, 4U); + + ::arm_fir_fast_q15(&m_lpFilter, intBuffer, outBuffer, P25_RADIO_SYMBOL_LENGTH * 4U); + + io.write(STATE_P25, outBuffer, P25_RADIO_SYMBOL_LENGTH * 4U); +} + void CP25TX::setTXDelay(uint8_t delay) { m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay @@ -164,3 +194,8 @@ uint8_t CP25TX::getSpace() const { return m_buffer.getSpace() / P25_LDU_FRAME_LENGTH_BYTES; } + +void CP25TX::setParams(uint8_t txHang) +{ + m_txHang = txHang * 1200U; +} diff --git a/P25TX.h b/P25TX.h index 1d542f9..a7cd27d 100644 --- a/P25TX.h +++ b/P25TX.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2016,2017,2020 by Jonathan Naylor G4KLX * * 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 @@ -35,6 +35,8 @@ public: uint8_t getSpace() const; + void setParams(uint8_t txHang); + private: CSerialRB m_buffer; arm_fir_interpolate_instance_q15 m_modFilter; @@ -45,9 +47,11 @@ private: uint16_t m_poLen; uint16_t m_poPtr; uint16_t m_txDelay; + uint32_t m_txHang; + uint32_t m_txCount; void writeByte(uint8_t c); + void writeSilence(); }; #endif - diff --git a/SerialPort.cpp b/SerialPort.cpp index 820694f..b30af58 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -264,7 +264,7 @@ void CSerialPort::getVersion() uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) { - if (length < 19U) + if (length < 21U) return 4U; bool rxInvert = (data[0U] & 0x01U) == 0x01U; @@ -331,6 +331,10 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) uint8_t fmTXLevel = data[18U]; + uint8_t p25TXHang = data[19U]; + + uint8_t nxdnTXHang = data[20U]; + setMode(modemState); m_dstarEnable = dstarEnable; @@ -356,6 +360,8 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) dmrIdleRX.setColorCode(colorCode); ysfTX.setParams(ysfLoDev, ysfTXHang); + p25TX.setParams(p25TXHang); + nxdnTX.setParams(nxdnTXHang); io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, fmTXLevel, txDCOffset, rxDCOffset);