From 20013e3adcb95cdbbfc3713f7355bf388bdd72d8 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 13 Jul 2020 17:13:01 +0100 Subject: [PATCH 1/3] Make Use COS As Lockout a run-time parameter and allow for different access modes. --- Config.h | 3 -- FM.cpp | 87 ++++++++++++++++++++++++++++++-------------------- FM.h | 4 +-- IO.cpp | 10 +++--- IO.h | 4 ++- SerialPort.cpp | 21 ++++++------ 6 files changed, 75 insertions(+), 54 deletions(-) diff --git a/Config.h b/Config.h index 73e716b..da79210 100644 --- a/Config.h +++ b/Config.h @@ -35,9 +35,6 @@ // For 19.2 MHz // #define EXTERNAL_OSC 19200000 -// Allow the use of the COS line to lockout the modem -// #define USE_COS_AS_LOCKOUT - // Use pins to output the current mode via LEDs // #define MODE_LEDS diff --git a/FM.cpp b/FM.cpp index 458fc28..e1d5f76 100644 --- a/FM.cpp +++ b/FM.cpp @@ -20,6 +20,14 @@ #include "Globals.h" #include "FM.h" +/* + * Access Mode values are: + * 0 - Carrier access with COS + * 1 - CTCSS only access without COS + * 2 - CTCSS only access with COS + * 3 - CTCSS only access with COS to start, then carrier access with COS + */ + CFM::CFM() : m_callsign(), m_rfAck(), @@ -41,7 +49,7 @@ m_filterStage1( 724, 1448, 724, 32768, -37895, 21352),//3rd order Cheby Fil m_filterStage2(32768, 0,-32768, 32768, -50339, 19052), m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460), m_blanking(), -m_useCOS(true), +m_accessMode(1U), m_cosInvert(false), m_rfAudioBoost(1U), m_downsampler(128U),//Size might need adjustement @@ -54,12 +62,8 @@ m_outputRB(2400U) // 100ms of audio void CFM::samples(bool cos, const q15_t* samples, uint8_t length) { - if (m_useCOS) { - if (m_cosInvert) - cos = !cos; - } else { - cos = true; - } + if (m_cosInvert) + cos = !cos; clock(length); @@ -70,33 +74,48 @@ void CFM::samples(bool cos, const q15_t* samples, uint8_t length) uint8_t ctcssState = m_ctcssRX.process(currentSample); - if (!m_useCOS) { - // Delay the audio by 100ms to better match the CTCSS detector output - m_inputRB.put(currentSample); - m_inputRB.get(currentSample); + switch (m_accessMode) { + case 0U: + stateMachine(cos); + break; + + case 1U: + // Delay the audio by 100ms to better match the CTCSS detector output + m_inputRB.put(currentSample); + m_inputRB.get(currentSample); + + if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { + // Not enough samples to determine if you have CTCSS, just carry on + } else { + bool validCTCSS = CTCSS_VALID(ctcssState); + stateMachine(validCTCSS); + } + break; + + case 2U: + if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { + // Not enough samples to determine if you have CTCSS, just carry on + } else { + bool validCTCSS = CTCSS_VALID(ctcssState); + stateMachine(validCTCSS && cos); + } + break; + + default: + if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { + // Not enough samples to determine if you have CTCSS, just carry on + } else if (CTCSS_READY(ctcssState) && m_modemState != STATE_FM) { + // We had enough samples for CTCSS and we are in some other mode than FM + bool validCTCSS = CTCSS_VALID(ctcssState); + stateMachine(validCTCSS && cos); + } else { + stateMachine(cos); + } + break; } - if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { - //Not enough samples to determine if you have CTCSS, just carry on + if (m_modemState != STATE_FM) continue; - } else if (CTCSS_READY(ctcssState) && m_modemState != STATE_FM) { - //we had enough samples for CTCSS and we are in some other mode than FM - bool validCTCSS = CTCSS_VALID(ctcssState); - stateMachine(validCTCSS && cos); - if (m_modemState != STATE_FM) - continue; - } else if (CTCSS_READY(ctcssState) && m_modemState == STATE_FM) { - //We had enough samples for CTCSS and we are in FM mode, trigger the state machine - bool validCTCSS = CTCSS_VALID(ctcssState); - stateMachine(validCTCSS && cos); - if (m_modemState != STATE_FM) - break; - } else if (CTCSS_NOT_READY(ctcssState) && m_modemState == STATE_FM && i == length - 1) { - //Not enough samples for CTCSS but we already are in FM, trigger the state machine - //but do not trigger the state machine on every single sample, save CPU! - bool validCTCSS = CTCSS_VALID(ctcssState); - stateMachine(validCTCSS && cos); - } // Only let audio through when relaying audio if (m_state == FS_RELAYING || m_state == FS_KERCHUNK) { @@ -183,10 +202,10 @@ uint8_t CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_ return m_rfAck.setParams(rfAck, speed, frequency, level, level); } -uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssHighThreshold, uint8_t ctcssLowThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, bool cosInvert, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel) +uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssHighThreshold, uint8_t ctcssLowThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, uint8_t accessMode, bool cosInvert, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel) { - m_useCOS = useCOS; - m_cosInvert = cosInvert; + m_accessMode = accessMode; + m_cosInvert = cosInvert; m_rfAudioBoost = q15_t(rfAudioBoost); diff --git a/FM.h b/FM.h index 6411585..3168dc3 100644 --- a/FM.h +++ b/FM.h @@ -56,7 +56,7 @@ public: uint8_t setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch); uint8_t setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level); - uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssHighThreshold, uint8_t ctcssLowThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, bool cosInvert, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel); + uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssHighThreshold, uint8_t ctcssLowThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, uint8_t accessMode, bool cosInvert, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel); private: CFMKeyer m_callsign; @@ -79,7 +79,7 @@ private: CFMDirectFormI m_filterStage2; CFMDirectFormI m_filterStage3; CFMBlanking m_blanking; - bool m_useCOS; + uint8_t m_accessMode; bool m_cosInvert; q15_t m_rfAudioBoost; CFMDownsampler m_downsampler; diff --git a/IO.cpp b/IO.cpp index a737693..c36b6a9 100644 --- a/IO.cpp +++ b/IO.cpp @@ -83,6 +83,7 @@ m_pocsagTXLevel(128 * 128), m_fmTXLevel(128 * 128), m_rxDCOffset(DC_OFFSET), m_txDCOffset(DC_OFFSET), +m_useCOSAsLockout(false), m_ledCount(0U), m_ledValue(true), m_detect(false), @@ -256,9 +257,8 @@ void CIO::process() return; } -#if defined(USE_COS_AS_LOCKOUT) - m_lockout = getCOSInt(); -#endif + if (m_useCOSAsLockout) + m_lockout = getCOSInt(); // Switch off the transmitter if needed if (m_txBuffer.getData() == 0U && m_tx) { @@ -529,7 +529,7 @@ void CIO::setMode() #endif } -void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, uint8_t fmTXLevel, int16_t txDCOffset, int16_t rxDCOffset) +void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, uint8_t fmTXLevel, int16_t txDCOffset, int16_t rxDCOffset, bool useCOSAsLockout) { m_pttInvert = pttInvert; @@ -545,6 +545,8 @@ void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rx m_rxDCOffset = DC_OFFSET + rxDCOffset; m_txDCOffset = DC_OFFSET + txDCOffset; + + m_useCOSAsLockout = useCOSAsLockout; if (rxInvert) m_rxLevel = -m_rxLevel; diff --git a/IO.h b/IO.h index 07ed700..4a8231f 100644 --- a/IO.h +++ b/IO.h @@ -42,7 +42,7 @@ public: void interrupt(); - void setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, uint8_t fmTXLevel, int16_t txDCOffset, int16_t rxDCOffset); + void setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, uint8_t fmTXLevel, int16_t txDCOffset, int16_t rxDCOffset, bool useCOSAsLockout); void getOverflow(bool& adcOverflow, bool& dacOverflow); @@ -91,6 +91,8 @@ private: uint16_t m_rxDCOffset; uint16_t m_txDCOffset; + bool m_useCOSAsLockout; + uint32_t m_ledCount; bool m_ledValue; diff --git a/SerialPort.cpp b/SerialPort.cpp index 4e22028..171f460 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -103,7 +103,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200712 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200713 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" @@ -267,11 +267,12 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) if (length < 21U) return 4U; - bool rxInvert = (data[0U] & 0x01U) == 0x01U; - bool txInvert = (data[0U] & 0x02U) == 0x02U; - bool pttInvert = (data[0U] & 0x04U) == 0x04U; - bool ysfLoDev = (data[0U] & 0x08U) == 0x08U; - bool simplex = (data[0U] & 0x80U) == 0x80U; + bool rxInvert = (data[0U] & 0x01U) == 0x01U; + bool txInvert = (data[0U] & 0x02U) == 0x02U; + bool pttInvert = (data[0U] & 0x04U) == 0x04U; + bool ysfLoDev = (data[0U] & 0x08U) == 0x08U; + bool useCOSAsLockout = (data[0U] & 0x20U) == 0x20U; + bool simplex = (data[0U] & 0x80U) == 0x80U; m_debug = (data[0U] & 0x10U) == 0x10U; @@ -363,7 +364,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) p25TX.setParams(p25TXHang); nxdnTX.setParams(nxdnTXHang); - io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, fmTXLevel, txDCOffset, rxDCOffset); + io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, fmTXLevel, txDCOffset, rxDCOffset, useCOSAsLockout); io.start(); @@ -431,14 +432,14 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) uint8_t kerchunkTime = data[6U]; uint8_t hangTime = data[7U]; - bool useCOS = (data[8U] & 0x01U) == 0x01U; - bool cosInvert = (data[8U] & 0x02U) == 0x02U; + uint8_t accessMode = data[8U] & 0x7FU; + bool cosInvert = (data[8U] & 0x80U) == 0x80U; uint8_t rfAudioBoost = data[9U]; uint8_t maxDev = data[10U]; uint8_t rxLevel = data[11U]; - return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssHighThreshold, ctcssLowThreshold, ctcssLevel, kerchunkTime, hangTime, useCOS, cosInvert, rfAudioBoost, maxDev, rxLevel); + return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssHighThreshold, ctcssLowThreshold, ctcssLevel, kerchunkTime, hangTime, accessMode, cosInvert, rfAudioBoost, maxDev, rxLevel); } uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length) From dc41fd83d2352f7de790982cbad698c7f59088d3 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Tue, 14 Jul 2020 12:20:42 +0100 Subject: [PATCH 2/3] Test the use of a reverse burst at the end of a transmission. --- FM.cpp | 17 +++++++++++++---- FM.h | 1 + FMCTCSSTX.cpp | 9 ++++++--- FMCTCSSTX.h | 2 +- SerialPort.cpp | 2 +- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/FM.cpp b/FM.cpp index e1d5f76..1c98d90 100644 --- a/FM.cpp +++ b/FM.cpp @@ -45,6 +45,7 @@ m_kerchunkTimer(), m_ackMinTimer(), m_ackDelayTimer(), m_hangTimer(), +m_reverseTimer(), m_filterStage1( 724, 1448, 724, 32768, -37895, 21352),//3rd order Cheby Filter 300 to 2700Hz, 0.2dB passband ripple, sampling rate 24kHz m_filterStage2(32768, 0,-32768, 32768, -50339, 19052), m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460), @@ -57,6 +58,8 @@ m_rxLevel(1), m_inputRB(4800U), // 200ms of audio m_outputRB(2400U) // 100ms of audio { + m_reverseTimer.setTimeout(0U, 150U); + insertDelay(100U); } @@ -141,10 +144,9 @@ void CFM::samples(bool cos, const q15_t* samples, uint8_t length) if (!m_callsign.isRunning() && !m_rfAck.isRunning()) currentSample += m_timeoutTone.getAudio(); - currentSample += m_ctcssTX.getAudio(); + currentSample += m_ctcssTX.getAudio(m_reverseTimer.isRunning()); - if (m_modemState == STATE_FM) - m_outputRB.put(currentSample); + m_outputRB.put(currentSample); } } @@ -165,6 +167,7 @@ void CFM::reset() m_ackMinTimer.stop(); m_ackDelayTimer.stop(); m_hangTimer.stop(); + m_reverseTimer.stop(); m_ctcssRX.reset(); m_rfAck.stop(); @@ -254,7 +257,10 @@ void CFM::stateMachine(bool validSignal) } if (m_state == FS_LISTENING && m_modemState == STATE_FM) { - if (!m_callsign.isWanted() && !m_rfAck.isWanted()) { + if (!m_callsign.isWanted() && !m_rfAck.isWanted() && !m_reverseTimer.isRunning()) + m_reverseTimer.start(); + + if (!m_callsign.isWanted() && !m_rfAck.isWanted() && m_reverseTimer.isRunning() && m_reverseTimer.hasExpired()) { DEBUG1("Change to STATE_IDLE"); m_modemState = STATE_IDLE; m_callsignTimer.stop(); @@ -263,6 +269,7 @@ void CFM::stateMachine(bool validSignal) m_ackMinTimer.stop(); m_ackDelayTimer.stop(); m_hangTimer.stop(); + m_reverseTimer.stop(); } } } @@ -276,6 +283,7 @@ void CFM::clock(uint8_t length) m_ackMinTimer.clock(length); m_ackDelayTimer.clock(length); m_hangTimer.clock(length); + m_reverseTimer.clock(length); } void CFM::listeningState(bool validSignal) @@ -299,6 +307,7 @@ void CFM::listeningState(bool validSignal) beginRelaying(); m_callsignTimer.start(); + m_reverseTimer.stop(); io.setDecode(true); io.setADCDetection(true); diff --git a/FM.h b/FM.h index 3168dc3..6843f86 100644 --- a/FM.h +++ b/FM.h @@ -75,6 +75,7 @@ private: CFMTimer m_ackMinTimer; CFMTimer m_ackDelayTimer; CFMTimer m_hangTimer; + CFMTimer m_reverseTimer; CFMDirectFormI m_filterStage1; CFMDirectFormI m_filterStage2; CFMDirectFormI m_filterStage3; diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index fcffbb5..a2253a6 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -115,11 +115,14 @@ uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) return 0U; } -q15_t CFMCTCSSTX::getAudio() +q15_t CFMCTCSSTX::getAudio(bool reverse) { q15_t sample = m_values[m_n++]; - if(m_n >= m_length) + if (m_n >= m_length) m_n = 0U; - return sample; + if (reverse) + return -sample; + else + return sample; } diff --git a/FMCTCSSTX.h b/FMCTCSSTX.h index 0c3e6e5..b6f9b12 100644 --- a/FMCTCSSTX.h +++ b/FMCTCSSTX.h @@ -27,7 +27,7 @@ public: uint8_t setParams(uint8_t frequency, uint8_t level); - q15_t getAudio(); + q15_t getAudio(bool reverse); private: q15_t* m_values; diff --git a/SerialPort.cpp b/SerialPort.cpp index 171f460..00380d8 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -103,7 +103,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200713 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200714 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" From 1ac0628c1c745e6d006783a32873a5d75cd5c7c7 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Tue, 14 Jul 2020 14:54:47 +0100 Subject: [PATCH 3/3] Improve the code efficiency slightly. --- FM.cpp | 57 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/FM.cpp b/FM.cpp index 1c98d90..40934bf 100644 --- a/FM.cpp +++ b/FM.cpp @@ -75,44 +75,49 @@ void CFM::samples(bool cos, const q15_t* samples, uint8_t length) // ARMv7-M has hardware integer division q15_t currentSample = q15_t((q31_t(samples[i]) << 8) / m_rxLevel); - uint8_t ctcssState = m_ctcssRX.process(currentSample); - switch (m_accessMode) { case 0U: stateMachine(cos); break; - case 1U: - // Delay the audio by 100ms to better match the CTCSS detector output - m_inputRB.put(currentSample); - m_inputRB.get(currentSample); + case 1U: { + uint8_t ctcssState = m_ctcssRX.process(currentSample); - if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { - // Not enough samples to determine if you have CTCSS, just carry on - } else { - bool validCTCSS = CTCSS_VALID(ctcssState); - stateMachine(validCTCSS); + // Delay the audio by 100ms to better match the CTCSS detector output + m_inputRB.put(currentSample); + m_inputRB.get(currentSample); + + if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { + // Not enough samples to determine if you have CTCSS, just carry on + } else { + bool validCTCSS = CTCSS_VALID(ctcssState); + stateMachine(validCTCSS); + } } break; - case 2U: - if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { - // Not enough samples to determine if you have CTCSS, just carry on - } else { - bool validCTCSS = CTCSS_VALID(ctcssState); - stateMachine(validCTCSS && cos); + case 2U: { + uint8_t ctcssState = m_ctcssRX.process(currentSample); + if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { + // Not enough samples to determine if you have CTCSS, just carry on + } else { + bool validCTCSS = CTCSS_VALID(ctcssState); + stateMachine(validCTCSS && cos); + } } break; - default: - if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { - // Not enough samples to determine if you have CTCSS, just carry on - } else if (CTCSS_READY(ctcssState) && m_modemState != STATE_FM) { - // We had enough samples for CTCSS and we are in some other mode than FM - bool validCTCSS = CTCSS_VALID(ctcssState); - stateMachine(validCTCSS && cos); - } else { - stateMachine(cos); + default: { + uint8_t ctcssState = m_ctcssRX.process(currentSample); + if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { + // Not enough samples to determine if you have CTCSS, just carry on + } else if (CTCSS_READY(ctcssState) && m_modemState != STATE_FM) { + // We had enough samples for CTCSS and we are in some other mode than FM + bool validCTCSS = CTCSS_VALID(ctcssState); + stateMachine(validCTCSS && cos); + } else { + stateMachine(cos); + } } break; }