From 77e0deee76d5a2a7cac9078cb1f767af541e125c Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 8 May 2020 09:01:00 +0200 Subject: [PATCH 01/13] Make whole FM chain RX level independant --- FM.cpp | 11 ++++++++--- FM.h | 1 + FMCTCSSRX.cpp | 23 ++++------------------- FMCTCSSRX.h | 5 +---- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/FM.cpp b/FM.cpp index 70b6b6a..ca09af9 100644 --- a/FM.cpp +++ b/FM.cpp @@ -46,7 +46,8 @@ m_blanking(), m_useCOS(true), m_cosInvert(false), m_rfAudioBoost(1U), -m_downsampler(128)//Size might need adjustement +m_downsampler(128),//Size might need adjustement +m_rxLevel(1) { } @@ -63,7 +64,8 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) uint8_t i = 0U; for (; i < length; i++) { - q15_t currentSample = samples[i];//save to a local variable to avoid indirection on every access + // 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); @@ -188,7 +190,9 @@ uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFreque m_timeoutTone.setParams(timeoutLevel); m_blanking.setParams(maxDev, timeoutLevel); - uint8_t ret = m_ctcssRX.setParams(ctcssFrequency, ctcssThreshold, rxLevel); + m_rxLevel = rxLevel; //q15_t(255)/q15_t(rxLevel >> 1); + + uint8_t ret = m_ctcssRX.setParams(ctcssFrequency, ctcssThreshold); if (ret != 0U) return ret; @@ -438,3 +442,4 @@ void CFM::beginRelaying() m_timeoutTimer.start(); m_ackMinTimer.start(); } + diff --git a/FM.h b/FM.h index 9e15c8a..fc528af 100644 --- a/FM.h +++ b/FM.h @@ -84,6 +84,7 @@ private: bool m_cosInvert; q15_t m_rfAudioBoost; CFMDownsampler m_downsampler; + q15_t m_rxLevel; void stateMachine(bool validSignal); void listeningState(bool validSignal); diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index ba0c03f..8b22fcd 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -86,15 +86,12 @@ m_threshold(0), m_count(0U), m_q0(0), m_q1(0), -m_result(CTS_NONE), -m_rxLevelInverse(1) +m_result(CTS_NONE) { } -uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold, uint8_t level) +uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) { - m_rxLevelInverse = q15Division(65535, q15_t(level * 128)); - m_coeffDivTwo = 0; for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) { @@ -114,7 +111,8 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold, uint8_t leve uint8_t CFMCTCSSRX::process(q15_t sample) { - q31_t sample31 = q31_t(sample) * m_rxLevelInverse; + //get more dynamic into the decoder by multiplying the sample by 1.5 + q31_t sample31 = q31_t(sample) + (q31_t(sample) >> 1); m_result &= ~CTS_READY; @@ -174,16 +172,3 @@ void CFMCTCSSRX::reset() m_result = CTS_NONE; m_count = 0U; } - -//Taken from https://en.wikipedia.org/wiki/Q_(number_format)#Division -q15_t CFMCTCSSRX::q15Division(q15_t a, q15_t divisor) -{ - q31_t a31 = q31_t(a) << 16; - - if (((a >> 31) & 1) == ((divisor >> 15) & 1)) - a31 += divisor >> 1; - else - a31 -= divisor >> 1; - - return a31 / divisor; -} diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index 2caa9e6..6b56ba4 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -34,7 +34,7 @@ class CFMCTCSSRX { public: CFMCTCSSRX(); - uint8_t setParams(uint8_t frequency, uint8_t threshold, uint8_t level); + uint8_t setParams(uint8_t frequency, uint8_t threshold); uint8_t process(q15_t sample); @@ -47,9 +47,6 @@ private: q31_t m_q0; q31_t m_q1; uint8_t m_result; - q15_t m_rxLevelInverse; - - q15_t q15Division(q15_t a, q15_t divisor); }; #endif From feff1ee2c7e91b91c489b2fd4ae986ac86225467 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 8 May 2020 09:04:28 +0200 Subject: [PATCH 02/13] remove emphasis --- FM.cpp | 7 +------ FM.h | 2 -- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/FM.cpp b/FM.cpp index ca09af9..c79a822 100644 --- a/FM.cpp +++ b/FM.cpp @@ -40,8 +40,6 @@ m_hangTimer(), 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), -m_preemphasis(32768, 13967, 0, 32768, -18801, 0),//75µS 24kHz sampling rate -m_deemphasis (32768, -18801, 0, 32768, 13967, 0),//75µS 24kHz sampling rate m_blanking(), m_useCOS(true), m_cosInvert(false), @@ -92,8 +90,7 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) } // Only let audio through when relaying audio - if (m_state == FS_RELAYING || m_state == FS_KERCHUNK) { - // currentSample = m_deemphasis.filter(currentSample); + if (m_state == FS_RELAYING || m_state == FS_KERCHUNK) { // m_downsampler.addSample(currentSample); currentSample = m_blanking.process(currentSample); currentSample *= m_rfAudioBoost; @@ -116,8 +113,6 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) currentSample = m_filterStage3.filter(m_filterStage2.filter(m_filterStage1.filter(currentSample))); - // currentSample = m_preemphasis.filter(currentSample); - currentSample += m_ctcssTX.getAudio(); samples[i] = currentSample; diff --git a/FM.h b/FM.h index fc528af..ef0f419 100644 --- a/FM.h +++ b/FM.h @@ -77,8 +77,6 @@ private: CFMDirectFormI m_filterStage1; CFMDirectFormI m_filterStage2; CFMDirectFormI m_filterStage3; - CFMDirectFormI m_preemphasis; - CFMDirectFormI m_deemphasis; CFMBlanking m_blanking; bool m_useCOS; bool m_cosInvert; From 9b0fed975c6e03ef035fcc438c03b7f7fd319440 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Fri, 8 May 2020 15:48:03 +0100 Subject: [PATCH 03/13] Buffer output after adding some initial silence. --- FM.cpp | 43 +++++++++++++++--- FM.h | 5 ++- FMDownsampleRB.h | 4 +- FMDownsampler.h | 2 +- FMRB.cpp | 110 +++++++++++++++++++++++++++++++++++++++++++++++ FMRB.h | 72 +++++++++++++++++++++++++++++++ SerialPort.cpp | 2 +- 7 files changed, 226 insertions(+), 12 deletions(-) create mode 100644 FMRB.cpp create mode 100644 FMRB.h diff --git a/FM.cpp b/FM.cpp index c79a822..5c882d0 100644 --- a/FM.cpp +++ b/FM.cpp @@ -44,12 +44,13 @@ m_blanking(), m_useCOS(true), m_cosInvert(false), m_rfAudioBoost(1U), -m_downsampler(128),//Size might need adjustement -m_rxLevel(1) +m_downsampler(128U),//Size might need adjustement +m_rxLevel(1), +m_outputRB(2400U) // 100ms of audio { } -void CFM::samples(bool cos, q15_t* samples, uint8_t length) +void CFM::samples(bool cos, const q15_t* samples, uint8_t length) { if (m_useCOS) { if (m_cosInvert) @@ -115,15 +116,32 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) currentSample += m_ctcssTX.getAudio(); - samples[i] = currentSample; + if (m_modemState == STATE_FM) + m_outputRB.put(currentSample); } - - if (m_modemState == STATE_FM) - io.write(STATE_FM, samples, i);//only write the actual number of processed samples to IO } void CFM::process() { + if (m_modemState != STATE_FM) + return; + + uint16_t length = m_outputRB.getData(); + if (length == 0U) + return; + + uint16_t space = io.getSpace(); + if (space == 0U) + return; + + if (space < length) + length = space; + + for (uint16_t i = 0U; i < length; i++) { + q15_t sample; + m_outputRB.get(sample); + io.write(STATE_FM, &sample, 1U); + } } void CFM::reset() @@ -141,6 +159,8 @@ void CFM::reset() m_rfAck.stop(); m_callsign.stop(); m_timeoutTone.stop(); + + m_outputRB.reset(); } uint8_t CFM::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) @@ -263,6 +283,8 @@ void CFM::listeningState(bool validSignal) sendCallsign(); } + insertSilence(50U); + beginRelaying(); m_callsignTimer.start(); @@ -438,3 +460,10 @@ void CFM::beginRelaying() m_ackMinTimer.start(); } +void CFM::insertSilence(uint16_t ms) +{ + uint32_t nSamples = ms * 24U; + + for (uint32_t i = 0U; i < nSamples; i++) + m_outputRB.put(0); +} diff --git a/FM.h b/FM.h index ef0f419..4901d6a 100644 --- a/FM.h +++ b/FM.h @@ -27,6 +27,7 @@ #include "FMTimeout.h" #include "FMKeyer.h" #include "FMTimer.h" +#include "FMRB.h" #include "FMDirectForm1.h" #include "FMDownsampler.h" @@ -47,7 +48,7 @@ class CFM { public: CFM(); - void samples(bool cos, q15_t* samples, uint8_t length); + void samples(bool cos, const q15_t* samples, uint8_t length); void process(); @@ -83,6 +84,7 @@ private: q15_t m_rfAudioBoost; CFMDownsampler m_downsampler; q15_t m_rxLevel; + CFMRB m_outputRB; void stateMachine(bool validSignal); void listeningState(bool validSignal); @@ -97,6 +99,7 @@ private: void sendCallsign(); void beginRelaying(); + void insertSilence(uint16_t ms); }; #endif diff --git a/FMDownsampleRB.h b/FMDownsampleRB.h index 6e8e466..df60daf 100644 --- a/FMDownsampleRB.h +++ b/FMDownsampleRB.h @@ -16,8 +16,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#if !defined(FMRB_H) -#define FMRB_H +#if !defined(FMDOWNSAMPLERB_H) +#define FMDOWNSAMPLERB_H #if defined(STM32F4XX) #include "stm32f4xx.h" diff --git a/FMDownsampler.h b/FMDownsampler.h index fd17e0a..48b0d44 100644 --- a/FMDownsampler.h +++ b/FMDownsampler.h @@ -40,4 +40,4 @@ private: uint8_t m_downSampleIndex; }; -#endif \ No newline at end of file +#endif diff --git a/FMRB.cpp b/FMRB.cpp new file mode 100644 index 0000000..0d8c6ef --- /dev/null +++ b/FMRB.cpp @@ -0,0 +1,110 @@ +/* +TX fifo control - Copyright (C) KI6ZUM 2015 +Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the +Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +Boston, MA 02110-1301, USA. +*/ + +#include "FMRB.h" + +CFMRB::CFMRB(uint16_t length) : +m_length(length), +m_head(0U), +m_tail(0U), +m_full(false), +m_overflow(false) +{ + m_samples = new q15_t[length]; +} + +uint16_t CFMRB::getSpace() const +{ + uint16_t n = 0U; + + if (m_tail == m_head) + n = m_full ? 0U : m_length; + else if (m_tail < m_head) + n = m_length - m_head + m_tail; + else + n = m_tail - m_head; + + if (n > m_length) + n = 0U; + + return n; +} + +uint16_t CFMRB::getData() const +{ + if (m_tail == m_head) + return m_full ? m_length : 0U; + else if (m_tail < m_head) + return m_head - m_tail; + else + return m_length - m_tail + m_head; +} + +bool CFMRB::put(q15_t sample) +{ + if (m_full) { + m_overflow = true; + return false; + } + + m_samples[m_head] = sample; + + m_head++; + if (m_head >= m_length) + m_head = 0U; + + if (m_head == m_tail) + m_full = true; + + return true; +} + +bool CFMRB::get(q15_t& sample) +{ + if (m_head == m_tail && !m_full) + return false; + + sample = m_samples[m_tail]; + + m_full = false; + + m_tail++; + if (m_tail >= m_length) + m_tail = 0U; + + return true; +} + +bool CFMRB::hasOverflowed() +{ + bool overflow = m_overflow; + + m_overflow = false; + + return overflow; +} + +void CFMRB::reset() +{ + m_head = 0U; + m_tail = 0U; + m_full = false; + m_overflow = false; +} diff --git a/FMRB.h b/FMRB.h new file mode 100644 index 0000000..7cf6cf4 --- /dev/null +++ b/FMRB.h @@ -0,0 +1,72 @@ +/* +Serial fifo control - Copyright (C) KI6ZUM 2015 +Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the +Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +Boston, MA 02110-1301, USA. +*/ + +#if !defined(FMRB_H) +#define FMRB_H + +#if defined(STM32F4XX) +#include "stm32f4xx.h" +#elif defined(STM32F7XX) +#include "stm32f7xx.h" +#elif defined(STM32F105xC) +#include "stm32f1xx.h" +#include +#else +#include +#endif + +#if defined(__SAM3X8E__) || defined(STM32F105xC) +#define ARM_MATH_CM3 +#elif defined(STM32F7XX) +#define ARM_MATH_CM7 +#elif defined(STM32F4XX) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) +#define ARM_MATH_CM4 +#else +#error "Unknown processor type" +#endif + +#include + +class CFMRB { +public: + CFMRB(uint16_t length); + + uint16_t getSpace() const; + + uint16_t getData() const; + + bool put(q15_t sample); + + bool get(q15_t& sample); + + bool hasOverflowed(); + + void reset(); + +private: + uint16_t m_length; + volatile q15_t* m_samples; + volatile uint16_t m_head; + volatile uint16_t m_tail; + volatile bool m_full; + bool m_overflow; +}; + +#endif diff --git a/SerialPort.cpp b/SerialPort.cpp index 711ba6e..69d691b 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -101,7 +101,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200506 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200508 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" From e30eb3adafb296840c1c4d32bfc4c6fbd517b21a Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Fri, 8 May 2020 17:18:26 +0100 Subject: [PATCH 04/13] Reduce the output buffer loading. --- FM.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/FM.cpp b/FM.cpp index 5c882d0..7695e81 100644 --- a/FM.cpp +++ b/FM.cpp @@ -131,9 +131,11 @@ void CFM::process() return; uint16_t space = io.getSpace(); - if (space == 0U) + if (space < 3U) return; + space -= 2U; + if (space < length) length = space; From fa29a27695d1b99208ae7b7add37814bc3951b51 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 9 May 2020 12:53:50 +0100 Subject: [PATCH 05/13] Ensure all output audio is flushed before leaving FM state. --- FM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FM.cpp b/FM.cpp index 7695e81..94bd693 100644 --- a/FM.cpp +++ b/FM.cpp @@ -245,7 +245,7 @@ void CFM::stateMachine(bool validSignal) } if (m_state == FS_LISTENING && m_modemState == STATE_FM) { - if (!m_callsign.isRunning() && !m_rfAck.isRunning()) { + if (!m_callsign.isRunning() && !m_rfAck.isRunning() && m_outputRB.getData() == 0U) { DEBUG1("Change to STATE_IDLE"); m_modemState = STATE_IDLE; m_callsignTimer.stop(); From 3bb7cde3223f448e74201b400a552043f14dfff5 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 9 May 2020 13:01:02 +0100 Subject: [PATCH 06/13] Add input audio delay to match the CTCSS decoder results. --- FM.cpp | 14 ++++++++++++++ FM.h | 3 +++ 2 files changed, 17 insertions(+) diff --git a/FM.cpp b/FM.cpp index 94bd693..66bd5af 100644 --- a/FM.cpp +++ b/FM.cpp @@ -46,8 +46,10 @@ m_cosInvert(false), m_rfAudioBoost(1U), m_downsampler(128U),//Size might need adjustement m_rxLevel(1), +m_inputRB(4800U), // 200ms of audio m_outputRB(2400U) // 100ms of audio { + insertDelay(100U); } void CFM::samples(bool cos, const q15_t* samples, uint8_t length) @@ -68,6 +70,10 @@ void CFM::samples(bool cos, const q15_t* samples, uint8_t length) uint8_t ctcssState = m_ctcssRX.process(currentSample); + // 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 continue; @@ -462,6 +468,14 @@ void CFM::beginRelaying() m_ackMinTimer.start(); } +void CFM::insertDelay(uint16_t ms) +{ + uint32_t nSamples = ms * 24U; + + for (uint32_t i = 0U; i < nSamples; i++) + m_inputRB.put(0); +} + void CFM::insertSilence(uint16_t ms) { uint32_t nSamples = ms * 24U; diff --git a/FM.h b/FM.h index 4901d6a..29887a8 100644 --- a/FM.h +++ b/FM.h @@ -84,6 +84,7 @@ private: q15_t m_rfAudioBoost; CFMDownsampler m_downsampler; q15_t m_rxLevel; + CFMRB m_inputRB; CFMRB m_outputRB; void stateMachine(bool validSignal); @@ -99,6 +100,8 @@ private: void sendCallsign(); void beginRelaying(); + + void insertDelay(uint16_t ms); void insertSilence(uint16_t ms); }; From a2518e99f270a96883f7e4852eb9bf33ae9fadf2 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 10 May 2020 17:29:17 +0200 Subject: [PATCH 07/13] Fix stuck on TX --- FM.cpp | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/FM.cpp b/FM.cpp index 66bd5af..6a14e45 100644 --- a/FM.cpp +++ b/FM.cpp @@ -129,25 +129,8 @@ void CFM::samples(bool cos, const q15_t* samples, uint8_t length) void CFM::process() { - if (m_modemState != STATE_FM) - return; - - uint16_t length = m_outputRB.getData(); - if (length == 0U) - return; - - uint16_t space = io.getSpace(); - if (space < 3U) - return; - - space -= 2U; - - if (space < length) - length = space; - - for (uint16_t i = 0U; i < length; i++) { - q15_t sample; - m_outputRB.get(sample); + q15_t sample; + while(io.getSpace() >= 3U && m_outputRB.get(sample)) { io.write(STATE_FM, &sample, 1U); } } @@ -251,7 +234,7 @@ void CFM::stateMachine(bool validSignal) } if (m_state == FS_LISTENING && m_modemState == STATE_FM) { - if (!m_callsign.isRunning() && !m_rfAck.isRunning() && m_outputRB.getData() == 0U) { + if (!m_callsign.isRunning() && !m_rfAck.isRunning()) { DEBUG1("Change to STATE_IDLE"); m_modemState = STATE_IDLE; m_callsignTimer.stop(); From 66ea3ad0d11cc6e705e2e32c0668bce109ed85fb Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 10 May 2020 17:15:54 +0100 Subject: [PATCH 08/13] Only use the CTCSS delay when not using COS. --- FM.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/FM.cpp b/FM.cpp index 6a14e45..6d2e0e3 100644 --- a/FM.cpp +++ b/FM.cpp @@ -70,9 +70,11 @@ void CFM::samples(bool cos, const q15_t* samples, uint8_t length) uint8_t ctcssState = m_ctcssRX.process(currentSample); - // Delay the audio by 100ms to better match the CTCSS detector output - m_inputRB.put(currentSample); - m_inputRB.get(currentSample); + if (!m_useCOS) { + // 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 @@ -130,9 +132,8 @@ void CFM::samples(bool cos, const q15_t* samples, uint8_t length) void CFM::process() { q15_t sample; - while(io.getSpace() >= 3U && m_outputRB.get(sample)) { + while(io.getSpace() >= 3U && m_outputRB.get(sample)) io.write(STATE_FM, &sample, 1U); - } } void CFM::reset() From d0062a55f85f474fc92e55343114b88f5c8bb767 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 10 May 2020 17:31:21 +0100 Subject: [PATCH 09/13] Rescale the FM calibration output. --- CalFM.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CalFM.cpp b/CalFM.cpp index 3f847cc..e89f0be 100644 --- a/CalFM.cpp +++ b/CalFM.cpp @@ -41,7 +41,7 @@ CCalFM::CCalFM() : m_frequency(0), m_length(0), m_tone(NULL), -m_level(128*32), +m_level(128 * 12), m_transmit(false), m_audioSeq(0), m_lastState(STATE_IDLE) @@ -56,25 +56,25 @@ void CCalFM::process() { switch (m_modemState) { case STATE_FMCAL10K: - m_frequency=956U; + m_frequency = 956U; break; case STATE_FMCAL12K: - m_frequency=1039U; + m_frequency = 1039U; break; case STATE_FMCAL15K: - m_frequency=1247U; + m_frequency = 1247U; break; case STATE_FMCAL20K: - m_frequency=1633U; + m_frequency = 1633U; break; case STATE_FMCAL25K: - m_frequency=2079U; + m_frequency = 2079U; break; case STATE_FMCAL30K: - m_frequency=2495U; + m_frequency = 2495U; break; default: - m_frequency=0; + m_frequency = 0U; break; } From 4cba5931f4e16f47a51e043da07f008cf0f8b830 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 10 May 2020 21:29:01 +0100 Subject: [PATCH 10/13] Loosen the conditions for returning to STATE_IDLE --- FM.cpp | 20 +++++++++----------- SerialPort.cpp | 2 +- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/FM.cpp b/FM.cpp index 6d2e0e3..543c497 100644 --- a/FM.cpp +++ b/FM.cpp @@ -132,7 +132,7 @@ void CFM::samples(bool cos, const q15_t* samples, uint8_t length) void CFM::process() { q15_t sample; - while(io.getSpace() >= 3U && m_outputRB.get(sample)) + while (io.getSpace() >= 3U && m_outputRB.get(sample)) io.write(STATE_FM, &sample, 1U); } @@ -235,16 +235,14 @@ void CFM::stateMachine(bool validSignal) } if (m_state == FS_LISTENING && m_modemState == STATE_FM) { - if (!m_callsign.isRunning() && !m_rfAck.isRunning()) { - DEBUG1("Change to STATE_IDLE"); - m_modemState = STATE_IDLE; - m_callsignTimer.stop(); - m_timeoutTimer.stop(); - m_kerchunkTimer.stop(); - m_ackMinTimer.stop(); - m_ackDelayTimer.stop(); - m_hangTimer.stop(); - } + DEBUG1("Change to STATE_IDLE"); + m_modemState = STATE_IDLE; + m_callsignTimer.stop(); + m_timeoutTimer.stop(); + m_kerchunkTimer.stop(); + m_ackMinTimer.stop(); + m_ackDelayTimer.stop(); + m_hangTimer.stop(); } } diff --git a/SerialPort.cpp b/SerialPort.cpp index 69d691b..4910c10 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -101,7 +101,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200508 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200510 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" From 17a3743cd8fb58524e43d4c3bcd54fd3bacec6e8 Mon Sep 17 00:00:00 2001 From: phl0 Date: Mon, 11 May 2020 14:27:23 +0200 Subject: [PATCH 11/13] Add GPIO and LEDs for FM mode --- IOSTM.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/IOSTM.cpp b/IOSTM.cpp index 1d565fe..1757237 100644 --- a/IOSTM.cpp +++ b/IOSTM.cpp @@ -547,6 +547,7 @@ YSF PC7 output P25 PC8 output NXDN PC9 output POCSAG PA8 output +FM PA11 output MDSTAR PC1 output MDMR PC2 output @@ -554,6 +555,7 @@ MYSF PC3 output MP25 PC4 output MNXDN PC10 output MPOCSAG PC11 output +MFM PC13 output RX PA0 analog input RSSI PA7 analog input @@ -590,6 +592,10 @@ EXT_CLK PA15 input #define PORT_POCSAG GPIOA #define RCC_Per_POCSAG RCC_AHB1Periph_GPIOA +#define PIN_FM GPIO_Pin_11 +#define PORT_FM GPIOA +#define RCC_Per_FM RCC_AHB1Periph_GPIOA + #define PIN_DSTAR GPIO_Pin_15 #define PORT_DSTAR GPIOB #define RCC_Per_DSTAR RCC_AHB1Periph_GPIOB @@ -626,6 +632,10 @@ EXT_CLK PA15 input #define PIN_MPOCSAG GPIO_Pin_11 #define PORT_MPOCSAG GPIOC #define RCC_Per_MPOCSAG RCC_AHB1Periph_GPIOC + +#define PIN_MFM GPIO_Pin_13 +#define PORT_MFM GPIOC +#define RCC_Per_MFM RCC_AHB1Periph_GPIOC #endif #define PIN_EXT_CLK GPIO_Pin_15 From 7ebc4044f9aa8fdb0dac2974fae3f59eccae4334 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 11 May 2020 13:41:10 +0100 Subject: [PATCH 12/13] Remove Coocox file. --- MMDVM_STM32F4xx.coproj | 340 ----------------------------------------- 1 file changed, 340 deletions(-) delete mode 100644 MMDVM_STM32F4xx.coproj diff --git a/MMDVM_STM32F4xx.coproj b/MMDVM_STM32F4xx.coproj deleted file mode 100644 index 4f1592a..0000000 --- a/MMDVM_STM32F4xx.coproj +++ /dev/null @@ -1,340 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 435544f7a1ec5b0e0c514792571d91107ecf420b Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Tue, 12 May 2020 13:55:30 +0100 Subject: [PATCH 13/13] Add CTCSS hysteresis. --- FM.cpp | 4 ++-- FM.h | 2 +- FMCTCSSRX.cpp | 17 ++++++++++++----- FMCTCSSRX.h | 5 +++-- SerialPort.cpp | 27 ++++++++++++++------------- 5 files changed, 32 insertions(+), 23 deletions(-) diff --git a/FM.cpp b/FM.cpp index 543c497..3bc0bbf 100644 --- a/FM.cpp +++ b/FM.cpp @@ -183,7 +183,7 @@ 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 ctcssThreshold, 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, bool useCOS, bool cosInvert, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel) { m_useCOS = useCOS; m_cosInvert = cosInvert; @@ -199,7 +199,7 @@ uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFreque m_rxLevel = rxLevel; //q15_t(255)/q15_t(rxLevel >> 1); - uint8_t ret = m_ctcssRX.setParams(ctcssFrequency, ctcssThreshold); + uint8_t ret = m_ctcssRX.setParams(ctcssFrequency, ctcssHighThreshold, ctcssLowThreshold); if (ret != 0U) return ret; diff --git a/FM.h b/FM.h index 29887a8..6411585 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 ctcssThreshold, 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, bool useCOS, bool cosInvert, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel); private: CFMKeyer m_callsign; diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 8b22fcd..e88ff98 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -82,7 +82,8 @@ const uint16_t N = 24000U / 4U; CFMCTCSSRX::CFMCTCSSRX() : m_coeffDivTwo(0), -m_threshold(0), +m_highThreshold(0), +m_lowThreshold(0), m_count(0U), m_q0(0), m_q1(0), @@ -90,7 +91,7 @@ m_result(CTS_NONE) { } -uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) +uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t highThreshold, uint8_t lowThreshold) { m_coeffDivTwo = 0; @@ -104,7 +105,8 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) if (m_coeffDivTwo == 0) return 4U; - m_threshold = q31_t(threshold); + m_highThreshold = q31_t(highThreshold); + m_lowThreshold = q31_t(lowThreshold); return 0U; } @@ -148,14 +150,19 @@ uint8_t CFMCTCSSRX::process(q15_t sample) q31_t value = t2 + t4 - t9; bool previousCTCSSValid = CTCSS_VALID(m_result); + + q31_t threshold = m_highThreshold; + if (previousCTCSSValid) + threshold = m_lowThreshold; + m_result |= CTS_READY; - if (value >= m_threshold) + if (value >= threshold) m_result |= CTS_VALID; else m_result &= ~CTS_VALID; if (previousCTCSSValid != CTCSS_VALID(m_result)) - DEBUG4("CTCSS Value / Threshold / Valid", value, m_threshold, CTCSS_VALID(m_result)); + DEBUG4("CTCSS Value / Threshold / Valid", value, threshold, CTCSS_VALID(m_result)); m_count = 0U; m_q0 = 0; diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index 6b56ba4..eed82de 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -34,7 +34,7 @@ class CFMCTCSSRX { public: CFMCTCSSRX(); - uint8_t setParams(uint8_t frequency, uint8_t threshold); + uint8_t setParams(uint8_t frequency, uint8_t highThreshold, uint8_t lowThreshold); uint8_t process(q15_t sample); @@ -42,7 +42,8 @@ public: private: q63_t m_coeffDivTwo; - q31_t m_threshold; + q31_t m_highThreshold; + q31_t m_lowThreshold; uint16_t m_count; q31_t m_q0; q31_t m_q1; diff --git a/SerialPort.cpp b/SerialPort.cpp index 4910c10..fe899ec 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -101,7 +101,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200510 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200512 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" @@ -409,27 +409,28 @@ uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length) uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) { - if (length < 11U) + if (length < 12U) return 4U; uint16_t timeout = data[0U] * 5U; uint8_t timeoutLevel = data[1U]; - uint8_t ctcssFrequency = data[2U]; - uint8_t ctcssThreshold = data[3U]; - uint8_t ctcssLevel = data[4U]; + uint8_t ctcssFrequency = data[2U]; + uint8_t ctcssHighThreshold = data[3U]; + uint8_t ctcssLowThreshold = data[4U]; + uint8_t ctcssLevel = data[5U]; - uint8_t kerchunkTime = data[5U]; - uint8_t hangTime = data[6U]; + uint8_t kerchunkTime = data[6U]; + uint8_t hangTime = data[7U]; - bool useCOS = (data[7U] & 0x01U) == 0x01U; - bool cosInvert = (data[7U] & 0x02U) == 0x02U; + bool useCOS = (data[8U] & 0x01U) == 0x01U; + bool cosInvert = (data[8U] & 0x02U) == 0x02U; - uint8_t rfAudioBoost = data[8U]; - uint8_t maxDev = data[9U]; - uint8_t rxLevel = data[10U]; + uint8_t rfAudioBoost = data[9U]; + uint8_t maxDev = data[10U]; + uint8_t rxLevel = data[11U]; - return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime, useCOS, cosInvert, rfAudioBoost, maxDev, rxLevel); + return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssHighThreshold, ctcssLowThreshold, ctcssLevel, kerchunkTime, hangTime, useCOS, cosInvert, rfAudioBoost, maxDev, rxLevel); } uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length)